diff --git a/docs/releasenotes/unreleased/rules.1.rst b/docs/releasenotes/unreleased/rules.1.rst new file mode 100644 index 000000000..83bd772b3 --- /dev/null +++ b/docs/releasenotes/unreleased/rules.1.rst @@ -0,0 +1,25 @@ +Allow to ignore run keywords in misaligned-continuation-row rule (#1065) +------------------------------------------------------------------------ + +W1015 ``misaligned-continuation-row`` detects if statements have misaligned rows:: + + *** Keywords *** + Misaligned example + Keyword Call + ... first argument + ... second argument # should be executed + +This rules contradicts with how Robotidy aligns nested keywords with ``IndentNestedKeywords`` tranformer to provide +extra alignment for readability purposes:: + + SSH Wait For Device To Close SSH + [Documentation] Wait until SSH connection is closed by device. + Wait Until Keyword Succeeds 2min 2s + ... Run Keyword And Expect Error SSHException: SSH session not active + ... SSH Log FW Version level=DEBUG + +It is now possible to ignore run keywords by setting ignore_run_keywords to True:: + + robocop -c misaligned-continuation-row:ignore_run_keywords:True src + +By default it is disabled and run keywords are not ignored. diff --git a/robocop/checkers/spacing.py b/robocop/checkers/spacing.py index a25e1fd56..3bff2cd66 100644 --- a/robocop/checkers/spacing.py +++ b/robocop/checkers/spacing.py @@ -7,7 +7,7 @@ from robot.api import Token from robot.parsing.model.blocks import Keyword, TestCase -from robot.parsing.model.statements import Comment, EmptyLine +from robot.parsing.model.statements import Comment, EmptyLine, KeywordCall from robot.parsing.model.visitor import ModelVisitor from robocop.utils.misc import ROBOT_VERSION @@ -20,6 +20,7 @@ from robocop.checkers import RawFileChecker, VisitorChecker from robocop.rules import Rule, RuleParam, RuleSeverity, SeverityThreshold from robocop.utils import get_errors, get_section_name, str2bool, token_col +from robocop.utils.run_keywords import is_run_keyword RULE_CATEGORY_ID = "10" @@ -230,6 +231,9 @@ ), "1015": Rule( RuleParam(name="ignore_docs", default=True, converter=str2bool, show_type="bool", desc="Ignore documentation"), + RuleParam( + name="ignore_run_keywords", default=False, converter=str2bool, show_type="bool", desc="Ignore run keywords" + ), rule_id="1015", name="misaligned-continuation-row", msg="Each next continuation line should be aligned with the previous one", @@ -806,6 +810,7 @@ class MisalignedContinuation(VisitorChecker, ModelVisitor): "misaligned-continuation", "misaligned-continuation-row", ) + # detect if run keyword, but not parse it @staticmethod def is_inline_if(node): @@ -816,8 +821,16 @@ def visit_If(self, node): if ROBOT_VERSION.major >= 5 and self.is_inline_if(node): return + def is_ignorable_run_keyword(self, node) -> bool: + return ( + isinstance(node, KeywordCall) + and self.param("misaligned-continuation-row", "ignore_run_keywords") + and is_run_keyword(node.keyword) + ) + # TODO: test on different version, may lack .keyword + def visit_Statement(self, node): # noqa - if not node.data_tokens: + if not node.data_tokens or self.is_ignorable_run_keyword(node): return starting_row = self.get_indent(node.tokens) first_column, indent = 0, 0 diff --git a/robocop/utils/run_keywords.py b/robocop/utils/run_keywords.py index f3a81f0be..b4e8851f8 100644 --- a/robocop/utils/run_keywords.py +++ b/robocop/utils/run_keywords.py @@ -132,3 +132,8 @@ def split_on_and(tokens): prefix, branch, tokens = split_on_token_value(tokens, "AND", 1) yield from parse_run_keyword(prefix) yield from parse_run_keyword(tokens) + + +def is_run_keyword(token_name: str) -> bool: + run_keyword = RUN_KEYWORDS[token_name] + return run_keyword is not None diff --git a/tests/atest/rules/spacing/misaligned_continuation_row/expected_output_run_kw.txt b/tests/atest/rules/spacing/misaligned_continuation_row/expected_output_run_kw.txt new file mode 100644 index 000000000..220cc57a8 --- /dev/null +++ b/tests/atest/rules/spacing/misaligned_continuation_row/expected_output_run_kw.txt @@ -0,0 +1 @@ +run_keyword.robot:5:8 [W] 1015 Each next continuation line should be aligned with the previous one \ No newline at end of file diff --git a/tests/atest/rules/spacing/misaligned_continuation_row/expected_output_run_kw_off.txt b/tests/atest/rules/spacing/misaligned_continuation_row/expected_output_run_kw_off.txt new file mode 100644 index 000000000..a9a58a380 --- /dev/null +++ b/tests/atest/rules/spacing/misaligned_continuation_row/expected_output_run_kw_off.txt @@ -0,0 +1,2 @@ +run_keyword.robot:5:8 [W] 1015 Each next continuation line should be aligned with the previous one +run_keyword.robot:11:8 [W] 1015 Each next continuation line should be aligned with the previous one \ No newline at end of file diff --git a/tests/atest/rules/spacing/misaligned_continuation_row/run_keyword.robot b/tests/atest/rules/spacing/misaligned_continuation_row/run_keyword.robot new file mode 100644 index 000000000..f272d0bdd --- /dev/null +++ b/tests/atest/rules/spacing/misaligned_continuation_row/run_keyword.robot @@ -0,0 +1,11 @@ +*** Keywords *** +Keyword with run keyword + Other Keyword + ... first argument + ... second argument + Other Keyword 2 + ... first argument + ... second argument + Wait Until Keyword Succeeds 2min 2s + ... Run Keyword And Expect Error SSHException: SSH session not active + ... SSH Log FW Version level=DEBUG diff --git a/tests/atest/rules/spacing/misaligned_continuation_row/test_rule.py b/tests/atest/rules/spacing/misaligned_continuation_row/test_rule.py index 9e7be7cad..b1dd7bbc4 100644 --- a/tests/atest/rules/spacing/misaligned_continuation_row/test_rule.py +++ b/tests/atest/rules/spacing/misaligned_continuation_row/test_rule.py @@ -1,9 +1,11 @@ +import pytest + from tests.atest.utils import RuleAcceptance class TestRuleAcceptance(RuleAcceptance): def test_rule(self): - self.check_rule(expected_file="expected_output.txt") + self.check_rule(src_files=["test.robot"], expected_file="expected_output.txt") def test_ignore_docs(self): self.check_rule( @@ -11,3 +13,14 @@ def test_ignore_docs(self): src_files=["test.robot"], expected_file="expected_output_ignore_docs.txt", ) + + @pytest.mark.parametrize( + "ignore_run_keywords, expected_file", + [(True, "expected_output_run_kw.txt"), (False, "expected_output_run_kw_off.txt")], + ) + def test_run_keyword(self, ignore_run_keywords, expected_file): + self.check_rule( + config=f"-c misaligned-continuation-row:ignore_run_keywords:{ignore_run_keywords}", + src_files=["run_keyword.robot"], + expected_file=expected_file, + )