We now warn:
* `expr.field` when `expr` may not be a map
* `expr.call()` when `expr` may not be an atom
* `expr.call(...)` when `expr` may not be an atom
* `&expr.foo/1` when `expr` may not be an atom
Furthermore, we lay down the ground work for checking undefined
and deprecation warnings across unions. For example, if you write
this code:
mod = if something?, do: Foo, else: Bar
mod.some_function()
In the future, it will warn if any of Foo OR Bar do not define the relevant function.
Finally, we improve pretty printing of maps and aliases in types.
Ideally we want to list them as requires but we don't have
the infrastructure to do so. So meanwhile, we list them
as exports, which is the same level used by require.
Previously, the internal Elixir expansion pass worked directly on
Macro.Env. However, this poised an issue, if we want to track
more information in the pass, we ended up exposing it on
Macro.Env, making it larger, and potentially slowing down
operations such as __ENV__ serialization.
This commit refactors the expansion pass to work with two
structures, the Macro.Env struct and a #elixir_ex{} record.
The Elixir compiler spawns a separate process per file.
When a file has to wait on another module, Elixir tracks
in the compiler that the file is waiting.
However, every time a module is defined, the Elixir compiler
spawns a separate process to compile to .beam, and this
process may expand structs in the typespec. Since this
new process is no longer the original file process, Elixir
was not able to track its waiting time.
This PR address this issue by passing the original file_pid
to the .beam compiler process. Note though that, if we
change typespecs to be compiled in the original file process,
this change is no longer required, but at the moment there are
no plans to make such change.
Closes#11036.
This feature allows IDEs and other tools wanting to
perform source code analysis to do so reliably without
a need to reimplement Elixir's compiler expansion and
without relying on Elixir's private APIs.
This commit also adds :parser_options to compiler
options, which allows developers to combine both options
to retrieve more accurate information, such as columns.
This is important when you may want to check if a module
is available but if that module is running a similar check
on you, you don't want the compiler to fail.
This change fixes inconsistencies when building structs: if the
struct was built using the map syntax the `:__struct__` key
was overridden by the given module name; however, if the struct
was built using `struct/1,2` the `:__struct__` key was kept.
For example:
defmodule X do
defstruct [:x]
end
defmodule Y do
defdelegate __struct__, to: X
defdelegate __struct__(args), to: X
end
iex> %Y{}
%Y{x: nil}
iex> struct(Y)
%X{x: nil}
Closes#8800
__struct__/0,1 is not meant to return an AST. Using
Macro.to_string/1 might lead to incorrect error messages:
defmodule MyStruct do
def __struct__, do: {:ok, :one, :two}
def __struct__(_), do: {:ok, :one, :two}
end
iex> %MyStruct{}
** (CompileError) iex:2: expected MyStruct.__struct__/1 to
return a map with a :__struct__ key that holds the name
of the struct (atom), got: ok
Prior to this patch, if a module depends on the struct of
another module and that other module depends on the struct
of the first module inside typespecs, it would lead to a
compiler deadlock, which this deadlock would not happen
in "regular code".
This commit also adds a test to ensure @enforce_keys are
not enforced when expanding typespecs.
For now, we are using a compiler private API, but we should
provide a public version for it in a future commit.
Optimizes :maps.put/3 to a VM operation instead of a remote call, including nested calls to it.
It does the same for :maps.merge/2 with a simple map on the right side.
Closes#7353
We use this option in ExUnit as we are escaping
the code to eventually convert it to a string
representation and therefore the metadata is not
relevant.
This is a first step to limiting recompilations in case a struct is used in a
module, the "parent module" of the struct changes, but struct definition itself
does not change.
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
Until now, "%x{__struct__: y}" would ignore the ":__struct__" part and only
use/match on "x", without warnings. With this commit, a warning is emitted in
this case and in the respective case when updating ("%SomeStruct{x | __struct__:
y}").