Skip to content

Commit

Permalink
Support for RF 7.2 GROUP syntax (#1157)
Browse files Browse the repository at this point in the history
  • Loading branch information
bhirsz authored Jan 2, 2025
1 parent d343cb8 commit 7a80205
Show file tree
Hide file tree
Showing 38 changed files with 226 additions and 19 deletions.
2 changes: 1 addition & 1 deletion docs/releasenotes/5.7.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ It is technically possible to define keyword argument default with empty string:
My Keyword
[Arguments] ${argument_name}=

This syntax is unclear and it is better to explicitly state that the valeu is empty using built-in variable::
This syntax is unclear and it is better to explicitly state that the value is empty using built-in variable::

*** Keywords ***
My Keyword
Expand Down
2 changes: 1 addition & 1 deletion robocop/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
from robocop.run import Robocop, run_robocop
from robocop.version import __version__

__all__ = ["__version__", "run_robocop", "checkers", "Robocop", "Config"]
__all__ = ["Config", "Robocop", "__version__", "checkers", "run_robocop"]
7 changes: 6 additions & 1 deletion robocop/checkers/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1383,7 +1383,7 @@ def visit_Error(self, node): # noqa: N802
)

def check_statement_in_loop(self, node, token_type):
if self.loops or node.errors and f"{token_type} can only be used inside a loop." not in node.errors:
if self.loops or (node.errors and f"{token_type} can only be used inside a loop." not in node.errors):
return
self.report(
"statement-outside-loop",
Expand Down Expand Up @@ -1642,6 +1642,11 @@ def visit_Try(self, node): # noqa: N802
if node.next:
self.visit_Try(node.next)

def visit_Group(self, node): # noqa: N802
for token in node.header.get_tokens(Token.ARGUMENT):
self.find_not_nested_variable(token.value, is_var=False)
self.generic_visit(node)

def visit_KeywordCall(self, node): # noqa: N802
for token in node.get_tokens(Token.ARGUMENT, Token.KEYWORD): # argument can be used in keyword name
self.find_not_nested_variable(token.value, is_var=False)
Expand Down
4 changes: 2 additions & 2 deletions robocop/checkers/spacing.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ def visit_For(self, node): # noqa: N802
self.verify_consecutive_empty_lines(node.body, check_trailing=True)
self.generic_visit(node)

visit_ForLoop = visit_While = visit_Try = visit_If = visit_For # noqa: N815
visit_ForLoop = visit_While = visit_Try = visit_If = visit_Group = visit_For # noqa: N815

def visit_File(self, node): # noqa: N802
for section in node.sections:
Expand Down Expand Up @@ -707,7 +707,7 @@ def visit_For(self, node): # noqa: N802
self.visit(child)
self.visit_Statement(node.end)

visit_While = visit_ForLoop = visit_For # noqa: N815
visit_While = visit_ForLoop = visit_Group = visit_For # noqa: N815

def get_common_if_indent(self, node):
indents = count_indents(node)
Expand Down
4 changes: 2 additions & 2 deletions robocop/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
)

__all__ = [ # TODO: replace with direct imports
"ROBOT_VERSION",
"AssignmentTypeDetector",
"DisablersFinder",
"FileType",
"FileTypeChecker",
"ROBOT_VERSION",
"AssignmentTypeDetector",
"RecommendationFinder",
"find_robot_vars",
"get_errors",
Expand Down
2 changes: 1 addition & 1 deletion robocop/utils/disablers.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def visit_Section(self, node): # noqa: N802
self.parse_disablers_in_node(node)
self.is_first_comment_section = False

visit_TestCase = visit_Keyword = visit_Try = visit_For = visit_ForLoop = visit_While = visit_Section # noqa: N815
visit_TestCase = visit_Keyword = visit_Try = visit_For = visit_ForLoop = visit_While = visit_Group = visit_Section # noqa: N815

def visit_If(self, node): # noqa: N802
last_line = node.body[-1].end_lineno if node.body else None
Expand Down
2 changes: 1 addition & 1 deletion robocop/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "5.7.0"
__version__ = "5.8.0"
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
Framework :: Robot Framework
Framework :: Robot Framework :: Tool
Topic :: Software Development :: Testing
Expand Down Expand Up @@ -50,7 +52,7 @@
include_package_data=True,
install_requires=[
"jinja2>=3.0,<4.0",
"robotframework>=3.2.2,<7.2",
"robotframework>=3.2.2,<7.3",
"pathspec>=0.9,<0.13",
"tomli>=2.0.0",
"pytz>=2022.7",
Expand Down
3 changes: 2 additions & 1 deletion tests/atest/rules/errors/invalid_if/expected_output_rf4.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ test.robot:14:5 [E] 0413 Invalid IF syntax: ELSE IF has more than one condition
test.robot:28:5 [E] 0413 Invalid IF syntax: IF has more than one condition
test.robot:53:9 [E] 0413 Invalid IF syntax: IF has no condition
test.robot:58:13 [E] 0413 Invalid IF syntax: IF has no condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF has no condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF has no condition
test.robot:72:9 [E] 0413 Invalid IF syntax: IF has no condition
3 changes: 2 additions & 1 deletion tests/atest/rules/errors/invalid_if/expected_output_rf5.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ test.robot:14:5 [E] 0413 Invalid IF syntax: ELSE IF cannot have more than one co
test.robot:28:5 [E] 0413 Invalid IF syntax: IF branch cannot be empty
test.robot:53:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:58:13 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:72:9 [E] 0413 Invalid IF syntax: IF must have a condition
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ test.robot:14:5 [E] 0413 Invalid IF syntax: ELSE IF cannot have more than one co
test.robot:28:5 [E] 0413 Invalid IF syntax: Inline IF branches cannot contain assignments
test.robot:53:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:58:13 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:72:9 [E] 0413 Invalid IF syntax: IF must have a condition
3 changes: 2 additions & 1 deletion tests/atest/rules/errors/invalid_if/expected_output_rf6.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ test.robot:14:5 [E] 0413 Invalid IF syntax: ELSE IF cannot have more than one co
test.robot:28:5 [E] 0413 Invalid IF syntax: Inline IF branches cannot contain assignments
test.robot:53:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:58:13 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:63:9 [E] 0413 Invalid IF syntax: IF must have a condition
test.robot:72:9 [E] 0413 Invalid IF syntax: IF must have a condition
7 changes: 7 additions & 0 deletions tests/atest/rules/errors/invalid_if/test.robot
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,10 @@ RF 5 syntax
FINALLY
Keyword
END

RF 7.2 Group
GROUP
IF
Keyword
END
END
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ test.robot:13:9 [E] 0403 Missing keyword name when calling some values
test.robot:16:9 [E] 0403 Missing keyword name when calling some values
test.robot:19:9 [E] 0403 Missing keyword name when calling some values
test.robot:22:9 [E] 0403 Missing keyword name when calling some values
test.robot:28:9 [E] 0403 Missing keyword name when calling some values
test.robot:28:9 [E] 0403 Missing keyword name when calling some values
test.robot:33:8 [E] 0403 Missing keyword name when calling some values
5 changes: 5 additions & 0 deletions tests/atest/rules/errors/missing_keyword_name/test.robot
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,8 @@ Nested in blocks
FINALLY
${var}
END

Group
GROUP Missing keyword name
${var}
END
38 changes: 38 additions & 0 deletions tests/atest/rules/errors/parsing_error/expected_output_rf7_2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
variable_errors.robot:5:1 [E] 0401 Robot Framework syntax error: Invalid variable name 'value'.
variable_errors.robot:7:1 [E] 0401 Robot Framework syntax error: Invalid dictionary variable item '1'. Items must use 'name=value' syntax or be dictionary variables themselves.
positional_args.robot:3:32 [E] 0401 Robot Framework syntax error: Positional argument '${arg2}' follows named argument
positional_args.robot:3:43 [E] 0401 Robot Framework syntax error: Positional argument '${arg3}' follows named argument
positional_args.robot:8:32 [E] 0401 Robot Framework syntax error: Positional argument '${arg2}' follows named argument
positional_args.robot:8:57 [E] 0401 Robot Framework syntax error: Positional argument '${arg1}' follows named argument
positional_args.robot:12:29 [E] 0401 Robot Framework syntax error: Positional argument '@{args}' follows named argument
positional_args.robot:12:38 [E] 0401 Robot Framework syntax error: Positional argument '${arg}' follows named argument
invalid_try_except.robot:4:1 [E] 0401 Robot Framework syntax error: TRY must have closing END.
invalid_try_except.robot:13:1 [E] 0401 Robot Framework syntax error: TRY branch cannot be empty.
invalid_try_except.robot:22:1 [E] 0401 Robot Framework syntax error: TRY structure must have EXCEPT or FINALLY branch.
invalid_try_except.robot:28:1 [E] 0401 Robot Framework syntax error: TRY structure must have EXCEPT or FINALLY branch.
invalid_try_except.robot:36:1 [E] 0401 Robot Framework syntax error: TRY does not accept arguments, got 'I should not be here'.
invalid_try_except.robot:48:1 [E] 0401 Robot Framework syntax error: EXCEPT branch cannot be empty.
invalid_try_except.robot:57:1 [E] 0401 Robot Framework syntax error: EXCEPT without patterns must be last.
invalid_try_except.robot:69:1 [E] 0401 Robot Framework syntax error: Only one EXCEPT without patterns allowed.
invalid_try_except.robot:83:1 [E] 0401 Robot Framework syntax error: EXCEPT AS requires a value.
invalid_try_except.robot:91:1 [E] 0401 Robot Framework syntax error: EXCEPT AS accepts only one value.
invalid_try_except.robot:99:1 [E] 0401 Robot Framework syntax error: EXCEPT AS variable 'foo' is invalid.
invalid_try_except.robot:109:1 [E] 0401 Robot Framework syntax error: ELSE does not accept arguments, got 'I should not be here'.
invalid_try_except.robot:121:1 [E] 0401 Robot Framework syntax error: ELSE branch cannot be empty.
invalid_try_except.robot:128:1 [E] 0401 Robot Framework syntax error: Only one ELSE allowed.
invalid_try_except.robot:146:1 [E] 0401 Robot Framework syntax error: FINALLY does not accept arguments, got 'ooops', 'i', 'did', 'it' and 'again'.
invalid_try_except.robot:154:1 [E] 0401 Robot Framework syntax error: FINALLY branch cannot be empty.
invalid_try_except.robot:159:1 [E] 0401 Robot Framework syntax error: Only one FINALLY allowed.
invalid_try_except.robot:171:1 [E] 0401 Robot Framework syntax error: EXCEPT not allowed after ELSE.
invalid_try_except.robot:185:1 [E] 0401 Robot Framework syntax error: EXCEPT not allowed after FINALLY.
invalid_try_except.robot:197:1 [E] 0401 Robot Framework syntax error: ELSE not allowed after FINALLY.
invalid_try_except.robot:210:1 [E] 0401 Robot Framework syntax error: TRY does not support templates.
invalid_try_except.robot:220:1 [E] 0401 Robot Framework syntax error: TRY does not support templates.
invalid_try_except.robot:233:1 [E] 0401 Robot Framework syntax error: TRY does not support templates.
invalid_try_except.robot:233:1 [E] 0401 Robot Framework syntax error: TRY must have closing END.
invalid_try_except.robot:246:1 [E] 0401 Robot Framework syntax error: BREAK cannot be used in FINALLY branch.
invalid_try_except.robot:256:1 [E] 0401 Robot Framework syntax error: CONTINUE cannot be used in FINALLY branch.
invalid_try_except.robot:269:1 [E] 0401 Robot Framework syntax error: RETURN cannot be used in FINALLY branch.
invalid_while.robot:10:1 [E] 0401 Robot Framework syntax error: WHILE accepts only one condition, got 3 conditions 'Too', 'many' and '!'.
invalid_while.robot:16:1 [E] 0401 Robot Framework syntax error: WHILE loop cannot be empty.
invalid_while.robot:21:1 [E] 0401 Robot Framework syntax error: WHILE loop must have closing END.
5 changes: 4 additions & 1 deletion tests/atest/rules/errors/parsing_error/test_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

class TestRule(RuleAcceptance):
def test_rule(self):
self.check_rule(expected_file="expected_output_rf7.txt", target_version=">=7")
self.check_rule(expected_file="expected_output_rf7_2.txt", target_version=">=7.2")

def test_rule_rf7_1(self):
self.check_rule(expected_file="expected_output_rf7_1.txt", target_version=">=7;<7.2")

def test_rule_rf61(self):
self.check_rule(expected_file="expected_output.txt", target_version="==6.1.*")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ test.robot:9:19 [E] 0414 RETURN can only be used inside a user keyword
test.robot:11:9 [E] 0414 RETURN can only be used inside a user keyword
test.robot:14:9 [E] 0414 RETURN can only be used inside a user keyword
test.robot:17:9 [E] 0414 RETURN can only be used inside a user keyword
test.robot:19:9 [E] 0414 RETURN can only be used inside a user keyword
test.robot:19:9 [E] 0414 RETURN can only be used inside a user keyword
test.robot:22:9 [E] 0414 RETURN can only be used inside a user keyword
3 changes: 3 additions & 0 deletions tests/atest/rules/errors/return_in_test_case/test.robot
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ RETURN
EXCEPT
RETURN
END
GROUP
RETURN
END
[Return]

*** Keywords ***
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
groups.robot:15:1 [W] 0503 Keyword 'Some Keyword' has too many keywords inside (11/10)
31 changes: 31 additions & 0 deletions tests/atest/rules/lengths/too_many_calls_in_keyword/groups.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
*** Settings ***
Documentation doc

*** Test Cases ***
Test
[Documentation] doc
[Tags] sometag
Pass
Keyword
One More


*** Keywords ***
Some Keyword
[Documentation] this is doc
No Operation
Pass
No Operation
Fail
No Operation
VAR ${variable} value
No Operation
GROUP
No Operation
END
No Operation
FOR ${var} IN RANGE 10
No Operation
No Operation
END
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@

class TestRuleAcceptance(RuleAcceptance):
def test_rule(self):
self.check_rule(src_files=["test.robot"], expected_file="expected_output.txt")
self.check_rule(src_files=["test.robot"], expected_file="expected_output.txt", target_version="<7.2")

def test_with_groups(self):
self.check_rule(src_files=["groups.robot"], expected_file="expected_output_groups.txt", target_version=">=7.2")

def test_severity(self):
self.check_rule(
Expand Down
12 changes: 12 additions & 0 deletions tests/atest/rules/misc/unused_argument/groups.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*** Keywords ***
Used in GROUP name
[Arguments] ${argument}
GROUP Name with ${argument}
No Operation
END

Used In GROUP body
[Arguments] ${argument}
GROUP Named
Log ${argument}
END
7 changes: 7 additions & 0 deletions tests/atest/rules/misc/unused_argument/test_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,10 @@ def test_rule_rf3(self):

def test_rule_rf4(self):
self.check_rule(src_files=["test.robot"], expected_file="expected_output_rf4.txt", target_version="==4.*")

def test_groups(self):
self.check_rule(
src_files=["groups.robot"],
expected_file=None,
target_version=">=7.2",
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
groups.robot:16:9 [I] 0920 Variable '${variable}' is assigned but not used
19 changes: 19 additions & 0 deletions tests/atest/rules/misc/unused_variable/groups.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
*** Test Cases ***
Used in GROUP name
${variable} Keyword Call
GROUP Name with ${variable}
No Operation
END

Used in GROUP body
${variable} Keyword Call
GROUP Named
Log ${variable}
END

Unused defined in GROUP
GROUP Named
${variable} Keyword Call
${used} Keyword Call
END
Log ${used}
3 changes: 3 additions & 0 deletions tests/atest/rules/misc/unused_variable/test_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ def test_rule_on_loops(self):
issue_format="end_col",
target_version=">=5",
)

def test_groups(self):
self.check_rule(src_files=["groups.robot"], expected_file="expected_output_groups.txt", target_version=">=7.2")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
groups.robot:5:16 [I] 0316 Variable '${variableName}' may overwrite similar variable inside 'VAR Syntax' TestCase. Note that variables are case-insensitive, and also spaces and underscores are ignored.
groups.robot:7:20 [I] 0316 Variable '${variable name}' may overwrite similar variable inside 'VAR Syntax' TestCase. Note that variables are case-insensitive, and also spaces and underscores are ignored.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
*** Test Cases ***
VAR Syntax
${variable_name} = Set Variable value
GROUP
VAR ${variableName} value
GROUP
VAR ${variable name} value
END
END
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,6 @@ def test_pre_rf6_1(self):

def test_rf3(self):
self.check_rule(src_files=["test.robot"], expected_file="expected_output_rf3.txt", target_version="==3.*")

def test_groups(self):
self.check_rule(src_files=["groups.robot"], expected_file="expected_groups.txt", target_version=">=7.2")
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
groups.robot:4:1 [E] 1017 Indent expected. Provide 2 or more spaces of indentation for statements inside block
groups.robot:10:1 [E] 1017 Indent expected. Provide 2 or more spaces of indentation for statements inside block
groups.robot:12:1 [E] 1017 Indent expected. Provide 2 or more spaces of indentation for statements inside block
13 changes: 13 additions & 0 deletions tests/atest/rules/spacing/bad_block_indent/groups.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
*** Test Cases ***
Test with groups
GROUP
Log Not enough indent
END
GROUP
Log Enough indent
END
GROUP Nested
GROUP
Single
END
END
4 changes: 4 additions & 0 deletions tests/atest/rules/spacing/bad_block_indent/test_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ class TestRuleAcceptance(RuleAcceptance):
@pytest.mark.parametrize(("file_suffix", "target_version"), [("", ">=5"), ("_rf4", "==4.*"), ("_rf3", "==3.*")])
def test_rule(self, file_suffix, target_version):
self.check_rule(
src_files=["comments.robot", "test.robot"],
expected_file=f"expected_output{file_suffix}.txt",
target_version=target_version,
)

def test_groups(self):
self.check_rule(src_files=["groups.robot"], expected_file="expected_groups.txt", target_version=">=7.2")
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
groups${/}groups.robot:5:1 [W] 1008 Line is over-indented
groups${/}groups.robot:9:1 [W] 1008 Line is over-indented
groups${/}groups.robot:15:1 [W] 1008 Line is under-indented
groups${/}groups.robot:17:1 [W] 1008 Line is over-indented
Loading

0 comments on commit 7a80205

Please sign in to comment.