Lint
is the linter's core: the rule registry, the AST walker, and the
public entry points (lint, lint-with, lint-source,
lint-source-with). Plugin authors interact with it via
register-rule!. See
WRITING-RULES.md
for the rule-author guide.
lint
(Fn [(Ref (Array (Box Located)) a)] (Array Diagnostic))
(lint forms)
applies every registered rule to every node in forms,
recursing into compound forms. Returns one Diagnostic per finding.
lint-source
(Fn [(Ref String a)] (Result (Array Diagnostic) ParseErr))
(lint-source src)
parses src via Reader.parse and lints with
every registered rule. Returns (Result (Array Diagnostic) ParseErr)
so a parse failure surfaces distinctly from a clean parse with
diagnostics.
lint-source-with
(Fn [(Ref (Fn [(Ref String StaticLifetime)] Bool) a), (Ref String b)] (Result (Array Diagnostic) ParseErr))
(lint-source-with keep? src)
like lint-source, but only runs rules whose
names satisfy keep?.
lint-with
(Fn [(Ref (Fn [(Ref String StaticLifetime)] Bool) a), (Ref (Array (Box Located)) b)] (Array Diagnostic))
(lint-with keep? forms)
like lint, but only runs rules whose names satisfy
keep?.
list-rules
(Fn [] (Array Rule))
(list-rules)
returns a copy of the currently registered rule
registry. Useful for angler --list-rules and for plugins that want
to inspect the active rule set.
load-rules-from-source!
(Fn [(Ref String a)] (Result Int ParseErr))
(load-rules-from-source! src)
parses src as a rules file and
registers each rule found. Each top-level form must be an array
literal [<pattern> "name" "message"]. Symbols starting with
? in the pattern are metavariables, just as in
register-pattern-rule!. Non-conforming top-level forms are silently
skipped. Returns the number of rules loaded, or a parse error.
lst-head?
(Fn [(Ref Form a), (Ref String b)] Bool)
(lst-head? f name)
true iff f is (Form.Lst …) whose first child is
a Form.Sym matching name.
nth-form
(Fn [(Ref (Array (Box Located)) a), Int] (Ref Form a))
(nth-form items i)
pulls the Form out of the i-th Located child of
an Array (Box Located). Most rules want the structural shape, not
the position, when descending into a compound form's children.
register-pattern-rule!
(Fn [String, String, (Ref String a), String] ())
(register-pattern-rule! name description pattern-str message)
registers a lint rule that fires when
pattern-str (a single Carp form) structurally matches a node.
Symbols starting with ? are metavariables: they match any form,
and if the same ?name appears more than once the bound forms
must have equal string representations.
(Lint.register-pattern-rule! @"set-self"
@"(set! x x) is a no-op"
"(set! ?x ?x)"
@"(set! x x) is a no-op")
register-rule!
(Fn [String, String, (Fn [(Ref Located a)] (Maybe Diagnostic))] ())
(register-rule! name description fn)
registers a rule under name with a one-line
description. Subsequent runs of Lint.lint / Lint.lint-source
include it. Built-ins call this at load time; plugins do the same
after loading angler.
Caveat: Carp initializes global defs in dependency-graph order,
not in textual or load order. If your plugin uses a top-level def
to call register-rule!, its slot in the registry — and therefore
its slot in the diagnostic output for any one form — is unspecified
relative to angler's built-ins. Rule firing is still correct;
only the order of diagnostics on a single node varies. If you need
strict ordering, register from main instead of from a top-level
def.
sym?
(Fn [(Ref Form a), (Ref String b)] Bool)
(sym? f name)
true iff f is a Form.Sym whose dotted name equals
name. name may itself contain . to match a multi-segment
symbol — e.g. (sym? f "Parser.try") matches Parser.try.