@@ -21,6 +21,8 @@ defmodule Gradient.AstSpecifier do
2121 # Expressions that could have missing location
2222 @ lineless_forms [ :atom , :char , :float , :integer , :string , :bin , :cons , :tuple ]
2323
24+ @ ensure_location [ :var | @ lineless_forms ]
25+
2426 # Api
2527
2628 @ doc """
@@ -255,7 +257,7 @@ defmodule Gradient.AstSpecifier do
255257 |> get_tuple ( opts )
256258 |> case do
257259 { :tuple , tokens } ->
258- { anno , opts } = update_line_from_tokens ( tokens , anno , opts )
260+ { anno , opts } = update_loc_from_tokens ( tokens , anno , opts )
259261 # drop a token that begins tuple
260262 tokens = drop_tokens_while ( tokens , fn t -> elem ( t , 0 ) in [ :"{" ] end )
261263
@@ -323,17 +325,38 @@ defmodule Gradient.AstSpecifier do
323325 |> pass_tokens ( tokens )
324326 end
325327
326- def mapper ( { :call , anno , name , args } , tokens , opts ) do
328+ def mapper ( { :call , anno , name , args } = c , tokens , opts ) do
327329 # anno has correct line
328- { :ok , _ , anno , opts , _ } = get_line ( anno , opts )
330+ { :ok , _ , _ , opts , _ } = get_line ( anno , opts )
329331 name = remote_mapper ( name )
330332
333+ { { :call , anno , _ , _ } , _tokens } = specify_line ( c , tokens , opts )
334+
335+ anno =
336+ if is_integer ( anno ) do
337+ anno
338+ else
339+ set_end_location ( anno , get_closing_paren_loc ( tokens ) )
340+ end
341+
331342 { args , tokens } = call_args_mapper ( args , tokens , name , opts )
332343
333344 { :call , anno , name , args }
334345 |> pass_tokens ( tokens )
335346 end
336347
348+ def set_end_location ( anno , :undefined ) , do: anno
349+ # def set_end_location(anno, location) when is_integer(anno) do
350+ # [location: anno, end_location: location]
351+ # end
352+ def set_end_location ( anno , location ) when is_tuple ( anno ) do
353+ [ location: anno , end_location: location ]
354+ end
355+
356+ def set_end_location ( anno , location ) do
357+ Keyword . put ( anno , :end_location , location )
358+ end
359+
337360 def mapper ( { :op , anno , op , left , right } , tokens , opts ) do
338361 # anno has correct line
339362 { :ok , _ , anno , opts , _ } = get_line ( anno , opts )
@@ -383,7 +406,7 @@ defmodule Gradient.AstSpecifier do
383406 end
384407
385408 def mapper ( { type , anno , value } , tokens , opts )
386- when type in @ lineless_forms do
409+ when type in @ ensure_location do
387410 { :ok , line } = Keyword . fetch ( opts , :line )
388411 anno = :erl_anno . set_line ( line , anno )
389412 anno = :erl_anno . set_generated ( Keyword . get ( opts , :generated , false ) , anno )
@@ -395,8 +418,7 @@ defmodule Gradient.AstSpecifier do
395418 def mapper ( skip , tokens , _opts )
396419 when elem ( skip , 0 ) in [
397420 :fun ,
398- :attribute ,
399- :var
421+ :attribute
400422 ] do
401423 # NOTE fun - I skipped here checking &name/arity or &module.name/arity
402424 # skip forms that don't need analysis and do not display warning
@@ -543,7 +565,7 @@ defmodule Gradient.AstSpecifier do
543565 def cons_mapper ( { :cons , anno , value , tail } , tokens , opts ) do
544566 { :ok , _ , anno0 , opts0 , _ } = get_line ( anno , opts )
545567
546- { anno , opts } = update_line_from_tokens ( tokens , anno0 , opts0 )
568+ { anno , opts } = update_loc_from_tokens ( tokens , anno0 , opts0 )
547569 # drop a token that begins list
548570 tokens = drop_tokens_while ( tokens , fn t -> elem ( t , 0 ) in [ :"[" ] end )
549571
@@ -662,59 +684,90 @@ defmodule Gradient.AstSpecifier do
662684 l2 <= l1
663685 end
664686
687+ defp match_token_to_form ( { :identifier , { l1 , _ , _ } , t_name } , { :var , l2 , raw_name } ) do
688+ l2 == l1 && String . contains? ( to_string ( raw_name ) , << to_string ( t_name ) :: binary , "@" >> )
689+ end
690+
691+ defp match_token_to_form ( { :paren_identifier , { l1 , _ , _ } , name1 } , { :call , l2 , name2 , _ } ) do
692+ :erl_anno . line ( l2 ) == l1 && match_remote_names ( name1 , name2 )
693+ end
694+
665695 defp match_token_to_form ( _ , _ ) do
666696 false
667697 end
668698
699+ def match_remote_names ( name , { :remote , _ , _ , { :atom , _ , name } } ) , do: true
700+ def match_remote_names ( name , { :atom , _ , name } ) , do: true
701+ def match_remote_names ( name , name ) , do: true
702+ def match_remote_names ( _ , _ ) , do: false
703+
704+ defp literal_loc ( { line , col_start , _ } , literal ) do
705+ col_end = col_start + length ( to_charlist ( literal ) )
706+ [ location: { line , col_start } , end_location: { line , col_end } ]
707+ end
708+
669709 @ spec take_loc_from_token ( token ( ) , form ( ) ) :: form ( )
670- defp take_loc_from_token ( { :int , { line , _ , _ } , _ } , { :integer , _ , value } ) do
671- { :integer , line , value }
710+ defp take_loc_from_token ( { :int , loc , _ } , { :integer , _ , value } ) do
711+ { :integer , literal_loc ( loc , value ) , value }
672712 end
673713
674- defp take_loc_from_token ( { :char , { line , _ , _ } , _ } , { :integer , _ , value } ) do
675- { :integer , line , value }
714+ defp take_loc_from_token ( { :char , loc , _ } , { :integer , _ , value } ) do
715+ { :integer , literal_loc ( loc , value ) , value }
676716 end
677717
678- defp take_loc_from_token ( { :flt , { line , _ , _ } , _ } , { :float , _ , value } ) do
679- { :float , line , value }
718+ defp take_loc_from_token ( { :flt , loc , _ } , { :float , _ , value } ) do
719+ { :float , literal_loc ( loc , value ) , value }
680720 end
681721
682- defp take_loc_from_token ( { :atom , { line , _ , _ } , _ } , { :atom , _ , value } ) do
683- { :atom , line , value }
722+ defp take_loc_from_token ( { :atom , loc , _ } , { :atom , _ , value } ) do
723+ { :atom , literal_loc ( loc , value ) , value }
684724 end
685725
686- defp take_loc_from_token ( { :alias , { line , _ , _ } , _ } , { :atom , _ , value } ) do
687- { :atom , line , value }
726+ defp take_loc_from_token ( { :alias , loc , _ } , { :atom , _ , value } ) do
727+ { :atom , literal_loc ( loc , value ) , value }
688728 end
689729
690- defp take_loc_from_token ( { :kw_identifier , { line , _ , _ } , _ } , { :atom , _ , value } ) do
691- { :atom , line , value }
730+ defp take_loc_from_token ( { :kw_identifier , loc , _ } , { :atom , _ , value } ) do
731+ { :atom , literal_loc ( loc , value ) , value }
692732 end
693733
694- defp take_loc_from_token ( { :list_string , { l1 , _ , _ } , _ } , { :cons , _ , _ , _ } = charlist ) do
695- charlist_set_loc ( charlist , l1 )
734+ defp take_loc_from_token ( { :list_string , loc , v } , { :cons , _ , _ , _ } = charlist ) do
735+ charlist_set_loc ( charlist , literal_loc ( loc , v ) )
696736 end
697737
698738 defp take_loc_from_token (
699- { :bin_string , { l1 , _ , _ } , _ } ,
739+ { :bin_string , loc , _ } ,
700740 { :bin , _ , [ { :bin_element , _ , { :string , _ , v2 } , :default , :default } ] }
701741 ) do
702- { :bin , l1 , [ { :bin_element , l1 , { :string , l1 , v2 } , :default , :default } ] }
742+ loc = literal_loc ( loc , v2 )
743+ { :bin , loc , [ { :bin_element , loc , { :string , loc , v2 } , :default , :default } ] }
703744 end
704745
705746 defp take_loc_from_token ( { :str , _ , _ } , { :string , loc , v2 } ) do
747+ # FIXME missing col
706748 { :string , loc , v2 }
707749 end
708750
709- defp take_loc_from_token ( { true , { line , _ , _ } } , { :atom , _ , true } ) do
710- { :atom , line , true }
751+ defp take_loc_from_token ( { true , loc } , { :atom , _ , true } ) do
752+ { :atom , literal_loc ( loc , true ) , true }
711753 end
712754
713- defp take_loc_from_token ( { false , { line , _ , _ } } , { :atom , _ , false } ) do
714- { :atom , line , false }
755+ defp take_loc_from_token ( { false , loc } , { :atom , _ , false } ) do
756+ { :atom , literal_loc ( loc , true ) , false }
715757 end
716758
717- defp take_loc_from_token ( _ , _ ) , do: nil
759+ defp take_loc_from_token ( { :identifier , loc , name } , { :var , _ , raw_name } ) do
760+ { :var , literal_loc ( loc , name ) , raw_name }
761+ end
762+
763+ defp take_loc_from_token ( { :paren_identifier , { line , col , _ } , _ } , { :call , anno , name , args } ) do
764+ { :call , :erl_anno . set_location ( { line , col } , anno ) , name , args }
765+ end
766+
767+ defp take_loc_from_token ( t , e ) do
768+ IO . puts ( IO.ANSI . format ( [ :red , "Cannot take loc from token - #{ inspect ( t ) } , #{ inspect ( e ) } " ] ) )
769+ nil
770+ end
718771
719772 def cons_to_charlist ( { nil , _ } ) , do: [ ]
720773
@@ -728,12 +781,12 @@ defmodule Gradient.AstSpecifier do
728781
729782 def charlist_set_loc ( { nil , _ } , loc ) , do: { nil , loc }
730783
731- def update_line_from_tokens ( [ token | _ ] , anno , opts ) do
732- line = get_line_from_token ( token )
733- { :erl_anno . set_line ( line , anno ) , Keyword . put ( opts , :line , line ) }
784+ def update_loc_from_tokens ( [ token | _ ] , anno , opts ) do
785+ { line , col } = get_loc_from_token ( token )
786+ { :erl_anno . set_location ( { line , col } , anno ) , Keyword . put ( opts , :line , line ) }
734787 end
735788
736- def update_line_from_tokens ( _ , anno , opts ) do
789+ def update_loc_from_tokens ( _ , anno , opts ) do
737790 { anno , opts }
738791 end
739792
@@ -763,7 +816,7 @@ defmodule Gradient.AstSpecifier do
763816 end
764817
765818 defp set_form_end_line ( opts , form , forms ) do
766- if elem ( form , 0 ) not in [ :bin , :cons ] do
819+ if elem ( form , 0 ) not in [ :bin ] do
767820 set_form_end_line_ ( opts , form , forms )
768821 else
769822 opts
0 commit comments