Skip to content

Commit 9cba5b0

Browse files
committed
fix(analyze): ignore intentionally unused variables in CLI diagnostics
Treat variable names starting with "_" as intentionally unused in CLI unused-variable diagnostics, matching the language server behavior. Closes #593
1 parent 4bbf944 commit 9cba5b0

4 files changed

Lines changed: 69 additions & 1 deletion

File tree

packages/analyze/src/robotcode/analyze/code/robot_framework_language_provider.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from robotcode.core.text_document import TextDocument
1414
from robotcode.core.uri import Uri
1515
from robotcode.core.workspace import WorkspaceFolder
16+
from robotcode.robot.diagnostics.diagnostic_rules import is_variable_name_intentionally_unused
1617
from robotcode.robot.diagnostics.document_cache_helper import DocumentsCacheHelper
1718
from robotcode.robot.diagnostics.entities import (
1819
ArgumentDefinition,
@@ -165,6 +166,9 @@ def collect_unused_variables(self, sender: Any, document: TextDocument) -> Optio
165166
):
166167
continue
167168

169+
if is_variable_name_intentionally_unused(var):
170+
continue
171+
168172
if var.source != namespace.source:
169173
continue
170174

packages/language_server/src/robotcode/language_server/robotframework/parts/diagnostics.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from robotcode.core.text_document import TextDocument
1414
from robotcode.core.utils.logging import LoggingDescriptor
1515
from robotcode.language_server.robotframework.configuration import AnalysisConfig
16+
from robotcode.robot.diagnostics.diagnostic_rules import is_variable_name_intentionally_unused
1617
from robotcode.robot.diagnostics.entities import (
1718
ArgumentDefinition,
1819
EnvironmentVariableDefinition,
@@ -235,7 +236,7 @@ def _collect_unused_variable_references(self, document: TextDocument) -> Diagnos
235236
):
236237
continue
237238

238-
if var.name_token is not None and var.name_token.value and var.name_token.value.startswith("_"):
239+
if is_variable_name_intentionally_unused(var):
239240
continue
240241

241242
references = self.parent.robot_references.find_variable_references(document, var, False, True)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
"""Shared diagnostic rule helpers."""
2+
3+
from .entities import VariableDefinition
4+
5+
6+
def is_variable_name_intentionally_unused(variable: VariableDefinition) -> bool:
7+
"""Return whether a variable follows the intentionally unused naming convention."""
8+
return (
9+
variable.name_token is not None
10+
and bool(variable.name_token.value)
11+
and variable.name_token.value.startswith("_")
12+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
from types import SimpleNamespace
2+
from typing import Any, cast
3+
4+
from pytest_mock import MockerFixture
5+
from robot.parsing.lexer.tokens import Token
6+
7+
from robotcode.analyze.code.robot_framework_language_provider import RobotFrameworkLanguageProvider
8+
from robotcode.core.lsp.types import Diagnostic
9+
from robotcode.robot.diagnostics.entities import LocalVariableDefinition
10+
from robotcode.robot.diagnostics.namespace import DocumentType
11+
12+
SOURCE = "/suite.robot"
13+
14+
15+
def _local_variable(name_base: str, line_no: int) -> LocalVariableDefinition:
16+
name_token = Token(Token.VARIABLE, name_base, line_no, 0)
17+
return LocalVariableDefinition(
18+
name=f"${{{name_base}}}",
19+
name_token=name_token,
20+
line_no=line_no,
21+
col_offset=0,
22+
end_line_no=line_no,
23+
end_col_offset=len(name_base),
24+
source=SOURCE,
25+
)
26+
27+
28+
def test_cli_unused_variable_diagnostics_ignore_intentionally_unused_variables(mocker: MockerFixture) -> None:
29+
intentionally_unused = _local_variable("_", 1)
30+
prefixed_intentionally_unused = _local_variable("_ignored", 2)
31+
unused = _local_variable("unused", 3)
32+
33+
variable_references: dict[LocalVariableDefinition, set[Any]] = {
34+
intentionally_unused: set(),
35+
prefixed_intentionally_unused: set(),
36+
unused: set(),
37+
}
38+
namespace = SimpleNamespace(source=SOURCE, variable_references=variable_references)
39+
document_cache = mocker.Mock()
40+
document_cache.get_namespace.return_value = namespace
41+
document_cache.get_project_index.return_value = mocker.Mock()
42+
document_cache.get_document_type.return_value = DocumentType.GENERAL
43+
44+
provider = cast(Any, object.__new__(RobotFrameworkLanguageProvider))
45+
provider.diagnostics_context = SimpleNamespace(collect_unused=True)
46+
provider._document_cache = document_cache
47+
48+
diagnostics = cast(list[Diagnostic], provider.collect_unused_variables(mocker.Mock(), mocker.Mock()))
49+
50+
assert [diagnostic.message for diagnostic in diagnostics] == ["Variable '${unused}' is not used."]
51+
assert [diagnostic.code for diagnostic in diagnostics] == ["VariableNotUsed"]

0 commit comments

Comments
 (0)