Finds the items the code declares: the code records the relation log relates
(Surfex.Scan.code/1), and what the spec's citations resolve to. A scanner knows one
language's declarations; it knows nothing about any spec, and Surfex.Cite knows
nothing about any language.
Surfex.Scanner.Elixir is built in. A project whose code is in another language (a C
reference implementation, a protocol described in headers) implements items/2 for
it (scanner: in .surfex.exs) and gets the rest unchanged.
A scanner should read source, never compile or load it: a check built on it then runs before, and independently of, the code it inspects, and cannot be fooled by a module that failed to build.
Several scanners combine (scanners: in .surfex.exs, spec §5.3): a project keeps the
Elixir scanner and adds its own, and one key from two scanners is an error.
DSL items
In a project built with a DSL, behaviour is declared in blocks inside a module (an
action, a policy) that no def names. A scanner for the DSL gives each block as a
Surfex.Item (spec §4.1), filled so the rest of surfex works unchanged:
kind— the DSL's own (:action,:policy, …)parent— the declaring module, andnamethe DSL's name for the block, so the key isModule.name: unique in the module, stable across edits that rename nothinghash—Surfex.SourceScan.definition_hash/1over the block's AST, so a change to the block changes its version and no sibling'slines— the block's span;calls— the keys of what the block references, so reach goes on into themshape—truefor a declaration with no behaviour of its own (an attribute)hiddenandrunsstay empty: a DSL item carries no hidden definitions. Its block is its whole definition and its hash covers it
Code reaches a DSL item through the DSL's entry functions, naming it in arguments, so a
DSL's scanner also implements the optional calls/3: asked about every remote call a
test or a function makes, it answers the keys of the items the call drives.
Surfex depends on no DSL: the project, or a companion package, writes the scanner, with
Surfex.SourceScan to read source.
Stability
This behaviour and Surfex.Item are supported API (spec §23.1): the callbacks keep their
names, arities and shapes, a callback added later is optional, and a change to either
comes in a minor release, listed under "Changed" in the CHANGELOG.
Summary
Callbacks
The keys of the items a remote call names, beyond the function it calls (spec §5.4).
Optional. A DSL's items are reached through its entry functions rather than called by
name: MyDsl.run(MyApp.Order, :checkout, input) drives the checkout action of
MyApp.Order. The test scanner and the Elixir scanner ask each configured scanner that
implements this about every remote call they read, and add the keys it answers to the
caller's calls, so reach goes into the DSL item and on through its own calls.
Every item under root, each with a stable key and a content hash that ignores
position and layout (see Surfex.SourceScan.definition_hash/1). file is relative to
root. opts is the scanner's own configuration.
Callbacks
@callback calls( call :: Macro.t(), aliases :: %{required(atom()) => String.t()}, opts :: keyword() ) :: [ String.t() ]
The keys of the items a remote call names, beyond the function it calls (spec §5.4).
Optional. A DSL's items are reached through its entry functions rather than called by
name: MyDsl.run(MyApp.Order, :checkout, input) drives the checkout action of
MyApp.Order. The test scanner and the Elixir scanner ask each configured scanner that
implements this about every remote call they read, and add the keys it answers to the
caller's calls, so reach goes into the DSL item and on through its own calls.
call is the call's AST as written, {{:., _, [module, fun]}, meta, args}, except that
a piped call has its piped value as its first argument. aliases maps each alias in
scope at the call (an atom, :Order) to the module it names ("MyApp.Order"), so the
scanner can resolve {:__aliases__, _, parts} itself. opts is the scanner's own
configuration. A call it doesn't recognise answers [].
@callback items(root :: String.t(), opts :: keyword()) :: [Surfex.Item.t()]
Every item under root, each with a stable key and a content hash that ignores
position and layout (see Surfex.SourceScan.definition_hash/1). file is relative to
root. opts is the scanner's own configuration.