lint

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package lint is m-cli's M source linter. Rules come in two shapes, both over the m-parse tree:

  • query rules carry a tree-sitter query + an OnMatch that turns each match into a finding (good for pattern detection, e.g. M-MOD-037);
  • walk rules carry an Inspect that traverses the tree / scans the source and returns findings (good for metrics + structure, e.g. line length, nesting depth, argument count).

Rules are tagged; profiles select rules by tag (spec §3.1). This is the engine + a growing rule set — the dataflow/taint rules (M-MOD-011..036) need the flow-analysis infra, a later port.

Index

Constants

This section is empty.

Variables

View Source
var Profiles = []string{"default", "modern", "pythonic", "pedantic", "xindex", "sac", "vista", "all"}

Profiles is the set of recognized profile names (for the CLI enum).

default · modern · pythonic · pedantic — the M-MOD modernization track (tag "modern")
xindex  — rules ported from the VA VistA Toolkit ^XINDEX scanner (tag "xindex")
sac     — the subset of xindex rules mapping to a documented VA SAC requirement (tag "sac")
vista   — VistA-Kernel-specific rules (tag "vista"); pure false positives off VistA
all     — every registered rule

Functions

func DefaultKernelLocals

func DefaultKernelLocals() map[string]bool

DefaultKernelLocals returns a copy of the built-in VistA Kernel auto-defined allowlist — the set [lint.vista] kernel_locals = "default" opts in to.

func DefaultTrustedRoutines

func DefaultTrustedRoutines() map[string]bool

DefaultTrustedRoutines returns the built-in VistA trusted-routine allowlist as an upper-cased set ([lint.vista] trusted_routines = "default").

func ResolveFilter

func ResolveFilter(profileFlag string, cfg config.Config) string

ResolveFilter layers rule selection identically for every entry point — the CLI lint/watch commands and the LSP — so the editor and CI can never drift (G2 diagnostic parity): an explicitly-set non-default --profile flag wins; otherwise the config's lint rules; otherwise the "default" profile. Callers with no profile flag (the LSP) pass "".

Types

type Finding

type Finding struct {
	Rule     string   `json:"rule"`
	Severity Severity `json:"severity"`
	Message  string   `json:"message"`
	Line     int      `json:"line"`
	Col      int      `json:"col"`
	EndLine  int      `json:"endLine"`
	EndCol   int      `json:"endCol"`
}

Finding is one lint result (1-based positions; End* mark the offending span).

type Linter

type Linter struct {
	// contains filtered or unexported fields
}

Linter compiles the query rules' queries once and runs the rule set over many sources.

func NewLinter

func NewLinter(p *parse.Parser, rules []Rule) (*Linter, error)

NewLinter compiles every query rule's query against the grammar.

func (*Linter) AttachWorkspace

func (l *Linter) AttachWorkspace(ws *workspace.Index)

AttachWorkspace gives the linter a cross-routine index so InspectWorkspace rules (M-XINDX-007/008/049) can run. Without it those rules are skipped.

func (*Linter) Close

func (l *Linter) Close()

Close frees the compiled queries.

func (*Linter) Lint

func (l *Linter) Lint(ctx context.Context, src []byte) ([]Finding, error)

Lint parses src and returns the findings from every rule, sorted by position. Name-aware rules (InspectNamed) see an empty routine name; use LintNamed to supply one.

func (*Linter) LintNamed

func (l *Linter) LintNamed(ctx context.Context, src []byte, routine string) ([]Finding, error)

LintNamed is Lint with the routine name (file base name without extension) threaded through to name-aware rules.

type Options

type Options struct {
	Thresholds        Thresholds
	Taint             flow.TaintConfig
	KernelLocals      map[string]bool     // M-MOD-024 allowlist; nil ⇒ strict (no allowlist)
	TrustedRoutines   map[string]bool     // M-XINDX-007 allowlist; nil ⇒ strict (any unknown routine flagged)
	SeverityOverrides map[string]Severity // [lint.severity]; re-stamps Rule.Severity at build
	Disable           map[string]bool     // [lint] disable; rule ids dropped after selection
}

Options carries the resolved, config-derived inputs baked into rule construction. NewLinter takes the rule set built from these, so Lint's own signature stays unchanged — the config flows in at build time, not call time.

func DefaultOptions

func DefaultOptions() Options

DefaultOptions is the built-in configuration when no config file is present. Faithful to the Python tool: the M-MOD-024 Kernel allowlist is OFF by default (KernelLocals is nil ⇒ strict) — opt in via [lint.vista] kernel_locals; taint treats formals as attack surface (flow.DefaultTaintConfig). With no config, DefaultOptions reproduces the previous hard-coded behavior except for the now-faithful strict-by-default Kernel allowlist.

func OptionsFromConfig

func OptionsFromConfig(cfg config.Config) Options

OptionsFromConfig resolves a loaded project config into rule-build Options, layering config values over DefaultOptions. Validation already happened in the config package, so the only error path is an internally-inconsistent config (none today). Layering: thresholds merge over the defaults; taint formals/ sanitizers override the built-in TaintConfig; [lint.vista] kernel_locals selects strict / built-in / explicit; severity + disable carry through.

type Rule

type Rule struct {
	ID       string
	Severity Severity
	Category string
	Title    string
	Tags     []string

	// Query rule: Query is tree-sitter source; OnMatch is called per match and
	// returns the finding message + whether to emit. The position is the start
	// of the match's first capture.
	Query   string
	OnMatch func(m parse.Match, src []byte) (string, bool)

	// Walk rule: Inspect traverses root / scans src and returns findings with
	// Line/Col/End* + Message set; the engine stamps Rule + Severity.
	Inspect func(root parse.Node, src []byte) []Finding

	// Name-aware walk rule: like Inspect but also receives the routine name
	// (the file's base name without extension, as passed to LintNamed). Used by
	// rules that compare against the routine identity (e.g. M-XINDX-017). When
	// the name is "" (plain Lint, or a non-file source) the rule should no-op.
	InspectNamed func(root parse.Node, src []byte, routine string) []Finding

	// Cross-routine walk rule: also receives the routine name and the workspace
	// index (labels + references across all linted files). Used by M-XINDX-007/
	// 008/049. Runs only when a workspace is attached (Linter.AttachWorkspace);
	// with no workspace these rules are skipped (faithful to the Python runner).
	InspectWorkspace func(root parse.Node, src []byte, routine string, ws *workspace.Index) []Finding
}

Rule is a lint rule. Exactly one of Query (with OnMatch) or Inspect is set.

func All

func All() []Rule

All returns every registered rule with the built-in default configuration.

func AllWith

func AllWith(opts Options) []Rule

AllWith returns every registered rule, baking the resolved config into the rules that need it (thresholds, Kernel allowlist, taint config). The config- neutral rules are returned as-is.

func Profile

func Profile(name string) []Rule

Profile resolves a profile name to its rules with the default config.

func ProfileWith

func ProfileWith(name string, opts Options) []Rule

ProfileWith resolves a profile name to its rules, by tag (spec §3.1):

default  = modern minus pedantic (the curated, low-noise set)
modern   = everything tagged "modern"
pythonic = modern (alias for now)
pedantic = everything tagged "pedantic"
all      = every rule

func Resolve

func Resolve(filter string, opts Options) ([]Rule, error)

Resolve turns a rule filter (a profile name, or a comma-list mixing profile names and rule IDs) plus build Options into the final rule set: profile/ID selection, then lint disable drops rules, then [lint.severity] re-stamps Rule.Severity. Faithful to the Python select_rules + post-selection layering.

func (Rule) NeedsWorkspace

func (r Rule) NeedsWorkspace() bool

NeedsWorkspace reports whether the rule requires a cross-routine workspace index (so callers can skip building one when no selected rule uses it).

type Severity

type Severity string

Severity ranks a finding.

const (
	Error   Severity = "error"
	Warning Severity = "warning"
	Style   Severity = "style"
	Info    Severity = "info"
)

type Thresholds

type Thresholds struct {
	LineLength    int // M-MOD-001
	DotBlockDepth int // M-MOD-007
	ArgumentCount int // M-MOD-008
	CommandsLine  int // M-MOD-009
}

Thresholds are the resolved numeric limits the metric rules enforce. Only the four the Go linter wires today live here; the config layer validates the full Python threshold set but silently ignores keys no rule consumes yet.

func DefaultThresholds

func DefaultThresholds() Thresholds

DefaultThresholds mirrors the Python defaults (lint/thresholds.py).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL