Skip to content

Commit 4e17c8f

Browse files
committed
fix(analyzer): corrected analyzing of [Return], [Setup], [Teardown] statement
1 parent 87e1dd9 commit 4e17c8f

File tree

1 file changed

+90
-17
lines changed

1 file changed

+90
-17
lines changed

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

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from io import StringIO
99
from pathlib import Path
1010
from tokenize import TokenError, generate_tokens
11-
from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Set, Tuple, Union, cast
11+
from typing import TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Set, Tuple, Union, cast
1212

1313
from robot.errors import VariableError
1414
from robot.parsing.lexer.tokens import Token
@@ -43,6 +43,7 @@
4343
Range,
4444
)
4545
from robotcode.core.uri import Uri
46+
from robotcode.core.utils.logging import LoggingDescriptor
4647

4748
from ..utils import get_robot_version
4849
from ..utils.ast import (
@@ -94,6 +95,9 @@ class AnalyzerResult:
9495

9596

9697
class NamespaceAnalyzer(Visitor):
98+
99+
_logger = LoggingDescriptor()
100+
97101
def __init__(
98102
self,
99103
model: ast.AST,
@@ -126,8 +130,11 @@ def __init__(
126130
self._overridden_variables: Dict[VariableDefinition, VariableDefinition] = {}
127131

128132
self._in_setting = False
133+
self._in_block_setting = False
129134

130135
self._suite_variables = self._variables.copy()
136+
self._block_variables: Optional[Dict[VariableMatcher, VariableDefinition]] = None
137+
self._end_block_handlers: Optional[List[Callable[[], None]]] = None
131138

132139
def run(self) -> AnalyzerResult:
133140
self._diagnostics = []
@@ -146,10 +153,11 @@ def run(self) -> AnalyzerResult:
146153
except BaseException as e:
147154
self._append_diagnostics(
148155
range_from_node(self._model),
149-
message=f"Fatal: can't analyze namespace '{e}')",
156+
message=f"Fatal: can't analyze namespace '{e}'.",
150157
severity=DiagnosticSeverity.ERROR,
151158
code=type(e).__qualname__,
152159
)
160+
self._logger.exception(e)
153161

154162
return AnalyzerResult(
155163
self._diagnostics,
@@ -379,6 +387,15 @@ def _visit_settings_statement(
379387
finally:
380388
self._in_setting = False
381389

390+
def _visit_block_settings_statement(
391+
self, node: Statement, severity: DiagnosticSeverity = DiagnosticSeverity.ERROR
392+
) -> None:
393+
self._in_block_setting = True
394+
try:
395+
self._visit_settings_statement(node, severity)
396+
finally:
397+
self._in_block_setting = False
398+
382399
def _analyze_token_expression_variables(
383400
self, token: Token, severity: DiagnosticSeverity = DiagnosticSeverity.ERROR
384401
) -> None:
@@ -896,7 +913,29 @@ def visit_Fixture(self, node: Fixture) -> None: # noqa: N802
896913

897914
if keyword_token is not None and keyword_token.value and keyword_token.value.upper() not in ("", "NONE"):
898915
self._analyze_token_variables(keyword_token)
899-
self._analyze_statement_variables(node)
916+
self._visit_block_settings_statement(node)
917+
918+
self._analyze_keyword_call(
919+
node,
920+
keyword_token,
921+
[e for e in node.get_tokens(Token.ARGUMENT)],
922+
allow_variables=True,
923+
ignore_errors_if_contains_variables=True,
924+
)
925+
926+
def visit_Teardown(self, node: Fixture) -> None: # noqa: N802
927+
keyword_token = node.get_token(Token.NAME)
928+
929+
# TODO: calculate possible variables in NAME
930+
931+
if keyword_token is not None and keyword_token.value and keyword_token.value.upper() not in ("", "NONE"):
932+
933+
def _handler() -> None:
934+
self._analyze_token_variables(keyword_token)
935+
self._analyze_statement_variables(node)
936+
937+
if self._end_block_handlers is not None:
938+
self._end_block_handlers.append(_handler)
900939

901940
self._analyze_keyword_call(
902941
node,
@@ -984,9 +1023,15 @@ def visit_TestCase(self, node: TestCase) -> None: # noqa: N802
9841023
self._current_testcase_or_keyword_name = node.name
9851024
old_variables = self._variables
9861025
self._variables = self._variables.copy()
1026+
self._end_block_handlers = []
9871027
try:
9881028
self.generic_visit(node)
1029+
1030+
for handler in self._end_block_handlers:
1031+
handler()
1032+
9891033
finally:
1034+
self._end_block_handlers = None
9901035
self._variables = old_variables
9911036
self._current_testcase_or_keyword_name = None
9921037
self._template = None
@@ -1029,13 +1074,21 @@ def visit_Keyword(self, node: Keyword) -> None: # noqa: N802
10291074
self._current_testcase_or_keyword_name = node.name
10301075
old_variables = self._variables
10311076
self._variables = self._variables.copy()
1077+
self._end_block_handlers = []
10321078
try:
10331079
arguments = next((v for v in node.body if isinstance(v, Arguments)), None)
10341080
if arguments is not None:
10351081
self._visit_Arguments(arguments)
1082+
self._block_variables = self._variables.copy()
10361083

10371084
self.generic_visit(node)
1085+
1086+
for handler in self._end_block_handlers:
1087+
handler()
1088+
10381089
finally:
1090+
self._end_block_handlers = None
1091+
self._block_variables = None
10391092
self._variables = old_variables
10401093
self._current_testcase_or_keyword_name = None
10411094
self._current_keyword_doc = None
@@ -1351,7 +1404,7 @@ def visit_DocumentationOrMetadata(self, node: Statement) -> None: # noqa: N802
13511404
self._visit_settings_statement(node, DiagnosticSeverity.HINT)
13521405

13531406
def visit_Timeout(self, node: Statement) -> None: # noqa: N802
1354-
self._analyze_statement_variables(node, DiagnosticSeverity.HINT)
1407+
self._visit_block_settings_statement(node)
13551408

13561409
def visit_SingleValue(self, node: Statement) -> None: # noqa: N802
13571410
self._visit_settings_statement(node, DiagnosticSeverity.HINT)
@@ -1399,19 +1452,35 @@ def visit_SectionHeader(self, node: Statement) -> None: # noqa: N802
13991452
code=Error.DEPRECATED_HEADER,
14001453
)
14011454

1402-
def visit_ReturnSetting(self, node: Statement) -> None: # noqa: N802
1403-
self._analyze_statement_variables(node)
1455+
if get_robot_version() >= (7, 0):
14041456

1405-
if get_robot_version() >= (7, 0):
1406-
token = node.get_token(Token.RETURN_SETTING)
1407-
if token is not None and token.error:
1408-
self._append_diagnostics(
1409-
range=range_from_node_or_token(node, token),
1410-
message=token.error,
1411-
severity=DiagnosticSeverity.WARNING,
1412-
tags=[DiagnosticTag.DEPRECATED],
1413-
code=Error.DEPRECATED_RETURN_SETTING,
1414-
)
1457+
def visit_ReturnSetting(self, node: Statement) -> None: # noqa: N802
1458+
1459+
def _handler() -> None:
1460+
self._analyze_statement_variables(node)
1461+
1462+
if self._end_block_handlers is not None:
1463+
self._end_block_handlers.append(_handler)
1464+
1465+
if get_robot_version() >= (7, 0):
1466+
token = node.get_token(Token.RETURN_SETTING)
1467+
if token is not None and token.error:
1468+
self._append_diagnostics(
1469+
range=range_from_node_or_token(node, token),
1470+
message=token.error,
1471+
severity=DiagnosticSeverity.WARNING,
1472+
tags=[DiagnosticTag.DEPRECATED],
1473+
code=Error.DEPRECATED_RETURN_SETTING,
1474+
)
1475+
1476+
else:
1477+
1478+
def visit_Return(self, node: Statement) -> None: # noqa: N802
1479+
def _handler() -> None:
1480+
self._analyze_statement_variables(node)
1481+
1482+
if self._end_block_handlers is not None:
1483+
self._end_block_handlers.append(_handler)
14151484

14161485
def _check_import_name(self, value: Optional[str], node: ast.AST, type: str) -> None:
14171486
if not value:
@@ -1540,7 +1609,11 @@ def _find_variable(self, name: str) -> Optional[VariableDefinition]:
15401609
default_value=default_value or None,
15411610
)
15421611

1543-
vars = self._suite_variables if self._in_setting else self._variables
1612+
vars = (
1613+
self._block_variables
1614+
if self._block_variables and self._in_block_setting
1615+
else self._suite_variables if self._in_setting else self._variables
1616+
)
15441617

15451618
matcher = VariableMatcher(name)
15461619

0 commit comments

Comments
 (0)