GtBridge.Analysis.Walker (gt_bridge v0.17.3)

Copy Markdown View Source

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

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

call_entry()

@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

collect_calls(ast, context_module \\ nil)

@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.

extract_alias_map(source)

@spec extract_alias_map(String.t()) :: %{required(String.t()) => String.t()}

I parse alias directives from source and return a map from the short name to the fully qualified module string.

extract_import_map(source)

@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

resolve_call_aliases(calls, aliases)

@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}.

resolve_import_targets(calls, imports)

@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.