elixir-lang / elixir

Elixir is a dynamic, functional language for building scalable and maintainable applications
https://elixir-lang.org/
Apache License 2.0
24.52k stars 3.38k forks source link

Formatter: unexpected formatting when using a module atrribute #9918

Closed pdgonzalez872 closed 4 years ago

pdgonzalez872 commented 4 years ago

Environment

Elixir 1.9.1 (compiled with Erlang/OTP 20)


* Operating system: OSX

### Current behavior

The formatter treats the arity (`/2`) of a function that is piped to another
function as a first argument as a division operator and a value to be
divided. Below we have an example of the issue. The function in question is
the `call/1`. This is the one that gets formatted in a way that we no longer
can compile the code.

without piping twice:

def call(args) do function_a(&@implementation.make_call/2, [args, %{}]) |> function_b() end

attempt to pipe, due to Credo raw value check/suggestion:

def call(args) do &@implementation.make_call/2 |> function_a([args, %{}]) |> function_b() end

Run mix format, this is the output:

def call(args) do &((@implementation.make_call / 2) |> function_a([args, %{}]) |> function_b()) end

try mix compile:

== Compilation error in file lib/formatter_issue.ex == ** (CompileError) lib/formatter_issue.ex:31: invalid args for &, expected an expression in the format of &Mod.fun/arity, &local/arity or a capture containing at least one argument as &1, got: @implementation.make_call() / 2 |> function_a([args, %{}]) |> function_b()

"fix it", then run mix format, output:

def call(args) do fun = &@implementation.make_call/2 fun |> function_a([args, %{}]) |> function_b() end



Here is a sample project: https://github.com/pdgonzalez872/formatter_issue , more specifically, here is a file with enough to cause the bug: https://github.com/pdgonzalez872/formatter_issue/blob/master/lib/formatter_issue.ex

### Expected behavior

Project would compile when we attempt to pipe `&@implementation.make_call/2` to a function and run the formatter.

Please let me know if you need more info. Thank you for your work, Elixir is ❤️.
josevalim commented 4 years ago

This is not a formatter bug, the formatter is actually revealing the bug in your code! |> has higher precedence than &, which means that:

&@implementation.make_call/2 |> function_a([args, %{}]) |> function_b()

Is actually the same as:

&(@implementation.make_call/2 |> function_a([args, %{}]) |> function_b())

What you want is to write this:

(&@implementation.make_call/2)
|> function_a([args, %{}])
|> function_b()

And everything should be good!

In any case, thank you for the great report and the nice words! Have a great weekend! :heart:

pdgonzalez872 commented 4 years ago

Ahh, awesome! Thank you @josevalim !