robotcode-robot 1.3.0.dev4__py3-none-any.whl → 1.3.0.dev6__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.
@@ -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,
@@ -58,14 +59,13 @@ from ..utils.variables import (
58
59
  InvalidVariableError,
59
60
  VariableMatcher,
60
61
  contains_variable,
61
- is_scalar_assign,
62
- is_variable,
63
62
  search_variable,
64
63
  split_from_equals,
65
64
  )
66
65
  from ..utils.visitor import Visitor
67
66
  from .entities import (
68
67
  ArgumentDefinition,
68
+ EmbeddedArgumentDefinition,
69
69
  EnvironmentVariableDefinition,
70
70
  GlobalVariableDefinition,
71
71
  LibraryEntry,
@@ -195,14 +195,15 @@ class NamespaceAnalyzer(Visitor):
195
195
  if name_token.value is not None:
196
196
  matcher = search_variable(
197
197
  name_token.value[:-1].rstrip() if name_token.value.endswith("=") else name_token.value,
198
+ parse_type=True,
198
199
  ignore_errors=True,
199
200
  )
200
- if not matcher.is_assign(allow_assign_mark=True) or matcher.name is None:
201
+ if not matcher.is_assign(allow_assign_mark=True, allow_nested=True) or matcher.name is None:
201
202
  return
202
203
 
203
204
  name = matcher.name
204
205
 
205
- stripped_name_token = strip_variable_token(name_token, matcher=matcher)
206
+ stripped_name_token = strip_variable_token(name_token, matcher=matcher, parse_type=True)
206
207
 
207
208
  r = range_from_token(stripped_name_token)
208
209
 
@@ -221,14 +222,15 @@ class NamespaceAnalyzer(Visitor):
221
222
  var_def = VariableDefinition(
222
223
  name=name,
223
224
  name_token=stripped_name_token,
224
- line_no=node.lineno,
225
- col_offset=node.col_offset,
226
- end_line_no=node.lineno,
227
- end_col_offset=node.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,
228
229
  source=self._namespace.source,
229
230
  has_value=has_value,
230
231
  resolvable=True,
231
232
  value=value,
233
+ value_type=matcher.type,
232
234
  )
233
235
 
234
236
  add_to_references = True
@@ -322,26 +324,25 @@ class NamespaceAnalyzer(Visitor):
322
324
 
323
325
  if get_robot_version() >= (7, 0):
324
326
 
325
- def visit_Var(self, node: Statement) -> None: # noqa: N802
327
+ def visit_Var(self, node: Var) -> None: # noqa: N802
326
328
  self._analyze_statement_variables(node)
327
329
 
328
- variable = node.get_token(Token.VARIABLE)
329
- if variable is None:
330
+ name_token = node.get_token(Token.VARIABLE)
331
+ if name_token is None:
330
332
  return
331
333
 
332
334
  try:
333
- var_name = variable.value
334
- if var_name.endswith("="):
335
- var_name = var_name[:-1].rstrip()
336
-
337
- if not is_variable(var_name):
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:
338
341
  return
339
342
 
340
- stripped_variable = strip_variable_token(
341
- Token(variable.type, var_name, variable.lineno, variable.col_offset, variable.error)
342
- )
343
+ stripped_name_token = strip_variable_token(name_token, matcher=matcher, parse_type=True)
343
344
 
344
- scope = cast(Var, node).scope
345
+ scope = node.scope
345
346
  if scope:
346
347
  scope = scope.upper()
347
348
 
@@ -355,13 +356,14 @@ class NamespaceAnalyzer(Visitor):
355
356
  var_type = LocalVariableDefinition
356
357
 
357
358
  var = var_type(
358
- name=var_name,
359
- name_token=strip_variable_token(stripped_variable),
360
- line_no=stripped_variable.lineno,
361
- col_offset=stripped_variable.col_offset,
362
- end_line_no=stripped_variable.lineno,
363
- end_col_offset=stripped_variable.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,
364
365
  source=self._namespace.source,
366
+ value_type=matcher.type,
365
367
  )
366
368
 
367
369
  if var.matcher not in self._variables:
@@ -371,7 +373,7 @@ class NamespaceAnalyzer(Visitor):
371
373
  existing_var = self._variables[var.matcher]
372
374
 
373
375
  location = Location(
374
- self._namespace.document_uri, range_from_token(strip_variable_token(stripped_variable))
376
+ self._namespace.document_uri, range_from_token(strip_variable_token(stripped_name_token))
375
377
  )
376
378
  self._variable_references[existing_var].add(location)
377
379
  if existing_var in self._overridden_variables:
@@ -1135,6 +1137,8 @@ class NamespaceAnalyzer(Visitor):
1135
1137
  self._current_testcase_or_keyword_name = None
1136
1138
  self._current_keyword_doc = None
1137
1139
 
1140
+ EMBEDDED_ARGUMENTS_MATCHER = re.compile("([^:]+): ([^:]+)(:(.*))?")
1141
+
1138
1142
  def visit_KeywordName(self, node: KeywordName) -> None: # noqa: N802
1139
1143
  name_token = node.get_token(Token.KEYWORD_NAME)
1140
1144
 
@@ -1144,14 +1148,28 @@ class NamespaceAnalyzer(Visitor):
1144
1148
  tokenize_variables(name_token, identifiers="$", ignore_errors=True),
1145
1149
  ):
1146
1150
  if variable_token.value:
1147
- match = search_variable(variable_token.value, "$", ignore_errors=True)
1148
- if match.base is None:
1151
+ matcher = search_variable(variable_token.value, "$", ignore_errors=True)
1152
+ if matcher.base is None:
1149
1153
  continue
1150
- name = match.base.split(":", 1)[0]
1151
- full_name = f"{match.identifier}{{{name}}}"
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}}}"
1152
1170
  var_token = strip_variable_token(variable_token)
1153
1171
  var_token.value = name
1154
- arg_def = ArgumentDefinition(
1172
+ arg_def = EmbeddedArgumentDefinition(
1155
1173
  name=full_name,
1156
1174
  name_token=var_token,
1157
1175
  line_no=variable_token.lineno,
@@ -1160,32 +1178,19 @@ class NamespaceAnalyzer(Visitor):
1160
1178
  end_col_offset=variable_token.end_col_offset,
1161
1179
  source=self._namespace.source,
1162
1180
  keyword_doc=self._current_keyword_doc,
1181
+ value_type=type,
1182
+ pattern=pattern,
1163
1183
  )
1164
1184
 
1165
1185
  self._variables[arg_def.matcher] = arg_def
1166
1186
  self._variable_references[arg_def] = set()
1167
1187
 
1168
- def _get_variable_token(self, token: Token) -> Optional[Token]:
1169
- return next(
1170
- (
1171
- v
1172
- for v in itertools.dropwhile(
1173
- lambda t: t.type in Token.NON_DATA_TOKENS,
1174
- tokenize_variables(token, ignore_errors=True, extra_types={Token.VARIABLE}),
1175
- )
1176
- if v.type == Token.VARIABLE
1177
- ),
1178
- None,
1179
- )
1180
-
1181
1188
  def _visit_Arguments(self, node: Statement) -> None: # noqa: N802
1182
1189
  args: Dict[VariableMatcher, VariableDefinition] = {}
1183
1190
 
1184
- arguments = node.get_tokens(Token.ARGUMENT)
1185
-
1186
- for argument_token in arguments:
1191
+ for argument_token in node.get_tokens(Token.ARGUMENT):
1187
1192
  try:
1188
- argument = self._get_variable_token(argument_token)
1193
+ argument = get_first_variable_token(argument_token)
1189
1194
 
1190
1195
  if argument is not None and argument.value != "@{}":
1191
1196
  if len(argument_token.value) > len(argument.value):
@@ -1199,18 +1204,23 @@ class NamespaceAnalyzer(Visitor):
1199
1204
  )
1200
1205
  )
1201
1206
 
1202
- 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)
1203
1212
 
1204
1213
  if matcher not in args:
1205
1214
  arg_def = ArgumentDefinition(
1206
- name=argument.value,
1207
- name_token=strip_variable_token(argument),
1208
- line_no=argument.lineno,
1209
- col_offset=argument.col_offset,
1210
- end_line_no=argument.lineno,
1211
- end_col_offset=argument.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,
1212
1221
  source=self._namespace.source,
1213
1222
  keyword_doc=self._current_keyword_doc,
1223
+ value_type=matcher.type,
1214
1224
  )
1215
1225
 
1216
1226
  args[matcher] = arg_def
@@ -1220,10 +1230,7 @@ class NamespaceAnalyzer(Visitor):
1220
1230
  self._variable_references[arg_def] = set()
1221
1231
  else:
1222
1232
  self._variable_references[args[matcher]].add(
1223
- Location(
1224
- self._namespace.document_uri,
1225
- range_from_token(strip_variable_token(argument)),
1226
- )
1233
+ Location(self._namespace.document_uri, range_from_token(stripped_argument_token))
1227
1234
  )
1228
1235
 
1229
1236
  except (VariableError, InvalidVariableError):
@@ -1232,75 +1239,79 @@ class NamespaceAnalyzer(Visitor):
1232
1239
  def _analyze_assign_statement(self, node: Statement) -> None:
1233
1240
  token_with_assign_mark: Optional[Token] = None
1234
1241
  for assign_token in node.get_tokens(Token.ASSIGN):
1235
- variable_token = self._get_variable_token(assign_token)
1242
+ try:
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
+ )
1236
1252
 
1237
- if token_with_assign_mark is not None:
1238
- r = range_from_token(token_with_assign_mark)
1239
- r.start.character = r.end.character - 1
1240
- self._append_diagnostics(
1241
- range=r,
1242
- message="Assign mark '=' can be used only with the last variable.",
1243
- severity=DiagnosticSeverity.ERROR,
1244
- code=Error.ASSIGN_MARK_ALLOWED_ONLY_ON_LAST_VAR,
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,
1245
1260
  )
1246
1261
 
1247
- if assign_token.value.endswith("="):
1248
- token_with_assign_mark = assign_token
1262
+ if not matcher.is_assign(allow_assign_mark=True) or matcher.name is None:
1263
+ return
1249
1264
 
1250
- try:
1251
- if variable_token is not None:
1252
- matcher = VariableMatcher(variable_token.value)
1253
- stripped_variable_token = strip_variable_token(variable_token, matcher=matcher)
1254
- if matcher.name is None:
1255
- return
1265
+ stripped_name_token = strip_variable_token(assign_token, matcher=matcher, parse_type=True)
1256
1266
 
1257
- if matcher.items:
1258
- existing_var = self._find_variable(matcher.name)
1259
- if existing_var is None:
1260
- self._handle_find_variable_result(
1261
- stripped_variable_token,
1262
- VariableNotFoundDefinition(
1263
- stripped_variable_token.lineno,
1264
- stripped_variable_token.col_offset,
1265
- stripped_variable_token.lineno,
1266
- stripped_variable_token.end_col_offset,
1267
- self._namespace.source,
1268
- matcher.name,
1269
- stripped_variable_token,
1270
- ),
1271
- )
1272
- return
1273
- else:
1274
- existing_var = next(
1275
- (
1276
- v
1277
- for k, v in self._variables.items()
1278
- if k == matcher
1279
- and v.type in [VariableDefinitionType.ARGUMENT, VariableDefinitionType.LOCAL_VARIABLE]
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
1280
  ),
1281
- None,
1282
1281
  )
1282
+ return
1283
+ else:
1284
+ existing_var = next(
1285
+ (
1286
+ v
1287
+ for k, v in self._variables.items()
1288
+ if k == matcher
1289
+ and v.type in [VariableDefinitionType.ARGUMENT, VariableDefinitionType.LOCAL_VARIABLE]
1290
+ ),
1291
+ None,
1292
+ )
1283
1293
 
1284
- if existing_var is None:
1285
- var_def = LocalVariableDefinition(
1286
- name=matcher.name,
1287
- name_token=stripped_variable_token,
1288
- line_no=variable_token.lineno,
1289
- col_offset=variable_token.col_offset,
1290
- end_line_no=variable_token.lineno,
1291
- end_col_offset=variable_token.end_col_offset,
1292
- source=self._namespace.source,
1293
- )
1294
- self._variables[matcher] = var_def
1295
- self._variable_references[var_def] = set()
1296
- self._local_variable_assignments[var_def].add(var_def.range)
1297
- else:
1298
- self._variable_references[existing_var].add(
1299
- Location(
1300
- self._namespace.document_uri,
1301
- range_from_token(stripped_variable_token),
1302
- )
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),
1303
1313
  )
1314
+ )
1304
1315
 
1305
1316
  except (VariableError, InvalidVariableError):
1306
1317
  pass
@@ -1313,24 +1324,27 @@ class NamespaceAnalyzer(Visitor):
1313
1324
  def visit_ForHeader(self, node: Statement) -> None: # noqa: N802
1314
1325
  self._analyze_statement_variables(node)
1315
1326
 
1316
- variables = node.get_tokens(Token.VARIABLE)
1317
- for variable in variables:
1318
- variable_token = self._get_variable_token(variable)
1319
- if variable_token is not None and is_variable(variable_token.value):
1320
- existing_var = self._find_variable(variable_token.value)
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)
1321
1334
 
1322
1335
  if existing_var is None or existing_var.type not in [
1323
1336
  VariableDefinitionType.ARGUMENT,
1324
1337
  VariableDefinitionType.LOCAL_VARIABLE,
1325
1338
  ]:
1326
1339
  var_def = LocalVariableDefinition(
1327
- name=variable_token.value,
1328
- name_token=strip_variable_token(variable_token),
1329
- line_no=variable_token.lineno,
1330
- col_offset=variable_token.col_offset,
1331
- end_line_no=variable_token.lineno,
1332
- end_col_offset=variable_token.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,
1333
1346
  source=self._namespace.source,
1347
+ value_type=matcher.type,
1334
1348
  )
1335
1349
  self._variables[var_def.matcher] = var_def
1336
1350
  self._variable_references[var_def] = set()
@@ -1342,7 +1356,7 @@ class NamespaceAnalyzer(Visitor):
1342
1356
  self._variable_references[existing_var].add(
1343
1357
  Location(
1344
1358
  self._namespace.document_uri,
1345
- range_from_token(strip_variable_token(variable_token)),
1359
+ range_from_token(stripped_variable_token),
1346
1360
  )
1347
1361
  )
1348
1362
 
@@ -1352,31 +1366,33 @@ class NamespaceAnalyzer(Visitor):
1352
1366
 
1353
1367
  variable_token = node.get_token(Token.VARIABLE)
1354
1368
 
1355
- if variable_token is not None and is_scalar_assign(variable_token.value):
1369
+ if variable_token is not None:
1356
1370
  try:
1357
- if variable_token is not None:
1358
- matcher = VariableMatcher(variable_token.value)
1359
- if (
1360
- next(
1361
- (
1362
- k
1363
- for k, v in self._variables.items()
1364
- if k == matcher
1365
- and v.type in [VariableDefinitionType.ARGUMENT, VariableDefinitionType.LOCAL_VARIABLE]
1366
- ),
1367
- None,
1368
- )
1369
- is None
1370
- ):
1371
- self._variables[matcher] = LocalVariableDefinition(
1372
- name=variable_token.value,
1373
- name_token=strip_variable_token(variable_token),
1374
- line_no=variable_token.lineno,
1375
- col_offset=variable_token.col_offset,
1376
- end_line_no=variable_token.lineno,
1377
- end_col_offset=variable_token.end_col_offset,
1378
- source=self._namespace.source,
1379
- )
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
+ )
1380
1396
 
1381
1397
  except (VariableError, InvalidVariableError):
1382
1398
  pass
@@ -326,10 +326,12 @@ def iter_over_keyword_names_and_owners(
326
326
  yield ".".join(tokens[:i]), ".".join(tokens[i:])
327
327
 
328
328
 
329
- def strip_variable_token(token: Token, identifiers: str = "$@&%*", matcher: Optional[VariableMatcher] = None) -> Token:
330
- if token.type == Token.VARIABLE:
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]:
331
333
  if matcher is None:
332
- matcher = search_variable(token.value, identifiers, ignore_errors=True)
334
+ matcher = search_variable(token.value, identifiers, parse_type=parse_type, ignore_errors=True)
333
335
 
334
336
  if matcher.is_variable():
335
337
  value = matcher.base
@@ -346,13 +348,13 @@ def strip_variable_token(token: Token, identifiers: str = "$@&%*", matcher: Opti
346
348
  return token
347
349
 
348
350
 
349
- def get_variable_token(token: Token) -> Optional[Token]:
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
  ),