Skip to content

Commit 216cfc6

Browse files
committed
Add sp_binop and sp_ternary rules to lint module checks (#19)
* Add sp_binop.rs to spacing rule tests module for BinaryExpressionContent * Add sp_ternary rule for ? : conditionals for TertiaryExpressionContent
1 parent ccfa36e commit 216cfc6

File tree

9 files changed

+295
-43
lines changed

9 files changed

+295
-43
lines changed

example_files/example_lint_cfg.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
{
22
"sp_brace": {},
33
"sp_punct": {},
4+
"sp_binop": {},
5+
"sp_ternary" : {},
46
"nsp_funpar": {},
57
"nsp_inparen": {},
68
"nsp_unary": {},

src/analysis/parsing/expression.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use crate::lint::{DMLStyleError,
2121
rules::{spacing::{NspFunparArgs,
2222
NspInparenArgs,
2323
NspUnaryArgs,
24+
SpBinopArgs,
25+
SpTernaryArgs,
2426
SpPunctArgs},
2527
CurrentRules},
2628
AuxParams};
@@ -92,6 +94,9 @@ impl TreeElement for BinaryExpressionContent {
9294
fn subs(&self) -> TreeElements<'_> {
9395
create_subs!(&self.left, &self.operation, &self.right)
9496
}
97+
fn evaluate_rules(&self, acc: &mut Vec<DMLStyleError>, rules: &CurrentRules, _aux: AuxParams) {
98+
rules.sp_binop.check(acc, SpBinopArgs::from_binary_expression_content(self));
99+
}
95100
}
96101

97102
#[derive(Debug, Clone, PartialEq)]
@@ -151,6 +156,9 @@ impl TreeElement for TertiaryExpressionContent {
151156
create_subs!(&self.left, &self.left_operation,
152157
&self.middle, &self.right_operation, &self.right)
153158
}
159+
fn evaluate_rules(&self, acc: &mut Vec<DMLStyleError>, rules: &CurrentRules, _aux: AuxParams) {
160+
rules.sp_ternary.check(acc, SpTernaryArgs::from_tertiary_expression_content(self));
161+
}
154162
}
155163

156164
#[derive(Debug, Clone, PartialEq)]

src/lint/features.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
Below are listed the currently supported rules for linting:
44

55
## Spacing
6+
- SpBinop, `sp_binop`: spaces around binary operators except for derefencing operators (dot `a.b` and arrow `a->b` )
7+
- SpTernary, `sp_ternary`: spaces around `?` and `:` in ternary conditional expressions
68
- SpBraces, `sp_brace`: spaces around braces (`{` and `}`)
79
- SpPunct, `sp_punct`: spaces after but not before colon, semicolon and comma
810
- NspFunpar, `nsp_funpar`: no spaces between a function/method name and its opening parenthesis

src/lint/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ use std::fmt;
22
use std::fs;
33
use std::path::{Path, PathBuf};
44
use log::{debug, error, trace};
5-
use rules::spacing::{SpPtrDeclOptions, NspPtrDeclOptions};
65
use serde::{Deserialize, Serialize};
76
use rules::{instantiate_rules, CurrentRules, RuleType};
8-
use rules::{spacing::{SpBraceOptions, SpPunctOptions, NspFunparOptions,
7+
use rules::{spacing::{SpBraceOptions, SpPunctOptions, SpBinopOptions, NspFunparOptions,
8+
SpTernaryOptions, SpPtrDeclOptions, NspPtrDeclOptions,
99
NspInparenOptions, NspUnaryOptions, NspTrailingOptions},
1010
indentation::{LongLineOptions, IndentSizeOptions, IndentCodeBlockOptions,
1111
IndentNoTabOptions, IndentClosingBraceOptions, IndentParenExprOptions, IndentSwitchCaseOptions, IndentEmptyLoopOptions},
@@ -51,6 +51,10 @@ pub struct LintCfg {
5151
#[serde(default)]
5252
pub sp_punct: Option<SpPunctOptions>,
5353
#[serde(default)]
54+
pub sp_binop: Option<SpBinopOptions>,
55+
#[serde(default)]
56+
pub sp_ternary: Option<SpTernaryOptions>,
57+
#[serde(default)]
5458
pub sp_ptrdecl: Option<SpPtrDeclOptions>,
5559
#[serde(default)]
5660
pub nsp_ptrdecl: Option<NspPtrDeclOptions>,
@@ -91,6 +95,8 @@ impl Default for LintCfg {
9195
LintCfg {
9296
sp_brace: Some(SpBraceOptions{}),
9397
sp_punct: Some(SpPunctOptions{}),
98+
sp_binop: Some(SpBinopOptions{}),
99+
sp_ternary: Some(SpTernaryOptions{}),
94100
sp_ptrdecl: Some(SpPtrDeclOptions{}),
95101
nsp_ptrdecl: Some(NspPtrDeclOptions{}),
96102
nsp_funpar: Some(NspFunparOptions{}),

src/lint/rules/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ pub mod indentation;
44
#[cfg(test)]
55
pub mod tests;
66

7-
use spacing::{NspFunparRule, NspInparenRule, NspTrailingRule, NspUnaryRule, SpBracesRule, SpPtrDeclRule, NspPtrDeclRule, SpPunctRule};
7+
use spacing::{NspFunparRule, NspInparenRule, NspTrailingRule, NspUnaryRule, SpBracesRule, SpBinopRule, SpTernaryRule, SpPtrDeclRule, NspPtrDeclRule, SpPunctRule};
88
use indentation::{LongLinesRule, IndentNoTabRule, IndentCodeBlockRule, IndentClosingBraceRule, IndentParenExprRule, IndentSwitchCaseRule, IndentEmptyLoopRule};
99
use crate::lint::{LintCfg, DMLStyleError};
1010
use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange};
1111

1212
pub struct CurrentRules {
1313
pub sp_brace: SpBracesRule,
1414
pub sp_punct: SpPunctRule,
15+
pub sp_binop: SpBinopRule,
16+
pub sp_ternary: SpTernaryRule,
1517
pub sp_ptrdecl: SpPtrDeclRule,
1618
pub nsp_ptrdecl: NspPtrDeclRule,
1719
pub nsp_funpar: NspFunparRule,
@@ -31,6 +33,8 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules {
3133
CurrentRules {
3234
sp_brace: SpBracesRule { enabled: cfg.sp_brace.is_some() },
3335
sp_punct: SpPunctRule { enabled: cfg.sp_punct.is_some() },
36+
sp_binop: SpBinopRule { enabled: cfg.sp_binop.is_some() },
37+
sp_ternary: SpTernaryRule { enabled: cfg.sp_ternary.is_some() },
3438
sp_ptrdecl: SpPtrDeclRule { enabled: cfg.sp_ptrdecl.is_some() },
3539
nsp_ptrdecl: NspPtrDeclRule { enabled: cfg.nsp_ptrdecl.is_some() },
3640
nsp_funpar: NspFunparRule { enabled: cfg.nsp_funpar.is_some() },
@@ -68,6 +72,8 @@ pub trait Rule {
6872
pub enum RuleType {
6973
SpBraces,
7074
SpPunct,
75+
SpBinop,
76+
SpTernary,
7177
SpPtrDecl,
7278
NspPtrDecl,
7379
NspFunpar,

src/lint/rules/spacing.rs

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ use crate::analysis::parsing::types::{BitfieldsContent, LayoutContent,
88
use crate::lint::{rules::{Rule, RuleType},
99
DMLStyleError};
1010
use crate::analysis::parsing::tree::{LeafToken, TreeElement, ZeroRange};
11-
use crate::analysis::parsing::expression::{FunctionCallContent, IndexContent,
11+
use crate::analysis::parsing::expression::{BinaryExpressionContent,
12+
FunctionCallContent, IndexContent,
1213
PostUnaryExpressionContent,
14+
TertiaryExpressionContent,
1315
UnaryExpressionContent};
1416
use crate::analysis::parsing::statement::{CompoundContent,
1517
ExpressionStmtContent,
@@ -127,6 +129,114 @@ impl Rule for SpBracesRule {
127129
}
128130
}
129131

132+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
133+
pub struct SpBinopOptions {}
134+
pub struct SpBinopRule {
135+
pub enabled: bool,
136+
}
137+
pub struct SpBinopArgs {
138+
left: ZeroRange,
139+
operator: ZeroRange,
140+
right: ZeroRange,
141+
}
142+
impl SpBinopArgs {
143+
pub fn from_binary_expression_content(node: &BinaryExpressionContent) -> Option<SpBinopArgs> {
144+
Some(SpBinopArgs {
145+
left: node.left.range(),
146+
operator: node.operation.range(),
147+
right: node.right.range(),
148+
})
149+
}
150+
}
151+
impl SpBinopRule {
152+
pub fn check(&self, acc: &mut Vec<DMLStyleError>,
153+
ranges: Option<SpBinopArgs>) {
154+
if !self.enabled { return; }
155+
if let Some(location) = ranges {
156+
if (location.left.row_end == location.operator.row_start)
157+
&& (location.left.col_end == location.operator.col_start) {
158+
acc.push(self.create_err(location.left));
159+
}
160+
if (location.right.row_start == location.operator.row_end)
161+
&& (location.operator.col_end == location.right.col_start) {
162+
163+
acc.push(self.create_err(location.right));
164+
}
165+
}
166+
}
167+
}
168+
impl Rule for SpBinopRule {
169+
fn name() -> &'static str {
170+
"SP_BINOP"
171+
}
172+
fn description() -> &'static str {
173+
"Missing space around binary operator"
174+
}
175+
fn get_rule_type() -> RuleType {
176+
RuleType::SpBinop
177+
}
178+
}
179+
180+
181+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
182+
pub struct SpTernaryOptions {}
183+
pub struct SpTernaryRule {
184+
pub enabled: bool,
185+
}
186+
pub struct SpTernaryArgs {
187+
left: ZeroRange,
188+
left_op: ZeroRange,
189+
middle: ZeroRange,
190+
right_op: ZeroRange,
191+
right: ZeroRange,
192+
}
193+
impl SpTernaryArgs {
194+
pub fn from_tertiary_expression_content(node: &TertiaryExpressionContent) -> Option<SpTernaryArgs> {
195+
Some(SpTernaryArgs {
196+
left: node.left.range(),
197+
left_op: node.left_operation.range(),
198+
middle: node.middle.range(),
199+
right_op: node.right_operation.range(),
200+
right: node.right.range(),
201+
})
202+
}
203+
}
204+
fn no_gap(left: ZeroRange, right: ZeroRange) -> bool {
205+
left.row_end == right.row_start
206+
&& left.col_end == right.col_start
207+
}
208+
impl SpTernaryRule {
209+
pub fn check(&self, acc: &mut Vec<DMLStyleError>,
210+
ranges: Option<SpTernaryArgs>) {
211+
if !self.enabled { return; }
212+
if let Some(SpTernaryArgs { left, left_op, middle, right_op, right }) = ranges {
213+
if no_gap(left, left_op) {
214+
acc.push(self.create_err(left));
215+
}
216+
if no_gap(left_op, middle) {
217+
acc.push(self.create_err(middle));
218+
}
219+
if no_gap(middle, right_op) {
220+
acc.push(self.create_err(middle));
221+
}
222+
if no_gap(right_op, right) {
223+
acc.push(self.create_err(right));
224+
}
225+
}
226+
}
227+
}
228+
impl Rule for SpTernaryRule {
229+
fn name() -> &'static str {
230+
"SP_TERNARY"
231+
}
232+
fn description() -> &'static str {
233+
"Missing space around ? or : in conditional expression"
234+
}
235+
fn get_rule_type() -> RuleType {
236+
RuleType::SpTernary
237+
}
238+
}
239+
130240
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
131241
pub struct SpPunctOptions {}
132242

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
use crate::lint::rules::tests::common::assert_snippet;
2-
use crate::lint::rules::instantiate_rules;
3-
use crate::lint::LintCfg;
41
mod nsp_funpar;
52
mod nsp_inparen;
63
mod nsp_ptrdecl;
@@ -9,6 +6,8 @@ mod nsp_unary;
96
mod sp_braces;
107
mod sp_ptrdecl;
118
mod sp_punct;
9+
mod sp_binop;
10+
mod sp_ternary;
1211

1312
// Put whitespace (space or newline):
1413
// SP.reserved around reserved words, such as if, else, default,
@@ -23,34 +22,6 @@ if(this_some_integer == 0x666)
2322
}
2423
";
2524

26-
// SP.binop around binary operators except the dereferencing operators dot
27-
// (a.b) and arrow (a->b)
28-
#[allow(dead_code)]
29-
static SP_BINOP: &str = "
30-
method this_is_some_method() {
31-
local int this_some_integer = 5+6;
32-
if (this_some_integer == 0x666)
33-
this_some_integer = this.val;
34-
}
35-
";
36-
37-
// SP.ternary around ? and : in the ternary ?: operator
38-
#[allow(dead_code)]
39-
static SP_TERNARY: &str = "
40-
method this_is_some_method(bool flag) {
41-
local int this_some_integer = (flag?5:7));
42-
}
43-
";
44-
45-
// SP.ptrdecl between a type and the * marking a pointer
46-
#[allow(dead_code)]
47-
static SP_PTRDECL: &str = "
48-
method this_is_some_method(conf_object_t* dummy_obj) {
49-
if(!dummy_obj)
50-
return;
51-
}
52-
";
53-
5425
// SP.comment around the comment delimiters //, /* and **/
5526
#[allow(dead_code)]
5627
static SP_COMMENT: &str = "
@@ -62,11 +33,3 @@ if(!dummy_obj)//Not null
6233
}
6334
";
6435

65-
// NSP.ptrdecl after the * marking a pointer in a declaration
66-
#[allow(dead_code)]
67-
static NSP_PTRDECL: &str = "
68-
method this_is_some_method(conf_object_t * dummy_obj) {
69-
if(!dummy_obj)
70-
return;
71-
}
72-
";
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
use crate::lint::rules::tests::common::{set_up, assert_snippet};
2+
use crate::lint::rules::RuleType;
3+
4+
// SP.binop around binary operators except the dereferencing operators dot
5+
// (a.b) and arrow (a->b)
6+
static ARTITHMETIC_OPERATOR_CORRECT: &str = "
7+
method this_is_some_method() {
8+
local int this_some_integer = 5 + 6;
9+
if (this_some_integer == 0x666) {
10+
this_some_integer = this.val;
11+
}
12+
}
13+
";
14+
15+
#[test]
16+
fn arithmetic_operator_correct() {
17+
let rules = set_up();
18+
assert_snippet(ARTITHMETIC_OPERATOR_CORRECT, vec![], &rules);
19+
}
20+
static ARTITHMETIC_OPERATOR_INCORRECT: &str = "
21+
method this_is_some_method() {
22+
local int this_some_integer = 5+6;
23+
if (this_some_integer == 0x666) {
24+
this_some_integer = this.val;
25+
}
26+
}
27+
";
28+
29+
#[test]
30+
fn arithmetic_operator_incorrect() {
31+
let rules = set_up();
32+
let expected_errors = define_expected_errors!(
33+
RuleType::SpBinop,
34+
(2, 2, 34, 35),
35+
(2, 2, 36, 37),
36+
);
37+
assert_snippet(ARTITHMETIC_OPERATOR_INCORRECT, expected_errors, &rules);
38+
}
39+
40+
static CONDITIONAL_OPERATOR_INCORRECT: &str = "
41+
method this_is_some_method() {
42+
local int this_some_integer = 5 + 6;
43+
if (this_some_integer==0x666) {
44+
this_some_integer = this.val;
45+
}
46+
}
47+
";
48+
#[test]
49+
fn conditional_operator_incorrect() {
50+
let rules = set_up();
51+
let expected_errors = define_expected_errors!(
52+
RuleType::SpBinop,
53+
(3, 3, 8, 25),
54+
(3, 3, 27, 32),
55+
);
56+
assert_snippet(CONDITIONAL_OPERATOR_INCORRECT, expected_errors, &rules);
57+
}
58+
59+
static SP_BINOP: &str = "
60+
method this_is_some_method() {
61+
local int this_some_integer = 5+6;
62+
if (this_some_integer == 0x666) {
63+
this_some_integer = this.val;
64+
}
65+
}
66+
";
67+
68+
#[test]
69+
fn rule_disable() {
70+
let mut rules = set_up();
71+
let expected_errors = define_expected_errors!(
72+
RuleType::SpBinop,
73+
(2, 2, 34, 35),
74+
(2, 2, 36, 37),
75+
);
76+
assert_snippet(SP_BINOP, expected_errors, &rules);
77+
rules.sp_binop.enabled = false;
78+
assert_snippet(SP_BINOP, vec![], &rules);
79+
}

0 commit comments

Comments
 (0)