Skip to main content

harn_lint/
diagnostic.rs

1//! Public diagnostic types emitted by the linter and caller-supplied
2//! options. Kept separate from the linter's walk state so the public
3//! surface is easy to locate and audit.
4
5use std::borrow::Cow;
6use std::path::PathBuf;
7
8use harn_lexer::{FixEdit, Span};
9use harn_parser::{DiagnosticCode as Code, Repair};
10
11/// A lint diagnostic reported by the linter.
12#[derive(Debug, Clone)]
13pub struct LintDiagnostic {
14    pub code: Code,
15    /// Stable rule identifier. Owned (`Cow`) so dynamically-registered
16    /// rules — engine patterns, `.harn`-authored rules — can carry a
17    /// runtime id, while built-in rules stay zero-cost `Cow::Borrowed`.
18    pub rule: Cow<'static, str>,
19    pub message: String,
20    pub span: Span,
21    pub severity: LintSeverity,
22    pub suggestion: Option<String>,
23    /// Machine-applicable fix edits (applied in order, non-overlapping).
24    pub fix: Option<Vec<FixEdit>>,
25}
26
27impl LintDiagnostic {
28    /// Materialize the structured [`Repair`] for this lint, derived from
29    /// the central diagnostic-code registry. Returns `None` when no
30    /// actionable repair shape is registered.
31    ///
32    /// `LintDiagnostic` does not store `repair` inline (unlike
33    /// `TypeDiagnostic`) because every lint's safety class is purely a
34    /// function of its `code`; storing a per-site copy would invite
35    /// drift. Callers that need the dispatch handle ask for it here.
36    pub fn repair(&self) -> Option<Repair> {
37        self.code.repair_template().map(Repair::from_template)
38    }
39}
40
41/// Severity level for lint diagnostics.
42#[derive(Debug, Clone, Copy, PartialEq, Eq)]
43pub enum LintSeverity {
44    Info,
45    Warning,
46    Error,
47}
48
49/// Default cyclomatic-complexity threshold. Callers can override via
50/// [`LintOptions::complexity_threshold`] (wired to
51/// `[lint].complexity_threshold` in `harn.toml`). Chosen to match
52/// Clippy's `cognitive_complexity` default and sit between ESLint (20)
53/// and gocyclo (30); Harn's scorer counts `&&`/`||` per operator, so
54/// real-world Harn functions score a notch higher than in tools that
55/// only count control-flow nodes.
56pub const DEFAULT_COMPLEXITY_THRESHOLD: usize = 25;
57
58/// Extra options for source-aware lint rules (path-aware rules, opt-in
59/// rules like `require-file-header`).
60#[derive(Debug, Default, Clone)]
61pub struct LintOptions<'a> {
62    /// Filesystem path of the source being linted. Used by rules like
63    /// `require-file-header` to derive a title from the basename.
64    pub file_path: Option<&'a std::path::Path>,
65    /// When true, the opt-in `require-file-header` rule runs.
66    pub require_file_header: bool,
67    /// When true, the opt-in `missing-harndoc` rule warns on public
68    /// functions without a `/** */` doc comment. Off by default —
69    /// enabled via `[lint] require_docstrings = true` in `harn.toml`,
70    /// and implied by `require_stdlib_metadata`.
71    pub require_docstrings: bool,
72    /// Override the cyclomatic-complexity threshold. `None` uses
73    /// [`DEFAULT_COMPLEXITY_THRESHOLD`].
74    pub complexity_threshold: Option<usize>,
75    /// Extra non-stdlib function names that persona bodies may call
76    /// without requiring a `@step` declaration.
77    pub persona_step_allowlist: &'a [String],
78    /// When true, the `HARN-STD-101` lint enforces an `@effects` +
79    /// `@errors` block on every `pub fn`. Auto-enabled by `harn lint`
80    /// for files under `crates/harn-stdlib/src/stdlib/`.
81    pub require_stdlib_metadata: bool,
82    /// TOML sources of declarative rule-engine rules to run as lint rules,
83    /// loaded from the project's `[rules] ruleDirs`. Each is compiled and
84    /// wrapped as a `Rule`; an invalid one is skipped, not fatal.
85    pub engine_rules: &'a [String],
86    /// Trusted native rule libraries to load for this lint run. These are
87    /// discovered only from explicit `[rules] nativeRuleDirs` entries in project
88    /// config; loading native code is an opt-in capability decision.
89    pub native_rule_paths: &'a [PathBuf],
90    /// Per-rule severity overrides: a rule id → the severity to report its
91    /// diagnostics at, applied after disable-filtering. Lets a project promote
92    /// a rule to `error` or demote one to `info` via `[lint]`. Owned (not
93    /// `&[..]`) because a `HashMap` reference has no const empty default.
94    pub severity_overrides: std::collections::HashMap<String, LintSeverity>,
95}