@@ -328,12 +328,9 @@ defmodule Gradient.AstSpecifier do
328328 def mapper ( { :call , anno , name , args } , tokens , opts ) do
329329 # anno has correct line
330330 { :ok , _ , anno , opts , _ } = get_line ( anno , opts )
331-
332331 name = remote_mapper ( name )
333332
334- { opts , args } = call_with_pipe_op ( tokens , args , opts )
335-
336- { args , tokens } = context_mapper_fold ( args , tokens , opts )
333+ { args , tokens } = call_args_mapper ( args , tokens , name , opts )
337334
338335 { :call , anno , name , args }
339336 |> pass_tokens ( tokens )
@@ -388,8 +385,7 @@ defmodule Gradient.AstSpecifier do
388385 end
389386
390387 def mapper ( { type , anno , value } , tokens , opts )
391- when type in [ :atom , :char , :float , :integer , :string , :bin ] do
392- # TODO check what happened for :string
388+ when type in @ lineless_forms do
393389 { :ok , line } = Keyword . fetch ( opts , :line )
394390 anno = :erl_anno . set_line ( line , anno )
395391 anno = :erl_anno . set_generated ( Keyword . get ( opts , :generated , false ) , anno )
@@ -594,6 +590,29 @@ defmodule Gradient.AstSpecifier do
594590 end
595591 end
596592
593+ @ doc """
594+ Update location in call args with the support to the pipe operator.
595+ """
596+ @ spec call_args_mapper ( [ abstract_expr ( ) ] , tokens ( ) , abstract_expr ( ) , options ( ) ) ::
597+ { options , [ abstract_expr ] }
598+ def call_args_mapper ( args , tokens , name , opts ) do
599+ # Check whether the call is after |> operator. If true, the parent location is set to 0
600+ # and the first arg location is cleared (if this arg is a lineless form).
601+ # NOTE If the call is to function from :erlang module then the first arg is swapped
602+ # with the second one because in Erlang the data is mostly in the second place.
603+ with true <- is_pipe_op? ( tokens , opts ) ,
604+ swapped? <- is_call_to_erlang? ( name ) ,
605+ [ fst_arg | tail_args ] <- maybe_swap_args ( swapped? , args ) ,
606+ true <- is_lineless? ( fst_arg ) do
607+ { arg , tokens } = mapper ( clear_location ( fst_arg ) , tokens , Keyword . put ( opts , :line , 0 ) )
608+ { args , tokens } = context_mapper_fold ( tail_args , tokens , opts )
609+ { maybe_swap_args ( swapped? , [ arg | args ] ) , tokens }
610+ else
611+ _ ->
612+ context_mapper_fold ( args , tokens , opts )
613+ end
614+ end
615+
597616 # Private Helpers
598617
599618 @ spec match_token_to_form ( token ( ) , form ( ) ) :: boolean ( )
@@ -790,32 +809,22 @@ defmodule Gradient.AstSpecifier do
790809 { form , tokens }
791810 end
792811
793- @ spec call_with_pipe_op ( tokens ( ) , [ abstract_expr ( ) ] , options ( ) ) :: { options , [ abstract_expr ] }
794- def call_with_pipe_op ( tokens , args , opts ) do
795- # Check whether the call is after |> operator. If true, the parent location is set to 0
796- # and the first arg location is cleared (if this arg is a lineless form).
797- # Clearing the location is required only for Elixir 1.13 or newer because from this version
798- # the missing locations are specified, unfortunately sometimes not precise enough.
799- { :ok , line } = Keyword . fetch ( opts , :line )
800-
801- case { List . first ( drop_tokens_to_line ( tokens , line ) ) , is_first_arg_lineless? ( args ) } do
802- { { :arrow_op , _loc , :|> } , true } ->
803- { Keyword . put ( opts , :line , 0 ) , clear_first_arg_location ( args ) }
804-
805- _ ->
806- { opts , args }
812+ defp is_pipe_op? ( tokens , opts ) do
813+ case List . first ( drop_tokens_to_line ( tokens , Keyword . fetch! ( opts , :line ) ) ) do
814+ { :arrow_op , _ , :|> } -> true
815+ _ -> false
807816 end
808817 end
809818
810- def is_first_arg_lineless? ( [ form | _ ] ) , do: is_lineless_form? ( form )
811- def is_first_arg_lineless? ( [ ] ) , do: false
819+ defp maybe_swap_args ( true , [ fst , snd | t ] ) , do: [ snd , fst | t ]
820+ defp maybe_swap_args ( _ , args ) , do: args
812821
813- def is_lineless_form? ( form ) do
814- elem ( form , 0 ) in @ lineless_forms
815- end
822+ defp is_call_to_erlang? ( { :remote , _ , { :atom , _ , :erlang } , _ } ) , do: true
823+ defp is_call_to_erlang? ( _ ) , do: false
816824
817- def clear_first_arg_location ( [ form | t ] ) , do: [ clear_location ( form ) | t ]
818- def clear_first_arg_location ( [ ] ) , do: [ ]
825+ defp is_lineless? ( expr ) do
826+ elem ( expr , 0 ) in @ lineless_forms
827+ end
819828
820- def clear_location ( form ) , do: put_elem ( form , 1 , :erl_anno . set_line ( 0 , elem ( form , 1 ) ) )
829+ defp clear_location ( form ) , do: put_elem ( form , 1 , :erl_anno . set_line ( 0 , elem ( form , 1 ) ) )
821830end
0 commit comments