Skip to content

Commit 49a1e79

Browse files
authored
Merge pull request #8 from kevv87/feat/in10-empty-loop-fix
Add IN10 rule for correct indentation in an empty loop
2 parents 3a7dd15 + 20003f6 commit 49a1e79

File tree

7 files changed

+192
-6
lines changed

7 files changed

+192
-6
lines changed

example_files/example_lint_cfg.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@
1010
"in2": {},
1111
"in3": {},
1212
"in6": {},
13-
"in9": {}
13+
"in9": {},
14+
"in10": {}
1415
}

src/analysis/parsing/statement.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0 and MIT
33
use log::error;
44

5+
use crate::lint::rules::indentation::IN10Args;
56
use crate::span::Range;
67
use crate::analysis::parsing::lexer::TokenKind;
78
use crate::analysis::parsing::parser::{Token, doesnt_understand_tokens,
@@ -540,6 +541,9 @@ impl TreeElement for WhileContent {
540541
&self.rparen,
541542
&self.statement)
542543
}
544+
fn evaluate_rules(&self, acc: &mut Vec<DMLStyleError>, rules: &CurrentRules, aux: &mut AuxParams) {
545+
rules.in10.check(acc, IN10Args::from_while_content(self, &mut aux.depth));
546+
}
543547
}
544548

545549
impl Parse<StatementContent> for WhileContent {
@@ -848,6 +852,9 @@ impl TreeElement for ForContent {
848852
&self.rparen,
849853
&self.statement)
850854
}
855+
fn evaluate_rules(&self, acc: &mut Vec<DMLStyleError>, rules: &CurrentRules, aux: &mut AuxParams) {
856+
rules.in10.check(acc, IN10Args::from_for_content(self, &mut aux.depth));
857+
}
851858
}
852859

853860
impl Parse<StatementContent> for ForContent {

src/lint/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use rules::{instantiate_rules, CurrentRules, RuleType};
77
use rules::{spacing::{SpBraceOptions, SpPunctOptions, NspFunparOptions,
88
NspInparenOptions, NspUnaryOptions, NspTrailingOptions},
99
indentation::{LongLineOptions, IN1Options, IN3Options,
10-
IN2Options, IN9Options, IN6Options},
10+
IN2Options, IN9Options, IN6Options,
11+
IN10Options},
1112
};
1213
use crate::analysis::{DMLError, IsolatedAnalysis, LocalDMLError};
1314
use crate::analysis::parsing::tree::TreeElement;
@@ -69,6 +70,8 @@ pub struct LintCfg {
6970
pub in6: Option<IN6Options>,
7071
#[serde(default)]
7172
pub in9: Option<IN9Options>,
73+
#[serde(default)]
74+
pub in10: Option<IN10Options>,
7275
}
7376

7477
impl Default for LintCfg {
@@ -86,6 +89,7 @@ impl Default for LintCfg {
8689
in3: Some(IN3Options{indentation_spaces: INDENTATION_LEVEL_DEFAULT}),
8790
in6: Some(IN6Options{indentation_spaces: INDENTATION_LEVEL_DEFAULT}),
8891
in9: Some(IN9Options{indentation_spaces: INDENTATION_LEVEL_DEFAULT}),
92+
in10: Some(IN10Options{indentation_spaces: INDENTATION_LEVEL_DEFAULT}),
8993
}
9094
}
9195
}

src/lint/rules/indentation.rs

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::convert::TryInto;
22

3-
use crate::analysis::parsing::{statement::{self, CompoundContent, SwitchCase},
3+
use crate::analysis::parsing::{statement::{self, CompoundContent, ForContent,
4+
SwitchCase, WhileContent},
45
structure::ObjectStatementsContent,
56
types::{LayoutContent, StructTypeContent}};
67
use crate::span::{Range, ZeroIndexed, Row, Column};
@@ -32,6 +33,9 @@ pub fn setup_indentation_size(cfg: &mut LintCfg) {
3233
if let Some(in9) = &mut cfg.in9 {
3334
in9.indentation_spaces = indentation_spaces;
3435
}
36+
if let Some(in10) = &mut cfg.in10 {
37+
in10.indentation_spaces = indentation_spaces;
38+
}
3539
}
3640
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
3741
pub struct LongLineOptions {
@@ -417,3 +421,98 @@ impl Rule for IN9Rule {
417421
RuleType::IN9
418422
}
419423
}
424+
425+
// IN10: Indentation in empty loop
426+
pub struct IN10Rule {
427+
pub enabled: bool,
428+
indentation_spaces: u32
429+
}
430+
431+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
432+
pub struct IN10Options {
433+
#[serde(default = "default_indentation_spaces")]
434+
pub indentation_spaces: u32,
435+
}
436+
437+
pub struct IN10Args<'a> {
438+
loop_keyword_range: ZeroRange,
439+
semicolon_range: ZeroRange,
440+
expected_depth: &'a mut u32,
441+
}
442+
443+
impl IN10Args<'_> {
444+
pub fn from_for_content<'a>(node: &ForContent, depth: &'a mut u32) -> Option<IN10Args<'a>> {
445+
if let Content::Some(statement::StatementContent::Empty(semicolon)) = node.statement.content.as_ref() {
446+
*depth += 1;
447+
return Some(IN10Args {
448+
loop_keyword_range: node.fortok.range(),
449+
semicolon_range: semicolon.range(),
450+
expected_depth: depth
451+
});
452+
453+
}
454+
return None;
455+
}
456+
457+
pub fn from_while_content<'a>(node: &WhileContent, depth: &'a mut u32) -> Option<IN10Args<'a>> {
458+
if let Content::Some(statement::StatementContent::Empty(semicolon)) = node.statement.content.as_ref() {
459+
*depth += 1;
460+
return Some(IN10Args {
461+
loop_keyword_range: node.whiletok.range(),
462+
semicolon_range: semicolon.range(),
463+
expected_depth: depth
464+
});
465+
466+
}
467+
return None;
468+
}
469+
}
470+
471+
impl IN10Rule {
472+
pub fn from_options(options: &Option<IN10Options>) -> IN10Rule {
473+
match options {
474+
Some(options) => IN10Rule {
475+
enabled: true,
476+
indentation_spaces: options.indentation_spaces
477+
},
478+
None => IN10Rule {
479+
enabled: false,
480+
indentation_spaces: 0
481+
}
482+
}
483+
}
484+
pub fn check<'a>(&self, acc: &mut Vec<DMLStyleError>,
485+
args: Option<IN10Args<'a>>)
486+
{
487+
if !self.enabled { return; }
488+
let Some(args) = args else { return; };
489+
if self.indentation_is_not_aligned(args.semicolon_range, *args.expected_depth) ||
490+
args.loop_keyword_range.row_start == args.semicolon_range.row_start {
491+
let dmlerror = DMLStyleError {
492+
error: LocalDMLError {
493+
range: Range::combine(args.loop_keyword_range, args.semicolon_range),
494+
description: Self::description().to_string(),
495+
},
496+
rule_type: Self::get_rule_type(),
497+
};
498+
acc.push(dmlerror);
499+
}
500+
}
501+
fn indentation_is_not_aligned(&self, member_range: ZeroRange, depth: u32) -> bool {
502+
let expected_column = self.indentation_spaces * depth;
503+
member_range.col_start.0 != expected_column
504+
}
505+
}
506+
507+
impl Rule for IN10Rule {
508+
fn name() -> &'static str {
509+
"IN10_INDENTATION_EMPTY_LOOP"
510+
}
511+
fn description() -> &'static str {
512+
"When the body of a while or for loop is left empty, \
513+
indent the semicolon to the appropriate statement level"
514+
}
515+
fn get_rule_type() -> RuleType {
516+
RuleType::IN10
517+
}
518+
}

src/lint/rules/mod.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ pub mod tests;
77
use spacing::{SpBracesRule,
88
SpPunctRule, NspFunparRule, NspInparenRule,
99
NspUnaryRule, NspTrailingRule};
10-
use indentation::{LongLinesRule, IN2Rule, IN3Rule, IN6Rule, IN9Rule};
10+
use indentation::{LongLinesRule, IN2Rule, IN3Rule, IN6Rule, IN9Rule, IN10Rule};
1111
use crate::lint::LintCfg;
1212

1313
pub struct CurrentRules {
@@ -21,7 +21,8 @@ pub struct CurrentRules {
2121
pub in2: IN2Rule,
2222
pub in3: IN3Rule,
2323
pub in6: IN6Rule,
24-
pub in9: IN9Rule
24+
pub in9: IN9Rule,
25+
pub in10: IN10Rule
2526
}
2627

2728
pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules {
@@ -37,6 +38,7 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules {
3738
in3: IN3Rule::from_options(&cfg.in3),
3839
in6: IN6Rule::from_options(&cfg.in6),
3940
in9: IN9Rule::from_options(&cfg.in9),
41+
in10: IN10Rule::from_options(&cfg.in10)
4042
}
4143
}
4244

@@ -59,5 +61,6 @@ pub enum RuleType {
5961
IN2,
6062
IN3,
6163
IN6,
62-
IN9
64+
IN9,
65+
IN10
6366
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use crate::lint::rules::tests::common::{set_up, assert_snippet};
2+
3+
pub static IN10_EMPTY_LOOP_INCORRECT: &str = "
4+
method some_function() {
5+
for (s = 0; (1 << s) < x; s++)
6+
;
7+
}
8+
";
9+
10+
pub static IN10_EMPTY_LOOP_INCORRECT_2: &str = "
11+
method some_function() {
12+
for (s = 0; (1 << s) < x; s++)
13+
;
14+
}
15+
";
16+
17+
pub static IN10_EMPTY_LOOP_INCORRECT_3: &str = "
18+
method some_function(int x) {
19+
local uint64 s = 0;
20+
while (s < x)
21+
;
22+
s++;
23+
}
24+
";
25+
26+
pub static IN10_EMPTY_LOOP_OK: &str = "
27+
method some_function() {
28+
for (s = 0; (1 << s) < x; s++)
29+
;
30+
}
31+
";
32+
33+
pub static IN10_EMPTY_LOOP_OK_2: &str = "
34+
method some_function(int x) {
35+
local uint64 s = 0;
36+
while (s < x)
37+
;
38+
s++;
39+
}
40+
";
41+
42+
pub static IN10_NESTED_LOOP_INCORRECT: &str = "
43+
method some_function() {
44+
for (i = 0; i < n; i++) {
45+
for (j = 0; j < m; j++)
46+
;
47+
}
48+
}
49+
";
50+
51+
pub static IN10_NESTED_LOOP_OK: &str = "
52+
method some_function() {
53+
for (i = 0; i < n; i++) {
54+
for (j = 0; j < m; j++)
55+
;
56+
}
57+
}
58+
";
59+
60+
#[test]
61+
fn in10_empty_loop() {
62+
let rules = set_up();
63+
64+
assert_snippet(IN10_EMPTY_LOOP_INCORRECT, 1, &rules);
65+
assert_snippet(IN10_EMPTY_LOOP_INCORRECT_2, 1, &rules);
66+
assert_snippet(IN10_EMPTY_LOOP_INCORRECT_3, 1, &rules);
67+
assert_snippet(IN10_EMPTY_LOOP_OK, 0, &rules);
68+
assert_snippet(IN10_EMPTY_LOOP_OK_2, 0, &rules);
69+
assert_snippet(IN10_NESTED_LOOP_INCORRECT, 1, &rules);
70+
assert_snippet(IN10_NESTED_LOOP_OK, 0, &rules);
71+
}

src/lint/rules/tests/indentation/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod in2;
22
pub mod in3;
33
pub mod in6;
44
pub mod in9;
5+
pub mod in10;
56

67
use crate::lint::rules::tests::common::{assert_snippet, run_linter};
78
use crate::lint::LintCfg;

0 commit comments

Comments
 (0)