Skip to main content

perl_diagnostics/codes/
mod.rs

1//! Diagnostic codes, severity levels, tags, and categories.
2//!
3//! This module contains the canonical definitions of all diagnostic codes used
4//! throughout the Perl LSP ecosystem. These codes are stable and can be
5//! referenced in documentation and error messages.
6//!
7//! # Code Ranges
8//!
9//! | Range       | Category                  |
10//! |-------------|---------------------------|
11//! | PL001-PL099 | Parser diagnostics        |
12//! | PL100-PL199 | Strict/warnings           |
13//! | PL200-PL299 | Package/module            |
14//! | PL300-PL399 | Subroutine                |
15//! | PL400-PL499 | Best practices            |
16//! | PL500-PL599 | Deprecated syntax         |
17//! | PL600-PL699 | Security                  |
18//! | PL700-PL799 | Import                    |
19//! | PL800-PL899 | Heredoc anti-patterns     |
20//! | PL900-PL999 | Version compatibility     |
21//! | PC001-PC005 | Perl::Critic violations   |
22
23use std::fmt;
24
25mod category;
26mod metadata;
27mod severity;
28mod tag;
29
30pub use category::DiagnosticCategory;
31pub use severity::DiagnosticSeverity;
32pub use tag::DiagnosticTag;
33
34/// Stable diagnostic codes for Perl LSP.
35///
36/// Each code has a fixed string representation and associated metadata.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
39pub enum DiagnosticCode {
40    // Parser diagnostics (PL001-PL099)
41    /// General parse error
42    #[default]
43    ParseError,
44    /// Syntax error
45    SyntaxError,
46    /// Unexpected end-of-file
47    UnexpectedEof,
48
49    // Strict/warnings (PL100-PL199)
50    /// Missing 'use strict' pragma
51    MissingStrict,
52    /// Missing 'use warnings' pragma
53    MissingWarnings,
54    /// Unused variable
55    UnusedVariable,
56    /// Undefined variable
57    UndefinedVariable,
58    /// Variable shadowing an outer declaration
59    VariableShadowing,
60    /// Variable redeclared in the same scope
61    VariableRedeclaration,
62    /// Duplicate parameter in a subroutine signature
63    DuplicateParameter,
64    /// Subroutine parameter shadows a global variable
65    ParameterShadowsGlobal,
66    /// Subroutine parameter is declared but never used
67    UnusedParameter,
68    /// Bareword used where a quoted string is expected (under strict)
69    UnquotedBareword,
70    /// Variable used before being initialized
71    UninitializedVariable,
72    /// Pragma name appears to be misspelled
73    MisspelledPragma,
74    /// Capture variable ($1, $2, etc.) used without a preceding regex match in scope
75    CaptureVarWithoutRegexMatch,
76
77    // Package/module (PL200-PL299)
78    /// Missing package declaration
79    MissingPackageDeclaration,
80    /// Duplicate package declaration
81    DuplicatePackage,
82
83    // Subroutine (PL300-PL399)
84    /// Duplicate subroutine definition
85    DuplicateSubroutine,
86    /// Missing explicit return statement
87    MissingReturn,
88    /// Invalid character(s) in a subroutine prototype
89    ///
90    /// Perl only allows `$`, `@`, `%`, `&`, `*`, `\`, `;`, `+`, `_`, and
91    /// spaces in old-style prototypes.  Any other character triggers Perl's
92    /// "Illegal character in prototype" warning.
93    InvalidPrototype,
94    /// Same-file Moo/Moose roles provide conflicting methods
95    RoleConflict,
96    /// Exported subroutine lacks POD documentation
97    MissingPodCoverage,
98
99    // Best practices (PL400-PL499)
100    /// Bareword filehandle usage
101    BarewordFilehandle,
102    /// Two-argument open() call
103    TwoArgOpen,
104    /// Implicit return value
105    ImplicitReturn,
106    /// Assignment used where a comparison was likely intended
107    AssignmentInCondition,
108    /// Numeric comparison against a potentially undefined value
109    NumericComparisonWithUndef,
110    /// printf/sprintf format specifier count does not match argument count
111    PrintfFormatMismatch,
112    /// Statement that cannot be reached due to preceding unconditional exit
113    UnreachableCode,
114    /// `$@` / `$EVAL_ERROR` reads that are not paired with a nearby `eval`/`try`
115    EvalErrorFlow,
116    /// Duplicate key in a hash literal or hash reference constructor
117    DuplicateHashKey,
118    /// `goto LABEL` references a label that does not exist in this file
119    GotoUndefinedLabel,
120    /// `next`/`last`/`redo LABEL` references a label that does not exist in this file
121    LoopControlUndefinedLabel,
122
123    // Pragma pitfalls / deprecated syntax (PL500-PL599)
124    /// Use of deprecated defined(@array) / defined(%hash)
125    DeprecatedDefined,
126    /// Use of deprecated $[ array base variable
127    DeprecatedArrayBase,
128    /// `use strict` appears only inside a phase block and does not affect file scope
129    PhaseScopedStrictPragma,
130    /// `use warnings` appears only inside a phase block and does not affect file scope
131    PhaseScopedWarningsPragma,
132
133    // Security (PL600-PL699)
134    /// String eval is a security risk
135    SecurityStringEval,
136    /// Backtick/qx command execution detected
137    SecurityBacktickExec,
138    /// Global assignment to `$SIG{__DIE__}` / `$SIG{__WARN__}`
139    SecuritySignalHandler,
140    /// `system()` call executes shell commands
141    SecuritySystemCall,
142    /// `exec()` call replaces the current process with a shell command
143    SecurityExecCall,
144    /// Pipe-open `open(FH, "|-", ...)` / `open(FH, "-|", ...)` executes shell commands
145    SecurityPipeOpen,
146    /// `readpipe()` function call executes shell commands (equivalent to qx//)
147    SecurityReadpipe,
148
149    // Import (PL700-PL799)
150    /// Module appears to be unused
151    UnusedImport,
152    /// Module not found in workspace or configured include paths
153    ModuleNotFound,
154
155    // Heredoc anti-patterns (PL800-PL899)
156    /// Heredoc used inside a format block
157    HeredocInFormat,
158    /// Heredoc used inside a BEGIN block
159    HeredocInBegin,
160    /// Heredoc delimiter is dynamic (variable interpolation)
161    HeredocDynamicDelimiter,
162    /// Heredoc used inside a source filter
163    HeredocInSourceFilter,
164    /// Heredoc used inside a regex code block
165    HeredocInRegexCode,
166    /// Heredoc used inside string eval
167    HeredocInEval,
168    /// Heredoc used with a tied filehandle
169    HeredocTiedHandle,
170
171    // Version compatibility (PL900-PL999)
172    /// Use of a Perl feature not available in the declared version
173    VersionIncompatFeature,
174
175    // Perl::Critic violations (PC001-PC005)
176    /// Perl::Critic brutal (severity 1) violation
177    CriticSeverity1,
178    /// Perl::Critic cruel (severity 2) violation
179    CriticSeverity2,
180    /// Perl::Critic harsh (severity 3) violation
181    CriticSeverity3,
182    /// Perl::Critic stern (severity 4) violation
183    CriticSeverity4,
184    /// Perl::Critic gentle (severity 5) violation
185    CriticSeverity5,
186}
187
188impl fmt::Display for DiagnosticCode {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        write!(f, "{}", self.as_str())
191    }
192}