Skip to main content

mago_analyzer/
settings.rs

1use mago_algebra::AlgebraThresholds;
2use mago_atom::AtomSet;
3use mago_php_version::PHPVersion;
4
5/// Default maximum logical formula size during conditional analysis.
6pub const DEFAULT_FORMULA_SIZE_THRESHOLD: u16 = 512;
7
8/// Default maximum number of combinations to track during string concatenation.
9pub const DEFAULT_STRING_CONCAT_COMBINATION_THRESHOLD: u16 = 512;
10
11/// Configuration settings that control the behavior of the Mago analyzer.
12///
13/// This struct allows you to enable/disable specific checks, suppress categories of issues,
14/// and tune the analyzer's performance and strictness.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Settings {
17    /// The target PHP version for the analysis.
18    pub version: PHPVersion,
19
20    /// Find and report expressions whose results are not used (e.g., `$a + $b;`). Defaults to `false`.
21    pub find_unused_expressions: bool,
22
23    /// Find and report unused definitions (e.g., private methods that are never called). Defaults to `false`.
24    pub find_unused_definitions: bool,
25
26    /// Analyze code that appears to be unreachable. Defaults to `false`.
27    pub analyze_dead_code: bool,
28
29    /// Track the literal values of class properties when they are assigned.
30    /// This improves type inference but may increase memory usage. Defaults to `true`.
31    pub memoize_properties: bool,
32
33    /// Allow accessing array keys that may not be defined without reporting an issue. Defaults to `true`.
34    pub allow_possibly_undefined_array_keys: bool,
35
36    /// Enable checking for unhandled thrown exceptions.
37    ///
38    /// When `true`, the analyzer will report any exception that is thrown but not caught
39    /// in a `try-catch` block or documented in a `@throws` tag.
40    ///
41    /// This check is disabled by default (`false`) as it can be computationally expensive.
42    pub check_throws: bool,
43
44    /// Exceptions to ignore including all subclasses (hierarchy-aware).
45    ///
46    /// When an exception class is in this set, any exception of that class or any of its
47    /// subclasses will be ignored during `check_throws` analysis.
48    ///
49    /// For example, adding `LogicException` will ignore `LogicException`, `InvalidArgumentException`,
50    /// `OutOfBoundsException`, and all other subclasses.
51    pub unchecked_exceptions: AtomSet,
52
53    /// Exceptions to ignore (exact class match only, not subclasses).
54    ///
55    /// When an exception class is in this set, only that exact class will be ignored
56    /// during `check_throws` analysis. Parent classes and subclasses are not affected.
57    pub unchecked_exception_classes: AtomSet,
58
59    /// Check for missing `#[Override]` attributes on overriding methods.
60    ///
61    /// When enabled, the analyzer reports methods that override a parent method without
62    /// the `#[Override]` attribute (PHP 8.3+).
63    ///
64    /// Defaults to `true`.
65    pub check_missing_override: bool,
66
67    /// Find and report unused function/method parameters.
68    ///
69    /// When enabled, the analyzer reports parameters that are declared but never used
70    /// within the function body.
71    ///
72    /// Defaults to `true`.
73    pub find_unused_parameters: bool,
74
75    /// Enforce strict checks when accessing list elements by index.
76    ///
77    /// When `true`, the analyzer requires that any integer used to access a `list`
78    /// element is provably non-negative (e.g., of type `int<0, max>`). This helps
79    /// prevent potential runtime errors from using a negative index.
80    ///
81    /// When `false` (the default), any `int` is permitted as an index, offering
82    /// more flexibility at the cost of type safety.
83    pub strict_list_index_checks: bool,
84
85    /// Disable comparisons to boolean literals (`true`/`false`).
86    ///
87    /// When enabled, comparisons to boolean literals will not be reported as issues.
88    ///
89    /// Defaults to `false`.
90    pub no_boolean_literal_comparison: bool,
91
92    /// Check for missing type hints on parameters, properties, and return types.
93    ///
94    /// When enabled, the analyzer will report warnings for function parameters, class properties,
95    /// and function return types that lack explicit type declarations. The analyzer uses its
96    /// type system knowledge to avoid false positives - for instance, it won't require a type hint
97    /// on a property if adding one would conflict with a parent class or trait that has no type hint.
98    ///
99    /// Defaults to `false`.
100    pub check_missing_type_hints: bool,
101
102    /// Check for missing type hints (both parameters and return types) in closures when `check_missing_type_hints` is enabled.
103    ///
104    /// When `true`, closures (anonymous functions declared with `function() {}`) will be
105    /// checked for missing type hints. When `false`, closures are ignored, which is useful
106    /// because closures often rely on type inference.
107    ///
108    /// Defaults to `false`.
109    pub check_closure_missing_type_hints: bool,
110
111    /// Check for missing type hints (both parameters and return types) in arrow functions when `check_missing_type_hints` is enabled.
112    ///
113    /// When `true`, arrow functions (declared with `fn() => ...`) will be checked for missing
114    /// type hints. When `false`, arrow functions are ignored, which is useful because arrow
115    /// functions often rely on type inference and are typically short, making types obvious.
116    ///
117    /// Defaults to `false`.
118    pub check_arrow_function_missing_type_hints: bool,
119
120    /// Register superglobals (e.g., `$_GET`, `$_POST`, `$_SERVER`) in the analysis context.
121    ///
122    /// If disabled, super globals won't be available unless explicitly imported using
123    /// the `global` keyword.
124    ///
125    /// Defaults to `true`.
126    pub register_super_globals: bool,
127
128    /// Enable colored output in terminal environments that support it. Defaults to `true`.
129    ///
130    /// This setting is primarily used for enabling/disabling colored diffs in
131    /// issue reports.
132    pub use_colors: bool,
133
134    /// **Internal use only.**
135    ///
136    /// Enables a diffing mode for incremental analysis, used by integrations like LSPs.
137    /// This avoids re-analyzing unchanged code in the same session. Defaults to `false`.
138    pub diff: bool,
139
140    /// Trust symbol existence checks to narrow types.
141    ///
142    /// When enabled, conditional checks like `method_exists()`, `property_exists()`,
143    /// `function_exists()`, and `defined()` will narrow the type within the conditional block,
144    /// suppressing errors for symbols that are verified to exist at runtime.
145    ///
146    /// When disabled, these checks are ignored and the analyzer requires explicit type hints,
147    /// which is stricter but may produce more false positives for dynamic code.
148    ///
149    /// Defaults to `true`.
150    pub trust_existence_checks: bool,
151
152    /// Method names treated as class initializers (like `__construct`).
153    ///
154    /// Properties initialized in these methods count as "definitely initialized"
155    /// just like in the constructor. This is useful for frameworks that use
156    /// lifecycle methods like `PHPUnit`'s `setUp()` or framework `boot()` methods.
157    ///
158    /// Example: `["setUp", "initialize", "boot"]`
159    ///
160    /// Defaults to empty (no additional initializers).
161    pub class_initializers: AtomSet,
162
163    /// Enable property initialization checking (`missing-constructor`, `uninitialized-property`).
164    ///
165    /// When `false`, disables both `missing-constructor` and `uninitialized-property` issues
166    /// entirely. This is useful for projects that prefer to rely on runtime errors for
167    /// property initialization.
168    ///
169    /// Defaults to `false`.
170    pub check_property_initialization: bool,
171
172    /// Check for non-existent symbols in use statements.
173    ///
174    /// When enabled, the analyzer will report use statements that import symbols
175    /// (classes, interfaces, traits, enums, functions, or constants) that do not exist
176    /// in the codebase.
177    ///
178    /// Defaults to `false`.
179    pub check_use_statements: bool,
180
181    // Performance tuning thresholds
182    // Higher values allow deeper analysis at the cost of performance.
183    // Lower values improve speed but may reduce precision on complex code.
184    /// Maximum number of clauses to process during CNF saturation.
185    ///
186    /// Controls how many clauses the simplification algorithm will work with.
187    /// If exceeded, saturation returns an empty result to avoid performance issues.
188    ///
189    /// Defaults to `8192`.
190    pub saturation_complexity_threshold: u16,
191
192    /// Maximum number of clauses per side in disjunction operations.
193    ///
194    /// Controls the complexity limit for OR operations between clause sets.
195    /// If either side exceeds this, the disjunction returns an empty result.
196    ///
197    /// Defaults to `4096`.
198    pub disjunction_complexity_threshold: u16,
199
200    /// Maximum cumulative complexity during formula negation.
201    ///
202    /// Controls how complex the negation of a formula can become.
203    /// If exceeded, negation gives up to avoid exponential blowup.
204    ///
205    /// Defaults to `4096`.
206    pub negation_complexity_threshold: u16,
207
208    /// Upper limit for consensus optimization during saturation.
209    ///
210    /// Controls when the consensus rule is applied during saturation.
211    /// Only applies when clause count is between 3 and this limit.
212    ///
213    /// Defaults to `256`.
214    pub consensus_limit_threshold: u16,
215
216    /// Maximum logical formula size during conditional analysis.
217    ///
218    /// Limits the size of generated formulas to prevent exponential blowup
219    /// in deeply nested conditionals.
220    ///
221    /// Defaults to `512`.
222    pub formula_size_threshold: u16,
223
224    /// Maximum number of combinations to track during string concatenation.
225    ///
226    /// Limits the number of possible string literal combinations to prevent
227    /// exponential blowup in large concatenation chains.
228    ///
229    /// Defaults to `512`.
230    pub string_concat_combination_threshold: u16,
231}
232
233impl Default for Settings {
234    fn default() -> Self {
235        Self::new(PHPVersion::LATEST)
236    }
237}
238
239impl Settings {
240    #[must_use]
241    pub fn new(version: PHPVersion) -> Self {
242        let default_thresholds = AlgebraThresholds::default();
243
244        Self {
245            version,
246            find_unused_expressions: true,
247            find_unused_definitions: true,
248            analyze_dead_code: false,
249            memoize_properties: true,
250            allow_possibly_undefined_array_keys: true,
251            check_throws: false,
252            unchecked_exceptions: AtomSet::default(),
253            unchecked_exception_classes: AtomSet::default(),
254            use_colors: true,
255            check_missing_override: false,
256            find_unused_parameters: false,
257            strict_list_index_checks: false,
258            no_boolean_literal_comparison: false,
259            check_missing_type_hints: false,
260            check_closure_missing_type_hints: false,
261            check_arrow_function_missing_type_hints: false,
262            register_super_globals: true,
263            diff: false,
264            trust_existence_checks: true,
265            class_initializers: AtomSet::default(),
266            check_property_initialization: false,
267            check_use_statements: false,
268            saturation_complexity_threshold: default_thresholds.saturation_complexity,
269            disjunction_complexity_threshold: default_thresholds.disjunction_complexity,
270            negation_complexity_threshold: default_thresholds.negation_complexity,
271            consensus_limit_threshold: default_thresholds.consensus_limit,
272            formula_size_threshold: DEFAULT_FORMULA_SIZE_THRESHOLD,
273            string_concat_combination_threshold: DEFAULT_STRING_CONCAT_COMBINATION_THRESHOLD,
274        }
275    }
276
277    /// Returns the algebra thresholds derived from the settings.
278    #[must_use]
279    pub fn algebra_thresholds(&self) -> AlgebraThresholds {
280        AlgebraThresholds {
281            saturation_complexity: self.saturation_complexity_threshold,
282            disjunction_complexity: self.disjunction_complexity_threshold,
283            negation_complexity: self.negation_complexity_threshold,
284            consensus_limit: self.consensus_limit_threshold,
285        }
286    }
287}