Skip to main content

normalize_syntax_rules/
lib.rs

1//! Syntax-based linting with tree-sitter queries.
2//!
3//! This crate provides:
4//! - Rule loading from multiple sources (builtins, user global, project)
5//! - Rule execution with combined query optimization
6//! - Pluggable data sources for rule conditionals
7//!
8//! # Rule File Format
9//!
10//! ```scm
11//! # ---
12//! # id = "no-unwrap"
13//! # severity = "warning"
14//! # message = "Avoid unwrap() on user input"
15//! # allow = ["**/tests/**"]
16//! # files = ["**/lib.rs", "**/mod.rs"]
17//! # requires = { "rust.edition" = ">=2024" }
18//! # enabled = true  # set to false to disable a builtin
19//! # fix = ""  # empty = delete match, or use "$capture" to substitute
20//! # ---
21//!
22//! (call_expression
23//!   function: (field_expression
24//!     field: (field_identifier) @method)
25//!   (#eq? @method "unwrap")) @match
26//! ```
27
28mod builtin;
29mod loader;
30pub mod query;
31mod runner;
32mod sources;
33
34#[cfg(feature = "cli")]
35pub mod service;
36
37pub use builtin::BUILTIN_RULES;
38pub use loader::{RuleOverride, RulesConfig, load_all_rules, parse_rule_content};
39pub use query::{MatchResult, is_sexp_pattern, run_astgrep_query, run_sexp_query};
40#[cfg(feature = "fix")]
41pub use runner::apply_fixes;
42pub use runner::{DebugFlags, Finding, evaluate_predicates, run_rules};
43pub use sources::{
44    EnvSource, GitSource, GoSource, PathSource, PythonSource, RuleSource, RustSource,
45    SourceContext, SourceRegistry, TypeScriptSource, builtin_registry,
46};
47
48/// Severity level for rule findings. Defined in normalize-rules-config for sharing
49/// across all rule engines (syntax, fact).
50pub use normalize_rules_config::Severity;
51
52use glob::Pattern;
53use std::collections::HashMap;
54use std::path::PathBuf;
55
56/// A syntax rule definition.
57#[derive(Debug)]
58pub struct Rule {
59    /// Unique identifier for this rule.
60    pub id: String,
61    /// The tree-sitter query pattern.
62    pub query_str: String,
63    /// Severity level.
64    pub severity: Severity,
65    /// Message to display when the rule matches.
66    pub message: String,
67    /// Glob patterns for files where matches are allowed.
68    pub allow: Vec<Pattern>,
69    /// Glob patterns for files where this rule applies (inclusion filter).
70    /// If non-empty, the rule only runs on files that match at least one pattern.
71    /// If empty, the rule runs on all files (subject to `allow` exclusions).
72    pub files: Vec<Pattern>,
73    /// Source file path of this rule (empty for builtins).
74    pub source_path: PathBuf,
75    /// Languages this rule applies to (inferred from query or explicit).
76    pub languages: Vec<String>,
77    /// Whether this rule is enabled.
78    pub enabled: bool,
79    /// Whether this is a builtin rule.
80    pub builtin: bool,
81    /// Conditions that must be met for this rule to apply.
82    /// Format: { "namespace.key" = "value" } or { "namespace.key" = ">=value" }
83    pub requires: HashMap<String, String>,
84    /// Auto-fix template using capture names from the query.
85    ///
86    /// Substitution syntax: `$capture_name` is replaced by the text of the named capture
87    /// (e.g. `$fn_name`), and `$match` is replaced by the entire matched node's text.
88    /// An empty string means "delete the matched node entirely".
89    /// `None` means the rule has no auto-fix.
90    pub fix: Option<String>,
91    /// Tags for grouping and filtering rules by concept (e.g. "debug-print", "security").
92    pub tags: Vec<String>,
93    /// Documentation from the markdown comment block between frontmatter and query.
94    pub doc: Option<String>,
95    /// Whether this rule is recommended for most projects (catches real bugs, not style).
96    pub recommended: bool,
97    /// Whether this rule should fire inside language-specific test regions
98    /// (e.g. Rust `#[cfg(test)] mod ...` blocks). Defaults to `false`, meaning
99    /// findings inside those regions are dropped — appropriate for rules like
100    /// `unwrap-in-impl` or `dbg-macro` where panics/debug calls are expected
101    /// in test code. Style rules, complexity ratchets, and tag-convention
102    /// rules that should fire in tests can opt in by setting this to `true`.
103    ///
104    /// Test-region detection is per-language and lives in
105    /// `{lang}.test_regions.scm` query files. Languages without such a file
106    /// have no AST-based test detection — path-based excludes (e.g.
107    /// `**/tests/**` or `*_test.go`) remain the only mechanism there.
108    pub applies_in_tests: bool,
109}
110
111/// A builtin rule definition (id, content).
112pub struct BuiltinRule {
113    pub id: &'static str,
114    pub content: &'static str,
115}