robotcode-robot 1.2.0__py3-none-any.whl → 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- robotcode/robot/__version__.py +1 -1
- robotcode/robot/config/model.py +2 -2
- robotcode/robot/diagnostics/entities.py +18 -45
- robotcode/robot/diagnostics/errors.py +1 -0
- robotcode/robot/diagnostics/imports_manager.py +3 -3
- robotcode/robot/diagnostics/keyword_finder.py +43 -12
- robotcode/robot/diagnostics/library_doc.py +11 -5
- robotcode/robot/diagnostics/model_helper.py +4 -4
- robotcode/robot/diagnostics/namespace.py +227 -196
- robotcode/robot/diagnostics/namespace_analyzer.py +194 -136
- robotcode/robot/utils/__init__.py +4 -7
- robotcode/robot/utils/ast.py +21 -19
- robotcode/robot/utils/variables.py +159 -4
- {robotcode_robot-1.2.0.dist-info → robotcode_robot-1.3.0.dist-info}/METADATA +2 -2
- robotcode_robot-1.3.0.dist-info/RECORD +32 -0
- robotcode_robot-1.2.0.dist-info/RECORD +0 -32
- {robotcode_robot-1.2.0.dist-info → robotcode_robot-1.3.0.dist-info}/WHEEL +0 -0
- {robotcode_robot-1.2.0.dist-info → robotcode_robot-1.3.0.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
import ast
|
2
2
|
import functools
|
3
|
-
import itertools
|
4
3
|
import os
|
4
|
+
import re
|
5
5
|
import token as python_token
|
6
6
|
from collections import defaultdict
|
7
7
|
from concurrent.futures import CancelledError
|
@@ -47,6 +47,7 @@ from robotcode.core.utils.logging import LoggingDescriptor
|
|
47
47
|
|
48
48
|
from ..utils import get_robot_version
|
49
49
|
from ..utils.ast import (
|
50
|
+
get_first_variable_token,
|
50
51
|
is_not_variable_token,
|
51
52
|
range_from_node,
|
52
53
|
range_from_node_or_token,
|
@@ -54,13 +55,19 @@ from ..utils.ast import (
|
|
54
55
|
strip_variable_token,
|
55
56
|
tokenize_variables,
|
56
57
|
)
|
57
|
-
from ..utils.variables import
|
58
|
+
from ..utils.variables import (
|
59
|
+
InvalidVariableError,
|
60
|
+
VariableMatcher,
|
61
|
+
contains_variable,
|
62
|
+
search_variable,
|
63
|
+
split_from_equals,
|
64
|
+
)
|
58
65
|
from ..utils.visitor import Visitor
|
59
66
|
from .entities import (
|
60
67
|
ArgumentDefinition,
|
68
|
+
EmbeddedArgumentDefinition,
|
61
69
|
EnvironmentVariableDefinition,
|
62
70
|
GlobalVariableDefinition,
|
63
|
-
InvalidVariableError,
|
64
71
|
LibraryEntry,
|
65
72
|
LocalVariableDefinition,
|
66
73
|
TagDefinition,
|
@@ -68,7 +75,6 @@ from .entities import (
|
|
68
75
|
TestVariableDefinition,
|
69
76
|
VariableDefinition,
|
70
77
|
VariableDefinitionType,
|
71
|
-
VariableMatcher,
|
72
78
|
VariableNotFoundDefinition,
|
73
79
|
)
|
74
80
|
from .errors import DIAGNOSTICS_SOURCE_NAME, Error
|
@@ -186,19 +192,19 @@ class NamespaceAnalyzer(Visitor):
|
|
186
192
|
if name_token is None:
|
187
193
|
return
|
188
194
|
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
195
|
+
if name_token.value is not None:
|
196
|
+
matcher = search_variable(
|
197
|
+
name_token.value[:-1].rstrip() if name_token.value.endswith("=") else name_token.value,
|
198
|
+
parse_type=True,
|
199
|
+
ignore_errors=True,
|
200
|
+
)
|
201
|
+
if not matcher.is_assign(allow_assign_mark=True, allow_nested=True) or matcher.name is None:
|
194
202
|
return
|
195
203
|
|
196
|
-
|
197
|
-
|
204
|
+
name = matcher.name
|
205
|
+
|
206
|
+
stripped_name_token = strip_variable_token(name_token, matcher=matcher, parse_type=True)
|
198
207
|
|
199
|
-
stripped_name_token = strip_variable_token(
|
200
|
-
Token(name_token.type, name, name_token.lineno, name_token.col_offset, name_token.error)
|
201
|
-
)
|
202
208
|
r = range_from_token(stripped_name_token)
|
203
209
|
|
204
210
|
existing_var = self._find_variable(name)
|
@@ -216,14 +222,15 @@ class NamespaceAnalyzer(Visitor):
|
|
216
222
|
var_def = VariableDefinition(
|
217
223
|
name=name,
|
218
224
|
name_token=stripped_name_token,
|
219
|
-
line_no=
|
220
|
-
col_offset=
|
221
|
-
end_line_no=
|
222
|
-
end_col_offset=
|
225
|
+
line_no=stripped_name_token.lineno,
|
226
|
+
col_offset=stripped_name_token.col_offset,
|
227
|
+
end_line_no=stripped_name_token.lineno,
|
228
|
+
end_col_offset=stripped_name_token.end_col_offset,
|
223
229
|
source=self._namespace.source,
|
224
230
|
has_value=has_value,
|
225
231
|
resolvable=True,
|
226
232
|
value=value,
|
233
|
+
value_type=matcher.type,
|
227
234
|
)
|
228
235
|
|
229
236
|
add_to_references = True
|
@@ -317,26 +324,25 @@ class NamespaceAnalyzer(Visitor):
|
|
317
324
|
|
318
325
|
if get_robot_version() >= (7, 0):
|
319
326
|
|
320
|
-
def visit_Var(self, node:
|
327
|
+
def visit_Var(self, node: Var) -> None: # noqa: N802
|
321
328
|
self._analyze_statement_variables(node)
|
322
329
|
|
323
|
-
|
324
|
-
if
|
330
|
+
name_token = node.get_token(Token.VARIABLE)
|
331
|
+
if name_token is None:
|
325
332
|
return
|
326
333
|
|
327
334
|
try:
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
335
|
+
matcher = search_variable(
|
336
|
+
name_token.value[:-1].rstrip() if name_token.value.endswith("=") else name_token.value,
|
337
|
+
parse_type=True,
|
338
|
+
ignore_errors=True,
|
339
|
+
)
|
340
|
+
if not matcher.is_assign(allow_assign_mark=True) or matcher.name is None:
|
333
341
|
return
|
334
342
|
|
335
|
-
|
336
|
-
Token(variable.type, var_name, variable.lineno, variable.col_offset, variable.error)
|
337
|
-
)
|
343
|
+
stripped_name_token = strip_variable_token(name_token, matcher=matcher, parse_type=True)
|
338
344
|
|
339
|
-
scope =
|
345
|
+
scope = node.scope
|
340
346
|
if scope:
|
341
347
|
scope = scope.upper()
|
342
348
|
|
@@ -350,13 +356,14 @@ class NamespaceAnalyzer(Visitor):
|
|
350
356
|
var_type = LocalVariableDefinition
|
351
357
|
|
352
358
|
var = var_type(
|
353
|
-
name=
|
354
|
-
name_token=strip_variable_token(
|
355
|
-
line_no=
|
356
|
-
col_offset=
|
357
|
-
end_line_no=
|
358
|
-
end_col_offset=
|
359
|
+
name=matcher.name,
|
360
|
+
name_token=strip_variable_token(stripped_name_token),
|
361
|
+
line_no=stripped_name_token.lineno,
|
362
|
+
col_offset=stripped_name_token.col_offset,
|
363
|
+
end_line_no=stripped_name_token.lineno,
|
364
|
+
end_col_offset=stripped_name_token.end_col_offset,
|
359
365
|
source=self._namespace.source,
|
366
|
+
value_type=matcher.type,
|
360
367
|
)
|
361
368
|
|
362
369
|
if var.matcher not in self._variables:
|
@@ -366,7 +373,7 @@ class NamespaceAnalyzer(Visitor):
|
|
366
373
|
existing_var = self._variables[var.matcher]
|
367
374
|
|
368
375
|
location = Location(
|
369
|
-
self._namespace.document_uri, range_from_token(strip_variable_token(
|
376
|
+
self._namespace.document_uri, range_from_token(strip_variable_token(stripped_name_token))
|
370
377
|
)
|
371
378
|
self._variable_references[existing_var].add(location)
|
372
379
|
if existing_var in self._overridden_variables:
|
@@ -413,7 +420,7 @@ class NamespaceAnalyzer(Visitor):
|
|
413
420
|
self, token: Token, severity: DiagnosticSeverity = DiagnosticSeverity.ERROR
|
414
421
|
) -> None:
|
415
422
|
for var_token, var in self._iter_expression_variables_from_token(token):
|
416
|
-
self._handle_find_variable_result(
|
423
|
+
self._handle_find_variable_result(var_token, var, severity)
|
417
424
|
|
418
425
|
def _append_error_from_node(
|
419
426
|
self,
|
@@ -479,11 +486,10 @@ class NamespaceAnalyzer(Visitor):
|
|
479
486
|
|
480
487
|
def _analyze_token_variables(self, token: Token, severity: DiagnosticSeverity = DiagnosticSeverity.ERROR) -> None:
|
481
488
|
for var_token, var in self._iter_variables_from_token(token):
|
482
|
-
self._handle_find_variable_result(
|
489
|
+
self._handle_find_variable_result(var_token, var, severity)
|
483
490
|
|
484
491
|
def _handle_find_variable_result(
|
485
492
|
self,
|
486
|
-
token: Token,
|
487
493
|
var_token: Token,
|
488
494
|
var: VariableDefinition,
|
489
495
|
severity: DiagnosticSeverity = DiagnosticSeverity.ERROR,
|
@@ -664,6 +670,9 @@ class NamespaceAnalyzer(Visitor):
|
|
664
670
|
else:
|
665
671
|
self._keyword_references[result].add(Location(self._namespace.document_uri, kw_range))
|
666
672
|
|
673
|
+
if result.is_embedded:
|
674
|
+
self._analyze_token_variables(keyword_token)
|
675
|
+
|
667
676
|
if result.errors:
|
668
677
|
self._append_diagnostics(
|
669
678
|
range=kw_range,
|
@@ -1016,7 +1025,6 @@ class NamespaceAnalyzer(Visitor):
|
|
1016
1025
|
)
|
1017
1026
|
return
|
1018
1027
|
|
1019
|
-
self._analyze_token_variables(keyword_token)
|
1020
1028
|
self._analyze_statement_variables(node)
|
1021
1029
|
|
1022
1030
|
self._analyze_keyword_call(
|
@@ -1129,6 +1137,8 @@ class NamespaceAnalyzer(Visitor):
|
|
1129
1137
|
self._current_testcase_or_keyword_name = None
|
1130
1138
|
self._current_keyword_doc = None
|
1131
1139
|
|
1140
|
+
EMBEDDED_ARGUMENTS_MATCHER = re.compile("([^:]+): ([^:]+)(:(.*))?")
|
1141
|
+
|
1132
1142
|
def visit_KeywordName(self, node: KeywordName) -> None: # noqa: N802
|
1133
1143
|
name_token = node.get_token(Token.KEYWORD_NAME)
|
1134
1144
|
|
@@ -1138,14 +1148,28 @@ class NamespaceAnalyzer(Visitor):
|
|
1138
1148
|
tokenize_variables(name_token, identifiers="$", ignore_errors=True),
|
1139
1149
|
):
|
1140
1150
|
if variable_token.value:
|
1141
|
-
|
1142
|
-
if
|
1151
|
+
matcher = search_variable(variable_token.value, "$", ignore_errors=True)
|
1152
|
+
if matcher.base is None:
|
1143
1153
|
continue
|
1144
|
-
|
1145
|
-
|
1154
|
+
if ":" not in matcher.base:
|
1155
|
+
name = matcher.base
|
1156
|
+
pattern = None
|
1157
|
+
type = None
|
1158
|
+
elif get_robot_version() >= (7, 3):
|
1159
|
+
re_match = self.EMBEDDED_ARGUMENTS_MATCHER.fullmatch(matcher.base)
|
1160
|
+
if re_match:
|
1161
|
+
name, type, _, pattern = re_match.groups()
|
1162
|
+
else:
|
1163
|
+
name, pattern = matcher.base.split(":", 1)
|
1164
|
+
type = None
|
1165
|
+
else:
|
1166
|
+
name, pattern = matcher.base.split(":", 1)
|
1167
|
+
type = None
|
1168
|
+
|
1169
|
+
full_name = f"{matcher.identifier}{{{name}}}"
|
1146
1170
|
var_token = strip_variable_token(variable_token)
|
1147
1171
|
var_token.value = name
|
1148
|
-
arg_def =
|
1172
|
+
arg_def = EmbeddedArgumentDefinition(
|
1149
1173
|
name=full_name,
|
1150
1174
|
name_token=var_token,
|
1151
1175
|
line_no=variable_token.lineno,
|
@@ -1154,32 +1178,19 @@ class NamespaceAnalyzer(Visitor):
|
|
1154
1178
|
end_col_offset=variable_token.end_col_offset,
|
1155
1179
|
source=self._namespace.source,
|
1156
1180
|
keyword_doc=self._current_keyword_doc,
|
1181
|
+
value_type=type,
|
1182
|
+
pattern=pattern,
|
1157
1183
|
)
|
1158
1184
|
|
1159
1185
|
self._variables[arg_def.matcher] = arg_def
|
1160
1186
|
self._variable_references[arg_def] = set()
|
1161
1187
|
|
1162
|
-
def _get_variable_token(self, token: Token) -> Optional[Token]:
|
1163
|
-
return next(
|
1164
|
-
(
|
1165
|
-
v
|
1166
|
-
for v in itertools.dropwhile(
|
1167
|
-
lambda t: t.type in Token.NON_DATA_TOKENS,
|
1168
|
-
tokenize_variables(token, ignore_errors=True, extra_types={Token.VARIABLE}),
|
1169
|
-
)
|
1170
|
-
if v.type == Token.VARIABLE
|
1171
|
-
),
|
1172
|
-
None,
|
1173
|
-
)
|
1174
|
-
|
1175
1188
|
def _visit_Arguments(self, node: Statement) -> None: # noqa: N802
|
1176
1189
|
args: Dict[VariableMatcher, VariableDefinition] = {}
|
1177
1190
|
|
1178
|
-
|
1179
|
-
|
1180
|
-
for argument_token in arguments:
|
1191
|
+
for argument_token in node.get_tokens(Token.ARGUMENT):
|
1181
1192
|
try:
|
1182
|
-
argument =
|
1193
|
+
argument = get_first_variable_token(argument_token)
|
1183
1194
|
|
1184
1195
|
if argument is not None and argument.value != "@{}":
|
1185
1196
|
if len(argument_token.value) > len(argument.value):
|
@@ -1193,18 +1204,23 @@ class NamespaceAnalyzer(Visitor):
|
|
1193
1204
|
)
|
1194
1205
|
)
|
1195
1206
|
|
1196
|
-
matcher = VariableMatcher(argument.value)
|
1207
|
+
matcher = VariableMatcher(argument.value, parse_type=True, ignore_errors=True)
|
1208
|
+
if not matcher.is_variable() or matcher.name is None:
|
1209
|
+
continue
|
1210
|
+
|
1211
|
+
stripped_argument_token = strip_variable_token(argument, parse_type=True, matcher=matcher)
|
1197
1212
|
|
1198
1213
|
if matcher not in args:
|
1199
1214
|
arg_def = ArgumentDefinition(
|
1200
|
-
name=
|
1201
|
-
name_token=
|
1202
|
-
line_no=
|
1203
|
-
col_offset=
|
1204
|
-
end_line_no=
|
1205
|
-
end_col_offset=
|
1215
|
+
name=matcher.name,
|
1216
|
+
name_token=stripped_argument_token,
|
1217
|
+
line_no=stripped_argument_token.lineno,
|
1218
|
+
col_offset=stripped_argument_token.col_offset,
|
1219
|
+
end_line_no=stripped_argument_token.lineno,
|
1220
|
+
end_col_offset=stripped_argument_token.end_col_offset,
|
1206
1221
|
source=self._namespace.source,
|
1207
1222
|
keyword_doc=self._current_keyword_doc,
|
1223
|
+
value_type=matcher.type,
|
1208
1224
|
)
|
1209
1225
|
|
1210
1226
|
args[matcher] = arg_def
|
@@ -1214,22 +1230,57 @@ class NamespaceAnalyzer(Visitor):
|
|
1214
1230
|
self._variable_references[arg_def] = set()
|
1215
1231
|
else:
|
1216
1232
|
self._variable_references[args[matcher]].add(
|
1217
|
-
Location(
|
1218
|
-
self._namespace.document_uri,
|
1219
|
-
range_from_token(strip_variable_token(argument)),
|
1220
|
-
)
|
1233
|
+
Location(self._namespace.document_uri, range_from_token(stripped_argument_token))
|
1221
1234
|
)
|
1222
1235
|
|
1223
1236
|
except (VariableError, InvalidVariableError):
|
1224
1237
|
pass
|
1225
1238
|
|
1226
1239
|
def _analyze_assign_statement(self, node: Statement) -> None:
|
1240
|
+
token_with_assign_mark: Optional[Token] = None
|
1227
1241
|
for assign_token in node.get_tokens(Token.ASSIGN):
|
1228
|
-
variable_token = self._get_variable_token(assign_token)
|
1229
|
-
|
1230
1242
|
try:
|
1231
|
-
if
|
1232
|
-
|
1243
|
+
if token_with_assign_mark is not None:
|
1244
|
+
r = range_from_token(token_with_assign_mark)
|
1245
|
+
r.start.character = r.end.character - 1
|
1246
|
+
self._append_diagnostics(
|
1247
|
+
range=r,
|
1248
|
+
message="Assign mark '=' can be used only with the last variable.",
|
1249
|
+
severity=DiagnosticSeverity.ERROR,
|
1250
|
+
code=Error.ASSIGN_MARK_ALLOWED_ONLY_ON_LAST_VAR,
|
1251
|
+
)
|
1252
|
+
|
1253
|
+
if assign_token.value.endswith("="):
|
1254
|
+
token_with_assign_mark = assign_token
|
1255
|
+
|
1256
|
+
matcher = search_variable(
|
1257
|
+
assign_token.value[:-1].rstrip() if assign_token.value.endswith("=") else assign_token.value,
|
1258
|
+
parse_type=True,
|
1259
|
+
ignore_errors=True,
|
1260
|
+
)
|
1261
|
+
|
1262
|
+
if not matcher.is_assign(allow_assign_mark=True) or matcher.name is None:
|
1263
|
+
return
|
1264
|
+
|
1265
|
+
stripped_name_token = strip_variable_token(assign_token, matcher=matcher, parse_type=True)
|
1266
|
+
|
1267
|
+
if matcher.items:
|
1268
|
+
existing_var = self._find_variable(matcher.name)
|
1269
|
+
if existing_var is None:
|
1270
|
+
self._handle_find_variable_result(
|
1271
|
+
stripped_name_token,
|
1272
|
+
VariableNotFoundDefinition(
|
1273
|
+
stripped_name_token.lineno,
|
1274
|
+
stripped_name_token.col_offset,
|
1275
|
+
stripped_name_token.lineno,
|
1276
|
+
stripped_name_token.end_col_offset,
|
1277
|
+
self._namespace.source,
|
1278
|
+
matcher.name,
|
1279
|
+
stripped_name_token,
|
1280
|
+
),
|
1281
|
+
)
|
1282
|
+
return
|
1283
|
+
else:
|
1233
1284
|
existing_var = next(
|
1234
1285
|
(
|
1235
1286
|
v
|
@@ -1239,26 +1290,28 @@ class NamespaceAnalyzer(Visitor):
|
|
1239
1290
|
),
|
1240
1291
|
None,
|
1241
1292
|
)
|
1242
|
-
|
1243
|
-
|
1244
|
-
|
1245
|
-
|
1246
|
-
|
1247
|
-
|
1248
|
-
|
1249
|
-
|
1250
|
-
|
1251
|
-
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1257
|
-
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1293
|
+
|
1294
|
+
if existing_var is None:
|
1295
|
+
var_def = LocalVariableDefinition(
|
1296
|
+
name=matcher.name,
|
1297
|
+
name_token=stripped_name_token,
|
1298
|
+
line_no=stripped_name_token.lineno,
|
1299
|
+
col_offset=stripped_name_token.col_offset,
|
1300
|
+
end_line_no=stripped_name_token.lineno,
|
1301
|
+
end_col_offset=stripped_name_token.end_col_offset,
|
1302
|
+
source=self._namespace.source,
|
1303
|
+
value_type=matcher.type,
|
1304
|
+
)
|
1305
|
+
self._variables[matcher] = var_def
|
1306
|
+
self._variable_references[var_def] = set()
|
1307
|
+
self._local_variable_assignments[var_def].add(var_def.range)
|
1308
|
+
else:
|
1309
|
+
self._variable_references[existing_var].add(
|
1310
|
+
Location(
|
1311
|
+
self._namespace.document_uri,
|
1312
|
+
range_from_token(stripped_name_token),
|
1261
1313
|
)
|
1314
|
+
)
|
1262
1315
|
|
1263
1316
|
except (VariableError, InvalidVariableError):
|
1264
1317
|
pass
|
@@ -1271,24 +1324,27 @@ class NamespaceAnalyzer(Visitor):
|
|
1271
1324
|
def visit_ForHeader(self, node: Statement) -> None: # noqa: N802
|
1272
1325
|
self._analyze_statement_variables(node)
|
1273
1326
|
|
1274
|
-
|
1275
|
-
|
1276
|
-
|
1277
|
-
if
|
1278
|
-
existing_var = self._find_variable(
|
1327
|
+
for variable_token in node.get_tokens(Token.VARIABLE):
|
1328
|
+
matcher = search_variable(variable_token.value, ignore_errors=True, parse_type=True)
|
1329
|
+
|
1330
|
+
if matcher.name is not None and matcher.is_scalar_assign():
|
1331
|
+
existing_var = self._find_variable(matcher.name)
|
1332
|
+
|
1333
|
+
stripped_variable_token = strip_variable_token(variable_token, parse_type=True, matcher=matcher)
|
1279
1334
|
|
1280
1335
|
if existing_var is None or existing_var.type not in [
|
1281
1336
|
VariableDefinitionType.ARGUMENT,
|
1282
1337
|
VariableDefinitionType.LOCAL_VARIABLE,
|
1283
1338
|
]:
|
1284
1339
|
var_def = LocalVariableDefinition(
|
1285
|
-
name=
|
1286
|
-
name_token=
|
1287
|
-
line_no=
|
1288
|
-
col_offset=
|
1289
|
-
end_line_no=
|
1290
|
-
end_col_offset=
|
1340
|
+
name=matcher.name,
|
1341
|
+
name_token=stripped_variable_token,
|
1342
|
+
line_no=stripped_variable_token.lineno,
|
1343
|
+
col_offset=stripped_variable_token.col_offset,
|
1344
|
+
end_line_no=stripped_variable_token.lineno,
|
1345
|
+
end_col_offset=stripped_variable_token.end_col_offset,
|
1291
1346
|
source=self._namespace.source,
|
1347
|
+
value_type=matcher.type,
|
1292
1348
|
)
|
1293
1349
|
self._variables[var_def.matcher] = var_def
|
1294
1350
|
self._variable_references[var_def] = set()
|
@@ -1300,7 +1356,7 @@ class NamespaceAnalyzer(Visitor):
|
|
1300
1356
|
self._variable_references[existing_var].add(
|
1301
1357
|
Location(
|
1302
1358
|
self._namespace.document_uri,
|
1303
|
-
range_from_token(
|
1359
|
+
range_from_token(stripped_variable_token),
|
1304
1360
|
)
|
1305
1361
|
)
|
1306
1362
|
|
@@ -1310,31 +1366,33 @@ class NamespaceAnalyzer(Visitor):
|
|
1310
1366
|
|
1311
1367
|
variable_token = node.get_token(Token.VARIABLE)
|
1312
1368
|
|
1313
|
-
if variable_token is not None
|
1369
|
+
if variable_token is not None:
|
1314
1370
|
try:
|
1315
|
-
|
1316
|
-
|
1317
|
-
|
1318
|
-
|
1319
|
-
|
1320
|
-
|
1321
|
-
|
1322
|
-
|
1323
|
-
|
1324
|
-
|
1325
|
-
|
1326
|
-
)
|
1327
|
-
|
1328
|
-
)
|
1329
|
-
|
1330
|
-
|
1331
|
-
|
1332
|
-
|
1333
|
-
|
1334
|
-
|
1335
|
-
|
1336
|
-
|
1337
|
-
|
1371
|
+
matcher = search_variable(variable_token.value, ignore_errors=True)
|
1372
|
+
if not matcher.is_scalar_assign():
|
1373
|
+
return
|
1374
|
+
|
1375
|
+
if (
|
1376
|
+
next(
|
1377
|
+
(
|
1378
|
+
k
|
1379
|
+
for k, v in self._variables.items()
|
1380
|
+
if k == matcher
|
1381
|
+
and v.type in [VariableDefinitionType.ARGUMENT, VariableDefinitionType.LOCAL_VARIABLE]
|
1382
|
+
),
|
1383
|
+
None,
|
1384
|
+
)
|
1385
|
+
is None
|
1386
|
+
):
|
1387
|
+
self._variables[matcher] = LocalVariableDefinition(
|
1388
|
+
name=variable_token.value,
|
1389
|
+
name_token=strip_variable_token(variable_token),
|
1390
|
+
line_no=variable_token.lineno,
|
1391
|
+
col_offset=variable_token.col_offset,
|
1392
|
+
end_line_no=variable_token.lineno,
|
1393
|
+
end_col_offset=variable_token.end_col_offset,
|
1394
|
+
source=self._namespace.source,
|
1395
|
+
)
|
1338
1396
|
|
1339
1397
|
except (VariableError, InvalidVariableError):
|
1340
1398
|
pass
|
@@ -1747,9 +1805,9 @@ class NamespaceAnalyzer(Visitor):
|
|
1747
1805
|
and var_token.value[1:2] == "{"
|
1748
1806
|
and var_token.value[-1:] == "}"
|
1749
1807
|
):
|
1750
|
-
|
1751
|
-
if
|
1752
|
-
base_name, _ =
|
1808
|
+
extended_match = ModelHelper.MATCH_EXTENDED.match(name[2:-1])
|
1809
|
+
if extended_match is not None:
|
1810
|
+
base_name, _ = extended_match.groups()
|
1753
1811
|
name = f"{name[0]}{{{base_name.strip()}}}"
|
1754
1812
|
var = self._find_variable(name)
|
1755
1813
|
sub_sub_token = Token(
|
@@ -1,17 +1,14 @@
|
|
1
|
-
|
1
|
+
import functools
|
2
2
|
|
3
3
|
import robot.version
|
4
4
|
from robotcode.core.utils.version import Version, create_version_from_str
|
5
5
|
|
6
|
-
_robot_version: Optional[Version] = None
|
7
|
-
|
8
6
|
|
7
|
+
@functools.lru_cache(maxsize=1)
|
9
8
|
def get_robot_version() -> Version:
|
10
|
-
|
11
|
-
if _robot_version is None:
|
12
|
-
_robot_version = create_version_from_str(robot.version.get_version())
|
13
|
-
return _robot_version
|
9
|
+
return create_version_from_str(robot.version.get_version())
|
14
10
|
|
15
11
|
|
12
|
+
@functools.lru_cache(maxsize=1)
|
16
13
|
def get_robot_version_str() -> str:
|
17
14
|
return str(robot.version.get_version())
|
robotcode/robot/utils/ast.py
CHANGED
@@ -11,6 +11,7 @@ from robot.parsing.lexer.tokens import Token
|
|
11
11
|
from robot.parsing.model.statements import EmptyLine, Statement
|
12
12
|
from robotcode.core.lsp.types import Position, Range
|
13
13
|
|
14
|
+
from ..utils.variables import VariableMatcher, search_variable
|
14
15
|
from . import get_robot_version
|
15
16
|
from .visitor import Visitor
|
16
17
|
|
@@ -325,34 +326,35 @@ def iter_over_keyword_names_and_owners(
|
|
325
326
|
yield ".".join(tokens[:i]), ".".join(tokens[i:])
|
326
327
|
|
327
328
|
|
328
|
-
def strip_variable_token(
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
329
|
+
def strip_variable_token(
|
330
|
+
token: Token, identifiers: str = "$@&%*", parse_type: bool = False, matcher: Optional[VariableMatcher] = None
|
331
|
+
) -> Token:
|
332
|
+
if token.type in [Token.VARIABLE, Token.ASSIGN]:
|
333
|
+
if matcher is None:
|
334
|
+
matcher = search_variable(token.value, identifiers, parse_type=parse_type, ignore_errors=True)
|
335
|
+
|
336
|
+
if matcher.is_variable():
|
337
|
+
value = matcher.base
|
338
|
+
|
339
|
+
stripped_value = value.lstrip()
|
340
|
+
stripped_offset = len(value) - len(stripped_value)
|
341
|
+
return Token(
|
342
|
+
token.type,
|
343
|
+
matcher.base.strip(),
|
344
|
+
token.lineno,
|
345
|
+
token.col_offset + 2 + stripped_offset,
|
346
|
+
)
|
345
347
|
|
346
348
|
return token
|
347
349
|
|
348
350
|
|
349
|
-
def
|
351
|
+
def get_first_variable_token(token: Token, extra_types: Optional[Set[str]] = None) -> Optional[Token]:
|
350
352
|
return next(
|
351
353
|
(
|
352
354
|
v
|
353
355
|
for v in itertools.dropwhile(
|
354
356
|
lambda t: t.type in Token.NON_DATA_TOKENS,
|
355
|
-
tokenize_variables(token, ignore_errors=True),
|
357
|
+
tokenize_variables(token, ignore_errors=True, extra_types=extra_types),
|
356
358
|
)
|
357
359
|
if v.type == Token.VARIABLE
|
358
360
|
),
|