Skip to main content

perl_lsp_tooling/perl_critic/
built_in.rs

1use crate::perl_critic::{QuickFix, Severity, Violation, built_in_quick_fix, insertion_range};
2use perl_parser_core::Node;
3
4/// Built-in policy analyzer that works without external perlcritic
5pub struct BuiltInAnalyzer {
6    /// Collection of registered policy implementations
7    policies: Vec<Box<dyn Policy>>,
8}
9
10/// Trait for implementing policies
11pub trait Policy: Send + Sync {
12    /// Returns the fully qualified policy name.
13    fn name(&self) -> &str;
14    /// Returns the severity level for violations of this policy.
15    fn severity(&self) -> Severity;
16    /// Analyzes the AST and source content, returning any violations found.
17    fn analyze(&self, ast: &Node, content: &str) -> Vec<Violation>;
18}
19
20/// Require 'use strict'
21struct RequireUseStrict;
22
23impl Policy for RequireUseStrict {
24    fn name(&self) -> &str {
25        "TestingAndDebugging::RequireUseStrict"
26    }
27
28    fn severity(&self) -> Severity {
29        Severity::Harsh
30    }
31
32    fn analyze(&self, _ast: &Node, content: &str) -> Vec<Violation> {
33        missing_use_statement_violation(
34            self,
35            content,
36            "strict",
37            "Always use strict to catch common mistakes",
38        )
39    }
40}
41
42/// Require 'use warnings'
43struct RequireUseWarnings;
44
45impl Policy for RequireUseWarnings {
46    fn name(&self) -> &str {
47        "TestingAndDebugging::RequireUseWarnings"
48    }
49
50    fn severity(&self) -> Severity {
51        Severity::Harsh
52    }
53
54    fn analyze(&self, _ast: &Node, content: &str) -> Vec<Violation> {
55        missing_use_statement_violation(
56            self,
57            content,
58            "warnings",
59            "Always use warnings to catch potential issues",
60        )
61    }
62}
63
64impl Default for BuiltInAnalyzer {
65    fn default() -> Self {
66        Self { policies: vec![Box::new(RequireUseStrict), Box::new(RequireUseWarnings)] }
67    }
68}
69
70impl BuiltInAnalyzer {
71    /// Creates a new analyzer with default built-in policies.
72    pub fn new() -> Self {
73        Self::default()
74    }
75
76    /// Analyze AST with built-in policies
77    pub fn analyze(&self, ast: &Node, content: &str) -> Vec<Violation> {
78        let mut violations = Vec::new();
79        for policy in &self.policies {
80            violations.extend(policy.analyze(ast, content));
81        }
82        violations
83    }
84
85    /// Get quick fix for a violation
86    pub fn get_quick_fix(&self, violation: &Violation, _content: &str) -> Option<QuickFix> {
87        built_in_quick_fix(violation)
88    }
89}
90
91fn missing_use_statement_violation(
92    policy: &dyn Policy,
93    content: &str,
94    feature: &str,
95    explanation: &str,
96) -> Vec<Violation> {
97    if content.contains(&format!("use {feature}")) {
98        return Vec::new();
99    }
100
101    vec![Violation {
102        policy: policy.name().to_string(),
103        description: format!("Code does not use {feature}"),
104        explanation: explanation.to_string(),
105        severity: policy.severity(),
106        range: insertion_range(),
107        file: String::new(),
108    }]
109}