Metastatic.Semantic.Callbacks (Metastatic v0.22.1)

View Source

Registry of known behaviour/base-class callbacks per language.

Maps {language, behaviour_module, function_name, arity} tuples to callback specs, enabling the enricher to annotate :function_def nodes with callback_for: metadata when the enclosing container uses/inherits a registered behaviour.

Storage uses :persistent_term (same strategy as Semantic.Patterns).

Built-in Registrations

The module ships with callbacks for common frameworks:

  • Elixir: Oban.Worker, GenServer, Phoenix.LiveView, Plug, Broadway
  • Ruby: ActiveJob::Base, Sidekiq::Worker, Resque
  • Python: celery.Task, RQ Worker, Dramatiq

Usage

# Check if a function is a known callback
Callbacks.lookup(:elixir, "Oban.Worker", "perform", 1)
#=> {:ok, %{framework: :oban, domain: :queue}}

# Register a custom callback
Callbacks.register(:elixir, "MyBehaviour", "handle_event", 2,
  %{framework: :custom, domain: nil})

Integration

The enricher calls lookup/4 during tree traversal. When a :container node declares a behaviour (via :import children or :parent metadata), its :function_def children are checked against this registry.

Summary

Types

Registry key: {language, behaviour_module, function_name, arity_or_nil}

Callback specification describing the framework and domain

Functions

Returns all registered behaviours for a language.

Checks whether a function in a given behaviour is a known callback.

Clears all registered callbacks. Primarily for testing.

Looks up a callback by language, behaviour, function name, and arity.

Registers a callback for a behaviour in a specific language.

Registers all built-in callbacks for all supported languages.

Types

callback_key()

@type callback_key() :: {atom(), String.t(), String.t(), non_neg_integer() | nil}

Registry key: {language, behaviour_module, function_name, arity_or_nil}

callback_spec()

@type callback_spec() :: %{framework: atom(), domain: atom() | nil}

Callback specification describing the framework and domain

Functions

behaviours_for_language(language)

@spec behaviours_for_language(atom()) :: [String.t()]

Returns all registered behaviours for a language.

Examples

iex> behaviours = Callbacks.behaviours_for_language(:elixir)
iex> "Oban.Worker" in behaviours
true

callback?(language, behaviour, function_name, arity)

@spec callback?(atom(), String.t(), String.t(), non_neg_integer() | nil) :: boolean()

Checks whether a function in a given behaviour is a known callback.

Examples

iex> Callbacks.callback?(:elixir, "Oban.Worker", "perform", 1)
true

iex> Callbacks.callback?(:elixir, "Oban.Worker", "unknown", 0)
false

clear()

@spec clear() :: :ok

Clears all registered callbacks. Primarily for testing.

lookup(language, behaviour, function_name, arity)

@spec lookup(atom(), String.t(), String.t(), non_neg_integer() | nil) ::
  {:ok, callback_spec()} | :no_match

Looks up a callback by language, behaviour, function name, and arity.

Tries an exact arity match first, then falls back to a wildcard (nil arity) match.

Examples

iex> Callbacks.lookup(:elixir, "Oban.Worker", "perform", 1)
{:ok, %{framework: :oban, domain: :queue}}

iex> Callbacks.lookup(:elixir, "Oban.Worker", "unknown", 0)
:no_match

register(language, behaviour, function_name, arity, spec)

@spec register(
  atom(),
  String.t(),
  String.t(),
  non_neg_integer() | nil,
  callback_spec()
) :: :ok

Registers a callback for a behaviour in a specific language.

When arity is nil, the callback matches any arity for that function name.

Parameters

  • language - Source language atom (:elixir, :ruby, :python, etc.)
  • behaviour - The behaviour/base-class module name (e.g., "Oban.Worker")
  • function_name - The callback function name (e.g., "perform")
  • arity - Expected arity, or nil for any arity
  • spec - A %{framework: atom(), domain: atom() | nil} map

Examples

iex> Callbacks.register(:elixir, "Oban.Worker", "perform", 1, %{framework: :oban, domain: :queue})
:ok

register_builtins()

@spec register_builtins() :: :ok

Registers all built-in callbacks for all supported languages.

Called during application startup. Can also be called manually to re-register after clear/0.