You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
On compilation the Module.get_attribute/3 function returns nil instead of the provided default value in certain circumstances. The bug was introduced in 1.15.0 and is still present in 1.16.2.
Output example on Elixir 1.14.5 and earlier (the correct behavior):
$ asdf global elixir 1.14.5-otp-25
$ mix compile --force
==> protocol_ex
Compiling 2 files (.ex)
Generated protocol_ex app
==> attr_bug
Compiling 2 files (.ex)
module: JsonDecodable.Customer
This should never be nil: %{}
module: JsonDecodable.Customer
This should never be nil: %{id: "id"}
module: JsonDecodable.Customer
This should never be nil: %{id: "id", name: "first_name"}
Generated attr_bug app
Output example on Elixir 1.15+ (the incorrect behavior):
$ asdf global elixir 1.15.7-otp-25
$ mix compile --force
==> protocol_ex
Compiling 2 files (.ex)
Generated protocol_ex app
==> attr_bug
Compiling 2 files (.ex)
module: JsonDecodable.Customer
This should never be nil: nil
== Compilation error in file lib/attr_bug.ex ==
** (BadMapError) expected a map, got: nil
lib/attr_bug.ex:12: (module)
(protocol_ex 0.4.4) lib/protocol_ex.ex:267: ProtocolEx.defimplEx_do/6
lib/attr_bug.ex:11: (file)
To reproduce the bug, create a new Elixir project with the following files (unfortunately, Elixir complains if trying to make a single file demo app):
mix.exs:
defmoduleAttrBug.MixProjectdouseMix.Projectdefprojectdo[app: :attr_bug,version: "0.1.0",elixir: "~> 1.14",start_permanent: Mix.env()==:prod,deps: deps()]end# Run "mix help compile.app" to learn about applications.defapplicationdo[extra_applications: [:logger]]end# Run "mix help deps" to learn about dependencies.defpdepsdo[{:protocol_ex,"~> 0.4.0"}]endend
lib/jsoned.ex:
importProtocolExdefprotocol_exJsonDecodabledodefdecode(json_decodable)enddefprotocol_exJsonDecodable.Fordodefdecodable_impl(for)doraiseProtocol.UndefinedError,protocol: __MODULE__,value: forenddefnew_for!(for,args)dofor.new!(args)endenddefmoduleJsoned.Decoderdodefmacro__using__(opts)dofor=opts[:for]impl=__CALLER__.modulequotedodefdecode({for,json})whenis_map(json)oris_list(json)do# Jsoned.Decoder.decode( { for, json}, unquote( impl))enddefoverridabledecode: 1importProtocolExdefimpl_exJsonedDecoder,unquote(for),for: JsonDecodable.Fordodefdecodable_impl(unquote(for))dounquote(impl)endendendenddefmacrodefdecode(key,type,opts)dojson_key="#{opts[:key]||key}"quotedodefdecode_property({unquote(json_key),value})dowith{:ok,value}<-Jsoned.Decoder.decode_value(unquote(type),value)do{:ok,{unquote(key),value}}endendendenddefdecode_value(_,nil),do: {:ok,nil}defdecode_value(:string,""<>data),do: {:ok,data}enddefmoduleJsoneddodefmacro__using__(opts)doquotedodef__json_keys__()doJsoned.json_keys(__MODULE__)enduseJsoned.Decoder,unquote(opts)importJsoned.Decoder,only: [defdecode: 3]Module.register_attribute(__MODULE__,:jsoned,persist: true)endenddefjson_keys(impl)whenis_atom(impl)doifattrs=impl.__info__(:attributes)[:jsoned]doList.first(attrs)||%{}endenddefmacrodefjsoned(opts\\[],[do: body])doKeyword.validate!(opts,[:for])opts=Keyword.put_new(opts,:for,__CALLER__.module)for=opts[:for]unlessfordoraiseArgumentError,"defjsoned/2 expects a :for option when declared outside a module"endquotedoimportProtocolExdefimpl_exunquote(for),{unquote(for),data}whenis_map(data)oris_list(data),for: JsonDecodabledouseJsoned,unquote(opts)unquote(body)endendenddefmacrodefprop(key,type,opts\\[])whenis_atom(key)andis_list(opts)dodo_defprop(key,type,opts)enddefpdo_defprop(key,type,opts)dojson_key="#{opts[:key]||key}"quotedo@jsoned__MODULE__|>IO.inspect(label: "module")|>Module.get_attribute(:jsoned,%{})|>IO.inspect(label: "This should never be nil")|>Map.put(unquote(key),unquote(json_key))defdecode(unquote(key),unquote(type),unquote(opts))endendend
Elixir and Erlang/OTP versions
Erlang/OTP 25 [erts-13.2.2.9] [source] [64-bit] [smp:24:24] [ds:24:24:10] [async-threads:1] [jit:ns]
Elixir 1.15.7 (compiled with Erlang/OTP 25)
Operating system
Linux Mint
Current behavior
On compilation the
Module.get_attribute/3
function returnsnil
instead of the provided default value in certain circumstances. The bug was introduced in 1.15.0 and is still present in 1.16.2.Output example on Elixir 1.14.5 and earlier (the correct behavior):
Output example on Elixir 1.15+ (the incorrect behavior):
To reproduce the bug, create a new Elixir project with the following files (unfortunately, Elixir complains if trying to make a single file demo app):
mix.exs
:lib/jsoned.ex
:lib/attr_bug.ex
:Expected behavior
The
Module.get_attribute/3
should continue to behave exactly as it used to prior to Elixir 1.15.0.The text was updated successfully, but these errors were encountered: