Skip to content

Commit 4928120

Browse files
committed
Add IN6 rule: continuation line support
1 parent 0381ecc commit 4928120

File tree

9 files changed

+362
-33
lines changed

9 files changed

+362
-33
lines changed

example_files/example_lint_cfg.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@
1212
"indent_closing_brace": {},
1313
"indent_paren_expr": {},
1414
"indent_switch_case": {},
15-
"indent_empty_loop": {}
15+
"indent_empty_loop": {},
16+
"indent_continuation_line": {}
1617
}

src/analysis/parsing/expression.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::lint::{DMLStyleError,
2424
SpPunctArgs},
2525
CurrentRules},
2626
AuxParams};
27-
use crate::lint::rules::indentation::{IndentParenExprArgs};
27+
use crate::lint::rules::indentation::IndentParenExprArgs;
2828

2929
#[derive(Debug, Clone, PartialEq)]
3030
pub struct UnaryExpressionContent {

src/analysis/parsing/statement.rs

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

5-
use crate::lint::rules::indentation::IndentEmptyLoopArgs;
5+
use crate::lint::rules::indentation::{IndentContinuationLineArgs, IndentEmptyLoopArgs};
66
use crate::span::Range;
77
use crate::analysis::parsing::lexer::TokenKind;
88
use crate::analysis::parsing::statement;
@@ -1839,6 +1839,9 @@ impl TreeElement for StatementContent {
18391839
Self::Return(content) => create_subs![content],
18401840
}
18411841
}
1842+
fn evaluate_rules(&self, acc: &mut Vec<DMLStyleError>, rules: &CurrentRules, aux: AuxParams) {
1843+
rules.indent_continuation_line.check(acc, IndentContinuationLineArgs::from_statement_content(self, aux.depth));
1844+
}
18421845
}
18431846

18441847
pub type Statement = AstObject<StatementContent>;

src/analysis/parsing/structure.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::lint::rules::spacing::{SpBracesArgs,
2020
NspInparenArgs,
2121
NspFunparArgs,
2222
SpPunctArgs};
23-
use crate::lint::rules::indentation::{IndentCodeBlockArgs, IndentClosingBraceArgs, IndentParenExprArgs};
23+
use crate::lint::rules::indentation::{IndentCodeBlockArgs, IndentClosingBraceArgs, IndentParenExprArgs, IndentContinuationLineArgs};
2424
use crate::lint::{rules::CurrentRules, AuxParams, DMLStyleError};
2525
use crate::analysis::reference::{Reference, ReferenceKind};
2626
use crate::analysis::FileSpec;
@@ -1905,6 +1905,9 @@ impl TreeElement for DMLObjectContent {
19051905
Self::Template(content) => create_subs![content],
19061906
}
19071907
}
1908+
fn evaluate_rules(&self, acc: &mut Vec<DMLStyleError>, rules: &CurrentRules, aux: AuxParams) {
1909+
rules.indent_continuation_line.check(acc, IndentContinuationLineArgs::from_dml_object_content(self, aux.depth));
1910+
}
19081911
}
19091912

19101913
pub type DMLObject = AstObject<DMLObjectContent>;

src/lint/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use rules::{instantiate_rules, CurrentRules, RuleType};
77
use rules::{spacing::{SpBraceOptions, SpPunctOptions, NspFunparOptions,
88
NspInparenOptions, NspUnaryOptions, NspTrailingOptions},
99
indentation::{LongLineOptions, IndentSizeOptions, IndentCodeBlockOptions,
10-
IndentNoTabOptions, IndentClosingBraceOptions, IndentParenExprOptions, IndentSwitchCaseOptions, IndentEmptyLoopOptions},
10+
IndentNoTabOptions, IndentClosingBraceOptions, IndentParenExprOptions, IndentSwitchCaseOptions, IndentEmptyLoopOptions, IndentContinuationLineOptions},
1111
};
1212
use crate::analysis::{DMLError, IsolatedAnalysis, LocalDMLError};
1313
use crate::analysis::parsing::tree::TreeElement;
@@ -73,6 +73,8 @@ pub struct LintCfg {
7373
pub indent_switch_case: Option<IndentSwitchCaseOptions>,
7474
#[serde(default)]
7575
pub indent_empty_loop: Option<IndentEmptyLoopOptions>,
76+
#[serde(default)]
77+
pub indent_continuation_line: Option<IndentContinuationLineOptions>,
7678
}
7779

7880
impl Default for LintCfg {
@@ -92,6 +94,7 @@ impl Default for LintCfg {
9294
indent_paren_expr: Some(IndentParenExprOptions{}),
9395
indent_switch_case: Some(IndentSwitchCaseOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}),
9496
indent_empty_loop: Some(IndentEmptyLoopOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}),
97+
indent_continuation_line: Some(IndentContinuationLineOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}),
9598
}
9699
}
97100
}

src/lint/rules/indentation.rs

Lines changed: 148 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
use std::convert::TryInto;
22

3-
use crate::analysis::parsing::{expression::{CastContent, FunctionCallContent, ParenExpressionContent},
4-
lexer::TokenKind,
5-
statement::{self, CompoundContent, DoContent, ForContent, ForeachContent,
6-
IfContent, SwitchCase, SwitchContent, WhileContent},
7-
structure::{MethodContent, ObjectStatementsContent},
8-
tree::TreeElementTokenIterator,
9-
types::{BitfieldsContent, LayoutContent, StructTypeContent}};
3+
use crate::analysis::parsing::{expression::{CastContent, FunctionCallContent, ParenExpressionContent}, lexer::TokenKind, parser::Token, statement::{self, CompoundContent, DoContent, ForContent, ForeachContent, IfContent, StatementContent, SwitchCase, SwitchContent, WhileContent}, structure::{DMLObjectContent, MethodContent, ObjectStatementsContent}, tree::TreeElementTokenIterator, types::{BitfieldsContent, LayoutContent, StructTypeContent}};
104
use crate::span::{Range, ZeroIndexed};
115
use crate::analysis::parsing::tree::{ZeroRange, Content, TreeElement};
126
use serde::{Deserialize, Serialize};
@@ -35,6 +29,9 @@ pub fn setup_indentation_size(cfg: &mut LintCfg) {
3529
if let Some(indent_empty_loop) = &mut cfg.indent_empty_loop {
3630
indent_empty_loop.indentation_spaces = indentation_spaces;
3731
}
32+
if let Some(indent_continuation_line) = &mut cfg.indent_continuation_line {
33+
indent_continuation_line.indentation_spaces = indentation_spaces;
34+
}
3835
}
3936
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
4037
pub struct LongLineOptions {
@@ -377,8 +374,8 @@ pub struct IndentParenExprArgs {
377374
}
378375

379376
impl IndentParenExprArgs {
380-
fn filter_out_parenthesized_ranges(expression_tokens: TreeElementTokenIterator) -> Vec<ZeroRange> {
381-
let mut token_ranges: Vec<ZeroRange> = vec![];
377+
pub fn filter_out_parenthesized_tokens(expression_tokens: TreeElementTokenIterator) -> Vec<Token> {
378+
let mut token_list: Vec<Token> = vec![];
382379
let mut paren_depth = 0;
383380
// paren_depth is used to identify nested
384381
// parenthesized expressions within other expressions
@@ -387,25 +384,32 @@ impl IndentParenExprArgs {
387384
for token in expression_tokens {
388385
match token.kind {
389386
TokenKind::LParen => {
387+
if paren_depth == 0 { token_list.push(token); }
390388
paren_depth += 1;
391-
token_ranges.push(token.range);
392389
},
393-
TokenKind::RParen => paren_depth-=1,
394-
_ => { if paren_depth == 0 { token_ranges.push(token.range); }
390+
TokenKind::RParen => {
391+
paren_depth-=1;
392+
if paren_depth == 0 { token_list.push(token); }
393+
},
394+
TokenKind::LBrace => {
395+
break;
396+
},
397+
_ => {
398+
if paren_depth == 0 { token_list.push(token); }
395399
}
396400
}
397401
}
398-
token_ranges
402+
token_list
399403
}
400404

401405
pub fn from_for(node: &ForContent) -> Option<IndentParenExprArgs> {
402406
// For loop has three parts within parentheses: pre, cond, and post
403407
let mut filtered_member_ranges: Vec<ZeroRange> = vec![];
404-
filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(node.pre.tokens()));
408+
filtered_member_ranges.extend(&mut Self::filter_out_parenthesized_tokens(node.pre.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range));
405409
filtered_member_ranges.push(node.lsemi.range());
406-
filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(node.cond.tokens()));
410+
filtered_member_ranges.extend(&mut Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range));
407411
filtered_member_ranges.push(node.rsemi.range());
408-
filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(node.post.tokens()));
412+
filtered_member_ranges.extend(&mut Self::filter_out_parenthesized_tokens(node.post.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range));
409413

410414
Some(IndentParenExprArgs {
411415
members_ranges: filtered_member_ranges,
@@ -415,15 +419,15 @@ impl IndentParenExprArgs {
415419

416420
pub fn from_foreach(node: &ForeachContent) -> Option<IndentParenExprArgs> {
417421
Some(IndentParenExprArgs {
418-
members_ranges: Self::filter_out_parenthesized_ranges(node.expression.tokens()),
422+
members_ranges: Self::filter_out_parenthesized_tokens(node.expression.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(),
419423
lparen: node.lparen.range(),
420424
})
421425
}
422426

423427
pub fn from_function_call(node: &FunctionCallContent) -> Option<IndentParenExprArgs> {
424428
let mut filtered_member_ranges: Vec<ZeroRange> = vec![];
425429
for (arg, _comma) in node.arguments.iter() {
426-
filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(arg.tokens()));
430+
filtered_member_ranges.extend(Self::filter_out_parenthesized_tokens(arg.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range));
427431
}
428432
Some(IndentParenExprArgs {
429433
members_ranges: filtered_member_ranges,
@@ -434,15 +438,15 @@ impl IndentParenExprArgs {
434438
pub fn from_paren_expression(node: &ParenExpressionContent)
435439
-> Option<IndentParenExprArgs> {
436440
Some(IndentParenExprArgs {
437-
members_ranges: Self::filter_out_parenthesized_ranges(node.expr.tokens()),
441+
members_ranges: Self::filter_out_parenthesized_tokens(node.expr.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(),
438442
lparen: node.lparen.range(),
439443
})
440444
}
441445

442446
pub fn from_method(node: &MethodContent) -> Option<IndentParenExprArgs> {
443447
let mut filtered_member_ranges: Vec<ZeroRange> = vec![];
444448
for (arg, _comma) in node.arguments.iter() {
445-
filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(arg.tokens()));
449+
filtered_member_ranges.extend(Self::filter_out_parenthesized_tokens(arg.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range));
446450
}
447451
Some(IndentParenExprArgs {
448452
members_ranges: filtered_member_ranges,
@@ -452,21 +456,21 @@ impl IndentParenExprArgs {
452456

453457
pub fn from_while(node: &WhileContent) -> Option<IndentParenExprArgs> {
454458
Some(IndentParenExprArgs {
455-
members_ranges: Self::filter_out_parenthesized_ranges(node.cond.tokens()),
459+
members_ranges: Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(),
456460
lparen: node.lparen.range(),
457461
})
458462
}
459463

460464
pub fn from_do_while(node: &DoContent) -> Option<IndentParenExprArgs> {
461465
Some(IndentParenExprArgs {
462-
members_ranges: Self::filter_out_parenthesized_ranges(node.cond.tokens()),
466+
members_ranges: Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(),
463467
lparen: node.lparen.range(),
464468
})
465469
}
466470

467471
pub fn from_if(node: &IfContent) -> Option<IndentParenExprArgs> {
468472
Some(IndentParenExprArgs {
469-
members_ranges: Self::filter_out_parenthesized_ranges(node.cond.tokens()),
473+
members_ranges: Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(),
470474
lparen: node.lparen.range(),
471475
})
472476
}
@@ -475,14 +479,14 @@ impl IndentParenExprArgs {
475479
let mut cast_member_tokens = node.from.tokens();
476480
cast_member_tokens.append(&mut node.to.tokens());
477481
Some(IndentParenExprArgs {
478-
members_ranges: Self::filter_out_parenthesized_ranges(cast_member_tokens),
482+
members_ranges: Self::filter_out_parenthesized_tokens(cast_member_tokens).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(),
479483
lparen: node.lparen.range(),
480484
})
481485
}
482486

483487
pub fn from_switch(node: &SwitchContent) -> Option<IndentParenExprArgs> {
484488
Some(IndentParenExprArgs {
485-
members_ranges: Self::filter_out_parenthesized_ranges(node.expr.tokens()),
489+
members_ranges: Self::filter_out_parenthesized_tokens(node.expr.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(),
486490
lparen: node.lparen.range(),
487491
})
488492
}
@@ -689,3 +693,122 @@ impl Rule for IndentEmptyLoopRule {
689693
RuleType::IN10
690694
}
691695
}
696+
697+
// in6
698+
pub struct IndentContinuationLineRule {
699+
pub enabled: bool,
700+
indentation_spaces: u32
701+
}
702+
703+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
704+
pub struct IndentContinuationLineOptions {
705+
#[serde(default = "default_indentation_spaces")]
706+
pub indentation_spaces: u32,
707+
}
708+
709+
pub struct IndentContinuationLineArgs {
710+
token_list: Vec<Token>,
711+
expected_depth: u32,
712+
}
713+
714+
impl IndentContinuationLineArgs {
715+
pub fn filter_out_last_semi_ranges(expression_tokens: &mut TreeElementTokenIterator) {
716+
// This function filters out the last semicolon in a list of tokens
717+
// as it is not part of the continuation line check.
718+
expression_tokens.pop_if(|token| token.kind == TokenKind::SemiColon);
719+
}
720+
721+
pub fn from_statement_content(node: &StatementContent, depth: u32) -> Option<IndentContinuationLineArgs> {
722+
match node {
723+
StatementContent::Compound(_) |
724+
StatementContent::If(_) |
725+
StatementContent::While(_) |
726+
StatementContent::Do(_) |
727+
StatementContent::For(_) |
728+
StatementContent::Try(_) |
729+
StatementContent::Foreach(_) |
730+
StatementContent::Throw(_) |
731+
StatementContent::Switch(_) => return None,
732+
_ => {}
733+
};
734+
let mut tokens = node.tokens();
735+
Self::filter_out_last_semi_ranges(&mut tokens);
736+
737+
Some(IndentContinuationLineArgs {
738+
token_list: IndentParenExprArgs::filter_out_parenthesized_tokens(tokens),
739+
expected_depth: depth,
740+
})
741+
}
742+
743+
pub fn from_dml_object_content(node: &DMLObjectContent, depth: u32) -> Option<IndentContinuationLineArgs> {
744+
match node {
745+
DMLObjectContent::Parameter(_) |
746+
DMLObjectContent::Hook(_) |
747+
DMLObjectContent::Import(_) |
748+
DMLObjectContent::InEach(_) |
749+
DMLObjectContent::Session(_) |
750+
DMLObjectContent::Typedef(_) => {
751+
let mut tokens = node.tokens();
752+
Self::filter_out_last_semi_ranges(&mut tokens);
753+
return Some(IndentContinuationLineArgs {
754+
token_list: IndentParenExprArgs::filter_out_parenthesized_tokens(tokens),
755+
expected_depth: depth,
756+
})},
757+
_ => return None,
758+
}
759+
}
760+
}
761+
762+
impl IndentContinuationLineRule {
763+
pub fn from_options(options: &Option<IndentContinuationLineOptions>) -> IndentContinuationLineRule {
764+
match options {
765+
Some(options) => IndentContinuationLineRule {
766+
enabled: true,
767+
indentation_spaces: options.indentation_spaces
768+
},
769+
None => IndentContinuationLineRule {
770+
enabled: false,
771+
indentation_spaces: 0
772+
}
773+
}
774+
}
775+
776+
pub fn check<'a> (&self, acc: &mut Vec<DMLStyleError>,
777+
args: Option<IndentContinuationLineArgs>) {
778+
if !self.enabled { return; }
779+
let Some(args) = args else { return; };
780+
if args.token_list.is_empty() { return; }
781+
let expected_line_start = self.indentation_spaces * (args.expected_depth + 1);
782+
let mut last_row = args.token_list.first().unwrap().range.row_start.0;
783+
784+
for token in args.token_list {
785+
match token.kind {
786+
TokenKind::RParen => {
787+
if token.range.row_start.0 != last_row {
788+
last_row = token.range.row_start.0;
789+
}
790+
}
791+
_ => {
792+
if token.range.row_start.0 != last_row {
793+
last_row = token.range.row_start.0;
794+
if token.range.col_start.0 != expected_line_start {
795+
acc.push(self.create_err(token.range))
796+
}
797+
}
798+
}
799+
}
800+
}
801+
}
802+
}
803+
804+
impl Rule for IndentContinuationLineRule {
805+
fn name() -> &'static str {
806+
"INDENT_CONTINUATION_LINE"
807+
}
808+
fn description() -> &'static str {
809+
"A continuation line not broken inside a parenthesized expression is indented one level."
810+
}
811+
fn get_rule_type() -> RuleType {
812+
RuleType::IN6
813+
}
814+
}

src/lint/rules/mod.rs

Lines changed: 5 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, IndentNoTabRule, IndentCodeBlockRule, IndentClosingBraceRule, IndentParenExprRule, IndentSwitchCaseRule, IndentEmptyLoopRule};
10+
use indentation::{LongLinesRule, IndentNoTabRule, IndentCodeBlockRule, IndentClosingBraceRule, IndentParenExprRule, IndentSwitchCaseRule, IndentEmptyLoopRule, IndentContinuationLineRule};
1111
use crate::lint::{LintCfg, DMLStyleError};
1212
use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange};
1313

@@ -24,7 +24,8 @@ pub struct CurrentRules {
2424
pub indent_closing_brace: IndentClosingBraceRule,
2525
pub indent_paren_expr: IndentParenExprRule,
2626
pub indent_switch_case: IndentSwitchCaseRule,
27-
pub indent_empty_loop: IndentEmptyLoopRule
27+
pub indent_empty_loop: IndentEmptyLoopRule,
28+
pub indent_continuation_line: IndentContinuationLineRule,
2829
}
2930

3031
pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules {
@@ -41,7 +42,8 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules {
4142
indent_closing_brace: IndentClosingBraceRule::from_options(&cfg.indent_closing_brace),
4243
indent_paren_expr: IndentParenExprRule { enabled: cfg.indent_paren_expr.is_some() },
4344
indent_switch_case: IndentSwitchCaseRule::from_options(&cfg.indent_switch_case),
44-
indent_empty_loop: IndentEmptyLoopRule::from_options(&cfg.indent_empty_loop)
45+
indent_empty_loop: IndentEmptyLoopRule::from_options(&cfg.indent_empty_loop),
46+
indent_continuation_line: IndentContinuationLineRule::from_options(&cfg.indent_continuation_line),
4547
}
4648
}
4749

0 commit comments

Comments
 (0)