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}