I walk Elixir AST and extract call sites.
I am the AST-walking subsystem of GtBridge.Analysis: given a
parsed quoted form I collect remote and local call entries, and
given source text I extract the alias map needed to resolve them
to fully qualified module names.
Public API
collect_calls/2: walk an AST and return raw call entriesextract_alias_map/1: parsealiasdirectives from sourceextract_import_map/1: parseimportdirectives from sourceresolve_call_aliases/2: rewrite call entries through an alias mapresolve_import_targets/2: rewrite local entries through an import map
Summary
Functions
I walk ast and return raw call entries.
I parse alias directives from source and return a map from
the short name to the fully qualified module string.
I parse import directives from source and return a map from
{function_name, arity} to the full module string.
I rewrite each call entry's :target_module through aliases and
return the list sorted by {line, column}.
I rewrite local entries (call.local == true) whose
{function, arity} is in imports, setting :target_module to
the imported module and clearing :local. Remote entries pass
through unchanged.
Types
@type call_entry() :: %{ target_module: String.t(), function: String.t(), arity: non_neg_integer(), line: pos_integer() | nil, column: pos_integer() | nil, local: boolean() }
Functions
@spec collect_calls(Macro.t(), module() | nil) :: [call_entry()]
I walk ast and return raw call entries.
When context_module is non-nil, local calls inside that module
are emitted with :target_module set to the module's inspect form;
otherwise only remote calls are emitted.
I parse alias directives from source and return a map from
the short name to the fully qualified module string.
@spec extract_import_map(String.t()) :: %{ required({String.t(), non_neg_integer()}) => String.t() }
I parse import directives from source and return a map from
{function_name, arity} to the full module string.
Three forms recognized at the module level: import Foo all exported funs from Foo import Foo, only: [bar: 1, baz: 2] whitelist import Foo.{A, B} all funs from each
@spec resolve_call_aliases([call_entry()], %{required(String.t()) => String.t()}) :: [ call_entry() ]
I rewrite each call entry's :target_module through aliases and
return the list sorted by {line, column}.
@spec resolve_import_targets([call_entry()], %{ required({String.t(), non_neg_integer()}) => String.t() }) :: [call_entry()]
I rewrite local entries (call.local == true) whose
{function, arity} is in imports, setting :target_module to
the imported module and clearing :local. Remote entries pass
through unchanged.