Repository: MarketSquare/robotframework-robocop (language: python)

Issue:
One argument per continuation line
See style guide 


Discussion / hints:
Proposing the rule name `Arguments per continuation line` defaulting one, but could also be configured. If configured, it is the max count per line i.e. less is ok too.

E.g. following is valid with a configuration of `2` or above: 
```
*** Keywords ***
Custom Keyword With Required Arguments
    [Arguments]    ${name}    ${surname}
    ...            ${street}
    ...            ${block}
    ...            ${apartment}
    ...            ${state}    ${zip}
    Do Something
```

Fix patch:
diff --git a/docs/releasenotes/unreleased/rules.7.rst b/docs/releasenotes/unreleased/rules.7.rst
new file mode 100644
index 000000000..519a9b587
--- /dev/null
+++ b/docs/releasenotes/unreleased/rules.7.rst
@@ -0,0 +1,14 @@
+New rule arguments-per-line (#1000)
+------------------------------------------------
+
+Added new I0532 ``arguments-per-line`` rule.
+
+If the keyword's ``[Arguments]`` are split into multiple lines, it is recommended to put only one argument per
+every line.
+
+Example of rule violation::
+
+    *** Keywords ***
+    Keyword With Multiple Arguments
+    [Arguments]    ${first_arg}
+    ...    ${second_arg}    ${third_arg}=default
diff --git a/robocop/checkers/lengths.py b/robocop/checkers/lengths.py
index 34fe32b51..d5bbbc1c2 100644
--- a/robocop/checkers/lengths.py
+++ b/robocop/checkers/lengths.py
@@ -2,6 +2,7 @@
 Lengths checkers
 """
 import re
+from typing import List, Optional
 
 from robot.api import Token
 from robot.parsing.model.blocks import CommentSection, TestCase
@@ -337,6 +338,41 @@
         version=">=6",
         added_in_version="3.3.0",
     ),
+    "0532": Rule(
+        RuleParam(
+            name="max_args",
+            default=1,
+            converter=int,
+            desc="maximum number of arguments allowed in the continuation line",
+        ),
+        rule_id="0532",
+        name="arguments-per-line",
+        msg="There is too many arguments per continuation line ({{ arguments_count }} / {{ max_arguments_count }})",
+        severity=RuleSeverity.INFO,
+        added_in_version="5.0.0",
+        docs="""
+        If the keyword's ``[Arguments]`` are split into multiple lines, it is recommended to put only one argument
+        per every line.
+        
+        Bad |:x:| ::
+
+            *** Keywords ***
+            Keyword With Multiple Arguments
+            [Arguments]    ${first_arg
+            ...    ${second_arg}    ${third_arg}=default
+
+         Good |:white_check_mark:| ::
+
+        ..  code-block:: none
+
+            *** Keywords ***
+            Keyword With Multiple Arguments
+            [Arguments]    ${first_arg
+            ...    ${second_arg}
+            ...    ${third_arg}=default
+
+        """,
+    ),
 }
 
 
@@ -793,3 +829,33 @@ def visit_TestCaseSection(self, node):  # noqa
                 end_col=node.header.end_col_offset,
                 sev_threshold_value=discovered_testcases,
             )
+
+
+class TooManyArgumentsInLineChecker(VisitorChecker):
+    reports = ("arguments-per-line",)
+
+    def visit_Arguments(self, node):  # noqa
+        any_cont_token = node.get_token(Token.CONTINUATION)
+        if not any_cont_token:  # only one line, ignoring
+            return
+        max_args = self.param("arguments-per-line", "max_args")
+        for index, line in enumerate(node.lines):
+            args_count = sum(1 for token in line if token.type == Token.ARGUMENT)
+            if args_count > max_args:
+                data_token = self.first_non_sep(line)
+                last_token = line[-1]
+                if data_token:
+                    self.report(
+                        "arguments-per-line",
+                        node=data_token,
+                        col=data_token.col_offset + 1,
+                        end_col=last_token.end_col_offset + 1,
+                        arguments_count=args_count,
+                        max_arguments_count=max_args,
+                    )
+
+    @staticmethod
+    def first_non_sep(line: List[Token]) -> Token:
+        for token in line:
+            if token.type != Token.SEPARATOR:
+                return token


Test patch:
diff --git a/tests/atest/rules/lengths/arguments_per_line/__init__.py b/tests/atest/rules/lengths/arguments_per_line/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/atest/rules/lengths/arguments_per_line/expected_output.txt b/tests/atest/rules/lengths/arguments_per_line/expected_output.txt
new file mode 100644
index 000000000..4b3a5429a
--- /dev/null
+++ b/tests/atest/rules/lengths/arguments_per_line/expected_output.txt
@@ -0,0 +1,5 @@
+test.robot:21:5 [I] 0532 There is too many arguments per continuation line (2 / 1)
+test.robot:26:5 [I] 0532 There is too many arguments per continuation line (3 / 1)
+test.robot:31:5 [I] 0532 There is too many arguments per continuation line (2 / 1)
+test.robot:33:5 [I] 0532 There is too many arguments per continuation line (2 / 1)
+test.robot:37:5 [I] 0532 There is too many arguments per continuation line (2 / 1)
\ No newline at end of file
diff --git a/tests/atest/rules/lengths/arguments_per_line/expected_output_max_2.txt b/tests/atest/rules/lengths/arguments_per_line/expected_output_max_2.txt
new file mode 100644
index 000000000..44097a963
--- /dev/null
+++ b/tests/atest/rules/lengths/arguments_per_line/expected_output_max_2.txt
@@ -0,0 +1,1 @@
+test.robot:26:5 [I] 0532 There is too many arguments per continuation line (3 / 2)
\ No newline at end of file
diff --git a/tests/atest/rules/lengths/arguments_per_line/test.robot b/tests/atest/rules/lengths/arguments_per_line/test.robot
new file mode 100644
index 000000000..53ec3649f
--- /dev/null
+++ b/tests/atest/rules/lengths/arguments_per_line/test.robot
@@ -0,0 +1,42 @@
+*** Keywords ***
+Keyword Without Arguments
+    No Operation
+
+Keyword With Empty Arguments
+    [Arguments]
+    No Operation
+
+Keyword With Arguments In One Line
+    [Arguments]  ${arg}    ${arg2}    ${arg3}
+    No Operation
+
+Keyword With Arguments In Multiple Lines - Good
+    [Arguments]      ${arg}
+    ...    ${arg2}
+    ...    ${arg3}
+    No Operation
+
+Keyword With Arguments In Multiple Lines - Bad
+    [Arguments]  ${arg}
+    ...    ${arg2}    ${arg3}
+    No Operation
+
+Keyword With Arguments In Multiple Lines - Bad 2
+    [Arguments]  ${arg}
+    ...    ${arg2}    ${arg3}    ${arg4}=default
+    No Operation
+
+Keyword With Arguments In Multiple Lines - Bad 3
+    [Arguments]  ${arg}
+    ...    ${arg2}    ${arg3}
+    # ... it works for all except RF 4.0 which has critical bug on empty arguments
+    ...    ${arg5}    ${arg6}
+    No Operation
+
+Keyword With Arguments In Multiple Lines - Bad 4
+    [Arguments]  ${arg}    ${arg2}
+    ...    ${arg3}
+    No Operation
+
+Keyword With ${embedded} Arguments
+    Should Be Ignored
diff --git a/tests/atest/rules/lengths/arguments_per_line/test_rule.py b/tests/atest/rules/lengths/arguments_per_line/test_rule.py
new file mode 100644
index 000000000..5fd881039
--- /dev/null
+++ b/tests/atest/rules/lengths/arguments_per_line/test_rule.py
@@ -0,0 +1,13 @@
+from tests.atest.utils import RuleAcceptance
+
+
+class TestRuleAcceptance(RuleAcceptance):
+    def test_rule(self):
+        self.check_rule(src_files=["test.robot"], expected_file="expected_output.txt")
+
+    def test_rule_max_2(self):
+        self.check_rule(
+            src_files=["test.robot"],
+            expected_file="expected_output_max_2.txt",
+            config=" --configure arguments-per-line:max_args:2",
+        )