Skip to content

Commit 6a372fe

Browse files
committed
feat(robot): track tag and metadata references in namespace analysis
Add structured reference tracking for tags and metadata during analysis. Tags are split into keyword_tag_references and testcase_tag_references to reflect their different semantics in Robot Framework (keyword visibility vs test selection/reporting). Metadata references track settings-level metadata entries by key. All reference keys are normalized for consistent lookups. Remove unused TagDefinition class.
1 parent deedd7b commit 6a372fe

File tree

3 files changed

+52
-28
lines changed

3 files changed

+52
-28
lines changed

packages/robot/src/robotcode/robot/diagnostics/entities.py

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -372,21 +372,3 @@ def __hash__(self) -> int:
372372
self.name,
373373
)
374374
)
375-
376-
377-
@dataclass(slots=True)
378-
class TagDefinition(SourceEntity):
379-
name: str
380-
381-
def __hash__(self) -> int:
382-
return hash(
383-
(
384-
type(self),
385-
self.line_no,
386-
self.col_offset,
387-
self.end_line_no,
388-
self.end_col_offset,
389-
self.source,
390-
self.name,
391-
)
392-
)

packages/robot/src/robotcode/robot/diagnostics/namespace.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
LibraryImport,
3939
ResourceEntry,
4040
ResourceImport,
41-
TagDefinition,
4241
TestCaseDefinition,
4342
VariableDefinition,
4443
VariablesEntry,
@@ -116,7 +115,9 @@ def __init__(
116115
local_variable_assignments: Dict[VariableDefinition, Set[Range]],
117116
namespace_references: Dict[LibraryEntry, Set[Location]],
118117
test_case_definitions: List[TestCaseDefinition],
119-
tag_definitions: List[TagDefinition],
118+
keyword_tag_references: Dict[str, Set[Location]],
119+
testcase_tag_references: Dict[str, Set[Location]],
120+
metadata_references: Dict[str, Set[Location]],
120121
scope_tree: ScopeTree,
121122
finder: KeywordFinder,
122123
sentinel: object,
@@ -140,7 +141,9 @@ def __init__(
140141
self._local_variable_assignments = local_variable_assignments
141142
self._namespace_references = namespace_references
142143
self._test_case_definitions = test_case_definitions
143-
self._tag_definitions = tag_definitions
144+
self._keyword_tag_references = keyword_tag_references
145+
self._testcase_tag_references = testcase_tag_references
146+
self._metadata_references = metadata_references
144147
self._scope_tree = scope_tree
145148
self._finder: KeywordFinder = finder
146149
self._sentinel = sentinel # prevent GC — ref-counted by imports_manager
@@ -189,6 +192,18 @@ def local_variable_assignments(self) -> Dict[VariableDefinition, Set[Range]]:
189192
def namespace_references(self) -> Dict[LibraryEntry, Set[Location]]:
190193
return self._namespace_references
191194

195+
@property
196+
def keyword_tag_references(self) -> Dict[str, Set[Location]]:
197+
return self._keyword_tag_references
198+
199+
@property
200+
def testcase_tag_references(self) -> Dict[str, Set[Location]]:
201+
return self._testcase_tag_references
202+
203+
@property
204+
def metadata_references(self) -> Dict[str, Set[Location]]:
205+
return self._metadata_references
206+
192207
@property
193208
def import_entries(self) -> Dict[Import, LibraryEntry]:
194209
return self._import_entries
@@ -494,7 +509,9 @@ def build(self) -> Namespace:
494509
local_variable_assignments=analyzer_result.local_variable_assignments,
495510
namespace_references=analyzer_result.namespace_references,
496511
test_case_definitions=analyzer_result.test_case_definitions,
497-
tag_definitions=analyzer_result.tag_definitions,
512+
keyword_tag_references=analyzer_result.keyword_tag_references,
513+
testcase_tag_references=analyzer_result.testcase_tag_references,
514+
metadata_references=analyzer_result.metadata_references,
498515
scope_tree=analyzer_result.scope_tree,
499516
finder=finder,
500517
sentinel=sentinel,

packages/robot/src/robotcode/robot/diagnostics/namespace_analyzer.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
strip_variable_token,
5757
tokenize_variables,
5858
)
59+
from ..utils.match import normalize
5960
from ..utils.stubs import Languages
6061
from ..utils.variables import (
6162
BUILTIN_VARIABLES,
@@ -74,7 +75,6 @@
7475
GlobalVariableDefinition,
7576
LibraryEntry,
7677
LocalVariableDefinition,
77-
TagDefinition,
7878
TestCaseDefinition,
7979
TestVariableDefinition,
8080
VariableDefinition,
@@ -106,11 +106,11 @@ class AnalyzerResult:
106106
local_variable_assignments: Dict[VariableDefinition, Set[Range]]
107107
namespace_references: Dict[LibraryEntry, Set[Location]]
108108
test_case_definitions: List[TestCaseDefinition]
109-
tag_definitions: List[TagDefinition]
109+
keyword_tag_references: Dict[str, Set[Location]]
110+
testcase_tag_references: Dict[str, Set[Location]]
111+
metadata_references: Dict[str, Set[Location]]
110112
scope_tree: ScopeTree = None # type: ignore[assignment] # set by run()
111113

112-
# TODO Tag references
113-
114114

115115
_builtin_variables: Optional[List[VariableDefinition]] = None
116116

@@ -150,7 +150,9 @@ def __init__(
150150
self._local_variable_assignments: Dict[VariableDefinition, Set[Range]] = defaultdict(set)
151151
self._namespace_references: Dict[LibraryEntry, Set[Location]] = defaultdict(set)
152152
self._test_case_definitions: List[TestCaseDefinition] = []
153-
self._tag_definitions: List[TagDefinition] = []
153+
self._keyword_tag_references: Dict[str, Set[Location]] = defaultdict(set)
154+
self._testcase_tag_references: Dict[str, Set[Location]] = defaultdict(set)
155+
self._metadata_references: Dict[str, Set[Location]] = defaultdict(set)
154156

155157
# Phase 1+2 results (set by resolve())
156158
self._library_doc: ResourceDoc = None # type: ignore[assignment] # set by resolve()
@@ -246,7 +248,9 @@ def run(self, finder: KeywordFinder) -> AnalyzerResult:
246248
self._local_variable_assignments,
247249
self._namespace_references,
248250
self._test_case_definitions,
249-
self._tag_definitions,
251+
self._keyword_tag_references,
252+
self._testcase_tag_references,
253+
self._metadata_references,
250254
self._scope_builder.build(self._variable_scope),
251255
)
252256

@@ -1595,11 +1599,18 @@ def visit_TemplateArguments(self, node: TemplateArguments) -> None: # noqa: N80
15951599

15961600
self.generic_visit(node)
15971601

1602+
def _collect_tag_references(self, node: Statement, refs: Dict[str, Set[Location]]) -> None:
1603+
for token in node.get_tokens(Token.ARGUMENT):
1604+
if token.value:
1605+
refs[normalize(token.value)].add(Location(self._document_uri, range_from_token(token)))
1606+
15981607
def visit_DefaultTags(self, node: Statement) -> None: # noqa: N802
15991608
self._analyze_statement_variables(node, DiagnosticSeverity.HINT)
1609+
self._collect_tag_references(node, self._testcase_tag_references)
16001610

16011611
def visit_ForceTags(self, node: Statement) -> None: # noqa: N802
16021612
self._analyze_statement_variables(node, DiagnosticSeverity.HINT)
1613+
self._collect_tag_references(node, self._testcase_tag_references)
16031614

16041615
if get_robot_version() >= (6, 0):
16051616
tag = node.get_token(Token.FORCE_TAGS)
@@ -1614,6 +1625,7 @@ def visit_ForceTags(self, node: Statement) -> None: # noqa: N802
16141625

16151626
def visit_TestTags(self, node: Statement) -> None: # noqa: N802
16161627
self._analyze_statement_variables(node, DiagnosticSeverity.HINT)
1628+
self._collect_tag_references(node, self._testcase_tag_references)
16171629

16181630
if get_robot_version() >= (6, 0):
16191631
tag = node.get_token(Token.FORCE_TAGS)
@@ -1629,9 +1641,18 @@ def visit_TestTags(self, node: Statement) -> None: # noqa: N802
16291641
def visit_Arguments(self, node: Statement) -> None: # noqa: N802
16301642
pass
16311643

1644+
def visit_KeywordTags(self, node: Statement) -> None: # noqa: N802
1645+
self._visit_settings_statement(node, DiagnosticSeverity.HINT)
1646+
self._collect_tag_references(node, self._keyword_tag_references)
1647+
16321648
def visit_DocumentationOrMetadata(self, node: Statement) -> None: # noqa: N802
16331649
self._visit_settings_statement(node, DiagnosticSeverity.HINT)
16341650

1651+
if hasattr(node, "name") and node.name:
1652+
name_token = node.get_token(Token.NAME)
1653+
if name_token is not None:
1654+
self._metadata_references[node.name].add(Location(self._document_uri, range_from_token(name_token)))
1655+
16351656
def visit_Timeout(self, node: Statement) -> None: # noqa: N802
16361657
self._visit_block_settings_statement(node)
16371658

@@ -1643,6 +1664,10 @@ def visit_MultiValue(self, node: Statement) -> None: # noqa: N802
16431664

16441665
def visit_Tags(self, node: Statement) -> None: # noqa: N802
16451666
self._visit_settings_statement(node, DiagnosticSeverity.HINT)
1667+
if any(isinstance(n, Keyword) for n in self._node_stack):
1668+
self._collect_tag_references(node, self._keyword_tag_references)
1669+
else:
1670+
self._collect_tag_references(node, self._testcase_tag_references)
16461671

16471672
if (6, 0) < get_robot_version() < (7, 0):
16481673
for tag in node.get_tokens(Token.ARGUMENT):

0 commit comments

Comments
 (0)