Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 58 additions & 4 deletions robocop/checkers/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -855,10 +855,10 @@ def comma_separated_list(value: str) -> list[str]:
optionally overwrite this default.

When you use an argument default, you should be as clear as possible. This improves the
readability of your code. The syntax `${argument}=` is unclear unless you happen to know
that it is technically equivalent to `${argument}=${EMPTY}`. To prevent people from
readability of your code. The syntax ``${argument}=`` is unclear unless you happen to know
that it is technically equivalent to ``${argument}=${EMPTY}``. To prevent people from
misreading your keyword arguments, explicitly state that the value is empty using the
built-in `${EMPTY}` variable.
built-in ``${EMPTY}`` variable.

Example of a rule violation::

Expand All @@ -867,6 +867,29 @@ def comma_separated_list(value: str) -> list[str]:
[Arguments] ${argument_name}=
""",
),
"0933": DefaultRule(
rule_id="0933",
name="undefined-argument-value",
msg="Undefined argument value, use {{ arg_name }}=${EMPTY} instead",
severity=RuleSeverity.ERROR,
added_in_version="5.7.0",
docs="""
When calling a keyword, it can accept named arguments.

When you call a keyword, you should be as clear as possible. This improves the
readability of your code. The syntax ``argument=`` is unclear unless you happen to know
that it is technically equivalent to ``argument=${EMPTY}``. To prevent people from
misreading your keyword arguments, explicitly state that the value is empty using the
built-in ``${EMPTY}`` variable.

If your argument is falsly flagged by this rule, escape the ``=`` character in your argument
value by like so: ``\\=``.

Example of a rule violation::

My Amazing Keyword argument_name=
""",
),
}


Expand Down Expand Up @@ -1965,7 +1988,10 @@ def _report(self, rule_name: str, node):


class UndefinedArgumentDefaultChecker(VisitorChecker):
reports = ("undefined-argument-default",)
reports = (
"undefined-argument-default",
"undefined-argument-value",
)

def visit_Arguments(self, node: Arguments): # noqa: N802
for token in node.get_tokens(Token.ARGUMENT):
Expand All @@ -1989,3 +2015,31 @@ def visit_Arguments(self, node: Arguments): # noqa: N802
end_col=token.col_offset + len(token.value) + 1,
arg_name=arg_name + "}",
)

def visit_KeywordCall(self, node: KeywordCall): # noqa: N802
for token in node.get_tokens(Token.ARGUMENT):
arg = token.value

if "=" not in arg or arg.startswith("="):
# Is a positional arg
continue

arg_name, default_val = arg.split("=", maxsplit=1)
if arg_name.endswith("\\"):
# `=` is escaped
continue

if default_val != "":
# Has a value
continue

# Falsly triggers if a positional argument ends with `=`
# The language server has the same behavior
self.report(
"undefined-argument-value",
node=token,
lineno=token.lineno,
col=token.col_offset + 1,
end_col=token.col_offset + len(token.value) + 1,
arg_name=arg_name,
)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
test.robot:3:12 [E] 0933 Undefined argument value, use message=${EMPTY} instead
test.robot:4:12 [E] 0933 Undefined argument value, use message=${EMPTY} instead
test.robot:4:24 [E] 0933 Undefined argument value, use level=${EMPTY} instead
test.robot:5:27 [E] 0933 Undefined argument value, use level=${EMPTY} instead
test.robot:6:12 [E] 0933 Undefined argument value, use A great log message=${EMPTY} instead
test.robot:7:12 [E] 0933 Undefined argument value, use A great log message =${EMPTY} instead
20 changes: 20 additions & 0 deletions tests/atest/rules/misc/undefined_argument_value/test.robot
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
*** Test Cases ***
With undefined value
Log message=
Log message= level=
Log Hello world level= html=${True}
Log A great log message=
Log A great log message =

With escaped equals sign
Log A great log message \=
Log A great log message\=
Log Hello\=world
Log mess\=age=foo

With defined values
Log Hello = world
Log message=Hello world
Log message==
Log =
Log = amazing!
6 changes: 6 additions & 0 deletions tests/atest/rules/misc/undefined_argument_value/test_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
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")
Loading