Skip to content

Commit a75eff4

Browse files
psteinroeclaude
andauthored
refactor(analyser): separate concerns (#617)
Refactors `pgls_analyse` and `pgls_analyser` so that "source code linter"-concerns only live in `analyser`. `analyse` only contains shared concerns such as `RuleMeta` and a generic `RuleVisitor`. This is in preparation for the integration of other type of linters (e.g. `splinter`), which will use the same infrastructure as much as possible. Every execution concern was moved to `pgls_analyser`, since other linters will not run on the AST and have a different execution model. --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7545ca7 commit a75eff4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+852
-541
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

PLAN.md

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Splinter Integration Plan
2+
3+
## Goal
4+
Integrate splinter into the codegen/rule setup used for the analyser, providing a consistent API (both internally and user-facing) for all types of analysers/linters.
5+
6+
## Architecture Vision
7+
8+
### Crate Responsibilities
9+
10+
**`pgls_analyse`** - Generic framework for all analyzer types
11+
- Generic traits: `RuleMeta`, `RuleGroup`, `GroupCategory`, `RegistryVisitor`
12+
- Shared types: `RuleMetadata`, `RuleCategory`
13+
- Configuration traits (execution-agnostic)
14+
- Macros: `declare_rule!`, `declare_group!`, `declare_category!`
15+
16+
**`pgls_linter`** (renamed from `pgls_analyser`) - AST-based source code linting
17+
- `LinterRule` trait (extends `RuleMeta`)
18+
- `LinterRuleContext` (wraps AST nodes)
19+
- `LinterDiagnostic` (span-based diagnostics)
20+
- `LinterRuleRegistry` (type-erased executors)
21+
22+
**`pgls_splinter`** - Database-level linting
23+
- `SplinterRule` trait (extends `RuleMeta`)
24+
- `SplinterRuleRegistry` (metadata-based)
25+
- `SplinterDiagnostic` (db-object-based diagnostics)
26+
- Generated rule types from SQL files
27+
28+
**`pgls_configuration`**
29+
- `analyser/linter/` - Generated from `pgls_linter`
30+
- `analyser/splinter/` - Generated from `pgls_splinter`
31+
- Per-rule configuration for both
32+
33+
## Implementation Phases
34+
35+
### Phase 1: Refactor pgls_analyse ⏳ IN PROGRESS
36+
Extract AST-specific code into pgls_linter, keep only generic framework in pgls_analyse.
37+
38+
**Tasks:**
39+
- [x] Analyze current `pgls_analyse` exports
40+
- [x] Identify AST-specific vs generic code
41+
- [x] Create new modules in `pgls_analyser`:
42+
- [x] `linter_rule.rs` - LinterRule trait, LinterDiagnostic
43+
- [x] `linter_context.rs` - LinterRuleContext, AnalysedFileContext
44+
- [x] `linter_options.rs` - LinterOptions, LinterRules
45+
- [x] `linter_registry.rs` - LinterRuleRegistry, LinterRegistryVisitor
46+
- [x] Create `pgls_analyse/src/metadata.rs` - Generic traits only
47+
- [x] Update `pgls_analyse/src/registry.rs` - Keep MetadataRegistry only
48+
- [x] Update `pgls_analyse/src/lib.rs` - Export generic framework
49+
- [x] Update `pgls_analyser/src/lib.rs` - Use new modules
50+
- [x] Fix imports in filter.rs (RuleMeta instead of Rule)
51+
- [x] Update generated files (options.rs, registry.rs)
52+
- [x] Fix imports in all rule files
53+
- [x] Add rustc-hash dependency
54+
- [x] Verify compilation completes - **RESOLVED**
55+
- [x] Separate visitor concerns from executor creation
56+
- [x] Update codegen to generate factory function
57+
- [x] Fix all import paths across workspace
58+
- [x] Verify full workspace compiles
59+
- [ ] Run tests
60+
61+
**Resolution:**
62+
Separated two concerns:
63+
1. **Visitor pattern** (generic): Collects rule keys that match the filter
64+
- Implementation in `LinterRuleRegistryBuilder::record_rule`
65+
- Only requires `R: RuleMeta` (satisfies trait)
66+
2. **Factory mapping** (AST-specific): Maps rule keys to executor factories
67+
- Function `get_linter_rule_factory` in `registry.rs`
68+
- Will be generated by codegen with full type information
69+
- Each factory can require `R: LinterRule<Options: Default>`
70+
71+
**Changes Made:**
72+
- `LinterRuleRegistryBuilder` stores `Vec<RuleKey>` instead of factories
73+
- `record_rule` just collects keys (no LinterRule bounds needed)
74+
- `build()` calls `get_linter_rule_factory` to create executors
75+
- Added stub `get_linter_rule_factory` in `registry.rs` (will be generated)
76+
77+
**Next Steps:**
78+
- Update codegen to generate `get_linter_rule_factory` with match on all rules
79+
80+
**Design Decisions:**
81+
- ✅ Keep `RuleDiagnostic` generic or make it linter-specific? → **Move to pgls_linter as LinterDiagnostic** (Option A)
82+
- Rationale: Fundamentally different location models (spans vs db objects)
83+
- LinterDiagnostic: span-based
84+
- SplinterDiagnostic: db-object-based
85+
86+
**Code Classification:**
87+
88+
AST-specific (move to pgls_analyser):
89+
- `Rule` trait
90+
- `RuleContext`
91+
- `RuleDiagnostic``LinterDiagnostic`
92+
- `AnalysedFileContext`
93+
- `RegistryRuleParams`
94+
- `RuleRegistry`, `RuleRegistryBuilder` (AST execution)
95+
- `AnalyserOptions`, `AnalyserRules` (rule options storage)
96+
97+
Generic (keep in pgls_analyse):
98+
- `RuleMeta` trait
99+
- `RuleMetadata` struct
100+
- `RuleGroup` trait
101+
- `GroupCategory` trait
102+
- `RegistryVisitor` trait
103+
- `RuleCategory` enum
104+
- `RuleSource` enum
105+
- `RuleFilter`, `AnalysisFilter`, `RuleKey`, `GroupKey`
106+
- `MetadataRegistry`
107+
- Macros: `declare_rule!`, `declare_lint_rule!`, `declare_lint_group!`, `declare_category!`
108+
109+
---
110+
111+
### Phase 2: Enhance pgls_splinter 📋 PLANNED
112+
Add rule type generation and registry similar to linter.
113+
114+
**Tasks:**
115+
- [ ] Create `pgls_splinter/src/rule.rs` with `SplinterRule` trait
116+
- [ ] Create `pgls_splinter/src/rules/` directory structure
117+
- [ ] Generate rule types from SQL files
118+
- [ ] Generate registry with `visit_registry()` function
119+
- [ ] Update diagnostics to use generated categories
120+
121+
**Structure:**
122+
```
123+
pgls_splinter/src/
124+
rules/
125+
performance/
126+
unindexed_foreign_keys.rs
127+
auth_rls_initplan.rs
128+
security/
129+
auth_users_exposed.rs
130+
rule.rs # SplinterRule trait
131+
registry.rs # Generated visit_registry()
132+
```
133+
134+
---
135+
136+
### Phase 3: Update codegen for both linters 📋 PLANNED
137+
Generalize codegen to handle both linter types.
138+
139+
**Tasks:**
140+
- [ ] Rename `generate_analyser.rs``generate_linter.rs`
141+
- [ ] Enhance `generate_splinter.rs` to generate rules + registry
142+
- [ ] Update `generate_configuration.rs` for both linters
143+
- [ ] Update justfile commands
144+
- [ ] Test full generation cycle
145+
146+
**Codegen outputs:**
147+
- Linter: registry.rs, options.rs, configuration
148+
- Splinter: rules/, registry.rs, configuration
149+
150+
---
151+
152+
### Phase 4: Rename pgls_analyser → pgls_linter 📋 PLANNED
153+
Final rename to clarify purpose.
154+
155+
**Tasks:**
156+
- [ ] Rename crate in Cargo.toml
157+
- [ ] Update all imports
158+
- [ ] Update documentation
159+
- [ ] Update CLAUDE.md / AGENTS.md
160+
- [ ] Verify tests pass
161+
162+
---
163+
164+
## Progress Tracking
165+
166+
### Current Status
167+
- [x] Requirements gathering & design
168+
- [x] Architecture proposal (Option 1 - Dual-Track)
169+
- [ ] Phase 1: Refactor pgls_analyse - **IN PROGRESS**
170+
- [ ] Phase 2: Enhance pgls_splinter
171+
- [ ] Phase 3: Update codegen
172+
- [ ] Phase 4: Rename to pgls_linter
173+
174+
### Open Questions
175+
None currently
176+
177+
### Decisions Made
178+
1. Use `LinterRule` (not `ASTRule` or `SourceCodeRule`) for clarity
179+
2. Use `SplinterRule` for database-level rules
180+
3. Keep codegen in xtask (not build.rs) for consistency
181+
4. Mirror file structure between linter and splinter
182+
183+
---
184+
185+
## Testing Strategy
186+
- [ ] Existing linter tests continue to pass
187+
- [ ] Splinter rules generate correctly from SQL
188+
- [ ] Configuration schema validates
189+
- [ ] Integration test: enable/disable rules via config
190+
- [ ] Integration test: severity overrides work
191+
192+
---
193+
194+
Last updated: 2025-12-14

crates/pgls_analyse/src/context.rs

Lines changed: 0 additions & 96 deletions
This file was deleted.

crates/pgls_analyse/src/filter.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::fmt::{Debug, Display, Formatter};
22

33
use crate::{
44
categories::RuleCategories,
5-
rule::{GroupCategory, Rule, RuleGroup},
5+
metadata::{GroupCategory, RuleGroup, RuleMeta},
66
};
77

88
/// Allow filtering a single rule or group of rules by their names
@@ -52,7 +52,7 @@ impl<'analysis> AnalysisFilter<'analysis> {
5252
}
5353

5454
/// Return `true` if the rule `R` matches this filter
55-
pub fn match_rule<R: Rule>(&self) -> bool {
55+
pub fn match_rule<R: RuleMeta>(&self) -> bool {
5656
self.match_category::<<R::Group as RuleGroup>::Category>()
5757
&& self.enabled_rules.is_none_or(|enabled_rules| {
5858
enabled_rules.iter().any(|filter| filter.match_rule::<R>())
@@ -83,7 +83,7 @@ impl<'a> RuleFilter<'a> {
8383
/// Return `true` if the rule `R` matches this filter
8484
pub fn match_rule<R>(self) -> bool
8585
where
86-
R: Rule,
86+
R: RuleMeta,
8787
{
8888
match self {
8989
RuleFilter::Group(group) => group == <R::Group as RuleGroup>::NAME,
@@ -160,7 +160,7 @@ impl RuleKey {
160160
Self { group, rule }
161161
}
162162

163-
pub fn rule<R: Rule>() -> Self {
163+
pub fn rule<R: RuleMeta>() -> Self {
164164
Self::new(<R::Group as RuleGroup>::NAME, R::METADATA.name)
165165
}
166166

crates/pgls_analyse/src/lib.rs

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,16 @@
1-
mod analysed_file_context;
21
mod categories;
3-
pub mod context;
42
mod filter;
53
pub mod macros;
6-
pub mod options;
4+
mod metadata;
75
mod registry;
8-
mod rule;
96

107
// Re-exported for use in the `declare_group` macro
118
pub use pgls_diagnostics::category_concat;
129

13-
pub use crate::analysed_file_context::AnalysedFileContext;
1410
pub use crate::categories::{
1511
ActionCategory, RefactorKind, RuleCategories, RuleCategoriesBuilder, RuleCategory,
1612
SUPPRESSION_ACTION_CATEGORY, SourceActionKind,
1713
};
1814
pub use crate::filter::{AnalysisFilter, GroupKey, RuleFilter, RuleKey};
19-
pub use crate::options::{AnalyserOptions, AnalyserRules};
20-
pub use crate::registry::{
21-
MetadataRegistry, RegistryRuleParams, RegistryVisitor, RuleRegistry, RuleRegistryBuilder,
22-
};
23-
pub use crate::rule::{
24-
GroupCategory, Rule, RuleDiagnostic, RuleGroup, RuleMeta, RuleMetadata, RuleSource,
25-
};
15+
pub use crate::metadata::{GroupCategory, RuleGroup, RuleMeta, RuleMetadata, RuleSource};
16+
pub use crate::registry::{MetadataRegistry, RegistryVisitor};

0 commit comments

Comments
 (0)