Skip to content

Commit

Permalink
sqf: add lint for unneeded not (#803)
Browse files Browse the repository at this point in the history
  • Loading branch information
PabstMirror authored Dec 18, 2024
1 parent 65cb4e0 commit cf3a9e4
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 0 deletions.
134 changes: 134 additions & 0 deletions libs/sqf/src/analyze/lints/s19_extra_not.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use std::{ops::Range, sync::Arc};

use hemtt_common::config::LintConfig;
use hemtt_workspace::{
lint::{AnyLintRunner, Lint, LintRunner},
reporting::{Code, Codes, Diagnostic, Processed, Severity},
};

use crate::{analyze::SqfLintData, Expression, UnaryCommand};

crate::analyze::lint!(LintS19ExtraNot);

impl Lint<SqfLintData> for LintS19ExtraNot {
fn ident(&self) -> &'static str {
"extra_not"
}

fn sort(&self) -> u32 {
190
}

fn description(&self) -> &'static str {
"Checks for extra not before a comparison"
}

fn documentation(&self) -> &'static str {
r"### Example
**Incorrect**
```sqf
! (5 isEqualTo 6)
```
**Correct**
```sqf
(5 isNotEqualTo 6)
```
"
}

fn default_config(&self) -> LintConfig {
LintConfig::help()
}

fn runners(&self) -> Vec<Box<dyn AnyLintRunner<SqfLintData>>> {
vec![Box::new(Runner)]
}
}

struct Runner;
impl LintRunner<SqfLintData> for Runner {
type Target = crate::Expression;

fn run(
&self,
_project: Option<&hemtt_common::config::ProjectConfig>,
config: &LintConfig,
processed: Option<&hemtt_workspace::reporting::Processed>,
target: &Self::Target,
_data: &SqfLintData,
) -> Codes {
const COMPARISON_CMDS: &[&str] = &[
"==",
"!=",
"isEqualTo",
"isNotEqualTo",
"<",
"<=",
">",
">=",
];
let Some(processed) = processed else {
return Vec::new();
};
let Expression::UnaryCommand(UnaryCommand::Not, rhs, range) = target else {
return Vec::new();
};
let Expression::BinaryCommand(ref last_cmd, _, _, _) = **rhs else {
return Vec::new();
};
if !COMPARISON_CMDS.contains(&last_cmd.as_str()) {
return Vec::new();
}

vec![Arc::new(Code19ExtraNot::new(
range.clone(),
processed,
config.severity(),
))]
}
}

#[allow(clippy::module_name_repetitions)]
pub struct Code19ExtraNot {
span: Range<usize>,
severity: Severity,
diagnostic: Option<Diagnostic>,
}

impl Code for Code19ExtraNot {
fn ident(&self) -> &'static str {
"L-S19"
}
fn link(&self) -> Option<&str> {
Some("/analysis/sqf.html#extra_not")
}
fn severity(&self) -> Severity {
self.severity
}
fn message(&self) -> String {
"Unneeded Not".to_string()
}
fn label_message(&self) -> String {
"unneeded not".to_string()
}
fn diagnostic(&self) -> Option<Diagnostic> {
self.diagnostic.clone()
}
}

impl Code19ExtraNot {
#[must_use]
pub fn new(span: Range<usize>, processed: &Processed, severity: Severity) -> Self {
Self {
span,
severity,
diagnostic: None,
}
.generate_processed(processed)
}
fn generate_processed(mut self, processed: &Processed) -> Self {
self.diagnostic = Diagnostic::from_code_processed(&self, self.span.clone(), processed);
self
}
}
1 change: 1 addition & 0 deletions libs/sqf/tests/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ lint!(s09_banned_command);
lint!(s11_if_not_else);
lint!(s17_var_all_caps);
lint!(s18_in_vehicle_check);
lint!(s19_extra_not);
lint!(s20_bool_static_comparison);
lint!(s21_invalid_comparisons);
lint!(s22_this_call);
Expand Down
1 change: 1 addition & 0 deletions libs/sqf/tests/lints/s19_extra_not.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
! (5 isEqualTo 6)
9 changes: 9 additions & 0 deletions libs/sqf/tests/snapshots/lints__simple_s19_extra_not.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: libs/sqf/tests/lints.rs
expression: lint(stringify! (s19_extra_not))
---
help[L-S19]: Unneeded Not
┌─ s19_extra_not.sqf:1:1
│
1 │ ! (5 isEqualTo 6)
│ ^ unneeded not

0 comments on commit cf3a9e4

Please sign in to comment.