Skip to content

Commit 6089413

Browse files
committed
find unused references for arguments and local variables
1 parent d11e5be commit 6089413

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed

robotcode/language_server/robotframework/diagnostics/analyzer.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
range_from_node,
3131
range_from_node_or_token,
3232
range_from_token,
33+
strip_variable_token,
3334
tokenize_variables,
3435
)
3536
from ..utils.async_ast import AsyncVisitor
@@ -100,6 +101,46 @@ async def yield_argument_name_and_rest(self, node: ast.AST, token: Token) -> Asy
100101
else:
101102
yield token
102103

104+
async def visit_Variable(self, node: ast.AST) -> None: # noqa: N802
105+
from robot.parsing.lexer.tokens import Token as RobotToken
106+
from robot.parsing.model.statements import Variable
107+
from robot.variables import search_variable
108+
109+
variable = cast(Variable, node)
110+
111+
name_token = variable.get_token(RobotToken.VARIABLE)
112+
name = name_token.value
113+
114+
if name is not None:
115+
116+
match = search_variable(name, ignore_errors=True)
117+
if not match.is_assign(allow_assign_mark=True):
118+
return
119+
120+
if name.endswith("="):
121+
name = name[:-1].rstrip()
122+
123+
r = range_from_token(
124+
strip_variable_token(
125+
RobotToken(name_token.type, name, name_token.lineno, name_token.col_offset, name_token.error)
126+
)
127+
)
128+
129+
var_def = next(
130+
(
131+
v
132+
for v in await self.namespace.get_own_variables()
133+
if v.name_token is not None and range_from_token(v.name_token) == r
134+
),
135+
None,
136+
)
137+
138+
if var_def is None:
139+
return
140+
141+
if var_def not in self._variable_references:
142+
self._variable_references[var_def] = set()
143+
103144
async def visit(self, node: ast.AST) -> None:
104145
from robot.parsing.lexer.tokens import Token as RobotToken
105146
from robot.parsing.model.statements import (
@@ -155,6 +196,12 @@ async def visit(self, node: ast.AST) -> None:
155196
self._variable_references[var].add(
156197
Location(self.namespace.document.document_uri, var_range)
157198
)
199+
elif var not in self._variable_references and token1.type in [
200+
RobotToken.ASSIGN,
201+
RobotToken.ARGUMENT,
202+
]:
203+
self._variable_references[var] = set()
204+
158205
if (
159206
isinstance(node, Statement)
160207
and isinstance(node, self.get_expression_statement_types())

robotcode/language_server/robotframework/diagnostics/entities.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ class LocalVariableDefinition(VariableDefinition):
194194
type: VariableDefinitionType = VariableDefinitionType.LOCAL_VARIABLE
195195

196196
def __hash__(self) -> int:
197-
return hash((type(self), self.name, self.type))
197+
return hash((type(self), self.name, self.type, self.range, self.source))
198198

199199

200200
@dataclass
@@ -221,15 +221,15 @@ class ArgumentDefinition(VariableDefinition):
221221
keyword_doc: Optional["KeywordDoc"] = None
222222

223223
def __hash__(self) -> int:
224-
return hash((type(self), self.name, self.type))
224+
return hash((type(self), self.name, self.type, self.range, self.source))
225225

226226

227227
@dataclass
228228
class ImportedVariableDefinition(VariableDefinition):
229229
type: VariableDefinitionType = VariableDefinitionType.IMPORTED_VARIABLE
230230

231231
def __hash__(self) -> int:
232-
return hash((type(self), self.name, self.type))
232+
return hash((type(self), self.name, self.type, self.source))
233233

234234

235235
@dataclass

robotcode/language_server/robotframework/parts/diagnostics.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ...common.text_document import TextDocument
1919
from ..configuration import AnalysisConfig
2020
from ..diagnostics.analyzer import Analyzer
21+
from ..diagnostics.entities import ArgumentDefinition
2122
from ..utils.ast_utils import (
2223
HeaderAndBodyBlock,
2324
Token,
@@ -278,13 +279,14 @@ async def _collect_unused_references(self, document: TextDocument, full: bool) -
278279
)
279280
)
280281

281-
for var in await namespace.get_own_variables():
282+
for var in (await namespace.get_variable_references()).keys():
282283
references = await self.parent.robot_references.find_variable_references(document, var, False)
283284
if not references and not await Analyzer.should_ignore(document, var.name_range):
284285
result.append(
285286
Diagnostic(
286287
range=var.name_range,
287-
message=f"Variable '{var.name}' is not used.",
288+
message=f"{'Argument' if isinstance(var, ArgumentDefinition) else 'Variable'}"
289+
f" '{var.name}' is not used.",
288290
severity=DiagnosticSeverity.WARNING,
289291
source=self.source_name,
290292
code="VariableNotUsed",

0 commit comments

Comments
 (0)