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}