@@ -39,12 +39,14 @@ debug_info(_, _, _, _) ->
3939% % Builds Erlang AST annotation.
4040
4141get_ann (Opts ) when is_list (Opts ) ->
42- get_ann (Opts , false , 0 ).
42+ get_ann (Opts , false , 0 , undefined ).
4343
44- get_ann ([{generated , true } | T ], _ , Line ) -> get_ann (T , true , Line );
45- get_ann ([{line , Line } | T ], Gen , _ ) when is_integer (Line ) -> get_ann (T , Gen , Line );
46- get_ann ([_ | T ], Gen , Line ) -> get_ann (T , Gen , Line );
47- get_ann ([], Gen , Line ) -> erl_anno :set_generated (Gen , erl_anno :new (Line )).
44+ get_ann ([{generated , true } | T ], _ , Line , Column ) -> get_ann (T , true , Line , Column );
45+ get_ann ([{line , Line } | T ], Gen , _ , Column ) when is_integer (Line ) -> get_ann (T , Gen , Line , Column );
46+ get_ann ([{column , Column } | T ], Gen , Line , _ ) when is_integer (Column ) -> get_ann (T , Gen , Line , Column );
47+ get_ann ([_ | T ], Gen , Line , Column ) -> get_ann (T , Gen , Line , Column );
48+ get_ann ([], Gen , Line , undefined ) -> erl_anno :set_generated (Gen , erl_anno :new (Line ));
49+ get_ann ([], Gen , Line , Column ) -> erl_anno :set_generated (Gen , erl_anno :new ({Line , Column })).
4850
4951% % Converts an Elixir definition to an anonymous function.
5052
@@ -122,7 +124,7 @@ consolidate(Map, TypeSpecs, Chunks) ->
122124
123125% % Dynamic compilation hook, used in regular compiler
124126
125- compile (#{module := Module , line := Line } = Map ) ->
127+ compile (#{module := Module , anno := Anno } = Map ) ->
126128 {Set , Bag } = elixir_module :data_tables (Module ),
127129
128130 TranslatedTypespecs =
@@ -135,13 +137,14 @@ compile(#{module := Module, line := Line} = Map) ->
135137 {Prefix , Forms , Def , Defmacro , Macros } = dynamic_form (Map ),
136138 {Types , Callbacks , TypeSpecs } = typespecs_form (Map , TranslatedTypespecs , Macros ),
137139
138- DocsChunk = docs_chunk (Set , Module , Line , Def , Defmacro , Types , Callbacks ),
140+ DocsChunk = docs_chunk (Map , Set , Module , Anno , Def , Defmacro , Types , Callbacks ),
139141 CheckerChunk = checker_chunk (Def , Defmacro , Map ),
140142 load_form (Map , Prefix , Forms , TypeSpecs , DocsChunk ++ CheckerChunk ).
141143
142- dynamic_form (#{module := Module , line := Line , relative_file := RelativeFile ,
144+ dynamic_form (#{module := Module , anno := Anno , relative_file := RelativeFile ,
143145 attributes := Attributes , definitions := Definitions , unreachable := Unreachable ,
144146 deprecated := Deprecated , compile_opts := Opts } = Map ) ->
147+ Line = erl_anno :line (Anno ),
145148 {Def , Defmacro , Macros , Exports , Functions } =
146149 split_definition (Definitions , Unreachable , Line , [], [], [], [], {[], []}),
147150
@@ -168,13 +171,14 @@ split_definition([{Tuple, Kind, Meta, Clauses} | T], Unreachable, Line,
168171 true ->
169172 split_definition (T , Unreachable , Line , Def , Defmacro , Macros , Exports , Functions )
170173 end ;
174+
171175split_definition ([], _Unreachable , _Line , Def , Defmacro , Macros , Exports , {Head , Tail }) ->
172- {lists :usort (Def ), lists :usort (Defmacro ), Macros , Exports , Head ++ Tail }.
176+ {lists :sort (Def ), lists :sort (Defmacro ), Macros , Exports , Head ++ Tail }.
173177
174178split_definition (Tuple , def , Meta , Clauses , T , Unreachable , Line ,
175179 Def , Defmacro , Macros , Exports , Functions ) ->
176180 {_ , _ , N , A , _ } = Entry = translate_definition (def , Line , Meta , Tuple , Clauses ),
177- split_definition (T , Unreachable , Line , [Tuple | Def ], Defmacro , Macros , [{N , A } | Exports ],
181+ split_definition (T , Unreachable , Line , [{ Tuple , Meta } | Def ], Defmacro , Macros , [{N , A } | Exports ],
178182 add_definition (Meta , Entry , Functions ));
179183
180184split_definition (Tuple , defp , Meta , Clauses , T , Unreachable , Line ,
@@ -186,7 +190,7 @@ split_definition(Tuple, defp, Meta, Clauses, T, Unreachable, Line,
186190split_definition (Tuple , defmacro , Meta , Clauses , T , Unreachable , Line ,
187191 Def , Defmacro , Macros , Exports , Functions ) ->
188192 {_ , _ , N , A , _ } = Entry = translate_definition (defmacro , Line , Meta , Tuple , Clauses ),
189- split_definition (T , Unreachable , Line , Def , [Tuple | Defmacro ], [Tuple | Macros ], [{N , A } | Exports ],
193+ split_definition (T , Unreachable , Line , Def , [{ Tuple , Meta } | Defmacro ], [Tuple | Macros ], [{N , A } | Exports ],
190194 add_definition (Meta , Entry , Functions ));
191195
192196split_definition (Tuple , defmacrop , Meta , Clauses , T , Unreachable , Line ,
@@ -261,6 +265,9 @@ functions_form(Line, Module, Def, Defmacro, Exports, Body, Deprecated, Struct) -
261265 [{attribute , Line , export , lists :usort ([{'__info__' , 1 } | Exports ])}, Spec , Info | Body ].
262266
263267add_info_function (Line , Module , Def , Defmacro , Deprecated , Struct ) ->
268+ DefNA = [NA || {NA , _Meta } <- Def ],
269+ DefmacroNA = [NA || {NA , _Meta } <- Defmacro ],
270+
264271 AllowedAttrs = [attributes , compile , functions , macros , md5 , exports_md5 , module , deprecated , struct ],
265272 AllowedArgs = lists :map (fun (Atom ) -> {atom , Line , Atom } end , AllowedAttrs ),
266273
@@ -277,10 +284,10 @@ add_info_function(Line, Module, Def, Defmacro, Deprecated, Struct) ->
277284 Info =
278285 {function , 0 , '__info__' , 1 , [
279286 get_module_info (Module ),
280- functions_info (Def ),
281- macros_info (Defmacro ),
287+ functions_info (DefNA ),
288+ macros_info (DefmacroNA ),
282289 struct_info (Struct ),
283- exports_md5_info (Struct , Def , Defmacro ),
290+ exports_md5_info (Struct , DefNA , DefmacroNA ),
284291 get_module_info (Module , attributes ),
285292 get_module_info (Module , compile ),
286293 get_module_info (Module , md5 ),
@@ -331,19 +338,34 @@ typespecs_form(Map, TranslatedTypespecs, MacroNames) ->
331338 Forms2 = callspecs_form (spec , Specs , [], MacroNames , Forms1 , Map ),
332339 Forms3 = callspecs_form (callback , AllCallbacks , OptionalCallbacks , MacroCallbackNames , Forms2 , Map ),
333340
334- AllCallbacksWithoutSpecs = lists : usort ([
335- {Kind , Name , Arity } || {Kind , {Name , Arity }, _Line , _Spec } <- AllCallbacks
341+ AllCallbacksWithoutSpecs = usort_callbacks ([
342+ {{ Kind , Name , Arity }, Meta } || {Kind , {Name , Arity }, Meta , _Spec } <- AllCallbacks
336343 ]),
337344
338345 {Types , AllCallbacksWithoutSpecs , Forms3 }.
339346
347+ usort_callbacks (Callbacks ) ->
348+ % Sort and deduplicate callbacks. For duplicated callbacks we take
349+ % the one with earliest line.
350+
351+ LineComparator = fun
352+ ({Callback1 , Meta1 }, {Callback1 , Meta2 }) -> ? line (Meta1 ) =< ? line (Meta2 );
353+ ({Callback1 , _Meta1 }, {Callback2 , _Meta2 }) -> Callback1 =< Callback2
354+ end ,
355+
356+ UniqFun = fun ({Callback , _Meta }) -> Callback end ,
357+
358+ lists :uniq (UniqFun , lists :sort (LineComparator , Callbacks )).
359+
340360% % Types
341361
342362types_form (Types , Forms ) ->
343363 Fun = fun
344- ({Kind , NameArity , Line , Expr , true }, Acc ) ->
364+ ({Kind , NameArity , Meta , Expr , true }, Acc ) ->
365+ Line = ? line (Meta ),
345366 [{attribute , Line , export_type , [NameArity ]}, {attribute , Line , Kind , Expr } | Acc ];
346- ({Kind , _NameArity , Line , Expr , false }, Acc ) ->
367+ ({Kind , _NameArity , Meta , Expr , false }, Acc ) ->
368+ Line = ? line (Meta ),
347369 [{attribute , Line , Kind , Expr } | Acc ]
348370 end ,
349371
@@ -387,7 +409,9 @@ callspecs_form(Kind, Entries, Optional, Macros, Forms, ModuleMap) ->
387409 #{unreachable := Unreachable } = ModuleMap ,
388410
389411 {SpecsMap , Signatures } =
390- lists :foldl (fun ({_ , NameArity , Line , Spec }, {Acc , NA }) ->
412+ lists :foldl (fun ({_ , NameArity , Meta , Spec }, {Acc , NA }) ->
413+ Line = ? line (Meta ),
414+
391415 case Kind of
392416 spec -> validate_spec_for_existing_function (ModuleMap , NameArity , Line );
393417 _ -> ok
@@ -440,7 +464,7 @@ validate_spec_for_existing_function(ModuleMap, NameAndArity, Line) ->
440464
441465 case lists :keymember (NameAndArity , 1 , Defs ) of
442466 true -> ok ;
443- false -> file_error (#{line => Line , file => File }, {spec_for_undefined_function , NameAndArity })
467+ false -> file_error (#{anno => erl_anno : new ( Line ) , file => File }, {spec_for_undefined_function , NameAndArity })
444468 end .
445469
446470% Attributes
@@ -472,22 +496,30 @@ take_debug_opts(Opts) ->
472496extra_chunks_opts ([], Opts ) -> Opts ;
473497extra_chunks_opts (Chunks , Opts ) -> [{extra_chunks , Chunks } | Opts ].
474498
475- docs_chunk (Set , Module , Line , Def , Defmacro , Types , Callbacks ) ->
499+ docs_chunk (Map , Set , Module , Anno , Def , Defmacro , Types , Callbacks ) ->
500+ #{file := File , uses_behaviours := UsesBehaviours } = Map ,
501+
476502 case elixir_config :get (docs ) of
477503 true ->
478- {ModuleDocLine , ModuleDoc } = get_moduledoc (Line , Set ),
504+ {ModuleDocLine , ModuleDoc } = get_moduledoc (erl_anno : line ( Anno ) , Set ),
479505 ModuleDocMeta = get_moduledoc_meta (Set ),
480506 FunctionDocs = get_docs (Set , Module , Def , function ),
481507 MacroDocs = get_docs (Set , Module , Defmacro , macro ),
482508 CallbackDocs = get_callback_docs (Set , Callbacks ),
483509 TypeDocs = get_type_docs (Set , Types ),
484510
511+ ModuleMeta = ModuleDocMeta #{
512+ source_path => File ,
513+ source_annos => [Anno ],
514+ behaviours => UsesBehaviours
515+ },
516+
485517 DocsChunkData = term_to_binary ({docs_v1 ,
486518 erl_anno :new (ModuleDocLine ),
487519 elixir ,
488520 <<" text/markdown" >>,
489521 ModuleDoc ,
490- ModuleDocMeta ,
522+ ModuleMeta ,
491523 FunctionDocs ++ MacroDocs ++ CallbackDocs ++ TypeDocs
492524 }, [deterministic , compressed ]),
493525
@@ -529,8 +561,8 @@ get_docs(Set, Module, Definitions, Kind) ->
529561 maybe_generated (erl_anno :new (Line ), Ctx ),
530562 [signature_to_binary (Module , Name , Signature )],
531563 doc_value (Doc , Name ),
532- Meta
533- } || {Name , Arity } <- Definitions ,
564+ Meta #{ source_annos => [ ? ann ( DefinitionMeta )]}
565+ } || {{ Name , Arity }, DefinitionMeta } <- Definitions ,
534566 {Key , Ctx , Line , Signature , Doc , Meta } <- ets :lookup (Set , {Kind , Name , Arity })].
535567
536568maybe_generated (Ann , nil ) -> Ann ;
@@ -541,16 +573,16 @@ get_callback_docs(Set, Callbacks) ->
541573 erl_anno :new (Line ),
542574 [],
543575 doc_value (Doc , Name ),
544- Meta
545- } || Callback <- Callbacks , {{ _ , Name , _ } = Key , Line , Doc , Meta } <- ets :lookup (Set , Callback )].
576+ Meta #{ source_annos => [ ? ann ( DefinitionMeta )]}
577+ } || {{ Kind , Name , Arity }, DefinitionMeta } <- Callbacks , { Key , Line , Doc , Meta } <- ets :lookup (Set , { Kind , Name , Arity } )].
546578
547579get_type_docs (Set , Types ) ->
548580 [{Key ,
549581 erl_anno :new (Line ),
550582 [],
551583 doc_value (Doc , Name ),
552- Meta
553- } || {_Kind , {Name , Arity }, _ , _ , true } <- Types ,
584+ Meta #{ source_annos => [ ? ann ( DefinitionMeta )]}
585+ } || {_Kind , {Name , Arity }, DefinitionMeta , _ , true } <- Types ,
554586 {Key , Line , Doc , Meta } <- ets :lookup (Set , {type , Name , Arity })].
555587
556588signature_to_binary (_Module , Name , _Signature ) when Name == '__aliases__' ; Name == '__block__' ->
@@ -580,22 +612,23 @@ checker_chunk(Def, Defmacro, #{deprecated := Deprecated, defines_behaviour := De
580612
581613 Exports =
582614 [{FA , #{kind => def , deprecated_reason => maps :get (FA , DeprecatedMap , nil )}}
583- || FA <- prepend_behaviour_info (DefinesBehaviour , Def )] ++
615+ || { FA , _Meta } <- prepend_behaviour_info (DefinesBehaviour , Def )] ++
584616 [{FA , #{kind => defmacro , deprecated_reason => maps :get (FA , DeprecatedMap , nil )}}
585- || FA <- Defmacro ],
617+ || { FA , _Meta } <- Defmacro ],
586618
587619 Contents = #{
588620 exports => Exports
589621 },
590622
591623 [{<<" ExCk" >>, term_to_binary ({elixir_checker_v1 , Contents }, [deterministic ])}].
592624
593- prepend_behaviour_info (true , Def ) -> [{behaviour_info , 1 } | Def ];
625+ prepend_behaviour_info (true , Def ) -> [{{ behaviour_info , 1 }, [] } | Def ];
594626prepend_behaviour_info (false , Def ) -> Def .
595627
596628% % Errors
597629
598- file_error (#{line := Line , file := File }, Error ) ->
630+ file_error (#{anno := Anno , file := File }, Error ) ->
631+ Line = erl_anno :line (Anno ),
599632 elixir_errors :file_error ([{line , Line }], File , ? MODULE , Error ).
600633
601634format_error ({ill_defined_optional_callback , Callback }) ->
0 commit comments