robotcode-robot 0.68.0__py3-none-any.whl → 0.68.2__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/loader.py +20 -6
- robotcode/robot/config/model.py +120 -62
- robotcode/robot/config/utils.py +20 -8
- robotcode/robot/diagnostics/__init__.py +0 -0
- robotcode/robot/diagnostics/entities.py +377 -0
- robotcode/robot/diagnostics/library_doc.py +2799 -0
- robotcode/robot/utils/__init__.py +1 -2
- robotcode/robot/utils/ast.py +34 -29
- robotcode/robot/utils/markdownformatter.py +2 -2
- robotcode/robot/utils/match.py +23 -0
- robotcode/robot/utils/robot_path.py +70 -0
- robotcode/robot/utils/stubs.py +24 -0
- robotcode/robot/utils/variables.py +37 -0
- robotcode/robot/utils/{visitors.py → visitor.py} +10 -21
- {robotcode_robot-0.68.0.dist-info → robotcode_robot-0.68.2.dist-info}/METADATA +4 -4
- robotcode_robot-0.68.2.dist-info/RECORD +22 -0
- robotcode_robot-0.68.0.dist-info/RECORD +0 -15
- {robotcode_robot-0.68.0.dist-info → robotcode_robot-0.68.2.dist-info}/WHEEL +0 -0
- {robotcode_robot-0.68.0.dist-info → robotcode_robot-0.68.2.dist-info}/licenses/LICENSE.txt +0 -0
@@ -0,0 +1,377 @@
|
|
1
|
+
from dataclasses import dataclass, field
|
2
|
+
from enum import Enum
|
3
|
+
from typing import (
|
4
|
+
TYPE_CHECKING,
|
5
|
+
Any,
|
6
|
+
Callable,
|
7
|
+
List,
|
8
|
+
Optional,
|
9
|
+
Tuple,
|
10
|
+
TypeVar,
|
11
|
+
cast,
|
12
|
+
)
|
13
|
+
|
14
|
+
from robot.parsing.lexer.tokens import Token
|
15
|
+
from robotcode.core.lsp.types import Position, Range
|
16
|
+
from robotcode.robot.utils.ast import range_from_token
|
17
|
+
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
from robotcode.robot.diagnostics.library_doc import KeywordDoc, LibraryDoc
|
20
|
+
|
21
|
+
_F = TypeVar("_F", bound=Callable[..., Any])
|
22
|
+
|
23
|
+
|
24
|
+
_NOT_SET = object()
|
25
|
+
|
26
|
+
|
27
|
+
def single_call(func: _F) -> _F:
|
28
|
+
name = f"__single_result_{func.__name__}__"
|
29
|
+
|
30
|
+
def wrapper(self: Any, *args: Any, **kwargs: Any) -> Any:
|
31
|
+
result = self.__dict__.get(name, _NOT_SET)
|
32
|
+
if result is _NOT_SET:
|
33
|
+
result = func(self, *args, **kwargs)
|
34
|
+
self.__dict__[name] = result
|
35
|
+
return result
|
36
|
+
|
37
|
+
return cast(_F, wrapper)
|
38
|
+
|
39
|
+
|
40
|
+
@dataclass
|
41
|
+
class SourceEntity:
|
42
|
+
line_no: int
|
43
|
+
col_offset: int
|
44
|
+
end_line_no: int
|
45
|
+
end_col_offset: int
|
46
|
+
source: Optional[str]
|
47
|
+
|
48
|
+
@property
|
49
|
+
def range(self) -> Range:
|
50
|
+
return Range(
|
51
|
+
start=Position(line=self.line_no - 1, character=self.col_offset),
|
52
|
+
end=Position(line=self.end_line_no - 1, character=self.end_col_offset),
|
53
|
+
)
|
54
|
+
|
55
|
+
@single_call
|
56
|
+
def __hash__(self) -> int:
|
57
|
+
return hash(
|
58
|
+
(
|
59
|
+
self.line_no,
|
60
|
+
self.col_offset,
|
61
|
+
self.end_line_no,
|
62
|
+
self.end_col_offset,
|
63
|
+
self.source,
|
64
|
+
)
|
65
|
+
)
|
66
|
+
|
67
|
+
|
68
|
+
@dataclass
|
69
|
+
class Import(SourceEntity):
|
70
|
+
name: Optional[str]
|
71
|
+
name_token: Optional[Token]
|
72
|
+
|
73
|
+
@property
|
74
|
+
def range(self) -> Range:
|
75
|
+
return Range(
|
76
|
+
start=Position(
|
77
|
+
line=self.name_token.lineno - 1 if self.name_token is not None else self.line_no - 1,
|
78
|
+
character=self.name_token.col_offset if self.name_token is not None else self.col_offset,
|
79
|
+
),
|
80
|
+
end=Position(
|
81
|
+
line=self.name_token.lineno - 1 if self.name_token is not None else self.end_line_no - 1,
|
82
|
+
character=self.name_token.end_col_offset if self.name_token is not None else self.end_col_offset,
|
83
|
+
),
|
84
|
+
)
|
85
|
+
|
86
|
+
|
87
|
+
@dataclass
|
88
|
+
class LibraryImport(Import):
|
89
|
+
args: Tuple[str, ...] = ()
|
90
|
+
alias: Optional[str] = None
|
91
|
+
alias_token: Optional[Token] = None
|
92
|
+
|
93
|
+
@property
|
94
|
+
def alias_range(self) -> Range:
|
95
|
+
return Range(
|
96
|
+
start=Position(
|
97
|
+
line=self.alias_token.lineno - 1 if self.alias_token is not None else -1,
|
98
|
+
character=self.alias_token.col_offset if self.alias_token is not None else -1,
|
99
|
+
),
|
100
|
+
end=Position(
|
101
|
+
line=self.alias_token.lineno - 1 if self.alias_token is not None else -1,
|
102
|
+
character=self.alias_token.end_col_offset if self.alias_token is not None else -1,
|
103
|
+
),
|
104
|
+
)
|
105
|
+
|
106
|
+
@single_call
|
107
|
+
def __hash__(self) -> int:
|
108
|
+
return hash((type(self), self.name, self.args, self.alias))
|
109
|
+
|
110
|
+
|
111
|
+
@dataclass
|
112
|
+
class ResourceImport(Import):
|
113
|
+
@single_call
|
114
|
+
def __hash__(self) -> int:
|
115
|
+
return hash((type(self), self.name))
|
116
|
+
|
117
|
+
|
118
|
+
@dataclass
|
119
|
+
class VariablesImport(Import):
|
120
|
+
args: Tuple[str, ...] = ()
|
121
|
+
|
122
|
+
@single_call
|
123
|
+
def __hash__(self) -> int:
|
124
|
+
return hash((type(self), self.name, self.args))
|
125
|
+
|
126
|
+
|
127
|
+
class InvalidVariableError(Exception):
|
128
|
+
pass
|
129
|
+
|
130
|
+
|
131
|
+
class VariableMatcher:
|
132
|
+
def __init__(self, name: str) -> None:
|
133
|
+
from robot.variables.search import search_variable
|
134
|
+
from robotcode.robot.utils.match import normalize
|
135
|
+
|
136
|
+
self.name = name
|
137
|
+
|
138
|
+
match = search_variable(name, "$@&%", ignore_errors=True)
|
139
|
+
|
140
|
+
if match.base is None:
|
141
|
+
raise InvalidVariableError(f"Invalid variable '{name}'")
|
142
|
+
|
143
|
+
self.base = match.base
|
144
|
+
|
145
|
+
self.normalized_name = str(normalize(self.base))
|
146
|
+
|
147
|
+
def __eq__(self, o: object) -> bool:
|
148
|
+
from robot.utils.normalizing import normalize
|
149
|
+
from robot.variables.search import search_variable
|
150
|
+
|
151
|
+
if isinstance(o, VariableMatcher):
|
152
|
+
return o.normalized_name == self.normalized_name
|
153
|
+
|
154
|
+
if isinstance(o, str):
|
155
|
+
match = search_variable(o, "$@&%", ignore_errors=True)
|
156
|
+
base = match.base
|
157
|
+
normalized = str(normalize(base))
|
158
|
+
return self.normalized_name == normalized
|
159
|
+
|
160
|
+
return False
|
161
|
+
|
162
|
+
def __hash__(self) -> int:
|
163
|
+
return hash(self.name)
|
164
|
+
|
165
|
+
def __str__(self) -> str:
|
166
|
+
return self.name
|
167
|
+
|
168
|
+
def __repr__(self) -> str:
|
169
|
+
return f"{type(self).__name__}(name={self.name!r})"
|
170
|
+
|
171
|
+
|
172
|
+
class VariableDefinitionType(Enum):
|
173
|
+
VARIABLE = "suite variable"
|
174
|
+
LOCAL_VARIABLE = "local variable"
|
175
|
+
ARGUMENT = "argument"
|
176
|
+
COMMAND_LINE_VARIABLE = "global variable [command line]"
|
177
|
+
BUILTIN_VARIABLE = "builtin variable"
|
178
|
+
IMPORTED_VARIABLE = "suite variable [imported]"
|
179
|
+
ENVIRONMENT_VARIABLE = "environment variable"
|
180
|
+
VARIABLE_NOT_FOUND = "variable not found"
|
181
|
+
|
182
|
+
|
183
|
+
@dataclass
|
184
|
+
class VariableDefinition(SourceEntity):
|
185
|
+
name: str
|
186
|
+
name_token: Optional[Token]
|
187
|
+
type: VariableDefinitionType = VariableDefinitionType.VARIABLE
|
188
|
+
|
189
|
+
has_value: bool = field(default=False, compare=False)
|
190
|
+
resolvable: bool = field(default=False, compare=False)
|
191
|
+
|
192
|
+
value: Any = field(default=None, compare=False)
|
193
|
+
value_is_native: bool = field(default=False, compare=False)
|
194
|
+
|
195
|
+
@property
|
196
|
+
def matcher(self) -> VariableMatcher:
|
197
|
+
if not hasattr(self, "__matcher"):
|
198
|
+
self.__matcher = VariableMatcher(self.name)
|
199
|
+
return self.__matcher
|
200
|
+
|
201
|
+
@single_call
|
202
|
+
def __hash__(self) -> int:
|
203
|
+
return hash((type(self), self.name, self.type, self.range, self.source))
|
204
|
+
|
205
|
+
@property
|
206
|
+
def name_range(self) -> Range:
|
207
|
+
if self.name_token is not None:
|
208
|
+
return range_from_token(self.name_token)
|
209
|
+
|
210
|
+
return self.range
|
211
|
+
|
212
|
+
@property
|
213
|
+
def range(self) -> Range:
|
214
|
+
return Range(
|
215
|
+
start=Position(line=self.line_no - 1, character=self.col_offset),
|
216
|
+
end=Position(line=self.end_line_no - 1, character=self.end_col_offset),
|
217
|
+
)
|
218
|
+
|
219
|
+
|
220
|
+
@dataclass
|
221
|
+
class LocalVariableDefinition(VariableDefinition):
|
222
|
+
type: VariableDefinitionType = VariableDefinitionType.LOCAL_VARIABLE
|
223
|
+
|
224
|
+
@single_call
|
225
|
+
def __hash__(self) -> int:
|
226
|
+
return hash((type(self), self.name, self.type, self.range, self.source))
|
227
|
+
|
228
|
+
|
229
|
+
@dataclass
|
230
|
+
class BuiltInVariableDefinition(VariableDefinition):
|
231
|
+
type: VariableDefinitionType = VariableDefinitionType.BUILTIN_VARIABLE
|
232
|
+
resolvable: bool = True
|
233
|
+
|
234
|
+
@single_call
|
235
|
+
def __hash__(self) -> int:
|
236
|
+
return hash((type(self), self.name, self.type))
|
237
|
+
|
238
|
+
|
239
|
+
@dataclass
|
240
|
+
class CommandLineVariableDefinition(VariableDefinition):
|
241
|
+
type: VariableDefinitionType = VariableDefinitionType.COMMAND_LINE_VARIABLE
|
242
|
+
resolvable: bool = True
|
243
|
+
|
244
|
+
@single_call
|
245
|
+
def __hash__(self) -> int:
|
246
|
+
return hash((type(self), self.name, self.type))
|
247
|
+
|
248
|
+
|
249
|
+
@dataclass
|
250
|
+
class ArgumentDefinition(VariableDefinition):
|
251
|
+
type: VariableDefinitionType = VariableDefinitionType.ARGUMENT
|
252
|
+
keyword_doc: Optional["KeywordDoc"] = field(default=None, compare=False, metadata={"nosave": True})
|
253
|
+
|
254
|
+
@single_call
|
255
|
+
def __hash__(self) -> int:
|
256
|
+
return hash((type(self), self.name, self.type, self.range, self.source))
|
257
|
+
|
258
|
+
|
259
|
+
@dataclass
|
260
|
+
class LibraryArgumentDefinition(ArgumentDefinition):
|
261
|
+
@single_call
|
262
|
+
def __hash__(self) -> int:
|
263
|
+
return hash((type(self), self.name, self.type, self.range, self.source))
|
264
|
+
|
265
|
+
|
266
|
+
@dataclass(frozen=True, eq=False, repr=False)
|
267
|
+
class NativeValue:
|
268
|
+
value: Any
|
269
|
+
|
270
|
+
def __repr__(self) -> str:
|
271
|
+
return repr(self.value)
|
272
|
+
|
273
|
+
def __str__(self) -> str:
|
274
|
+
return str(self.value)
|
275
|
+
|
276
|
+
|
277
|
+
@dataclass
|
278
|
+
class ImportedVariableDefinition(VariableDefinition):
|
279
|
+
type: VariableDefinitionType = VariableDefinitionType.IMPORTED_VARIABLE
|
280
|
+
value: Optional[NativeValue] = field(default=None, compare=False)
|
281
|
+
|
282
|
+
@single_call
|
283
|
+
def __hash__(self) -> int:
|
284
|
+
return hash((type(self), self.name, self.type, self.source))
|
285
|
+
|
286
|
+
|
287
|
+
@dataclass
|
288
|
+
class EnvironmentVariableDefinition(VariableDefinition):
|
289
|
+
type: VariableDefinitionType = VariableDefinitionType.ENVIRONMENT_VARIABLE
|
290
|
+
resolvable: bool = True
|
291
|
+
|
292
|
+
default_value: Any = field(default=None, compare=False)
|
293
|
+
|
294
|
+
@single_call
|
295
|
+
def __hash__(self) -> int:
|
296
|
+
return hash((type(self), self.name, self.type))
|
297
|
+
|
298
|
+
|
299
|
+
@dataclass
|
300
|
+
class VariableNotFoundDefinition(VariableDefinition):
|
301
|
+
type: VariableDefinitionType = VariableDefinitionType.VARIABLE_NOT_FOUND
|
302
|
+
resolvable: bool = False
|
303
|
+
|
304
|
+
@single_call
|
305
|
+
def __hash__(self) -> int:
|
306
|
+
return hash((type(self), self.name, self.type))
|
307
|
+
|
308
|
+
|
309
|
+
@dataclass
|
310
|
+
class LibraryEntry:
|
311
|
+
name: str
|
312
|
+
import_name: str
|
313
|
+
library_doc: "LibraryDoc" = field(compare=False)
|
314
|
+
args: Tuple[Any, ...] = ()
|
315
|
+
alias: Optional[str] = None
|
316
|
+
import_range: Range = field(default_factory=Range.zero)
|
317
|
+
import_source: Optional[str] = None
|
318
|
+
alias_range: Range = field(default_factory=Range.zero)
|
319
|
+
|
320
|
+
def __str__(self) -> str:
|
321
|
+
result = self.import_name
|
322
|
+
if self.args:
|
323
|
+
result += f" {self.args!s}"
|
324
|
+
if self.alias:
|
325
|
+
result += f" WITH NAME {self.alias}"
|
326
|
+
return result
|
327
|
+
|
328
|
+
@single_call
|
329
|
+
def __hash__(self) -> int:
|
330
|
+
return hash(
|
331
|
+
(
|
332
|
+
type(self),
|
333
|
+
self.name,
|
334
|
+
self.import_name,
|
335
|
+
self.args,
|
336
|
+
self.alias,
|
337
|
+
self.import_range,
|
338
|
+
self.import_source,
|
339
|
+
self.alias_range,
|
340
|
+
)
|
341
|
+
)
|
342
|
+
|
343
|
+
|
344
|
+
@dataclass
|
345
|
+
class ResourceEntry(LibraryEntry):
|
346
|
+
imports: List[Import] = field(default_factory=list, compare=False)
|
347
|
+
variables: List[VariableDefinition] = field(default_factory=list, compare=False)
|
348
|
+
|
349
|
+
@single_call
|
350
|
+
def __hash__(self) -> int:
|
351
|
+
return hash(
|
352
|
+
(
|
353
|
+
type(self),
|
354
|
+
self.name,
|
355
|
+
self.import_name,
|
356
|
+
self.import_range,
|
357
|
+
self.import_source,
|
358
|
+
)
|
359
|
+
)
|
360
|
+
|
361
|
+
|
362
|
+
@dataclass
|
363
|
+
class VariablesEntry(LibraryEntry):
|
364
|
+
variables: List[ImportedVariableDefinition] = field(default_factory=list, compare=False)
|
365
|
+
|
366
|
+
@single_call
|
367
|
+
def __hash__(self) -> int:
|
368
|
+
return hash(
|
369
|
+
(
|
370
|
+
type(self),
|
371
|
+
self.name,
|
372
|
+
self.import_name,
|
373
|
+
self.args,
|
374
|
+
self.import_range,
|
375
|
+
self.import_source,
|
376
|
+
)
|
377
|
+
)
|