Documentation

Commentary

This library implements an analysis that determines the role of each
symbol in Emacs Lisp code.

The analysis assigns to each symbol a "symbol role", such as
`function', `bound-variable', `binding-variable', `face', etc.  Each
symbol role has associated properties, such as the `:face' property,
which specifies a face that is applied to symbols with that role when
using semantic highlighting with `elisp-fontify-semantically'.
To define new symbol roles, see `elisp-scope-define-symbol-role'.

The entry point of the analysis in the function
`elisp-scope-analyze-form'.  It takes a caller-provided callback
function which will be called to report the information we find about
each analyzed symbol: the callback gets the position and length of
the analyzed symbol, along with its inferred role and, for
locally-bound variables, the position of the binder.
`elisp-scope-analyze-form' reads a form from the current buffer,
starting from point, using `read-positioning-symbols' to attach
position information to symbols.  It then recursively analyzes the
form, reporting information about each symbol it encounters via the
caller-provided callback function.

The core of the analysis that `elisp-scope-analyze-form' performs is
implemented in the recursive function `elisp-scope-1', which analyzes
an sexp as an evaluated form, propagating contextual information such
as local variable bindings down to analyzed sub-forms.
`elisp-scope-1' takes two arguments: FORM, which is the form to
analyze, and OUTSPEC, which is a specification of the expected
value of FORM used to analyze quoted data.  The analysis proceeds
as follows:

- If FORM is a symbol, `elisp-scope-1' reports it as a variable.

- If FORM is a cons cell (HEAD . ARGS), then the analysis depends
  on HEAD.  HEAD can have a bespoke "analyzer function" AF,
  which is called as (AF HEAD . ARGS) and is responsible for
  (recursively) analyzing FORM.  The analyzer function can be
  associated to HEAD either locally, as an alist entry in
  `elisp-scope-local-definitions', or globally, via the symbol
  property `elisp-scope-analyzer'.

  An analyzer may use the functions `elisp-scope-report-s',
  `elisp-scope-1' and `elisp-scope-n' to analyze its arguments, and
  it can consult the variable `elisp-scope-output-spec' to obtain the
  expected output spec of the analyzed form.  For example, the
  following is a suitable analyzer for the `identity' function:

    (lambda (fsym arg)
      (elisp-scope-report-s fsym 'function)
      (elisp-scope-1 arg elisp-scope-output-spec))

  In particular, the analyzer function of `quote' analyzes its
  argument according to `elisp-scope-output-spec', which is bound to
  the value of the `outspec' argument passed to `elisp-scope-1'.

- If HEAD is a macro, normally it is expanded, and then the
  expanded form is analyzed recursively.  Since macro-expansion may
  involve arbitrary code execution, only "safe" macro invocations are
  expanded: if HEAD is one of the macros in
  `elisp-scope-unsafe-macros', then it is never considered safe.
  Otherwise, HEAD is safe if it specified in the variable
  `elisp-scope-safe-macros'; or if it has a non-nil `safe-macro'
  symbol property; or if the current buffer is trusted according to
  `trusted-content-p'.  If a macro HEAD is not safe to expand (and
  has no associated analyzer function), then the macro arguments
  ARGS are not analyzed.

- If HEAD is a function, it is reported as such, and ARGS are
  recursively analyzed as evaluated forms.

- Otherwise, if HEAD has no associated analyzer function, and it is
  not a known macro or function, then it is reported with the `unknown'
  symbol role.  If the variable `elisp-scope-assume-func' is non-nil,
  then unknown HEAD is assumed to be a function call, and thus ARGS
  are analyzed as evaluated forms; otherwise ARGS are not analyzed.

When `elisp-scope-1' encounters a variable reference VAR, it checks
whether VAR has a local binding in `elisp-scope-local-bindings', and
whether VAR is a known special variable.  If VAR is a locally-bound
special variable, `elisp-scope-1' reports the role `shadowed-variable'.
If VAR is locally-bound and not a special variable, it gets the role
`bound-variable'.  Lastly, if it not locally-bound, then it gets the
role `free-variable'.

When analyzer functions invoke `elisp-scope-1/n' to analyze some
sub-forms, they specify the OUTSPEC argument to convey information
but the expected value of the evaluated sub-form(s), so
`elisp-scope-1/n' will know what to do with a sub-form that is just
(quoted) data.  For example, the analyzer function for
`face-attribute' calls `elisp-scope-1' to analyze its first argument
with an OUTSPEC which says that a quoted symbol in this position
refers to a face name.
That way, in a form such as (face-attribute 'default :foreground),
the symbol `default' is reported as a face reference (`face' role).
Moreover, the OUTSPEC is passed down as appropriate through various
predefined analyzers, so every quoted symbol in a "tail position" of
the first argument to `face-attribute' will also be recognized as a
face.  For instance, in the following form, both `success' and
`error' are reported as face references:

  (face-attribute (if (something-p)
                      'success
                    (message "oops")
                    'error)
                  :foreground)

See also the docstring of `elisp-scope-1' for details about the
format of the `outspec' argument.

Requires

Dependencies

Consumers

Reverse Dependencies

No reverse dependencies recorded.