8
8
from io import StringIO
9
9
from pathlib import Path
10
10
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
12
12
13
13
from robot .errors import VariableError
14
14
from robot .parsing .lexer .tokens import Token
43
43
Range ,
44
44
)
45
45
from robotcode .core .uri import Uri
46
+ from robotcode .core .utils .logging import LoggingDescriptor
46
47
47
48
from ..utils import get_robot_version
48
49
from ..utils .ast import (
@@ -94,6 +95,9 @@ class AnalyzerResult:
94
95
95
96
96
97
class NamespaceAnalyzer (Visitor ):
98
+
99
+ _logger = LoggingDescriptor ()
100
+
97
101
def __init__ (
98
102
self ,
99
103
model : ast .AST ,
@@ -126,8 +130,11 @@ def __init__(
126
130
self ._overridden_variables : Dict [VariableDefinition , VariableDefinition ] = {}
127
131
128
132
self ._in_setting = False
133
+ self ._in_block_setting = False
129
134
130
135
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
131
138
132
139
def run (self ) -> AnalyzerResult :
133
140
self ._diagnostics = []
@@ -146,10 +153,11 @@ def run(self) -> AnalyzerResult:
146
153
except BaseException as e :
147
154
self ._append_diagnostics (
148
155
range_from_node (self ._model ),
149
- message = f"Fatal: can't analyze namespace '{ e } ') " ,
156
+ message = f"Fatal: can't analyze namespace '{ e } '. " ,
150
157
severity = DiagnosticSeverity .ERROR ,
151
158
code = type (e ).__qualname__ ,
152
159
)
160
+ self ._logger .exception (e )
153
161
154
162
return AnalyzerResult (
155
163
self ._diagnostics ,
@@ -379,6 +387,15 @@ def _visit_settings_statement(
379
387
finally :
380
388
self ._in_setting = False
381
389
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
+
382
399
def _analyze_token_expression_variables (
383
400
self , token : Token , severity : DiagnosticSeverity = DiagnosticSeverity .ERROR
384
401
) -> None :
@@ -896,7 +913,29 @@ def visit_Fixture(self, node: Fixture) -> None: # noqa: N802
896
913
897
914
if keyword_token is not None and keyword_token .value and keyword_token .value .upper () not in ("" , "NONE" ):
898
915
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 )
900
939
901
940
self ._analyze_keyword_call (
902
941
node ,
@@ -984,9 +1023,15 @@ def visit_TestCase(self, node: TestCase) -> None: # noqa: N802
984
1023
self ._current_testcase_or_keyword_name = node .name
985
1024
old_variables = self ._variables
986
1025
self ._variables = self ._variables .copy ()
1026
+ self ._end_block_handlers = []
987
1027
try :
988
1028
self .generic_visit (node )
1029
+
1030
+ for handler in self ._end_block_handlers :
1031
+ handler ()
1032
+
989
1033
finally :
1034
+ self ._end_block_handlers = None
990
1035
self ._variables = old_variables
991
1036
self ._current_testcase_or_keyword_name = None
992
1037
self ._template = None
@@ -1029,13 +1074,21 @@ def visit_Keyword(self, node: Keyword) -> None: # noqa: N802
1029
1074
self ._current_testcase_or_keyword_name = node .name
1030
1075
old_variables = self ._variables
1031
1076
self ._variables = self ._variables .copy ()
1077
+ self ._end_block_handlers = []
1032
1078
try :
1033
1079
arguments = next ((v for v in node .body if isinstance (v , Arguments )), None )
1034
1080
if arguments is not None :
1035
1081
self ._visit_Arguments (arguments )
1082
+ self ._block_variables = self ._variables .copy ()
1036
1083
1037
1084
self .generic_visit (node )
1085
+
1086
+ for handler in self ._end_block_handlers :
1087
+ handler ()
1088
+
1038
1089
finally :
1090
+ self ._end_block_handlers = None
1091
+ self ._block_variables = None
1039
1092
self ._variables = old_variables
1040
1093
self ._current_testcase_or_keyword_name = None
1041
1094
self ._current_keyword_doc = None
@@ -1351,7 +1404,7 @@ def visit_DocumentationOrMetadata(self, node: Statement) -> None: # noqa: N802
1351
1404
self ._visit_settings_statement (node , DiagnosticSeverity .HINT )
1352
1405
1353
1406
def visit_Timeout (self , node : Statement ) -> None : # noqa: N802
1354
- self ._analyze_statement_variables (node , DiagnosticSeverity . HINT )
1407
+ self ._visit_block_settings_statement (node )
1355
1408
1356
1409
def visit_SingleValue (self , node : Statement ) -> None : # noqa: N802
1357
1410
self ._visit_settings_statement (node , DiagnosticSeverity .HINT )
@@ -1399,19 +1452,35 @@ def visit_SectionHeader(self, node: Statement) -> None: # noqa: N802
1399
1452
code = Error .DEPRECATED_HEADER ,
1400
1453
)
1401
1454
1402
- def visit_ReturnSetting (self , node : Statement ) -> None : # noqa: N802
1403
- self ._analyze_statement_variables (node )
1455
+ if get_robot_version () >= (7 , 0 ):
1404
1456
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 )
1415
1484
1416
1485
def _check_import_name (self , value : Optional [str ], node : ast .AST , type : str ) -> None :
1417
1486
if not value :
@@ -1540,7 +1609,11 @@ def _find_variable(self, name: str) -> Optional[VariableDefinition]:
1540
1609
default_value = default_value or None ,
1541
1610
)
1542
1611
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
+ )
1544
1617
1545
1618
matcher = VariableMatcher (name )
1546
1619
0 commit comments