From 6cc0d84e3aee9d337a49ecdf80b0ad701d0cf89b Mon Sep 17 00:00:00 2001 From: Marlon Mata Date: Fri, 9 May 2025 17:54:33 -0600 Subject: [PATCH 1/8] Add IN6 rule: continuation line support --- example_files/example_lint_cfg.json | 1 + src/analysis/parsing/expression.rs | 2 +- src/analysis/parsing/statement.rs | 11 +- src/analysis/parsing/structure.rs | 13 +- src/lint/mod.rs | 40 ++-- src/lint/rules/indentation.rs | 173 +++++++++++++--- src/lint/rules/mod.rs | 15 +- .../tests/indentation/continuation_line.rs | 193 ++++++++++++++++++ src/lint/rules/tests/indentation/mod.rs | 1 + 9 files changed, 400 insertions(+), 49 deletions(-) create mode 100644 src/lint/rules/tests/indentation/continuation_line.rs diff --git a/example_files/example_lint_cfg.json b/example_files/example_lint_cfg.json index e1aa021..cd764cc 100644 --- a/example_files/example_lint_cfg.json +++ b/example_files/example_lint_cfg.json @@ -18,5 +18,6 @@ "indent_paren_expr": {}, "indent_switch_case": {}, "indent_empty_loop": {}, + "indent_continuation_line": {}, "annotate_lints": true } diff --git a/src/analysis/parsing/expression.rs b/src/analysis/parsing/expression.rs index 1189d01..0add648 100644 --- a/src/analysis/parsing/expression.rs +++ b/src/analysis/parsing/expression.rs @@ -26,7 +26,7 @@ use crate::lint::{DMLStyleError, SpPunctArgs}, CurrentRules}, AuxParams}; -use crate::lint::rules::indentation::{IndentParenExprArgs}; +use crate::lint::rules::indentation::IndentParenExprArgs; #[derive(Debug, Clone, PartialEq)] pub struct UnaryExpressionContent { diff --git a/src/analysis/parsing/statement.rs b/src/analysis/parsing/statement.rs index fb44fe3..5d3da73 100644 --- a/src/analysis/parsing/statement.rs +++ b/src/analysis/parsing/statement.rs @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 and MIT use log::error; -use crate::lint::rules::indentation::IndentEmptyLoopArgs; +use crate::lint::rules::indentation::{IndentEmptyLoopArgs, + IndentContinuationLineArgs}; use crate::lint::rules::spacing::SpReservedArgs; use crate::span::Range; use crate::analysis::parsing::lexer::TokenKind; @@ -26,7 +27,10 @@ use crate::analysis::parsing::structure::{parse_vardecl, VarDecl}; use crate::analysis::LocalDMLError; use crate::lint::{DMLStyleError, rules::{CurrentRules, - indentation::{IndentCodeBlockArgs, IndentClosingBraceArgs, IndentParenExprArgs, IndentSwitchCaseArgs}, + indentation::{IndentCodeBlockArgs, + IndentClosingBraceArgs, + IndentParenExprArgs, + IndentSwitchCaseArgs}, spacing::{NspInparenArgs, SpBracesArgs, SpPunctArgs}}, @@ -1847,6 +1851,9 @@ impl TreeElement for StatementContent { Self::Return(content) => create_subs![content], } } + fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { + rules.indent_continuation_line.check(acc, IndentContinuationLineArgs::from_statement_content(self, aux.depth)); + } } pub type Statement = AstObject; diff --git a/src/analysis/parsing/structure.rs b/src/analysis/parsing/structure.rs index eec4969..39267ab 100644 --- a/src/analysis/parsing/structure.rs +++ b/src/analysis/parsing/structure.rs @@ -16,8 +16,14 @@ use crate::analysis::parsing::parser::{doesnt_understand_tokens, FileParser, Parse, ParseContext, FileInfo}; use crate::analysis::LocalDMLError; -use crate::lint::rules::spacing::{NspFunparArgs, NspInparenArgs, SpBracesArgs, SpPunctArgs}; -use crate::lint::rules::indentation::{IndentCodeBlockArgs, IndentClosingBraceArgs, IndentParenExprArgs}; +use crate::lint::rules::spacing::{SpBracesArgs, + NspInparenArgs, + NspFunparArgs, + SpPunctArgs}; +use crate::lint::rules::indentation::{IndentCodeBlockArgs, + IndentClosingBraceArgs, + IndentParenExprArgs, + IndentContinuationLineArgs}; use crate::lint::{rules::CurrentRules, AuxParams, DMLStyleError}; use crate::analysis::reference::{Reference, ReferenceKind}; use crate::analysis::FileSpec; @@ -1928,6 +1934,9 @@ impl TreeElement for DMLObjectContent { Self::Template(content) => create_subs![content], } } + fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { + rules.indent_continuation_line.check(acc, IndentContinuationLineArgs::from_dml_object_content(self, aux.depth)); + } } pub type DMLObject = AstObject; diff --git a/src/lint/mod.rs b/src/lint/mod.rs index 9229216..e50ec23 100644 --- a/src/lint/mod.rs +++ b/src/lint/mod.rs @@ -9,20 +9,25 @@ use serde::{Deserialize, Serialize}; use regex::Regex; use rules::{instantiate_rules, CurrentRules, RuleType}; use rules::{spacing::{SpReservedOptions, - SpBraceOptions, - SpPunctOptions, - SpBinopOptions, - NspFunparOptions, - SpTernaryOptions, - SpPtrDeclOptions, - NspPtrDeclOptions, - NspInparenOptions, - NspUnaryOptions, - NspTrailingOptions}, - indentation::{LongLineOptions, IndentSizeOptions, IndentCodeBlockOptions, - IndentNoTabOptions, IndentClosingBraceOptions, IndentParenExprOptions, - IndentSwitchCaseOptions, IndentEmptyLoopOptions}, - }; + SpBraceOptions, + SpPunctOptions, + SpBinopOptions, + NspFunparOptions, + SpTernaryOptions, + SpPtrDeclOptions, + NspPtrDeclOptions, + NspInparenOptions, + NspUnaryOptions, + NspTrailingOptions}, + indentation::{LongLineOptions, + IndentSizeOptions, + IndentCodeBlockOptions, + IndentNoTabOptions, + IndentClosingBraceOptions, + IndentParenExprOptions, + IndentSwitchCaseOptions, + IndentEmptyLoopOptions, + IndentContinuationLineOptions}}; use crate::analysis::{DMLError, IsolatedAnalysis, LocalDMLError, ZeroRange}; use crate::analysis::parsing::tree::TreeElement; use crate::file_management::CanonPath; @@ -106,6 +111,8 @@ pub struct LintCfg { pub indent_switch_case: Option, #[serde(default)] pub indent_empty_loop: Option, + #[serde(default)] + pub indent_continuation_line: Option, #[serde(default = "get_true")] pub annotate_lints: bool, } @@ -151,6 +158,7 @@ impl Default for LintCfg { indent_paren_expr: Some(IndentParenExprOptions{}), indent_switch_case: Some(IndentSwitchCaseOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), indent_empty_loop: Some(IndentEmptyLoopOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), + indent_continuation_line: Some(IndentContinuationLineOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), annotate_lints: true, } } @@ -206,7 +214,7 @@ impl LinterAnalysis { } } -pub fn begin_style_check(ast: TopAst, file: &str, rules: &CurrentRules) -> Result, Error> { +fn begin_style_check(ast: TopAst, file: &str, rules: &CurrentRules) -> Result, Error> { let (mut invalid_lint_annot, lint_annot) = obtain_lint_annotations(file); let mut linting_errors: Vec = vec![]; ast.style_check(&mut linting_errors, rules, AuxParams { depth: 0 }); @@ -552,7 +560,7 @@ pub mod tests { env_logger::init(); let source = - " +" dml 1.4; // dml-lint: allow-file=nsp_unary diff --git a/src/lint/rules/indentation.rs b/src/lint/rules/indentation.rs index 1408cc9..c0c1eba 100644 --- a/src/lint/rules/indentation.rs +++ b/src/lint/rules/indentation.rs @@ -1,12 +1,6 @@ use std::convert::TryInto; -use crate::analysis::parsing::{expression::{CastContent, FunctionCallContent, ParenExpressionContent}, - lexer::TokenKind, - statement::{self, CompoundContent, DoContent, ForContent, ForeachContent, - IfContent, SwitchCase, SwitchContent, WhileContent}, - structure::{MethodContent, ObjectStatementsContent}, - tree::TreeElementTokenIterator, - types::{BitfieldsContent, LayoutContent, StructTypeContent}}; +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}}; use crate::span::{Range, ZeroIndexed}; use crate::analysis::parsing::tree::{ZeroRange, Content, TreeElement}; use serde::{Deserialize, Serialize}; @@ -35,6 +29,9 @@ pub fn setup_indentation_size(cfg: &mut LintCfg) { if let Some(indent_empty_loop) = &mut cfg.indent_empty_loop { indent_empty_loop.indentation_spaces = indentation_spaces; } + if let Some(indent_continuation_line) = &mut cfg.indent_continuation_line { + indent_continuation_line.indentation_spaces = indentation_spaces; + } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct LongLineOptions { @@ -379,8 +376,8 @@ pub struct IndentParenExprArgs { } impl IndentParenExprArgs { - fn filter_out_parenthesized_ranges(expression_tokens: TreeElementTokenIterator) -> Vec { - let mut token_ranges: Vec = vec![]; + pub fn filter_out_parenthesized_tokens(expression_tokens: TreeElementTokenIterator) -> Vec { + let mut token_list: Vec = vec![]; let mut paren_depth = 0; // paren_depth is used to identify nested // parenthesized expressions within other expressions @@ -389,25 +386,32 @@ impl IndentParenExprArgs { for token in expression_tokens { match token.kind { TokenKind::LParen => { + if paren_depth == 0 { token_list.push(token); } paren_depth += 1; - token_ranges.push(token.range); }, - TokenKind::RParen => paren_depth-=1, - _ => { if paren_depth == 0 { token_ranges.push(token.range); } + TokenKind::RParen => { + paren_depth-=1; + if paren_depth == 0 { token_list.push(token); } + }, + TokenKind::LBrace => { + break; + }, + _ => { + if paren_depth == 0 { token_list.push(token); } } } } - token_ranges + token_list } pub fn from_for(node: &ForContent) -> Option { // For loop has three parts within parentheses: pre, cond, and post let mut filtered_member_ranges: Vec = vec![]; - filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(node.pre.tokens())); + filtered_member_ranges.extend(&mut Self::filter_out_parenthesized_tokens(node.pre.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range)); filtered_member_ranges.push(node.lsemi.range()); - filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(node.cond.tokens())); + filtered_member_ranges.extend(&mut Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range)); filtered_member_ranges.push(node.rsemi.range()); - filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(node.post.tokens())); + filtered_member_ranges.extend(&mut Self::filter_out_parenthesized_tokens(node.post.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range)); Some(IndentParenExprArgs { members_ranges: filtered_member_ranges, @@ -417,7 +421,7 @@ impl IndentParenExprArgs { pub fn from_foreach(node: &ForeachContent) -> Option { Some(IndentParenExprArgs { - members_ranges: Self::filter_out_parenthesized_ranges(node.expression.tokens()), + members_ranges: Self::filter_out_parenthesized_tokens(node.expression.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(), lparen: node.lparen.range(), }) } @@ -425,7 +429,7 @@ impl IndentParenExprArgs { pub fn from_function_call(node: &FunctionCallContent) -> Option { let mut filtered_member_ranges: Vec = vec![]; for (arg, _comma) in node.arguments.iter() { - filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(arg.tokens())); + filtered_member_ranges.extend(Self::filter_out_parenthesized_tokens(arg.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range)); } Some(IndentParenExprArgs { members_ranges: filtered_member_ranges, @@ -436,7 +440,7 @@ impl IndentParenExprArgs { pub fn from_paren_expression(node: &ParenExpressionContent) -> Option { Some(IndentParenExprArgs { - members_ranges: Self::filter_out_parenthesized_ranges(node.expr.tokens()), + members_ranges: Self::filter_out_parenthesized_tokens(node.expr.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(), lparen: node.lparen.range(), }) } @@ -444,7 +448,7 @@ impl IndentParenExprArgs { pub fn from_method(node: &MethodContent) -> Option { let mut filtered_member_ranges: Vec = vec![]; for (arg, _comma) in node.arguments.iter() { - filtered_member_ranges.append(&mut Self::filter_out_parenthesized_ranges(arg.tokens())); + filtered_member_ranges.extend(Self::filter_out_parenthesized_tokens(arg.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range)); } Some(IndentParenExprArgs { members_ranges: filtered_member_ranges, @@ -454,21 +458,21 @@ impl IndentParenExprArgs { pub fn from_while(node: &WhileContent) -> Option { Some(IndentParenExprArgs { - members_ranges: Self::filter_out_parenthesized_ranges(node.cond.tokens()), + members_ranges: Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(), lparen: node.lparen.range(), }) } pub fn from_do_while(node: &DoContent) -> Option { Some(IndentParenExprArgs { - members_ranges: Self::filter_out_parenthesized_ranges(node.cond.tokens()), + members_ranges: Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(), lparen: node.lparen.range(), }) } pub fn from_if(node: &IfContent) -> Option { Some(IndentParenExprArgs { - members_ranges: Self::filter_out_parenthesized_ranges(node.cond.tokens()), + members_ranges: Self::filter_out_parenthesized_tokens(node.cond.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(), lparen: node.lparen.range(), }) } @@ -477,14 +481,14 @@ impl IndentParenExprArgs { let mut cast_member_tokens = node.from.tokens(); cast_member_tokens.append(&mut node.to.tokens()); Some(IndentParenExprArgs { - members_ranges: Self::filter_out_parenthesized_ranges(cast_member_tokens), + members_ranges: Self::filter_out_parenthesized_tokens(cast_member_tokens).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(), lparen: node.lparen.range(), }) } pub fn from_switch(node: &SwitchContent) -> Option { Some(IndentParenExprArgs { - members_ranges: Self::filter_out_parenthesized_ranges(node.expr.tokens()), + members_ranges: Self::filter_out_parenthesized_tokens(node.expr.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range).collect(), lparen: node.lparen.range(), }) } @@ -692,3 +696,122 @@ impl Rule for IndentEmptyLoopRule { RuleType::IN10 } } + +// in6 +pub struct IndentContinuationLineRule { + pub enabled: bool, + indentation_spaces: u32 +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct IndentContinuationLineOptions { + #[serde(default = "default_indentation_spaces")] + pub indentation_spaces: u32, +} + +pub struct IndentContinuationLineArgs { + token_list: Vec, + expected_depth: u32, +} + +impl IndentContinuationLineArgs { + pub fn filter_out_last_semi_ranges(expression_tokens: &mut TreeElementTokenIterator) { + // This function filters out the last semicolon in a list of tokens + // as it is not part of the continuation line check. + expression_tokens.pop_if(|token| token.kind == TokenKind::SemiColon); + } + + pub fn from_statement_content(node: &StatementContent, depth: u32) -> Option { + match node { + StatementContent::Compound(_) | + StatementContent::If(_) | + StatementContent::While(_) | + StatementContent::Do(_) | + StatementContent::For(_) | + StatementContent::Try(_) | + StatementContent::Foreach(_) | + StatementContent::Throw(_) | + StatementContent::Switch(_) => return None, + _ => {} + }; + let mut tokens = node.tokens(); + Self::filter_out_last_semi_ranges(&mut tokens); + + Some(IndentContinuationLineArgs { + token_list: IndentParenExprArgs::filter_out_parenthesized_tokens(tokens), + expected_depth: depth, + }) + } + + pub fn from_dml_object_content(node: &DMLObjectContent, depth: u32) -> Option { + match node { + DMLObjectContent::Parameter(_) | + DMLObjectContent::Hook(_) | + DMLObjectContent::Import(_) | + DMLObjectContent::InEach(_) | + DMLObjectContent::Session(_) | + DMLObjectContent::Typedef(_) => { + let mut tokens = node.tokens(); + Self::filter_out_last_semi_ranges(&mut tokens); + return Some(IndentContinuationLineArgs { + token_list: IndentParenExprArgs::filter_out_parenthesized_tokens(tokens), + expected_depth: depth, + })}, + _ => return None, + } + } +} + +impl IndentContinuationLineRule { + pub fn from_options(options: &Option) -> IndentContinuationLineRule { + match options { + Some(options) => IndentContinuationLineRule { + enabled: true, + indentation_spaces: options.indentation_spaces + }, + None => IndentContinuationLineRule { + enabled: false, + indentation_spaces: 0 + } + } + } + + pub fn check<'a> (&self, acc: &mut Vec, + args: Option) { + if !self.enabled { return; } + let Some(args) = args else { return; }; + if args.token_list.is_empty() { return; } + let expected_line_start = self.indentation_spaces * (args.expected_depth + 1); + let mut last_row = args.token_list.first().unwrap().range.row_start.0; + + for token in args.token_list { + match token.kind { + TokenKind::RParen => { + if token.range.row_start.0 != last_row { + last_row = token.range.row_start.0; + } + } + _ => { + if token.range.row_start.0 != last_row { + last_row = token.range.row_start.0; + if token.range.col_start.0 != expected_line_start { + acc.push(self.create_err(token.range)) + } + } + } + } + } + } +} + +impl Rule for IndentContinuationLineRule { + fn name() -> &'static str { + "INDENT_CONTINUATION_LINE" + } + fn description() -> &'static str { + "A continuation line not broken inside a parenthesized expression is indented one level." + } + fn get_rule_type() -> RuleType { + RuleType::IN6 + } +} diff --git a/src/lint/rules/mod.rs b/src/lint/rules/mod.rs index aec8549..7a45d37 100644 --- a/src/lint/rules/mod.rs +++ b/src/lint/rules/mod.rs @@ -15,7 +15,14 @@ use spacing::{NspFunparRule, NspPtrDeclRule, SpPunctRule, SpReservedRule}; -use indentation::{LongLinesRule, IndentNoTabRule, IndentCodeBlockRule, IndentClosingBraceRule, IndentParenExprRule, IndentSwitchCaseRule, IndentEmptyLoopRule}; +use indentation::{LongLinesRule, + IndentNoTabRule, + IndentCodeBlockRule, + IndentClosingBraceRule, + IndentParenExprRule, + IndentSwitchCaseRule, + IndentEmptyLoopRule, + IndentContinuationLineRule}; use crate::lint::{LintCfg, DMLStyleError}; use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange}; @@ -37,7 +44,8 @@ pub struct CurrentRules { pub indent_closing_brace: IndentClosingBraceRule, pub indent_paren_expr: IndentParenExprRule, pub indent_switch_case: IndentSwitchCaseRule, - pub indent_empty_loop: IndentEmptyLoopRule + pub indent_empty_loop: IndentEmptyLoopRule, + pub indent_continuation_line: IndentContinuationLineRule, } pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { @@ -59,7 +67,8 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { indent_closing_brace: IndentClosingBraceRule::from_options(&cfg.indent_closing_brace), indent_paren_expr: IndentParenExprRule { enabled: cfg.indent_paren_expr.is_some() }, indent_switch_case: IndentSwitchCaseRule::from_options(&cfg.indent_switch_case), - indent_empty_loop: IndentEmptyLoopRule::from_options(&cfg.indent_empty_loop) + indent_empty_loop: IndentEmptyLoopRule::from_options(&cfg.indent_empty_loop), + indent_continuation_line: IndentContinuationLineRule::from_options(&cfg.indent_continuation_line), } } diff --git a/src/lint/rules/tests/indentation/continuation_line.rs b/src/lint/rules/tests/indentation/continuation_line.rs new file mode 100644 index 0000000..94af322 --- /dev/null +++ b/src/lint/rules/tests/indentation/continuation_line.rs @@ -0,0 +1,193 @@ +use crate::lint::rules::tests::common::{set_up, robust_assert_snippet as assert_snippet}; +use crate::lint::rules::RuleType; + +static CONTINUATION_LINE_CORRECT: &str = " +method set_irq() { + interrupt_enabled = + irq_enabled(interrupt_device); +} +"; +#[test] +fn continuation_line_correct() { + let rules = set_up(); + assert_snippet(CONTINUATION_LINE_CORRECT, vec![], &rules); +} + +static CONTINUATION_LINE_RETURN_CORRECT: &str = " +method calculate_sum(uint64 a, uint64 b) -> (uint64) { + return (a + b) * (a - b) + + (a * b); +} +"; +#[test] +fn continuation_line_return_correct() { + let rules = set_up(); + assert_snippet(CONTINUATION_LINE_RETURN_CORRECT, vec![], &rules); +} + +static CONTINUATION_LINE_LOCAL_DECLA_CORRECT: &str = " +bank regs { + register example_register size 4 @ 0x00 { + method read() -> (uint64) { + local uint64 value = (this.val + 10) * + (this.val - 5); + return value; + } + } +} +"; +#[test] +fn continuation_line_local_decla_correct() { + let rules = set_up(); + assert_snippet(CONTINUATION_LINE_LOCAL_DECLA_CORRECT, vec![], &rules); +} + +pub static CONTINUATION_LINE_FUNC_CALL_CORRECT: &str = " +method check_hashes(uint64 key, x_range *range) -> (int) { + local const uint8 *digest = get_key(key); + local bool same_hash = memcmp(range->hash_bytes, + digest, SIZE) == 0; + return (same_hash) ? CORRECT : INCORRECT; +} +"; +#[test] +fn continuation_line_func_call_correct() { + let rules = set_up(); + assert_snippet(CONTINUATION_LINE_FUNC_CALL_CORRECT, vec![], &rules); +} + +pub static CONTINUATION_LINE_IN_EACH_CORRECT: &str =" +in each reg_read_write { + param y_val_reg = CONST_X; + is ip_disable; +} +"; +#[test] +fn continuation_line_in_each_correct() { + let rules = set_up(); + assert_snippet(CONTINUATION_LINE_IN_EACH_CORRECT, vec![], &rules); +} + +static CONTINUATION_LINE_INCORRECT: &str = " +method set_irq() { + interrupt_enabled = +irq_enabled(interrupt_device); +} +"; +#[test] +fn continuation_line_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::IN6, + (3, 3, 0, 11), + ); + assert_snippet(CONTINUATION_LINE_INCORRECT, expected_errors, &rules); +} + +pub static CONTINUATION_LINE_BITWISE_INCORRECT: &str = " +method write(uint64 value) { + local uint64 a = value; + local uint64 result = a << + 2; + log info: 'Writing to register, result after left shift = %x', result; +} +"; +#[test] +fn continuation_line_bitwise_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::IN6, + (4, 4, 31, 32), + ); + assert_snippet(CONTINUATION_LINE_BITWISE_INCORRECT, expected_errors, &rules); +} + +pub static CONTINUATION_LINE_PARAM_INCORRECT: &str = " +bank regs { + register control size 4 @ 0x00 { + field status @ [31:3] { + param init_val = 1 << 2 | + 1 << 1; + } + } +} +"; +#[test] +fn continuation_line_param_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::IN6, + (5, 5, 44, 45), + ); + assert_snippet(CONTINUATION_LINE_PARAM_INCORRECT, expected_errors, &rules); +} + +pub static CONTINUATION_LINE_HOOK_INCORRECT: &str = " +hook(int *, bool) +h3; + +template hooktest { + hook() + h2; +} +"; +#[test] +fn continuation_line_hook_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::IN6, + (2, 2, 0, 2), + (6, 6, 16, 18), + ); + assert_snippet(CONTINUATION_LINE_HOOK_INCORRECT, expected_errors, &rules); +} + +pub static CONTINUATION_LINE_IN_EACH_INCORRECT: &str =" +in each +reg_read_write { + param y_val_reg = CONST_X; + is ip_disable; +} +"; +#[test] +fn continuation_line_in_each_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::IN6, + (2, 2, 0, 14), + ); + assert_snippet(CONTINUATION_LINE_IN_EACH_INCORRECT, expected_errors, &rules); +} + +pub static CONTINUATION_LINE_SESSION_INCORRECT: &str =" +session + int max_buffer_index = 1; +session +int last_index_sent = 0; +"; +#[test] +fn continuation_line_session_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::IN6, + (4, 4, 0, 3), + ); + assert_snippet(CONTINUATION_LINE_SESSION_INCORRECT, expected_errors, &rules); +} + +pub static CONTINUATION_LINE_TYPEDEF_INCORRECT: &str =" +typedef + struct { + uint32 data; + uint32 address; +}entry; +"; +#[test] +fn continuation_line_typedef_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::IN6, + (2, 2, 8, 14), + ); + assert_snippet(CONTINUATION_LINE_TYPEDEF_INCORRECT, expected_errors, &rules); +} \ No newline at end of file diff --git a/src/lint/rules/tests/indentation/mod.rs b/src/lint/rules/tests/indentation/mod.rs index 4eb63c9..a0cb896 100644 --- a/src/lint/rules/tests/indentation/mod.rs +++ b/src/lint/rules/tests/indentation/mod.rs @@ -4,6 +4,7 @@ mod closing_brace; mod paren_expr; mod switch_case; mod empty_loop; +mod continuation_line; use crate::lint::rules::tests::common::assert_snippet; use crate::lint::rules::RuleType; From 3f8f13131a44d9343fb8bc95534a35237aa0e6ca Mon Sep 17 00:00:00 2001 From: "Vasquez Alfaro, Juan J" Date: Thu, 29 May 2025 18:51:45 -0600 Subject: [PATCH 2/8] Add rule LL6 for funcall broken after left paren --- example_files/example_lint_cfg.json | 1 + src/analysis/parsing/expression.rs | 13 +- src/analysis/parsing/structure.rs | 4 +- src/lint/mod.rs | 5 + src/lint/rules/indentation.rs | 15 +- src/lint/rules/linebreaking.rs | 164 ++++++++++++++++++ src/lint/rules/mod.rs | 5 + .../tests/indentation/continuation_line.rs | 2 +- .../rules/tests/indentation/paren_expr.rs | 10 +- .../func_call_break_open_paren.rs | 163 +++++++++++++++++ .../rules/tests/line_length_breaking/mod.rs | 1 + src/lint/rules/tests/mod.rs | 2 + 12 files changed, 373 insertions(+), 12 deletions(-) create mode 100644 src/lint/rules/linebreaking.rs create mode 100644 src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs create mode 100644 src/lint/rules/tests/line_length_breaking/mod.rs diff --git a/example_files/example_lint_cfg.json b/example_files/example_lint_cfg.json index cd764cc..4535685 100644 --- a/example_files/example_lint_cfg.json +++ b/example_files/example_lint_cfg.json @@ -19,5 +19,6 @@ "indent_switch_case": {}, "indent_empty_loop": {}, "indent_continuation_line": {}, + "func_call_break_on_open_paren": {}, "annotate_lints": true } diff --git a/src/analysis/parsing/expression.rs b/src/analysis/parsing/expression.rs index 0add648..f317c50 100644 --- a/src/analysis/parsing/expression.rs +++ b/src/analysis/parsing/expression.rs @@ -27,6 +27,7 @@ use crate::lint::{DMLStyleError, CurrentRules}, AuxParams}; use crate::lint::rules::indentation::IndentParenExprArgs; +use crate::lint::rules::linebreaking::FuncCallBreakOnOpenParenArgs; #[derive(Debug, Clone, PartialEq)] pub struct UnaryExpressionContent { @@ -175,8 +176,10 @@ impl TreeElement for ParenExpressionContent { fn subs(&self) -> TreeElements<'_> { create_subs!(&self.lparen, &self.expr, &self.rparen) } - fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, _aux: AuxParams) { + fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { rules.indent_paren_expr.check(IndentParenExprArgs::from_paren_expression(self), acc); + rules.func_call_break_on_open_paren.check( + FuncCallBreakOnOpenParenArgs::from_paren_expression(self, aux.depth), acc); } } @@ -222,11 +225,13 @@ impl TreeElement for FunctionCallContent { noderef, ReferenceKind::Callable)); } } - fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, _aux: AuxParams) { + fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { rules.nsp_funpar.check(NspFunparArgs::from_function_call(self), acc); rules.nsp_inparen.check(NspInparenArgs::from_function_call(self), acc); rules.sp_punct.check(SpPunctArgs::from_function_call(self), acc); rules.indent_paren_expr.check(IndentParenExprArgs::from_function_call(self), acc); + rules.func_call_break_on_open_paren + .check(FuncCallBreakOnOpenParenArgs::from_function_call(self, aux.depth), acc); } } @@ -340,8 +345,10 @@ impl TreeElement for CastContent { create_subs!(&self.cast, &self.lparen, &self.from, &self.comma, &self.to, &self.rparen) } - fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, _aux: AuxParams) { + fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { rules.indent_paren_expr.check(IndentParenExprArgs::from_cast(self), acc); + rules.func_call_break_on_open_paren + .check(FuncCallBreakOnOpenParenArgs::from_cast(self, aux.depth), acc); } } diff --git a/src/analysis/parsing/structure.rs b/src/analysis/parsing/structure.rs index 39267ab..da46d45 100644 --- a/src/analysis/parsing/structure.rs +++ b/src/analysis/parsing/structure.rs @@ -16,6 +16,7 @@ use crate::analysis::parsing::parser::{doesnt_understand_tokens, FileParser, Parse, ParseContext, FileInfo}; use crate::analysis::LocalDMLError; +use crate::lint::rules::linebreaking::FuncCallBreakOnOpenParenArgs; use crate::lint::rules::spacing::{SpBracesArgs, NspInparenArgs, NspFunparArgs, @@ -238,11 +239,12 @@ impl TreeElement for MethodContent { } errors } - fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, _aux: AuxParams) { + fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { rules.nsp_funpar.check(NspFunparArgs::from_method(self), acc); rules.nsp_inparen.check(NspInparenArgs::from_method(self), acc); rules.sp_punct.check(SpPunctArgs::from_method(self), acc); rules.indent_paren_expr.check(IndentParenExprArgs::from_method(self), acc); + rules.func_call_break_on_open_paren.check(FuncCallBreakOnOpenParenArgs::from_method(self, aux.depth), acc); } } diff --git a/src/lint/mod.rs b/src/lint/mod.rs index e50ec23..0d02c45 100644 --- a/src/lint/mod.rs +++ b/src/lint/mod.rs @@ -5,6 +5,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use lazy_static::lazy_static; use log::{debug, error, trace}; +use rules::linebreaking::FuncCallBreakOnOpenParenOptions; use serde::{Deserialize, Serialize}; use regex::Regex; use rules::{instantiate_rules, CurrentRules, RuleType}; @@ -113,6 +114,8 @@ pub struct LintCfg { pub indent_empty_loop: Option, #[serde(default)] pub indent_continuation_line: Option, + #[serde(default)] + pub func_call_break_on_open_paren: Option, #[serde(default = "get_true")] pub annotate_lints: bool, } @@ -159,11 +162,13 @@ impl Default for LintCfg { indent_switch_case: Some(IndentSwitchCaseOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), indent_empty_loop: Some(IndentEmptyLoopOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), indent_continuation_line: Some(IndentContinuationLineOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), + func_call_break_on_open_paren: Some(FuncCallBreakOnOpenParenOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), annotate_lints: true, } } } + #[derive(Debug, Clone)] pub struct DMLStyleError { pub error: LocalDMLError, diff --git a/src/lint/rules/indentation.rs b/src/lint/rules/indentation.rs index c0c1eba..2854540 100644 --- a/src/lint/rules/indentation.rs +++ b/src/lint/rules/indentation.rs @@ -376,6 +376,9 @@ pub struct IndentParenExprArgs { } impl IndentParenExprArgs { + pub fn is_broken_after_lparen(lparen_range: ZeroRange, next_token_range: ZeroRange) -> bool { + lparen_range.row_start != next_token_range.row_start + } pub fn filter_out_parenthesized_tokens(expression_tokens: TreeElementTokenIterator) -> Vec { let mut token_list: Vec = vec![]; let mut paren_depth = 0; @@ -431,6 +434,11 @@ impl IndentParenExprArgs { for (arg, _comma) in node.arguments.iter() { filtered_member_ranges.extend(Self::filter_out_parenthesized_tokens(arg.tokens()).iter().filter(|t| t.kind != TokenKind::RParen).map(|t| t.range)); } + if filtered_member_ranges.is_empty() + || Self::is_broken_after_lparen(node.lparen.range(), + filtered_member_ranges.first()?.to_owned()) { + return None + } Some(IndentParenExprArgs { members_ranges: filtered_member_ranges, lparen: node.lparen.range(), @@ -500,9 +508,14 @@ impl IndentParenExprRule { acc: &mut Vec) { if !self.enabled { return; } let Some(args) = args else { return; }; + if args.members_ranges.is_empty() { return; } let expected_line_start = args.lparen.col_start.0 + 1; let mut last_row = args.lparen.row_start.0; - + if last_row != args.members_ranges.first().unwrap().row_start.0 { + // If the first member is not on the same line as the lparen, + // then it is not a continuation line, so we do not check it. + return; + } for member_range in args.members_ranges { if member_range.row_start.0 != last_row { last_row = member_range.row_start.0; diff --git a/src/lint/rules/linebreaking.rs b/src/lint/rules/linebreaking.rs new file mode 100644 index 0000000..6512206 --- /dev/null +++ b/src/lint/rules/linebreaking.rs @@ -0,0 +1,164 @@ +use serde::{Deserialize, Serialize}; +use crate::analysis::parsing::lexer::TokenKind; +use crate::analysis::parsing::parser::Token; +use crate::analysis::parsing::structure::MethodContent; +use crate::analysis::parsing::tree::{TreeElement, TreeElementTokenIterator, ZeroRange}; +use crate::analysis::parsing::expression::{CastContent, FunctionCallContent, ParenExpressionContent}; +use crate::lint::{DMLStyleError, RuleType}; +use super::indentation::IndentParenExprArgs; +use super::Rule; + +pub const INDENTATION_LEVEL_DEFAULT: u32 = 4; + +fn default_indentation_spaces() -> u32 { + INDENTATION_LEVEL_DEFAULT +} + +pub struct FuncCallBreakOnOpenParenRule { + pub enabled: bool, + indentation_spaces: u32 +} + +pub struct FuncCallBreakOnOpenParenArgs { + pub members_ranges: Vec, + pub expected_depth: u32, + pub lparen: ZeroRange, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct FuncCallBreakOnOpenParenOptions{ + #[serde(default = "default_indentation_spaces")] + pub indentation_spaces: u32, +} + +impl Rule for FuncCallBreakOnOpenParenRule { + fn name() -> &'static str { + "FUNC_CALL_BREAK_ON_OPEN_PAREN" + } + fn description() -> &'static str { + "Function or method calls broken right after opening parenthesis should + indent continuation lines one more level." + } + fn get_rule_type() -> RuleType { + RuleType::LL6 + } +} + +impl FuncCallBreakOnOpenParenArgs { + pub fn filter_out_parenthesized_tokens(expression_tokens: TreeElementTokenIterator) -> Vec { + let mut token_list: Vec = vec![]; + let mut paren_depth = 0; + // paren_depth is used to identify nested + // parenthesized expressions within other expressions + // and avoid double checking this type, given + // ParenExpressionContent already checks indent_paren_expr on its own + for token in expression_tokens { + match token.kind { + TokenKind::LParen => { + if paren_depth == 0 { token_list.push(token); } + paren_depth += 1; + }, + TokenKind::RParen => { + paren_depth-=1; + }, + TokenKind::LBrace => { + break; + }, + _ => { + if paren_depth == 0 { token_list.push(token); } + } + } + } + token_list + } + pub fn from_function_call(node: &FunctionCallContent, depth: u32) -> Option { + let mut filtered_member_ranges: Vec = vec![]; + for (arg, _comma) in node.arguments.iter() { + filtered_member_ranges.extend( + Self::filter_out_parenthesized_tokens(arg.tokens()) + .iter().map(|t| t.range) + ); + } + if filtered_member_ranges.is_empty() + || ! IndentParenExprArgs::is_broken_after_lparen( + node.lparen.range(), + filtered_member_ranges.first()?.to_owned()) { + return None + } + Some(FuncCallBreakOnOpenParenArgs { + members_ranges: filtered_member_ranges, + expected_depth: depth, + lparen: node.lparen.range(), + }) + } + + pub fn from_paren_expression(node: &ParenExpressionContent, depth: u32) -> Option { + Some(FuncCallBreakOnOpenParenArgs { + members_ranges: Self::filter_out_parenthesized_tokens(node.expr.tokens()) + .iter().map(|t| t.range).collect(), + expected_depth: depth, + lparen: node.lparen.range(), + }) + } + + pub fn from_method(node: &MethodContent, depth: u32) -> Option { + let mut filtered_member_ranges: Vec = vec![]; + for (arg, _comma) in node.arguments.iter() { + filtered_member_ranges.extend( + Self::filter_out_parenthesized_tokens(arg.tokens()) + .iter().map(|t| t.range)); + } + Some(FuncCallBreakOnOpenParenArgs { + members_ranges: filtered_member_ranges, + expected_depth: depth, + lparen: node.lparen.range(), + }) + } + + pub fn from_cast(node: &CastContent, depth: u32) -> Option { + let mut cast_member_tokens = node.from.tokens(); + cast_member_tokens.append(&mut node.to.tokens()); + Some(FuncCallBreakOnOpenParenArgs { + members_ranges: Self::filter_out_parenthesized_tokens(cast_member_tokens) + .iter().map(|t| t.range).collect(), + expected_depth: depth, + lparen: node.lparen.range(), + }) + } +} + +impl FuncCallBreakOnOpenParenRule { + pub fn from_options(options: &Option) -> FuncCallBreakOnOpenParenRule { + match options { + Some(options) => FuncCallBreakOnOpenParenRule { + enabled: true, + indentation_spaces: options.indentation_spaces + }, + None => FuncCallBreakOnOpenParenRule { + enabled: false, + indentation_spaces: 0 + } + } + } + + pub fn check(&self, args: Option, acc: &mut Vec) { + if !self.enabled { return; } + let Some(args) = args else { return; }; + if args.members_ranges.is_empty() { return; } + let expected_line_start = self.indentation_spaces * (args.expected_depth + 1); + let mut last_row = args.lparen.row_start.0; + if last_row == args.members_ranges.first().unwrap().row_start.0 { + // If the first member is on the same line as the lparen, we don't + // need to check it. + return; + } + for member_range in args.members_ranges { + if member_range.row_start.0 != last_row { + last_row = member_range.row_start.0; + if member_range.col_start.0 != expected_line_start { + acc.push(self.create_err(member_range)); + } + } + } + } +} diff --git a/src/lint/rules/mod.rs b/src/lint/rules/mod.rs index 7a45d37..e068efe 100644 --- a/src/lint/rules/mod.rs +++ b/src/lint/rules/mod.rs @@ -1,5 +1,6 @@ pub mod spacing; pub mod indentation; +pub mod linebreaking; #[cfg(test)] pub mod tests; @@ -23,6 +24,7 @@ use indentation::{LongLinesRule, IndentSwitchCaseRule, IndentEmptyLoopRule, IndentContinuationLineRule}; +use linebreaking::FuncCallBreakOnOpenParenRule; use crate::lint::{LintCfg, DMLStyleError}; use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange}; @@ -46,6 +48,7 @@ pub struct CurrentRules { pub indent_switch_case: IndentSwitchCaseRule, pub indent_empty_loop: IndentEmptyLoopRule, pub indent_continuation_line: IndentContinuationLineRule, + pub func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule, } pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { @@ -69,6 +72,7 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { indent_switch_case: IndentSwitchCaseRule::from_options(&cfg.indent_switch_case), indent_empty_loop: IndentEmptyLoopRule::from_options(&cfg.indent_empty_loop), indent_continuation_line: IndentContinuationLineRule::from_options(&cfg.indent_continuation_line), + func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule::from_options(&cfg.func_call_break_on_open_paren), } } @@ -103,6 +107,7 @@ pub enum RuleType { NspUnary, NspTrailing, LL1, + LL6, IN2, IN3, IN4, diff --git a/src/lint/rules/tests/indentation/continuation_line.rs b/src/lint/rules/tests/indentation/continuation_line.rs index 94af322..18bff9e 100644 --- a/src/lint/rules/tests/indentation/continuation_line.rs +++ b/src/lint/rules/tests/indentation/continuation_line.rs @@ -1,4 +1,4 @@ -use crate::lint::rules::tests::common::{set_up, robust_assert_snippet as assert_snippet}; +use crate::lint::rules::tests::common::{set_up, assert_snippet}; use crate::lint::rules::RuleType; static CONTINUATION_LINE_CORRECT: &str = " diff --git a/src/lint/rules/tests/indentation/paren_expr.rs b/src/lint/rules/tests/indentation/paren_expr.rs index 762b6dc..d303e58 100644 --- a/src/lint/rules/tests/indentation/paren_expr.rs +++ b/src/lint/rules/tests/indentation/paren_expr.rs @@ -346,8 +346,7 @@ fn switch_paren_incorrect() { } static NESTED_PAREN_EXPR_CORRECT: &str = " -param result = ( - (reg0.val +param result = ((reg0.val * reg1.enable.val) & mask_reg @@ -362,8 +361,7 @@ fn nested_paren_expr_correct(){ } static NESTED_PAREN_EXPR_INCORRECT: &str = " -param result = ( - (reg0.val +param result = ((reg0.val * reg1.enable.val) & mask_reg @@ -375,8 +373,8 @@ fn nested_paren_expr_incorrect(){ let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::IN5, - (2, 2, 4, 5), - (5, 5, 17, 25), + (2, 2, 5, 6), + (4, 4, 17, 25), ); assert_snippet(NESTED_PAREN_EXPR_INCORRECT, expected_errors, &rules); } diff --git a/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs b/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs new file mode 100644 index 0000000..a6109f1 --- /dev/null +++ b/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs @@ -0,0 +1,163 @@ +use crate::lint::rules::tests::common::{set_up, assert_snippet}; +use crate::lint::rules::RuleType; + +// LL6: Function and method invocations can be broken after the opening parenthesis, +// with the continuation lines indented one level. + +static FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_CORRECT: &str = " +method effect() { + callback( + 0xABC, + identifier, + false); +} +"; +#[test] +fn funcall_broken_after_paren_exception_correct() { + let rules = set_up(); + assert_snippet(FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_CORRECT, vec![], &rules); +} + +static FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_INCORRECT: &str = " +method effect() { + callback( + 0xABC, + identifier, + false); +} +"; +#[test] +fn funcall_broken_after_paren_exception_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL6, + (4, 4, 12, 22), + (5, 5, 4, 9), + ); + assert_snippet(FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_INCORRECT, expected_errors, &rules); +} + +static FIRST_LINE_INCORRECT: &str = " +method effect() { + callback( + 0xABC, + identifier, + false); +} +"; +#[test] +fn first_line_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL6, + (3, 3, 15, 20) + ); + assert_snippet(FIRST_LINE_INCORRECT, expected_errors, &rules); +} + +static PAREN_NESTED_CORRECT: &str = " +param result = ( + (reg0.val + * reg1.enable.val) + & + mask_reg + + + 1); +"; + +#[test] +fn paren_nested_correct(){ + let rules = set_up(); + assert_snippet(PAREN_NESTED_CORRECT, vec![], &rules); +} + +static PAREN_NESTED_INCORRECT: &str = " +param result = ( + (reg0.val + * reg1.enable.val) + & + mask_reg + + + 1); +"; + +#[test] +fn paren_nested_incorrect(){ + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL6, + (4, 4, 16, 17), + (6, 6, 16, 17), + (7, 7, 16, 17), + ); + assert_snippet(PAREN_NESTED_INCORRECT, expected_errors, &rules); +} + +static METHOD_CORRECT: &str = " +method some_method( + int a, + int b, bool c) { + return c ? a + b : a - b; +} +"; +#[test] +fn method_correct() { + let rules = set_up(); + assert_snippet(METHOD_CORRECT, vec![], &rules); +} + +static METHOD_INCORRECT: &str = " +method some_method( + int a, + int b, + bool c) { + return c ? a + b : a - b; +} +"; +#[test] +fn method_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL6, + (4, 4, 8, 12), + ); + assert_snippet(METHOD_INCORRECT, expected_errors, &rules); +} + +static CAST_CORRECT: &str = " +method example_cast() { + local uint64 original_value = 42; + local uint32 casted_value; + + casted_value = cast(original_value, uint32); + + log info: casted_value; +} +"; +#[test] +fn cast_correct() { + let rules = set_up(); + assert_snippet(CAST_CORRECT, vec![], &rules); +} + +static CAST_INCORRECT: &str = " +method example_cast() { + local uint64 original_value = 42; + local uint32 casted_value; + + casted_value = cast( + original_value, + uint32); + + log info: casted_value; +} +"; +#[test] +fn cast_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL6, + (6, 6, 12, 26), + ); + assert_snippet(CAST_INCORRECT, expected_errors, &rules); +} diff --git a/src/lint/rules/tests/line_length_breaking/mod.rs b/src/lint/rules/tests/line_length_breaking/mod.rs new file mode 100644 index 0000000..b1c48d3 --- /dev/null +++ b/src/lint/rules/tests/line_length_breaking/mod.rs @@ -0,0 +1 @@ +mod func_call_break_open_paren; \ No newline at end of file diff --git a/src/lint/rules/tests/mod.rs b/src/lint/rules/tests/mod.rs index 6be3894..8e04a06 100644 --- a/src/lint/rules/tests/mod.rs +++ b/src/lint/rules/tests/mod.rs @@ -2,3 +2,5 @@ pub mod common; mod indentation; mod spacing; +mod line_length_breaking; + From 568cc5df0ba0fa8fa34a498ded6d04b7bc9070c1 Mon Sep 17 00:00:00 2001 From: alecalvop <104095255+alecalvop@users.noreply.github.com> Date: Thu, 14 Aug 2025 14:43:55 -0600 Subject: [PATCH 3/8] Add MethodOutputBreak (LL5) Rule (#24) * Add MethodOutputBreak (LL5) Rule --- example_files/example_lint_cfg.json | 1 + src/analysis/parsing/structure.rs | 3 +- src/lint/features.md | 7 +++ src/lint/mod.rs | 5 +- src/lint/rules/linebreaking.rs | 52 ++++++++++++++++ src/lint/rules/mod.rs | 6 +- .../method_output_break.rs | 60 +++++++++++++++++++ .../rules/tests/line_length_breaking/mod.rs | 3 +- 8 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 src/lint/rules/tests/line_length_breaking/method_output_break.rs diff --git a/example_files/example_lint_cfg.json b/example_files/example_lint_cfg.json index 4535685..2e6b29f 100644 --- a/example_files/example_lint_cfg.json +++ b/example_files/example_lint_cfg.json @@ -20,5 +20,6 @@ "indent_empty_loop": {}, "indent_continuation_line": {}, "func_call_break_on_open_paren": {}, + "method_output_break": {}, "annotate_lints": true } diff --git a/src/analysis/parsing/structure.rs b/src/analysis/parsing/structure.rs index da46d45..601937d 100644 --- a/src/analysis/parsing/structure.rs +++ b/src/analysis/parsing/structure.rs @@ -16,7 +16,7 @@ use crate::analysis::parsing::parser::{doesnt_understand_tokens, FileParser, Parse, ParseContext, FileInfo}; use crate::analysis::LocalDMLError; -use crate::lint::rules::linebreaking::FuncCallBreakOnOpenParenArgs; +use crate::lint::rules::linebreaking::{FuncCallBreakOnOpenParenArgs, MethodOutputBreakArgs}; use crate::lint::rules::spacing::{SpBracesArgs, NspInparenArgs, NspFunparArgs, @@ -245,6 +245,7 @@ impl TreeElement for MethodContent { rules.sp_punct.check(SpPunctArgs::from_method(self), acc); rules.indent_paren_expr.check(IndentParenExprArgs::from_method(self), acc); rules.func_call_break_on_open_paren.check(FuncCallBreakOnOpenParenArgs::from_method(self, aux.depth), acc); + rules.method_output_break.check(MethodOutputBreakArgs::from_method(self), acc); } } diff --git a/src/lint/features.md b/src/lint/features.md index 030d884..e76d3d2 100644 --- a/src/lint/features.md +++ b/src/lint/features.md @@ -47,5 +47,12 @@ Below are listed the currently supported rules for linting: ## Line Length - **LL1**, `long_lines`: Lines should be kept shorter than 80 characters. This limit can be set to a custom value +- **LL5**, `method_output_break`: Break long method declarations with output parameters before the arrow. +``` + method inquiry_status(uint64 physical_address) + -> (uint16 status) { + ... + } +``` ##### Check [Issue #76 For remaining and planned checks](https://github.com/intel/dml-language-server/issues/76) diff --git a/src/lint/mod.rs b/src/lint/mod.rs index 0d02c45..d6ff53e 100644 --- a/src/lint/mod.rs +++ b/src/lint/mod.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use lazy_static::lazy_static; use log::{debug, error, trace}; -use rules::linebreaking::FuncCallBreakOnOpenParenOptions; +use rules::linebreaking::{FuncCallBreakOnOpenParenOptions, MethodOutputBreakOptions}; use serde::{Deserialize, Serialize}; use regex::Regex; use rules::{instantiate_rules, CurrentRules, RuleType}; @@ -116,6 +116,8 @@ pub struct LintCfg { pub indent_continuation_line: Option, #[serde(default)] pub func_call_break_on_open_paren: Option, + #[serde(default)] + pub method_output_break: Option, #[serde(default = "get_true")] pub annotate_lints: bool, } @@ -163,6 +165,7 @@ impl Default for LintCfg { indent_empty_loop: Some(IndentEmptyLoopOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), indent_continuation_line: Some(IndentContinuationLineOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), func_call_break_on_open_paren: Some(FuncCallBreakOnOpenParenOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), + method_output_break: Some(MethodOutputBreakOptions{}), annotate_lints: true, } } diff --git a/src/lint/rules/linebreaking.rs b/src/lint/rules/linebreaking.rs index 6512206..bdffa52 100644 --- a/src/lint/rules/linebreaking.rs +++ b/src/lint/rules/linebreaking.rs @@ -14,6 +14,58 @@ fn default_indentation_spaces() -> u32 { INDENTATION_LEVEL_DEFAULT } +pub struct MethodOutputBreakRule { + pub enabled: bool +} + +pub struct MethodOutputBreakArgs { + pub before_arrow_range: ZeroRange, + pub arrow_range: ZeroRange, + pub after_arrow_range: ZeroRange, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct MethodOutputBreakOptions{ +} + +impl Rule for MethodOutputBreakRule { + fn name() -> &'static str { + "method_output_break" + } + fn description() -> &'static str { + "Break long method declarations with output parameters before the arrow." + } + fn get_rule_type() -> RuleType { + RuleType::LL5 + } +} + +impl MethodOutputBreakArgs { + pub fn from_method(node: &MethodContent) -> Option { + let Some(returns) = &node.returns else { return None; }; + Some(MethodOutputBreakArgs { + before_arrow_range: node.rparen.range(), + arrow_range: returns.0.range(), + after_arrow_range: returns.1.range(), + }) + } +} + +impl MethodOutputBreakRule { + pub fn check(&self, args: Option, acc: &mut Vec) { + if !self.enabled { return; } + let Some(args) = args else { return; }; + if args.before_arrow_range.row_end.0 == args.after_arrow_range.row_start.0 { + // If all parts are on the same line, we don't need to check it. + return; + } + // If the arrow and the return type are not on the same line, report an error. + if args.arrow_range.row_start.0 != args.after_arrow_range.row_start.0 { + acc.push(self.create_err(args.arrow_range)); + } + } +} + pub struct FuncCallBreakOnOpenParenRule { pub enabled: bool, indentation_spaces: u32 diff --git a/src/lint/rules/mod.rs b/src/lint/rules/mod.rs index e068efe..7c4685b 100644 --- a/src/lint/rules/mod.rs +++ b/src/lint/rules/mod.rs @@ -24,7 +24,8 @@ use indentation::{LongLinesRule, IndentSwitchCaseRule, IndentEmptyLoopRule, IndentContinuationLineRule}; -use linebreaking::FuncCallBreakOnOpenParenRule; +use linebreaking::{FuncCallBreakOnOpenParenRule, + MethodOutputBreakRule}; use crate::lint::{LintCfg, DMLStyleError}; use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange}; @@ -49,6 +50,7 @@ pub struct CurrentRules { pub indent_empty_loop: IndentEmptyLoopRule, pub indent_continuation_line: IndentContinuationLineRule, pub func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule, + pub method_output_break: MethodOutputBreakRule, } pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { @@ -73,6 +75,7 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { indent_empty_loop: IndentEmptyLoopRule::from_options(&cfg.indent_empty_loop), indent_continuation_line: IndentContinuationLineRule::from_options(&cfg.indent_continuation_line), func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule::from_options(&cfg.func_call_break_on_open_paren), + method_output_break: MethodOutputBreakRule { enabled: cfg.method_output_break.is_some() }, } } @@ -107,6 +110,7 @@ pub enum RuleType { NspUnary, NspTrailing, LL1, + LL5, LL6, IN2, IN3, diff --git a/src/lint/rules/tests/line_length_breaking/method_output_break.rs b/src/lint/rules/tests/line_length_breaking/method_output_break.rs new file mode 100644 index 0000000..6db2888 --- /dev/null +++ b/src/lint/rules/tests/line_length_breaking/method_output_break.rs @@ -0,0 +1,60 @@ +use crate::lint::rules::tests::common::{set_up, assert_snippet}; +use crate::lint::rules::RuleType; + +// LL5: Break long method declarations with output parameters before the arrow. + +static METHOD_OUTPUT_BREAK_CORRECT: &str = " +method inquiry_status(uint64 physical_address) + -> (uint16 status) { + return 0; +} + +method inquiry_status(uint64 physical_address) + -> (uint16 status) { + return 0; +} + +method inquiry_status(uint64 physical_address) -> (uint16 status) { + return 0; +} + +method other_method(uint64 arg1, + uint64 arg2) + -> (uint16 status) { + return 0; +} +"; +#[test] +fn method_output_break_correct() { + let rules = set_up(); + assert_snippet(METHOD_OUTPUT_BREAK_CORRECT, vec![], &rules); +} + +static METHOD_OUTPUT_BREAK_INCORRECT: &str = " +method inquiry_status(uint64 physical_address) -> + (uint16 status) { + return 0; +} + +method inquiry_status(uint64 physical_address) -> + (uint16 status) { + return 0; +} + +method other_method(uint64 arg1, + uint64 arg2) -> + (uint16 status) { + return 0; +} +"; +#[test] +fn method_output_break_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL5, + (1, 1, 47, 49), + (6, 6, 47, 49), + (12, 12, 33, 35), + ); + assert_snippet(METHOD_OUTPUT_BREAK_INCORRECT, expected_errors, &rules); +} \ No newline at end of file diff --git a/src/lint/rules/tests/line_length_breaking/mod.rs b/src/lint/rules/tests/line_length_breaking/mod.rs index b1c48d3..62bd822 100644 --- a/src/lint/rules/tests/line_length_breaking/mod.rs +++ b/src/lint/rules/tests/line_length_breaking/mod.rs @@ -1 +1,2 @@ -mod func_call_break_open_paren; \ No newline at end of file +mod func_call_break_open_paren; +mod method_output_break; \ No newline at end of file From e0b60f9f74e39cf05bb27574a183d657472a4f92 Mon Sep 17 00:00:00 2001 From: Dominic Tarassenko Date: Fri, 26 Sep 2025 04:41:45 +0000 Subject: [PATCH 4/8] Break on conditional expression (#23) * Add LL3 rule for conditional expression breaking * Cover more test cases * Add ll3 to features.md * Add to example_lint_cfg --- example_files/example_lint_cfg.json | 1 + src/analysis/parsing/expression.rs | 4 +- src/lint/features.md | 1 + src/lint/mod.rs | 6 +- src/lint/rules/indentation.rs | 7 +- src/lint/rules/linebreaking.rs | 76 ++++++++++++++++- src/lint/rules/mod.rs | 6 +- .../conditional_expression_break.rs | 82 +++++++++++++++++++ .../rules/tests/line_length_breaking/mod.rs | 3 +- 9 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 src/lint/rules/tests/line_length_breaking/conditional_expression_break.rs diff --git a/example_files/example_lint_cfg.json b/example_files/example_lint_cfg.json index 2e6b29f..9615583 100644 --- a/example_files/example_lint_cfg.json +++ b/example_files/example_lint_cfg.json @@ -21,5 +21,6 @@ "indent_continuation_line": {}, "func_call_break_on_open_paren": {}, "method_output_break": {}, + "conditional_expression_break_before_op": {}, "annotate_lints": true } diff --git a/src/analysis/parsing/expression.rs b/src/analysis/parsing/expression.rs index f317c50..4a519ff 100644 --- a/src/analysis/parsing/expression.rs +++ b/src/analysis/parsing/expression.rs @@ -27,7 +27,7 @@ use crate::lint::{DMLStyleError, CurrentRules}, AuxParams}; use crate::lint::rules::indentation::IndentParenExprArgs; -use crate::lint::rules::linebreaking::FuncCallBreakOnOpenParenArgs; +use crate::lint::rules::linebreaking::{ConditionalExpressionBreakBeforeOperatorArgs, FuncCallBreakOnOpenParenArgs}; #[derive(Debug, Clone, PartialEq)] pub struct UnaryExpressionContent { @@ -159,6 +159,8 @@ impl TreeElement for TertiaryExpressionContent { } fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, _aux: AuxParams) { rules.sp_ternary.check(SpTernaryArgs::from_tertiary_expression_content(self), acc); + rules.conditional_expression_break_before_op + .check(ConditionalExpressionBreakBeforeOperatorArgs::from_tertiary_expression(self), acc); } } diff --git a/src/lint/features.md b/src/lint/features.md index e76d3d2..87b1ff7 100644 --- a/src/lint/features.md +++ b/src/lint/features.md @@ -47,6 +47,7 @@ Below are listed the currently supported rules for linting: ## Line Length - **LL1**, `long_lines`: Lines should be kept shorter than 80 characters. This limit can be set to a custom value +- **LL3**, `conditional_expression_break`: Break conditional expressions before the ?, or both before the ? and before the :. - **LL5**, `method_output_break`: Break long method declarations with output parameters before the arrow. ``` method inquiry_status(uint64 physical_address) diff --git a/src/lint/mod.rs b/src/lint/mod.rs index d6ff53e..6c6d5dc 100644 --- a/src/lint/mod.rs +++ b/src/lint/mod.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use lazy_static::lazy_static; use log::{debug, error, trace}; -use rules::linebreaking::{FuncCallBreakOnOpenParenOptions, MethodOutputBreakOptions}; +use rules::linebreaking::{FuncCallBreakOnOpenParenOptions, MethodOutputBreakOptions, ConditionalExpressionBreakBeforeOperatorOptions}; use serde::{Deserialize, Serialize}; use regex::Regex; use rules::{instantiate_rules, CurrentRules, RuleType}; @@ -117,6 +117,8 @@ pub struct LintCfg { #[serde(default)] pub func_call_break_on_open_paren: Option, #[serde(default)] + pub conditional_expression_break_before_op: Option, + #[serde(default)] pub method_output_break: Option, #[serde(default = "get_true")] pub annotate_lints: bool, @@ -166,12 +168,12 @@ impl Default for LintCfg { indent_continuation_line: Some(IndentContinuationLineOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), func_call_break_on_open_paren: Some(FuncCallBreakOnOpenParenOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), method_output_break: Some(MethodOutputBreakOptions{}), + conditional_expression_break_before_op: Some(ConditionalExpressionBreakBeforeOperatorOptions{}), annotate_lints: true, } } } - #[derive(Debug, Clone)] pub struct DMLStyleError { pub error: LocalDMLError, diff --git a/src/lint/rules/indentation.rs b/src/lint/rules/indentation.rs index 2854540..f6fa412 100644 --- a/src/lint/rules/indentation.rs +++ b/src/lint/rules/indentation.rs @@ -731,7 +731,12 @@ impl IndentContinuationLineArgs { pub fn filter_out_last_semi_ranges(expression_tokens: &mut TreeElementTokenIterator) { // This function filters out the last semicolon in a list of tokens // as it is not part of the continuation line check. - expression_tokens.pop_if(|token| token.kind == TokenKind::SemiColon); + // expression_tokens.pop_if(|token| token.kind == TokenKind::SemiColon); + if let Some(last_token) = expression_tokens.last() { + if last_token.kind == TokenKind::SemiColon { + expression_tokens.pop(); + } + } } pub fn from_statement_content(node: &StatementContent, depth: u32) -> Option { diff --git a/src/lint/rules/linebreaking.rs b/src/lint/rules/linebreaking.rs index bdffa52..18ba359 100644 --- a/src/lint/rules/linebreaking.rs +++ b/src/lint/rules/linebreaking.rs @@ -2,8 +2,9 @@ use serde::{Deserialize, Serialize}; use crate::analysis::parsing::lexer::TokenKind; use crate::analysis::parsing::parser::Token; use crate::analysis::parsing::structure::MethodContent; -use crate::analysis::parsing::tree::{TreeElement, TreeElementTokenIterator, ZeroRange}; -use crate::analysis::parsing::expression::{CastContent, FunctionCallContent, ParenExpressionContent}; +use crate::analysis::parsing::tree::{ZeroRange, TreeElementTokenIterator, TreeElement}; +use crate::analysis::parsing::expression::{CastContent, FunctionCallContent, + ParenExpressionContent, TertiaryExpressionContent}; use crate::lint::{DMLStyleError, RuleType}; use super::indentation::IndentParenExprArgs; use super::Rule; @@ -214,3 +215,74 @@ impl FuncCallBreakOnOpenParenRule { } } } + +pub struct ConditionalExpressionBreakBeforeOperatorRule { + pub enabled: bool, +} + +pub struct ConditionalExpressionBreakBeforeOperatorArgs { + pub left: ZeroRange, + pub left_operation: ZeroRange, + pub middle: ZeroRange, + pub right_operation: ZeroRange, + pub right: ZeroRange +} + + +impl ConditionalExpressionBreakBeforeOperatorArgs { + pub fn from_tertiary_expression(node: &TertiaryExpressionContent) + -> Option { + Some(ConditionalExpressionBreakBeforeOperatorArgs { + left: node.left.range(), + left_operation: node.left_operation.range(), + middle: node.middle.range(), + right_operation: node.right_operation.range(), + right: node.right.range(), + }) + } +} + +impl ConditionalExpressionBreakBeforeOperatorRule { + pub fn from_options(options: &Option) -> ConditionalExpressionBreakBeforeOperatorRule { + match options { + Some(_options) => ConditionalExpressionBreakBeforeOperatorRule { + enabled: true, + }, + None => ConditionalExpressionBreakBeforeOperatorRule { + enabled: false, + } + } + } + + pub fn check(&self, args: Option, acc: &mut Vec) { + if !self.enabled { return; } + let Some(args) = args else { return; }; + let has_break_before_question_operator = args.left.row_end.0 != args.left_operation.row_start.0; + let has_break_after_question_operator = args.left_operation.row_end.0 != args.middle.row_start.0; + let has_break_before_colon_operator = args.middle.row_end.0 != args.right_operation.row_start.0; + let has_break_after_colon_operator = args.right_operation.row_end.0 != args.right.row_start.0; + if has_break_after_question_operator { + acc.push(self.create_err(args.left_operation)); + } + if has_break_after_colon_operator || (has_break_before_colon_operator && !has_break_before_question_operator ){ + acc.push(self.create_err(args.right_operation)); + } + } +} + + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct ConditionalExpressionBreakBeforeOperatorOptions{ +} + +impl Rule for ConditionalExpressionBreakBeforeOperatorRule { + fn name() -> &'static str { + "COND_EXPRESSION_BREAK_BEFORE_OPERATOR" + } + fn description() -> &'static str { + "Break conditional expressions before the ?, or both before the ? and before the :." + } + fn get_rule_type() -> RuleType { + RuleType::LL3 + } +} diff --git a/src/lint/rules/mod.rs b/src/lint/rules/mod.rs index 7c4685b..5277545 100644 --- a/src/lint/rules/mod.rs +++ b/src/lint/rules/mod.rs @@ -25,7 +25,8 @@ use indentation::{LongLinesRule, IndentEmptyLoopRule, IndentContinuationLineRule}; use linebreaking::{FuncCallBreakOnOpenParenRule, - MethodOutputBreakRule}; + MethodOutputBreakRule, + ConditionalExpressionBreakBeforeOperatorRule}; use crate::lint::{LintCfg, DMLStyleError}; use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange}; @@ -51,6 +52,7 @@ pub struct CurrentRules { pub indent_continuation_line: IndentContinuationLineRule, pub func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule, pub method_output_break: MethodOutputBreakRule, + pub conditional_expression_break_before_op: ConditionalExpressionBreakBeforeOperatorRule, } pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { @@ -76,6 +78,7 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { indent_continuation_line: IndentContinuationLineRule::from_options(&cfg.indent_continuation_line), func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule::from_options(&cfg.func_call_break_on_open_paren), method_output_break: MethodOutputBreakRule { enabled: cfg.method_output_break.is_some() }, + conditional_expression_break_before_op: ConditionalExpressionBreakBeforeOperatorRule::from_options(&cfg.conditional_expression_break_before_op), } } @@ -110,6 +113,7 @@ pub enum RuleType { NspUnary, NspTrailing, LL1, + LL3, LL5, LL6, IN2, diff --git a/src/lint/rules/tests/line_length_breaking/conditional_expression_break.rs b/src/lint/rules/tests/line_length_breaking/conditional_expression_break.rs new file mode 100644 index 0000000..9bda05e --- /dev/null +++ b/src/lint/rules/tests/line_length_breaking/conditional_expression_break.rs @@ -0,0 +1,82 @@ +use crate::lint::rules::tests::common::{set_up, assert_snippet}; +use crate::lint::rules::RuleType; + + +static CORRECT_BREAK_BEFORE_QUESTION_MARK: &str = " +method bootprep_type_to_string(uint8 prep_type) -> (const char*) { + return + prep_type == PREP_GENERAL + ? \"PrepGeneral\" : \"PrepEarly\"; +}"; +#[test] +fn condexpr_correct_break_before_question_mark() { + let rules = set_up(); + let expected_errors = vec![]; + assert_snippet(CORRECT_BREAK_BEFORE_QUESTION_MARK, expected_errors, &rules); +} + +static CORRECT_BREAK_BEFORE_COLON_AND_QUESTION_MARK: &str = " +method bootprep_type_to_string(uint8 prep_type) -> (const char*) { + return + prep_type == PREP_GENERAL + ? \"PrepGeneral\" + : \"PrepEarly\"; +}"; +#[test] +fn condexpr_correct_break_after_colon_and_question_mark() { + let rules = set_up(); + let expected_errors = vec![]; + assert_snippet(CORRECT_BREAK_BEFORE_COLON_AND_QUESTION_MARK, expected_errors, &rules); +} + +static BREAK_ONLY_BEFORE_COLON: &str = " +method harvest_resource() { + harvest_resource(my->gas < GAS_THRESHOLD ? Rsrc_Gas + : (my->minerals < MINERAL_THRESHOLD + ? Rsrc_Minerals : nearest_resource())); +}"; +#[test] +fn condexpr_only_before_colon() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL3, + (3, 3, 21, 22), + ); + assert_snippet(BREAK_ONLY_BEFORE_COLON, expected_errors, &rules); +} + +static CORRECT_NESTED: &str = " +method bootprep_type_to_string(uint8 prep_type) -> (const char*) { + return + prep_type == PREP_GENERAL + ? \"PrepGeneral\" + : prep_type == PREP_EARLY + ? \"PrepEarly\" : \"UnknownBootPrepType\"; +}"; +#[test] +fn condexpr_correct_nested() { + let rules = set_up(); + let expected_errors = vec![]; + assert_snippet(CORRECT_NESTED, expected_errors, &rules); +} + +static BREAK_AFTER_OPERATORS_NESTED: &str = " +method bootprep_type_to_string(uint8 prep_type) -> (const char*) { + return + prep_type == PREP_GENERAL ? + \"PrepGeneral\" : + prep_type == PREP_EARLY ? \"PrepEarly\" : + \"UnknownBootPrepType\"; +}"; +#[test] +fn condexpr_broken_after_operators_nested() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL3, + (3, 3, 34, 35), + (4, 4, 22, 23), + (5, 5, 46, 47), + ); + assert_snippet(BREAK_AFTER_OPERATORS_NESTED, expected_errors, &rules); +} + diff --git a/src/lint/rules/tests/line_length_breaking/mod.rs b/src/lint/rules/tests/line_length_breaking/mod.rs index 62bd822..1961a09 100644 --- a/src/lint/rules/tests/line_length_breaking/mod.rs +++ b/src/lint/rules/tests/line_length_breaking/mod.rs @@ -1,2 +1,3 @@ mod func_call_break_open_paren; -mod method_output_break; \ No newline at end of file +mod method_output_break; +mod conditional_expression_break; From 6b44a5957181c5dcb2f7a4736d810c3cd3566a6b Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Fri, 3 Oct 2025 14:01:46 -0700 Subject: [PATCH 5/8] Implement rule LL2 for breaking before binary ops --- example_files/example_lint_cfg.json | 1 + src/analysis/parsing/expression.rs | 5 +- src/lint/features.md | 6 ++ src/lint/mod.rs | 8 ++- src/lint/rules/linebreaking.rs | 55 +++++++++++++++++++ src/lint/rules/mod.rs | 10 +++- .../tests/indentation/continuation_line.rs | 18 +++--- .../rules/tests/indentation/paren_expr.rs | 24 ++++---- .../func_call_break_open_paren.rs | 15 ++--- .../rules/tests/line_length_breaking/mod.rs | 1 + 10 files changed, 105 insertions(+), 38 deletions(-) diff --git a/example_files/example_lint_cfg.json b/example_files/example_lint_cfg.json index 9615583..696a9fb 100644 --- a/example_files/example_lint_cfg.json +++ b/example_files/example_lint_cfg.json @@ -22,5 +22,6 @@ "func_call_break_on_open_paren": {}, "method_output_break": {}, "conditional_expression_break_before_op": {}, + "break_before_binary_op": {}, "annotate_lints": true } diff --git a/src/analysis/parsing/expression.rs b/src/analysis/parsing/expression.rs index 4a519ff..93401e2 100644 --- a/src/analysis/parsing/expression.rs +++ b/src/analysis/parsing/expression.rs @@ -27,7 +27,9 @@ use crate::lint::{DMLStyleError, CurrentRules}, AuxParams}; use crate::lint::rules::indentation::IndentParenExprArgs; -use crate::lint::rules::linebreaking::{ConditionalExpressionBreakBeforeOperatorArgs, FuncCallBreakOnOpenParenArgs}; +use crate::lint::rules::linebreaking::{BreakBeforeBinaryOpArgs, + ConditionalExpressionBreakBeforeOperatorArgs, + FuncCallBreakOnOpenParenArgs}; #[derive(Debug, Clone, PartialEq)] pub struct UnaryExpressionContent { @@ -97,6 +99,7 @@ impl TreeElement for BinaryExpressionContent { } fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, _aux: AuxParams) { rules.sp_binop.check(SpBinopArgs::from_binary_expression_content(self), acc); + rules.break_before_binary_op.check(BreakBeforeBinaryOpArgs::from_binary_expression(self), acc); } } diff --git a/src/lint/features.md b/src/lint/features.md index 87b1ff7..ec8c050 100644 --- a/src/lint/features.md +++ b/src/lint/features.md @@ -47,6 +47,12 @@ Below are listed the currently supported rules for linting: ## Line Length - **LL1**, `long_lines`: Lines should be kept shorter than 80 characters. This limit can be set to a custom value +- **LL2**, `break_before_binary_op`: Break long lines before binary operators, not after +``` + x = (a_very_long_expression + + another_very_long_expression) + * a_third_long_expression; +``` - **LL3**, `conditional_expression_break`: Break conditional expressions before the ?, or both before the ? and before the :. - **LL5**, `method_output_break`: Break long method declarations with output parameters before the arrow. ``` diff --git a/src/lint/mod.rs b/src/lint/mod.rs index 6c6d5dc..04d93c3 100644 --- a/src/lint/mod.rs +++ b/src/lint/mod.rs @@ -5,7 +5,10 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use lazy_static::lazy_static; use log::{debug, error, trace}; -use rules::linebreaking::{FuncCallBreakOnOpenParenOptions, MethodOutputBreakOptions, ConditionalExpressionBreakBeforeOperatorOptions}; +use rules::linebreaking::{BreakBeforeBinaryOpOptions, + FuncCallBreakOnOpenParenOptions, + MethodOutputBreakOptions, + ConditionalExpressionBreakBeforeOperatorOptions}; use serde::{Deserialize, Serialize}; use regex::Regex; use rules::{instantiate_rules, CurrentRules, RuleType}; @@ -120,6 +123,8 @@ pub struct LintCfg { pub conditional_expression_break_before_op: Option, #[serde(default)] pub method_output_break: Option, + #[serde(default)] + pub break_before_binary_op: Option, #[serde(default = "get_true")] pub annotate_lints: bool, } @@ -169,6 +174,7 @@ impl Default for LintCfg { func_call_break_on_open_paren: Some(FuncCallBreakOnOpenParenOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), method_output_break: Some(MethodOutputBreakOptions{}), conditional_expression_break_before_op: Some(ConditionalExpressionBreakBeforeOperatorOptions{}), + break_before_binary_op: Some(BreakBeforeBinaryOpOptions{}), annotate_lints: true, } } diff --git a/src/lint/rules/linebreaking.rs b/src/lint/rules/linebreaking.rs index 18ba359..11b2951 100644 --- a/src/lint/rules/linebreaking.rs +++ b/src/lint/rules/linebreaking.rs @@ -4,6 +4,7 @@ use crate::analysis::parsing::parser::Token; use crate::analysis::parsing::structure::MethodContent; use crate::analysis::parsing::tree::{ZeroRange, TreeElementTokenIterator, TreeElement}; use crate::analysis::parsing::expression::{CastContent, FunctionCallContent, + BinaryExpressionContent, ParenExpressionContent, TertiaryExpressionContent}; use crate::lint::{DMLStyleError, RuleType}; use super::indentation::IndentParenExprArgs; @@ -216,6 +217,60 @@ impl FuncCallBreakOnOpenParenRule { } } +pub struct BreakBeforeBinaryOpRule { + pub enabled: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct BreakBeforeBinaryOpOptions {} + +pub struct BreakBeforeBinaryOpArgs { + pub left: ZeroRange, + pub operation: ZeroRange, + pub right: ZeroRange +} + +impl BreakBeforeBinaryOpArgs { + pub fn from_binary_expression(node: &BinaryExpressionContent) + -> Option { + Some(BreakBeforeBinaryOpArgs { + left: node.left.range(), + operation: node.operation.range(), + right: node.right.range(), + }) + } +} + +impl BreakBeforeBinaryOpRule { + pub fn from_options(options: &Option) -> BreakBeforeBinaryOpRule { + BreakBeforeBinaryOpRule { + enabled: options.is_some(), + } + } + + pub fn check(&self, args: Option, acc: &mut Vec) { + if !self.enabled { return; } + let Some(args) = args else { return; }; + let binop_is_broken = args.left.row_start.0 != args.right.row_end.0; + let has_break_after_operator = args.operation.row_end.0 != args.right.row_start.0; + if binop_is_broken && has_break_after_operator { + acc.push(self.create_err(args.operation)); + } + } +} + +impl Rule for BreakBeforeBinaryOpRule { + fn name() -> &'static str { + "break_before_binary_op" + } + fn description() -> &'static str { + "Break binary expressions before the operator, not after." + } + fn get_rule_type() -> RuleType { + RuleType::LL2 + } +} + pub struct ConditionalExpressionBreakBeforeOperatorRule { pub enabled: bool, } diff --git a/src/lint/rules/mod.rs b/src/lint/rules/mod.rs index 5277545..ad7d3f5 100644 --- a/src/lint/rules/mod.rs +++ b/src/lint/rules/mod.rs @@ -24,9 +24,10 @@ use indentation::{LongLinesRule, IndentSwitchCaseRule, IndentEmptyLoopRule, IndentContinuationLineRule}; -use linebreaking::{FuncCallBreakOnOpenParenRule, - MethodOutputBreakRule, - ConditionalExpressionBreakBeforeOperatorRule}; +use linebreaking::{BreakBeforeBinaryOpRule, + FuncCallBreakOnOpenParenRule, + ConditionalExpressionBreakBeforeOperatorRule, + MethodOutputBreakRule}; use crate::lint::{LintCfg, DMLStyleError}; use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange}; @@ -53,6 +54,7 @@ pub struct CurrentRules { pub func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule, pub method_output_break: MethodOutputBreakRule, pub conditional_expression_break_before_op: ConditionalExpressionBreakBeforeOperatorRule, + pub break_before_binary_op: BreakBeforeBinaryOpRule, // Placeholder for future rule } pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { @@ -79,6 +81,7 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule::from_options(&cfg.func_call_break_on_open_paren), method_output_break: MethodOutputBreakRule { enabled: cfg.method_output_break.is_some() }, conditional_expression_break_before_op: ConditionalExpressionBreakBeforeOperatorRule::from_options(&cfg.conditional_expression_break_before_op), + break_before_binary_op: BreakBeforeBinaryOpRule { enabled: cfg.break_before_binary_op.is_some() }, } } @@ -113,6 +116,7 @@ pub enum RuleType { NspUnary, NspTrailing, LL1, + LL2, LL3, LL5, LL6, diff --git a/src/lint/rules/tests/indentation/continuation_line.rs b/src/lint/rules/tests/indentation/continuation_line.rs index 18bff9e..ca4aa1c 100644 --- a/src/lint/rules/tests/indentation/continuation_line.rs +++ b/src/lint/rules/tests/indentation/continuation_line.rs @@ -15,8 +15,8 @@ fn continuation_line_correct() { static CONTINUATION_LINE_RETURN_CORRECT: &str = " method calculate_sum(uint64 a, uint64 b) -> (uint64) { - return (a + b) * (a - b) + - (a * b); + return (a + b) * (a - b) + + (a * b); } "; #[test] @@ -29,8 +29,8 @@ static CONTINUATION_LINE_LOCAL_DECLA_CORRECT: &str = " bank regs { register example_register size 4 @ 0x00 { method read() -> (uint64) { - local uint64 value = (this.val + 10) * - (this.val - 5); + local uint64 value = (this.val + 10) + * (this.val - 5); return value; } } @@ -87,8 +87,8 @@ fn continuation_line_incorrect() { pub static CONTINUATION_LINE_BITWISE_INCORRECT: &str = " method write(uint64 value) { local uint64 a = value; - local uint64 result = a << - 2; + local uint64 result = a + << 2; log info: 'Writing to register, result after left shift = %x', result; } "; @@ -97,7 +97,7 @@ fn continuation_line_bitwise_incorrect() { let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::IN6, - (4, 4, 31, 32), + (4, 4, 31, 33), ); assert_snippet(CONTINUATION_LINE_BITWISE_INCORRECT, expected_errors, &rules); } @@ -106,8 +106,8 @@ pub static CONTINUATION_LINE_PARAM_INCORRECT: &str = " bank regs { register control size 4 @ 0x00 { field status @ [31:3] { - param init_val = 1 << 2 | - 1 << 1; + param init_val = 1 << 2 + | 1 << 1; } } } diff --git a/src/lint/rules/tests/indentation/paren_expr.rs b/src/lint/rules/tests/indentation/paren_expr.rs index d303e58..cfb4fea 100644 --- a/src/lint/rules/tests/indentation/paren_expr.rs +++ b/src/lint/rules/tests/indentation/paren_expr.rs @@ -130,8 +130,8 @@ fn funcall_nested_paren_incorrect() { static IF_PAREN_CORRECT: &str = " method callback() { - if (conditionX && - conditionY) { + if (conditionX + && conditionY) { return; } } @@ -144,8 +144,8 @@ fn if_paren_correct() { static IF_PAREN_INCORRECT: &str = " method callback() { - if (conditionX && - conditionY) { + if (conditionX + && conditionY) { return; } } @@ -155,7 +155,7 @@ fn if_paren_incorrect() { let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::IN5, - (3, 3, 10, 20), + (3, 3, 10, 12), ); assert_snippet(IF_PAREN_INCORRECT, expected_errors, &rules); } @@ -348,10 +348,8 @@ fn switch_paren_incorrect() { static NESTED_PAREN_EXPR_CORRECT: &str = " param result = ((reg0.val * reg1.enable.val) - & - mask_reg - + - 1); + & mask_reg + + 1); "; #[test] @@ -363,10 +361,8 @@ fn nested_paren_expr_correct(){ static NESTED_PAREN_EXPR_INCORRECT: &str = " param result = ((reg0.val * reg1.enable.val) - & - mask_reg - + - 1); + & mask_reg + + 1); "; #[test] fn nested_paren_expr_incorrect(){ @@ -374,7 +370,7 @@ fn nested_paren_expr_incorrect(){ let expected_errors = define_expected_errors!( RuleType::IN5, (2, 2, 5, 6), - (4, 4, 17, 25), + (3, 3, 17, 18), ); assert_snippet(NESTED_PAREN_EXPR_INCORRECT, expected_errors, &rules); } diff --git a/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs b/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs index a6109f1..1496840 100644 --- a/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs +++ b/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs @@ -59,10 +59,8 @@ static PAREN_NESTED_CORRECT: &str = " param result = ( (reg0.val * reg1.enable.val) - & - mask_reg - + - 1); + & mask_reg + + 1); "; #[test] @@ -75,10 +73,8 @@ static PAREN_NESTED_INCORRECT: &str = " param result = ( (reg0.val * reg1.enable.val) - & - mask_reg - + - 1); + & mask_reg + + 1); "; #[test] @@ -87,8 +83,7 @@ fn paren_nested_incorrect(){ let expected_errors = define_expected_errors!( RuleType::LL6, (4, 4, 16, 17), - (6, 6, 16, 17), - (7, 7, 16, 17), + (5, 5, 16, 17), ); assert_snippet(PAREN_NESTED_INCORRECT, expected_errors, &rules); } diff --git a/src/lint/rules/tests/line_length_breaking/mod.rs b/src/lint/rules/tests/line_length_breaking/mod.rs index 1961a09..9dbc199 100644 --- a/src/lint/rules/tests/line_length_breaking/mod.rs +++ b/src/lint/rules/tests/line_length_breaking/mod.rs @@ -1,3 +1,4 @@ mod func_call_break_open_paren; mod method_output_break; mod conditional_expression_break; +mod break_before_binary_op; From d9825d1d156a47e43763e98d421dec88971e8328 Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Fri, 3 Oct 2025 15:09:33 -0700 Subject: [PATCH 6/8] Apply consistent naming convention for linelength rules --- example_files/example_lint_cfg.json | 6 +- src/analysis/parsing/expression.rs | 22 ++--- src/analysis/parsing/structure.rs | 6 +- src/lint/features.md | 4 +- src/lint/mod.rs | 20 ++--- .../rules/{linebreaking.rs => linelength.rs} | 84 +++++++++---------- src/lint/rules/mod.rs | 22 ++--- .../rules/tests/line_length_breaking/mod.rs | 4 - .../break_conditional_expression.rs} | 30 +++---- .../break_func_call_open_paren.rs} | 24 +++--- .../break_method_output.rs} | 12 +-- src/lint/rules/tests/linelength/mod.rs | 4 + src/lint/rules/tests/mod.rs | 2 +- 13 files changed, 120 insertions(+), 120 deletions(-) rename src/lint/rules/{linebreaking.rs => linelength.rs} (80%) delete mode 100644 src/lint/rules/tests/line_length_breaking/mod.rs rename src/lint/rules/tests/{line_length_breaking/conditional_expression_break.rs => linelength/break_conditional_expression.rs} (66%) rename src/lint/rules/tests/{line_length_breaking/func_call_break_open_paren.rs => linelength/break_func_call_open_paren.rs} (80%) rename src/lint/rules/tests/{line_length_breaking/method_output_break.rs => linelength/break_method_output.rs} (78%) create mode 100644 src/lint/rules/tests/linelength/mod.rs diff --git a/example_files/example_lint_cfg.json b/example_files/example_lint_cfg.json index 696a9fb..cf7738a 100644 --- a/example_files/example_lint_cfg.json +++ b/example_files/example_lint_cfg.json @@ -19,9 +19,9 @@ "indent_switch_case": {}, "indent_empty_loop": {}, "indent_continuation_line": {}, - "func_call_break_on_open_paren": {}, - "method_output_break": {}, - "conditional_expression_break_before_op": {}, + "break_func_call_open_paren": {}, + "break_method_output": {}, + "break_conditional_expression": {}, "break_before_binary_op": {}, "annotate_lints": true } diff --git a/src/analysis/parsing/expression.rs b/src/analysis/parsing/expression.rs index 93401e2..8d8f0b6 100644 --- a/src/analysis/parsing/expression.rs +++ b/src/analysis/parsing/expression.rs @@ -27,9 +27,9 @@ use crate::lint::{DMLStyleError, CurrentRules}, AuxParams}; use crate::lint::rules::indentation::IndentParenExprArgs; -use crate::lint::rules::linebreaking::{BreakBeforeBinaryOpArgs, - ConditionalExpressionBreakBeforeOperatorArgs, - FuncCallBreakOnOpenParenArgs}; +use crate::lint::rules::linelength::{BreakBeforeBinaryOpArgs, + BreakConditionalExpression, + BreakFuncCallOpenParenArgs}; #[derive(Debug, Clone, PartialEq)] pub struct UnaryExpressionContent { @@ -162,8 +162,8 @@ impl TreeElement for TertiaryExpressionContent { } fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, _aux: AuxParams) { rules.sp_ternary.check(SpTernaryArgs::from_tertiary_expression_content(self), acc); - rules.conditional_expression_break_before_op - .check(ConditionalExpressionBreakBeforeOperatorArgs::from_tertiary_expression(self), acc); + rules.break_conditional_expression + .check(BreakConditionalExpression::from_tertiary_expression(self), acc); } } @@ -183,8 +183,8 @@ impl TreeElement for ParenExpressionContent { } fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { rules.indent_paren_expr.check(IndentParenExprArgs::from_paren_expression(self), acc); - rules.func_call_break_on_open_paren.check( - FuncCallBreakOnOpenParenArgs::from_paren_expression(self, aux.depth), acc); + rules.break_func_call_open_paren.check( + BreakFuncCallOpenParenArgs::from_paren_expression(self, aux.depth), acc); } } @@ -235,8 +235,8 @@ impl TreeElement for FunctionCallContent { rules.nsp_inparen.check(NspInparenArgs::from_function_call(self), acc); rules.sp_punct.check(SpPunctArgs::from_function_call(self), acc); rules.indent_paren_expr.check(IndentParenExprArgs::from_function_call(self), acc); - rules.func_call_break_on_open_paren - .check(FuncCallBreakOnOpenParenArgs::from_function_call(self, aux.depth), acc); + rules.break_func_call_open_paren + .check(BreakFuncCallOpenParenArgs::from_function_call(self, aux.depth), acc); } } @@ -352,8 +352,8 @@ impl TreeElement for CastContent { } fn evaluate_rules(&self, acc: &mut Vec, rules: &CurrentRules, aux: AuxParams) { rules.indent_paren_expr.check(IndentParenExprArgs::from_cast(self), acc); - rules.func_call_break_on_open_paren - .check(FuncCallBreakOnOpenParenArgs::from_cast(self, aux.depth), acc); + rules.break_func_call_open_paren + .check(BreakFuncCallOpenParenArgs::from_cast(self, aux.depth), acc); } } diff --git a/src/analysis/parsing/structure.rs b/src/analysis/parsing/structure.rs index 601937d..ee9e647 100644 --- a/src/analysis/parsing/structure.rs +++ b/src/analysis/parsing/structure.rs @@ -16,7 +16,7 @@ use crate::analysis::parsing::parser::{doesnt_understand_tokens, FileParser, Parse, ParseContext, FileInfo}; use crate::analysis::LocalDMLError; -use crate::lint::rules::linebreaking::{FuncCallBreakOnOpenParenArgs, MethodOutputBreakArgs}; +use crate::lint::rules::linelength::{BreakFuncCallOpenParenArgs, BreakMethodOutputArgs}; use crate::lint::rules::spacing::{SpBracesArgs, NspInparenArgs, NspFunparArgs, @@ -244,8 +244,8 @@ impl TreeElement for MethodContent { rules.nsp_inparen.check(NspInparenArgs::from_method(self), acc); rules.sp_punct.check(SpPunctArgs::from_method(self), acc); rules.indent_paren_expr.check(IndentParenExprArgs::from_method(self), acc); - rules.func_call_break_on_open_paren.check(FuncCallBreakOnOpenParenArgs::from_method(self, aux.depth), acc); - rules.method_output_break.check(MethodOutputBreakArgs::from_method(self), acc); + rules.break_func_call_open_paren.check(BreakFuncCallOpenParenArgs::from_method(self, aux.depth), acc); + rules.break_method_output.check(BreakMethodOutputArgs::from_method(self), acc); } } diff --git a/src/lint/features.md b/src/lint/features.md index ec8c050..2553b96 100644 --- a/src/lint/features.md +++ b/src/lint/features.md @@ -53,8 +53,8 @@ Below are listed the currently supported rules for linting: + another_very_long_expression) * a_third_long_expression; ``` -- **LL3**, `conditional_expression_break`: Break conditional expressions before the ?, or both before the ? and before the :. -- **LL5**, `method_output_break`: Break long method declarations with output parameters before the arrow. +- **LL3**, `break_conditional_expression`: Break conditional expressions before the ?, or both before the ? and before the :. +- **LL5**, `break_method_output`: Break long method declarations with output parameters before the arrow. ``` method inquiry_status(uint64 physical_address) -> (uint16 status) { diff --git a/src/lint/mod.rs b/src/lint/mod.rs index 04d93c3..7ddd41a 100644 --- a/src/lint/mod.rs +++ b/src/lint/mod.rs @@ -5,10 +5,10 @@ use std::path::{Path, PathBuf}; use std::str::FromStr; use lazy_static::lazy_static; use log::{debug, error, trace}; -use rules::linebreaking::{BreakBeforeBinaryOpOptions, - FuncCallBreakOnOpenParenOptions, - MethodOutputBreakOptions, - ConditionalExpressionBreakBeforeOperatorOptions}; +use rules::linelength::{BreakBeforeBinaryOpOptions, + BreakFuncCallOpenParenOptions, + BreakMethodOutputOptions, + BreakConditionalExpressionOptions}; use serde::{Deserialize, Serialize}; use regex::Regex; use rules::{instantiate_rules, CurrentRules, RuleType}; @@ -118,11 +118,11 @@ pub struct LintCfg { #[serde(default)] pub indent_continuation_line: Option, #[serde(default)] - pub func_call_break_on_open_paren: Option, + pub break_func_call_open_paren: Option, #[serde(default)] - pub conditional_expression_break_before_op: Option, + pub break_conditional_expression: Option, #[serde(default)] - pub method_output_break: Option, + pub break_method_output: Option, #[serde(default)] pub break_before_binary_op: Option, #[serde(default = "get_true")] @@ -171,9 +171,9 @@ impl Default for LintCfg { indent_switch_case: Some(IndentSwitchCaseOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), indent_empty_loop: Some(IndentEmptyLoopOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), indent_continuation_line: Some(IndentContinuationLineOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), - func_call_break_on_open_paren: Some(FuncCallBreakOnOpenParenOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), - method_output_break: Some(MethodOutputBreakOptions{}), - conditional_expression_break_before_op: Some(ConditionalExpressionBreakBeforeOperatorOptions{}), + break_func_call_open_paren: Some(BreakFuncCallOpenParenOptions{indentation_spaces: INDENTATION_LEVEL_DEFAULT}), + break_method_output: Some(BreakMethodOutputOptions{}), + break_conditional_expression: Some(BreakConditionalExpressionOptions{}), break_before_binary_op: Some(BreakBeforeBinaryOpOptions{}), annotate_lints: true, } diff --git a/src/lint/rules/linebreaking.rs b/src/lint/rules/linelength.rs similarity index 80% rename from src/lint/rules/linebreaking.rs rename to src/lint/rules/linelength.rs index 11b2951..4a84f95 100644 --- a/src/lint/rules/linebreaking.rs +++ b/src/lint/rules/linelength.rs @@ -16,23 +16,23 @@ fn default_indentation_spaces() -> u32 { INDENTATION_LEVEL_DEFAULT } -pub struct MethodOutputBreakRule { +pub struct BreakMethodOutputRule { pub enabled: bool } -pub struct MethodOutputBreakArgs { +pub struct BreakMethodOutputArgs { pub before_arrow_range: ZeroRange, pub arrow_range: ZeroRange, pub after_arrow_range: ZeroRange, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct MethodOutputBreakOptions{ +pub struct BreakMethodOutputOptions{ } -impl Rule for MethodOutputBreakRule { +impl Rule for BreakMethodOutputRule { fn name() -> &'static str { - "method_output_break" + "break_method_output" } fn description() -> &'static str { "Break long method declarations with output parameters before the arrow." @@ -42,10 +42,10 @@ impl Rule for MethodOutputBreakRule { } } -impl MethodOutputBreakArgs { - pub fn from_method(node: &MethodContent) -> Option { +impl BreakMethodOutputArgs { + pub fn from_method(node: &MethodContent) -> Option { let Some(returns) = &node.returns else { return None; }; - Some(MethodOutputBreakArgs { + Some(BreakMethodOutputArgs { before_arrow_range: node.rparen.range(), arrow_range: returns.0.range(), after_arrow_range: returns.1.range(), @@ -53,8 +53,8 @@ impl MethodOutputBreakArgs { } } -impl MethodOutputBreakRule { - pub fn check(&self, args: Option, acc: &mut Vec) { +impl BreakMethodOutputRule { + pub fn check(&self, args: Option, acc: &mut Vec) { if !self.enabled { return; } let Some(args) = args else { return; }; if args.before_arrow_range.row_end.0 == args.after_arrow_range.row_start.0 { @@ -68,26 +68,26 @@ impl MethodOutputBreakRule { } } -pub struct FuncCallBreakOnOpenParenRule { +pub struct BreakFuncCallOpenParenRule { pub enabled: bool, indentation_spaces: u32 } -pub struct FuncCallBreakOnOpenParenArgs { +pub struct BreakFuncCallOpenParenArgs { pub members_ranges: Vec, pub expected_depth: u32, pub lparen: ZeroRange, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct FuncCallBreakOnOpenParenOptions{ +pub struct BreakFuncCallOpenParenOptions{ #[serde(default = "default_indentation_spaces")] pub indentation_spaces: u32, } -impl Rule for FuncCallBreakOnOpenParenRule { +impl Rule for BreakFuncCallOpenParenRule { fn name() -> &'static str { - "FUNC_CALL_BREAK_ON_OPEN_PAREN" + "break_func_call_open_paren" } fn description() -> &'static str { "Function or method calls broken right after opening parenthesis should @@ -98,7 +98,7 @@ impl Rule for FuncCallBreakOnOpenParenRule { } } -impl FuncCallBreakOnOpenParenArgs { +impl BreakFuncCallOpenParenArgs { pub fn filter_out_parenthesized_tokens(expression_tokens: TreeElementTokenIterator) -> Vec { let mut token_list: Vec = vec![]; let mut paren_depth = 0; @@ -125,7 +125,7 @@ impl FuncCallBreakOnOpenParenArgs { } token_list } - pub fn from_function_call(node: &FunctionCallContent, depth: u32) -> Option { + pub fn from_function_call(node: &FunctionCallContent, depth: u32) -> Option { let mut filtered_member_ranges: Vec = vec![]; for (arg, _comma) in node.arguments.iter() { filtered_member_ranges.extend( @@ -139,15 +139,15 @@ impl FuncCallBreakOnOpenParenArgs { filtered_member_ranges.first()?.to_owned()) { return None } - Some(FuncCallBreakOnOpenParenArgs { + Some(BreakFuncCallOpenParenArgs { members_ranges: filtered_member_ranges, expected_depth: depth, lparen: node.lparen.range(), }) } - pub fn from_paren_expression(node: &ParenExpressionContent, depth: u32) -> Option { - Some(FuncCallBreakOnOpenParenArgs { + pub fn from_paren_expression(node: &ParenExpressionContent, depth: u32) -> Option { + Some(BreakFuncCallOpenParenArgs { members_ranges: Self::filter_out_parenthesized_tokens(node.expr.tokens()) .iter().map(|t| t.range).collect(), expected_depth: depth, @@ -155,24 +155,24 @@ impl FuncCallBreakOnOpenParenArgs { }) } - pub fn from_method(node: &MethodContent, depth: u32) -> Option { + pub fn from_method(node: &MethodContent, depth: u32) -> Option { let mut filtered_member_ranges: Vec = vec![]; for (arg, _comma) in node.arguments.iter() { filtered_member_ranges.extend( Self::filter_out_parenthesized_tokens(arg.tokens()) .iter().map(|t| t.range)); } - Some(FuncCallBreakOnOpenParenArgs { + Some(BreakFuncCallOpenParenArgs { members_ranges: filtered_member_ranges, expected_depth: depth, lparen: node.lparen.range(), }) } - pub fn from_cast(node: &CastContent, depth: u32) -> Option { + pub fn from_cast(node: &CastContent, depth: u32) -> Option { let mut cast_member_tokens = node.from.tokens(); cast_member_tokens.append(&mut node.to.tokens()); - Some(FuncCallBreakOnOpenParenArgs { + Some(BreakFuncCallOpenParenArgs { members_ranges: Self::filter_out_parenthesized_tokens(cast_member_tokens) .iter().map(|t| t.range).collect(), expected_depth: depth, @@ -181,21 +181,21 @@ impl FuncCallBreakOnOpenParenArgs { } } -impl FuncCallBreakOnOpenParenRule { - pub fn from_options(options: &Option) -> FuncCallBreakOnOpenParenRule { +impl BreakFuncCallOpenParenRule { + pub fn from_options(options: &Option) -> BreakFuncCallOpenParenRule { match options { - Some(options) => FuncCallBreakOnOpenParenRule { + Some(options) => BreakFuncCallOpenParenRule { enabled: true, indentation_spaces: options.indentation_spaces }, - None => FuncCallBreakOnOpenParenRule { + None => BreakFuncCallOpenParenRule { enabled: false, indentation_spaces: 0 } } } - pub fn check(&self, args: Option, acc: &mut Vec) { + pub fn check(&self, args: Option, acc: &mut Vec) { if !self.enabled { return; } let Some(args) = args else { return; }; if args.members_ranges.is_empty() { return; } @@ -271,11 +271,11 @@ impl Rule for BreakBeforeBinaryOpRule { } } -pub struct ConditionalExpressionBreakBeforeOperatorRule { +pub struct BreakConditionalExpressionRule { pub enabled: bool, } -pub struct ConditionalExpressionBreakBeforeOperatorArgs { +pub struct BreakConditionalExpression { pub left: ZeroRange, pub left_operation: ZeroRange, pub middle: ZeroRange, @@ -284,10 +284,10 @@ pub struct ConditionalExpressionBreakBeforeOperatorArgs { } -impl ConditionalExpressionBreakBeforeOperatorArgs { +impl BreakConditionalExpression { pub fn from_tertiary_expression(node: &TertiaryExpressionContent) - -> Option { - Some(ConditionalExpressionBreakBeforeOperatorArgs { + -> Option { + Some(BreakConditionalExpression { left: node.left.range(), left_operation: node.left_operation.range(), middle: node.middle.range(), @@ -297,19 +297,19 @@ impl ConditionalExpressionBreakBeforeOperatorArgs { } } -impl ConditionalExpressionBreakBeforeOperatorRule { - pub fn from_options(options: &Option) -> ConditionalExpressionBreakBeforeOperatorRule { +impl BreakConditionalExpressionRule { + pub fn from_options(options: &Option) -> BreakConditionalExpressionRule { match options { - Some(_options) => ConditionalExpressionBreakBeforeOperatorRule { + Some(_options) => BreakConditionalExpressionRule { enabled: true, }, - None => ConditionalExpressionBreakBeforeOperatorRule { + None => BreakConditionalExpressionRule { enabled: false, } } } - pub fn check(&self, args: Option, acc: &mut Vec) { + pub fn check(&self, args: Option, acc: &mut Vec) { if !self.enabled { return; } let Some(args) = args else { return; }; let has_break_before_question_operator = args.left.row_end.0 != args.left_operation.row_start.0; @@ -327,12 +327,12 @@ impl ConditionalExpressionBreakBeforeOperatorRule { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct ConditionalExpressionBreakBeforeOperatorOptions{ +pub struct BreakConditionalExpressionOptions{ } -impl Rule for ConditionalExpressionBreakBeforeOperatorRule { +impl Rule for BreakConditionalExpressionRule { fn name() -> &'static str { - "COND_EXPRESSION_BREAK_BEFORE_OPERATOR" + "break_conditional_expression" } fn description() -> &'static str { "Break conditional expressions before the ?, or both before the ? and before the :." diff --git a/src/lint/rules/mod.rs b/src/lint/rules/mod.rs index ad7d3f5..08c021f 100644 --- a/src/lint/rules/mod.rs +++ b/src/lint/rules/mod.rs @@ -1,6 +1,6 @@ pub mod spacing; pub mod indentation; -pub mod linebreaking; +pub mod linelength; #[cfg(test)] pub mod tests; @@ -24,10 +24,10 @@ use indentation::{LongLinesRule, IndentSwitchCaseRule, IndentEmptyLoopRule, IndentContinuationLineRule}; -use linebreaking::{BreakBeforeBinaryOpRule, - FuncCallBreakOnOpenParenRule, - ConditionalExpressionBreakBeforeOperatorRule, - MethodOutputBreakRule}; +use linelength::{BreakBeforeBinaryOpRule, + BreakFuncCallOpenParenRule, + BreakConditionalExpressionRule, + BreakMethodOutputRule}; use crate::lint::{LintCfg, DMLStyleError}; use crate::analysis::{LocalDMLError, parsing::tree::ZeroRange}; @@ -51,9 +51,9 @@ pub struct CurrentRules { pub indent_switch_case: IndentSwitchCaseRule, pub indent_empty_loop: IndentEmptyLoopRule, pub indent_continuation_line: IndentContinuationLineRule, - pub func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule, - pub method_output_break: MethodOutputBreakRule, - pub conditional_expression_break_before_op: ConditionalExpressionBreakBeforeOperatorRule, + pub break_func_call_open_paren: BreakFuncCallOpenParenRule, + pub break_method_output: BreakMethodOutputRule, + pub break_conditional_expression: BreakConditionalExpressionRule, pub break_before_binary_op: BreakBeforeBinaryOpRule, // Placeholder for future rule } @@ -78,9 +78,9 @@ pub fn instantiate_rules(cfg: &LintCfg) -> CurrentRules { indent_switch_case: IndentSwitchCaseRule::from_options(&cfg.indent_switch_case), indent_empty_loop: IndentEmptyLoopRule::from_options(&cfg.indent_empty_loop), indent_continuation_line: IndentContinuationLineRule::from_options(&cfg.indent_continuation_line), - func_call_break_on_open_paren: FuncCallBreakOnOpenParenRule::from_options(&cfg.func_call_break_on_open_paren), - method_output_break: MethodOutputBreakRule { enabled: cfg.method_output_break.is_some() }, - conditional_expression_break_before_op: ConditionalExpressionBreakBeforeOperatorRule::from_options(&cfg.conditional_expression_break_before_op), + break_func_call_open_paren: BreakFuncCallOpenParenRule::from_options(&cfg.break_func_call_open_paren), + break_method_output: BreakMethodOutputRule { enabled: cfg.break_method_output.is_some() }, + break_conditional_expression: BreakConditionalExpressionRule::from_options(&cfg.break_conditional_expression), break_before_binary_op: BreakBeforeBinaryOpRule { enabled: cfg.break_before_binary_op.is_some() }, } } diff --git a/src/lint/rules/tests/line_length_breaking/mod.rs b/src/lint/rules/tests/line_length_breaking/mod.rs deleted file mode 100644 index 9dbc199..0000000 --- a/src/lint/rules/tests/line_length_breaking/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -mod func_call_break_open_paren; -mod method_output_break; -mod conditional_expression_break; -mod break_before_binary_op; diff --git a/src/lint/rules/tests/line_length_breaking/conditional_expression_break.rs b/src/lint/rules/tests/linelength/break_conditional_expression.rs similarity index 66% rename from src/lint/rules/tests/line_length_breaking/conditional_expression_break.rs rename to src/lint/rules/tests/linelength/break_conditional_expression.rs index 9bda05e..bd523d4 100644 --- a/src/lint/rules/tests/line_length_breaking/conditional_expression_break.rs +++ b/src/lint/rules/tests/linelength/break_conditional_expression.rs @@ -2,20 +2,20 @@ use crate::lint::rules::tests::common::{set_up, assert_snippet}; use crate::lint::rules::RuleType; -static CORRECT_BREAK_BEFORE_QUESTION_MARK: &str = " +static BEFORE_QUESTION_MARK_CORRECT: &str = " method bootprep_type_to_string(uint8 prep_type) -> (const char*) { return prep_type == PREP_GENERAL ? \"PrepGeneral\" : \"PrepEarly\"; }"; #[test] -fn condexpr_correct_break_before_question_mark() { +fn before_question_mark_correct() { let rules = set_up(); let expected_errors = vec![]; - assert_snippet(CORRECT_BREAK_BEFORE_QUESTION_MARK, expected_errors, &rules); + assert_snippet(BEFORE_QUESTION_MARK_CORRECT, expected_errors, &rules); } -static CORRECT_BREAK_BEFORE_COLON_AND_QUESTION_MARK: &str = " +static BEFORE_COLON_AND_QUESTION_MARK_CORRECT: &str = " method bootprep_type_to_string(uint8 prep_type) -> (const char*) { return prep_type == PREP_GENERAL @@ -23,29 +23,29 @@ method bootprep_type_to_string(uint8 prep_type) -> (const char*) { : \"PrepEarly\"; }"; #[test] -fn condexpr_correct_break_after_colon_and_question_mark() { +fn after_colon_and_question_mark_correct() { let rules = set_up(); let expected_errors = vec![]; - assert_snippet(CORRECT_BREAK_BEFORE_COLON_AND_QUESTION_MARK, expected_errors, &rules); + assert_snippet(BEFORE_COLON_AND_QUESTION_MARK_CORRECT, expected_errors, &rules); } -static BREAK_ONLY_BEFORE_COLON: &str = " +static ONLY_BEFORE_COLON: &str = " method harvest_resource() { harvest_resource(my->gas < GAS_THRESHOLD ? Rsrc_Gas : (my->minerals < MINERAL_THRESHOLD ? Rsrc_Minerals : nearest_resource())); }"; #[test] -fn condexpr_only_before_colon() { +fn only_before_colon() { let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::LL3, (3, 3, 21, 22), ); - assert_snippet(BREAK_ONLY_BEFORE_COLON, expected_errors, &rules); + assert_snippet(ONLY_BEFORE_COLON, expected_errors, &rules); } -static CORRECT_NESTED: &str = " +static NESTED_CORRECT: &str = " method bootprep_type_to_string(uint8 prep_type) -> (const char*) { return prep_type == PREP_GENERAL @@ -54,13 +54,13 @@ method bootprep_type_to_string(uint8 prep_type) -> (const char*) { ? \"PrepEarly\" : \"UnknownBootPrepType\"; }"; #[test] -fn condexpr_correct_nested() { +fn nested_correct() { let rules = set_up(); let expected_errors = vec![]; - assert_snippet(CORRECT_NESTED, expected_errors, &rules); + assert_snippet(NESTED_CORRECT, expected_errors, &rules); } -static BREAK_AFTER_OPERATORS_NESTED: &str = " +static AFTER_OPERATORS_NESTED: &str = " method bootprep_type_to_string(uint8 prep_type) -> (const char*) { return prep_type == PREP_GENERAL ? @@ -69,7 +69,7 @@ method bootprep_type_to_string(uint8 prep_type) -> (const char*) { \"UnknownBootPrepType\"; }"; #[test] -fn condexpr_broken_after_operators_nested() { +fn after_operators_nested() { let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::LL3, @@ -77,6 +77,6 @@ fn condexpr_broken_after_operators_nested() { (4, 4, 22, 23), (5, 5, 46, 47), ); - assert_snippet(BREAK_AFTER_OPERATORS_NESTED, expected_errors, &rules); + assert_snippet(AFTER_OPERATORS_NESTED, expected_errors, &rules); } diff --git a/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs b/src/lint/rules/tests/linelength/break_func_call_open_paren.rs similarity index 80% rename from src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs rename to src/lint/rules/tests/linelength/break_func_call_open_paren.rs index 1496840..5562794 100644 --- a/src/lint/rules/tests/line_length_breaking/func_call_break_open_paren.rs +++ b/src/lint/rules/tests/linelength/break_func_call_open_paren.rs @@ -4,7 +4,7 @@ use crate::lint::rules::RuleType; // LL6: Function and method invocations can be broken after the opening parenthesis, // with the continuation lines indented one level. -static FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_CORRECT: &str = " +static AFTER_PAREN_EXCEPTION_CORRECT: &str = " method effect() { callback( 0xABC, @@ -13,12 +13,12 @@ method effect() { } "; #[test] -fn funcall_broken_after_paren_exception_correct() { +fn after_paren_exception_correct() { let rules = set_up(); - assert_snippet(FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_CORRECT, vec![], &rules); + assert_snippet(AFTER_PAREN_EXCEPTION_CORRECT, vec![], &rules); } -static FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_INCORRECT: &str = " +static AFTER_PAREN_EXCEPTION_INCORRECT: &str = " method effect() { callback( 0xABC, @@ -27,14 +27,14 @@ method effect() { } "; #[test] -fn funcall_broken_after_paren_exception_incorrect() { +fn after_paren_exception_incorrect() { let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::LL6, (4, 4, 12, 22), (5, 5, 4, 9), ); - assert_snippet(FUNCALL_BROKEN_AFTER_PAREN_EXCEPTION_INCORRECT, expected_errors, &rules); + assert_snippet(AFTER_PAREN_EXCEPTION_INCORRECT, expected_errors, &rules); } static FIRST_LINE_INCORRECT: &str = " @@ -55,7 +55,7 @@ fn first_line_incorrect() { assert_snippet(FIRST_LINE_INCORRECT, expected_errors, &rules); } -static PAREN_NESTED_CORRECT: &str = " +static NESTED_PAREN_CORRECT: &str = " param result = ( (reg0.val * reg1.enable.val) @@ -64,12 +64,12 @@ param result = ( "; #[test] -fn paren_nested_correct(){ +fn nested_paren_correct(){ let rules = set_up(); - assert_snippet(PAREN_NESTED_CORRECT, vec![], &rules); + assert_snippet(NESTED_PAREN_CORRECT, vec![], &rules); } -static PAREN_NESTED_INCORRECT: &str = " +static NESTED_PAREN_INCORRECT: &str = " param result = ( (reg0.val * reg1.enable.val) @@ -78,14 +78,14 @@ param result = ( "; #[test] -fn paren_nested_incorrect(){ +fn nested_paren_incorrect(){ let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::LL6, (4, 4, 16, 17), (5, 5, 16, 17), ); - assert_snippet(PAREN_NESTED_INCORRECT, expected_errors, &rules); + assert_snippet(NESTED_PAREN_INCORRECT, expected_errors, &rules); } static METHOD_CORRECT: &str = " diff --git a/src/lint/rules/tests/line_length_breaking/method_output_break.rs b/src/lint/rules/tests/linelength/break_method_output.rs similarity index 78% rename from src/lint/rules/tests/line_length_breaking/method_output_break.rs rename to src/lint/rules/tests/linelength/break_method_output.rs index 6db2888..3446dbc 100644 --- a/src/lint/rules/tests/line_length_breaking/method_output_break.rs +++ b/src/lint/rules/tests/linelength/break_method_output.rs @@ -3,7 +3,7 @@ use crate::lint::rules::RuleType; // LL5: Break long method declarations with output parameters before the arrow. -static METHOD_OUTPUT_BREAK_CORRECT: &str = " +static OUTPUT_CORRECT: &str = " method inquiry_status(uint64 physical_address) -> (uint16 status) { return 0; @@ -25,12 +25,12 @@ method other_method(uint64 arg1, } "; #[test] -fn method_output_break_correct() { +fn output_correct() { let rules = set_up(); - assert_snippet(METHOD_OUTPUT_BREAK_CORRECT, vec![], &rules); + assert_snippet(OUTPUT_CORRECT, vec![], &rules); } -static METHOD_OUTPUT_BREAK_INCORRECT: &str = " +static OUTPUT_INCORRECT: &str = " method inquiry_status(uint64 physical_address) -> (uint16 status) { return 0; @@ -48,7 +48,7 @@ method other_method(uint64 arg1, } "; #[test] -fn method_output_break_incorrect() { +fn output_incorrect() { let rules = set_up(); let expected_errors = define_expected_errors!( RuleType::LL5, @@ -56,5 +56,5 @@ fn method_output_break_incorrect() { (6, 6, 47, 49), (12, 12, 33, 35), ); - assert_snippet(METHOD_OUTPUT_BREAK_INCORRECT, expected_errors, &rules); + assert_snippet(OUTPUT_INCORRECT, expected_errors, &rules); } \ No newline at end of file diff --git a/src/lint/rules/tests/linelength/mod.rs b/src/lint/rules/tests/linelength/mod.rs new file mode 100644 index 0000000..225c3ab --- /dev/null +++ b/src/lint/rules/tests/linelength/mod.rs @@ -0,0 +1,4 @@ +mod break_func_call_open_paren; +mod break_method_output; +mod break_conditional_expression; +mod break_before_binary_op; diff --git a/src/lint/rules/tests/mod.rs b/src/lint/rules/tests/mod.rs index 8e04a06..643e146 100644 --- a/src/lint/rules/tests/mod.rs +++ b/src/lint/rules/tests/mod.rs @@ -2,5 +2,5 @@ pub mod common; mod indentation; mod spacing; -mod line_length_breaking; +mod linelength; From 3a3d6054f2ccc08e97713f0afcca26cf4fbe3498 Mon Sep 17 00:00:00 2001 From: Calvo Date: Fri, 3 Oct 2025 17:08:27 -0600 Subject: [PATCH 7/8] Add CHANGELOG entry --- CHANGELOG.md | 4 ++++ src/lint/features.md | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9197d12..90c20bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ --> # Change Log +## 0.9.14 +- Added support for line length and breaking rules break_func_call_open_paren, break_method_output, break_conditional_expression and break_before_binary_op. +- Added support for indendation rule indent_continuation_line. + ## 0.9.13 - Corrected the name of "explicit\_param\_decls" provisional. Note that it still has no semantic effect. diff --git a/src/lint/features.md b/src/lint/features.md index 2553b96..1d69d28 100644 --- a/src/lint/features.md +++ b/src/lint/features.md @@ -61,5 +61,10 @@ Below are listed the currently supported rules for linting: ... } ``` +- **LL6**, `break_func_call_open_paren`: Function and method invocations can be broken after the opening parenthesis, with the continuation lines indented one level. +``` + signal_interface *my_interface = SIM_get_interface( + other_object, SIGNAL_INTERFACE); +``` ##### Check [Issue #76 For remaining and planned checks](https://github.com/intel/dml-language-server/issues/76) From 4c336d5e660b69e94433c27e1acac846d4b02294 Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Wed, 8 Oct 2025 13:33:30 -0700 Subject: [PATCH 8/8] Add file for srcbreak_before_binary_op tests --- .../linelength/break_before_binary_op.rs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 src/lint/rules/tests/linelength/break_before_binary_op.rs diff --git a/src/lint/rules/tests/linelength/break_before_binary_op.rs b/src/lint/rules/tests/linelength/break_before_binary_op.rs new file mode 100644 index 0000000..730c97e --- /dev/null +++ b/src/lint/rules/tests/linelength/break_before_binary_op.rs @@ -0,0 +1,58 @@ +use crate::lint::rules::tests::common::{set_up, assert_snippet}; +use crate::lint::rules::RuleType; + +static PARAM_ASSIGN_LOGIC_OP_CORRECT: &str = " +param LOGIC_RESULT = initial_condition + | second_condition; +"; + +#[test] +fn param_assign_logic_op_correct() { + let rules = set_up(); + let expected_errors = vec![]; + assert_snippet(PARAM_ASSIGN_LOGIC_OP_CORRECT, expected_errors, &rules); +} + +static PARAM_ASSIGN_LOGIC_OP_INCORRECT: &str = " +param LOGIC_RESULT = initial_condition | + second_condition; +"; + +#[test] +fn param_assign_logic_op_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL2, + (1, 1, 39, 40), + ); + assert_snippet(PARAM_ASSIGN_LOGIC_OP_INCORRECT, expected_errors, &rules); +} + +static ASSIGN_ARITHMETIC_OP_CORRECT: &str = " +session int variable + = initial_condition + + second_condition; +"; + +#[test] +fn assign_arithmetic_op_correct() { + let rules = set_up(); + let expected_errors = vec![]; + assert_snippet(ASSIGN_ARITHMETIC_OP_CORRECT, expected_errors, &rules); +} + +static ASSIGN_ARITHMETIC_OP_INCORRECT: &str = " +session int variable = + initial_value + + second_condition; +"; + +#[test] +fn assign_arithmetic_op_incorrect() { + let rules = set_up(); + let expected_errors = define_expected_errors!( + RuleType::LL2, + (2, 2, 18, 19), + ); + assert_snippet(ASSIGN_ARITHMETIC_OP_INCORRECT, expected_errors, &rules); +} \ No newline at end of file