Skip to content

Commit cf3a9e4

Browse files
authored
sqf: add lint for unneeded not (#803)
1 parent 65cb4e0 commit cf3a9e4

File tree

4 files changed

+145
-0
lines changed

4 files changed

+145
-0
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
use std::{ops::Range, sync::Arc};
2+
3+
use hemtt_common::config::LintConfig;
4+
use hemtt_workspace::{
5+
lint::{AnyLintRunner, Lint, LintRunner},
6+
reporting::{Code, Codes, Diagnostic, Processed, Severity},
7+
};
8+
9+
use crate::{analyze::SqfLintData, Expression, UnaryCommand};
10+
11+
crate::analyze::lint!(LintS19ExtraNot);
12+
13+
impl Lint<SqfLintData> for LintS19ExtraNot {
14+
fn ident(&self) -> &'static str {
15+
"extra_not"
16+
}
17+
18+
fn sort(&self) -> u32 {
19+
190
20+
}
21+
22+
fn description(&self) -> &'static str {
23+
"Checks for extra not before a comparison"
24+
}
25+
26+
fn documentation(&self) -> &'static str {
27+
r"### Example
28+
29+
**Incorrect**
30+
```sqf
31+
! (5 isEqualTo 6)
32+
```
33+
**Correct**
34+
```sqf
35+
(5 isNotEqualTo 6)
36+
```
37+
"
38+
}
39+
40+
fn default_config(&self) -> LintConfig {
41+
LintConfig::help()
42+
}
43+
44+
fn runners(&self) -> Vec<Box<dyn AnyLintRunner<SqfLintData>>> {
45+
vec![Box::new(Runner)]
46+
}
47+
}
48+
49+
struct Runner;
50+
impl LintRunner<SqfLintData> for Runner {
51+
type Target = crate::Expression;
52+
53+
fn run(
54+
&self,
55+
_project: Option<&hemtt_common::config::ProjectConfig>,
56+
config: &LintConfig,
57+
processed: Option<&hemtt_workspace::reporting::Processed>,
58+
target: &Self::Target,
59+
_data: &SqfLintData,
60+
) -> Codes {
61+
const COMPARISON_CMDS: &[&str] = &[
62+
"==",
63+
"!=",
64+
"isEqualTo",
65+
"isNotEqualTo",
66+
"<",
67+
"<=",
68+
">",
69+
">=",
70+
];
71+
let Some(processed) = processed else {
72+
return Vec::new();
73+
};
74+
let Expression::UnaryCommand(UnaryCommand::Not, rhs, range) = target else {
75+
return Vec::new();
76+
};
77+
let Expression::BinaryCommand(ref last_cmd, _, _, _) = **rhs else {
78+
return Vec::new();
79+
};
80+
if !COMPARISON_CMDS.contains(&last_cmd.as_str()) {
81+
return Vec::new();
82+
}
83+
84+
vec![Arc::new(Code19ExtraNot::new(
85+
range.clone(),
86+
processed,
87+
config.severity(),
88+
))]
89+
}
90+
}
91+
92+
#[allow(clippy::module_name_repetitions)]
93+
pub struct Code19ExtraNot {
94+
span: Range<usize>,
95+
severity: Severity,
96+
diagnostic: Option<Diagnostic>,
97+
}
98+
99+
impl Code for Code19ExtraNot {
100+
fn ident(&self) -> &'static str {
101+
"L-S19"
102+
}
103+
fn link(&self) -> Option<&str> {
104+
Some("/analysis/sqf.html#extra_not")
105+
}
106+
fn severity(&self) -> Severity {
107+
self.severity
108+
}
109+
fn message(&self) -> String {
110+
"Unneeded Not".to_string()
111+
}
112+
fn label_message(&self) -> String {
113+
"unneeded not".to_string()
114+
}
115+
fn diagnostic(&self) -> Option<Diagnostic> {
116+
self.diagnostic.clone()
117+
}
118+
}
119+
120+
impl Code19ExtraNot {
121+
#[must_use]
122+
pub fn new(span: Range<usize>, processed: &Processed, severity: Severity) -> Self {
123+
Self {
124+
span,
125+
severity,
126+
diagnostic: None,
127+
}
128+
.generate_processed(processed)
129+
}
130+
fn generate_processed(mut self, processed: &Processed) -> Self {
131+
self.diagnostic = Diagnostic::from_code_processed(&self, self.span.clone(), processed);
132+
self
133+
}
134+
}

libs/sqf/tests/lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ lint!(s09_banned_command);
3232
lint!(s11_if_not_else);
3333
lint!(s17_var_all_caps);
3434
lint!(s18_in_vehicle_check);
35+
lint!(s19_extra_not);
3536
lint!(s20_bool_static_comparison);
3637
lint!(s21_invalid_comparisons);
3738
lint!(s22_this_call);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
! (5 isEqualTo 6)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
source: libs/sqf/tests/lints.rs
3+
expression: lint(stringify! (s19_extra_not))
4+
---
5+
help[L-S19]: Unneeded Not
6+
┌─ s19_extra_not.sqf:1:1
7+
│
8+
1 │ ! (5 isEqualTo 6)
9+
│ ^ unneeded not

0 commit comments

Comments
 (0)