Skip to content

Commit

Permalink
sqf: optimize _this call (#802)
Browse files Browse the repository at this point in the history
  • Loading branch information
BrettMayson authored Oct 18, 2024
1 parent 4d93c00 commit a148b53
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 0 deletions.
144 changes: 144 additions & 0 deletions libs/sqf/src/analyze/lints/s22_this_call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
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, BinaryCommand, Expression};

crate::lint!(LintS22InVehicleCheck);

impl Lint<SqfLintData> for LintS22InVehicleCheck {
fn ident(&self) -> &str {
"this_call"
}

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

fn description(&self) -> &str {
"Checks for usage of `_this call`, where `_this` is not necessary"
}

fn documentation(&self) -> &str {
r"### Example
**Incorrect**
```sqf
_this call _my_function;
```
**Correct**
```sqf
call _my_function;
```
### Explanation
When using `call`, the called code will inherit `_this` from the calling scope. This means that `_this` is not necessary in the call, and can be omitted for better performance.
"
}

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

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 {
let Some(processed) = processed else {
return Vec::new();
};
let Expression::BinaryCommand(BinaryCommand::Named(cmd), lhs, _, _) = target else {
return Vec::new();
};

if cmd.to_lowercase() != "call" {
return Vec::new();
}

if matches!(&**lhs, Expression::Variable(name, _) if name == "_this") {
return vec![Arc::new(CodeS22ThisCall::new(
lhs.span(),
processed,
config.severity(),
))];
}

Vec::new()
}
}

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

impl Code for CodeS22ThisCall {
fn ident(&self) -> &'static str {
"L-S22"
}

fn link(&self) -> Option<&str> {
Some("/analysis/sqf.html#this_call")
}

fn severity(&self) -> Severity {
self.severity
}

fn message(&self) -> String {
"Unnecessary `_this` in `call`".to_string()
}

fn label_message(&self) -> String {
String::new()
}

fn note(&self) -> Option<String> {
Some("`call` inherits `_this` from the calling scope".to_string())
}

fn help(&self) -> Option<String> {
Some("Remove `_this` from the call".to_string())
}

fn diagnostic(&self) -> Option<Diagnostic> {
self.diagnostic.clone()
}
}

impl CodeS22ThisCall {
#[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::new_for_processed(&self, self.span.clone(), processed);
self
}
}
9 changes: 9 additions & 0 deletions libs/sqf/src/compiler/optimizer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,15 @@ impl Expression {
left_o = consumable;
}
}
"call" => {
if matches!(&left_o, Self::Variable(name, _) if name == "_this") {
return Self::UnaryCommand(
UnaryCommand::Named("call".into()),
Box::new(right_o),
range.clone(),
);
}
}
_ => {}
},
BinaryCommand::Add => {
Expand Down
1 change: 1 addition & 0 deletions libs/sqf/tests/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ analyze!(s17_var_all_caps);
analyze!(s18_in_vehicle_check);
analyze!(s20_bool_static_comparison);
analyze!(s21_invalid_comparisons);
analyze!(s22_this_call);
3 changes: 3 additions & 0 deletions libs/sqf/tests/lints/project_tests.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ enabled = true
options.ignore = [
"AM_IGNORED",
]

[lints.sqf.this_call]
enabled = true
7 changes: 7 additions & 0 deletions libs/sqf/tests/lints/s22_this_call/source.sqf
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
private _my_fnc = {
systemChat _this;
};

_this call _my_fnc;

123 call _my_fnc;
9 changes: 9 additions & 0 deletions libs/sqf/tests/lints/s22_this_call/stdout.ansi
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
help[L-S22]: Unnecessary `_this` in `call`
┌─ source.sqf:5:1
│
5 │ _this call _my_fnc;
│ ^^^^^
│
= note: `call` inherits `_this` from the calling scope
= help: Remove `_this` from the call

0 comments on commit a148b53

Please sign in to comment.