brighterscript 0.41.2 → 1.0.0-alpha.12
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.
- package/CHANGELOG.md +112 -0
- package/README.md +2 -2
- package/dist/DiagnosticCollection.js +2 -2
- package/dist/DiagnosticCollection.js.map +1 -1
- package/dist/DiagnosticFilterer.js +2 -2
- package/dist/DiagnosticFilterer.js.map +1 -1
- package/dist/DiagnosticMessages.d.ts +6 -1
- package/dist/DiagnosticMessages.js +5 -0
- package/dist/DiagnosticMessages.js.map +1 -1
- package/dist/LanguageServer.d.ts +8 -2
- package/dist/LanguageServer.js +50 -44
- package/dist/LanguageServer.js.map +1 -1
- package/dist/Program.d.ts +61 -45
- package/dist/Program.js +305 -188
- package/dist/Program.js.map +1 -1
- package/dist/ProgramBuilder.d.ts +7 -7
- package/dist/ProgramBuilder.js +60 -51
- package/dist/ProgramBuilder.js.map +1 -1
- package/dist/Scope.d.ts +42 -19
- package/dist/Scope.js +261 -129
- package/dist/Scope.js.map +1 -1
- package/dist/SymbolTable.d.ts +73 -0
- package/dist/SymbolTable.js +157 -0
- package/dist/SymbolTable.js.map +1 -0
- package/dist/XmlScope.d.ts +5 -0
- package/dist/XmlScope.js +66 -28
- package/dist/XmlScope.js.map +1 -1
- package/dist/astUtils/creators.d.ts +15 -1
- package/dist/astUtils/creators.js +39 -9
- package/dist/astUtils/creators.js.map +1 -1
- package/dist/astUtils/reflection.d.ts +28 -16
- package/dist/astUtils/reflection.js +52 -30
- package/dist/astUtils/reflection.js.map +1 -1
- package/dist/astUtils/reflection.spec.js +3 -3
- package/dist/astUtils/reflection.spec.js.map +1 -1
- package/dist/astUtils/visitors.spec.js +12 -13
- package/dist/astUtils/visitors.spec.js.map +1 -1
- package/dist/astUtils/xml.d.ts +3 -3
- package/dist/astUtils/xml.js +2 -2
- package/dist/astUtils/xml.js.map +1 -1
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +5 -6
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js +24 -22
- package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
- package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js +2 -2
- package/dist/bscPlugin/semanticTokens/SemanticTokensProcessor.spec.js.map +1 -1
- package/dist/examples/plugins/removePrint.js +1 -1
- package/dist/examples/plugins/removePrint.js.map +1 -1
- package/dist/files/BrsFile.Class.spec.js +356 -41
- package/dist/files/BrsFile.Class.spec.js.map +1 -1
- package/dist/files/BrsFile.d.ts +55 -37
- package/dist/files/BrsFile.js +430 -399
- package/dist/files/BrsFile.js.map +1 -1
- package/dist/files/BrsFile.spec.js +199 -158
- package/dist/files/BrsFile.spec.js.map +1 -1
- package/dist/files/XmlFile.d.ts +20 -9
- package/dist/files/XmlFile.js +36 -31
- package/dist/files/XmlFile.js.map +1 -1
- package/dist/files/XmlFile.spec.js +113 -113
- package/dist/files/XmlFile.spec.js.map +1 -1
- package/dist/files/tests/imports.spec.js +32 -32
- package/dist/files/tests/imports.spec.js.map +1 -1
- package/dist/globalCallables.js +17 -6
- package/dist/globalCallables.js.map +1 -1
- package/dist/interfaces.d.ts +155 -39
- package/dist/parser/BrsTranspileState.d.ts +7 -0
- package/dist/parser/BrsTranspileState.js +10 -1
- package/dist/parser/BrsTranspileState.js.map +1 -1
- package/dist/parser/Expression.d.ts +23 -12
- package/dist/parser/Expression.js +45 -30
- package/dist/parser/Expression.js.map +1 -1
- package/dist/parser/Parser.Class.spec.js +100 -1
- package/dist/parser/Parser.Class.spec.js.map +1 -1
- package/dist/parser/Parser.d.ts +118 -5
- package/dist/parser/Parser.js +398 -37
- package/dist/parser/Parser.js.map +1 -1
- package/dist/parser/Parser.spec.js +404 -7
- package/dist/parser/Parser.spec.js.map +1 -1
- package/dist/parser/SGParser.d.ts +41 -4
- package/dist/parser/SGParser.js +185 -174
- package/dist/parser/SGParser.js.map +1 -1
- package/dist/parser/SGParser.spec.js +17 -4
- package/dist/parser/SGParser.spec.js.map +1 -1
- package/dist/parser/SGTypes.d.ts +203 -38
- package/dist/parser/SGTypes.js +464 -160
- package/dist/parser/SGTypes.js.map +1 -1
- package/dist/parser/SGTypes.spec.d.ts +1 -0
- package/dist/parser/SGTypes.spec.js +351 -0
- package/dist/parser/SGTypes.spec.js.map +1 -0
- package/dist/parser/Statement.d.ts +37 -26
- package/dist/parser/Statement.js +81 -20
- package/dist/parser/Statement.js.map +1 -1
- package/dist/parser/Statement.spec.js +5 -5
- package/dist/parser/Statement.spec.js.map +1 -1
- package/dist/parser/TranspileState.d.ts +1 -1
- package/dist/parser/TranspileState.js +15 -7
- package/dist/parser/TranspileState.js.map +1 -1
- package/dist/parser/tests/controlFlow/ForEach.spec.js +5 -4
- package/dist/parser/tests/controlFlow/ForEach.spec.js.map +1 -1
- package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +1 -1
- package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +1 -1
- package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/TemplateStringExpression.spec.js +1 -1
- package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/TernaryExpression.spec.js +1 -1
- package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
- package/dist/types/ArrayType.d.ts +1 -0
- package/dist/types/ArrayType.js +23 -19
- package/dist/types/ArrayType.js.map +1 -1
- package/dist/types/BooleanType.d.ts +3 -2
- package/dist/types/BooleanType.js +6 -3
- package/dist/types/BooleanType.js.map +1 -1
- package/dist/types/BscType.d.ts +19 -4
- package/dist/types/BscType.js +9 -0
- package/dist/types/BscType.js.map +1 -1
- package/dist/types/CustomType.d.ts +8 -5
- package/dist/types/CustomType.js +12 -12
- package/dist/types/CustomType.js.map +1 -1
- package/dist/types/DoubleType.d.ts +1 -0
- package/dist/types/DoubleType.js +11 -11
- package/dist/types/DoubleType.js.map +1 -1
- package/dist/types/DynamicType.d.ts +1 -0
- package/dist/types/DynamicType.js +4 -0
- package/dist/types/DynamicType.js.map +1 -1
- package/dist/types/FloatType.d.ts +2 -1
- package/dist/types/FloatType.js +11 -11
- package/dist/types/FloatType.js.map +1 -1
- package/dist/types/FunctionType.d.ts +13 -10
- package/dist/types/FunctionType.js +34 -18
- package/dist/types/FunctionType.js.map +1 -1
- package/dist/types/FunctionType.spec.js +8 -2
- package/dist/types/FunctionType.spec.js.map +1 -1
- package/dist/types/IntegerType.d.ts +2 -1
- package/dist/types/IntegerType.js +11 -11
- package/dist/types/IntegerType.js.map +1 -1
- package/dist/types/InvalidType.d.ts +3 -2
- package/dist/types/InvalidType.js +7 -4
- package/dist/types/InvalidType.js.map +1 -1
- package/dist/types/LazyType.d.ts +17 -0
- package/dist/types/LazyType.js +44 -0
- package/dist/types/LazyType.js.map +1 -0
- package/dist/types/LongIntegerType.d.ts +2 -1
- package/dist/types/LongIntegerType.js +11 -11
- package/dist/types/LongIntegerType.js.map +1 -1
- package/dist/types/ObjectType.d.ts +6 -2
- package/dist/types/ObjectType.js +9 -3
- package/dist/types/ObjectType.js.map +1 -1
- package/dist/types/StringType.d.ts +3 -2
- package/dist/types/StringType.js +6 -3
- package/dist/types/StringType.js.map +1 -1
- package/dist/types/UninitializedType.d.ts +4 -2
- package/dist/types/UninitializedType.js +8 -3
- package/dist/types/UninitializedType.js.map +1 -1
- package/dist/types/VoidType.d.ts +3 -2
- package/dist/types/VoidType.js +6 -3
- package/dist/types/VoidType.js.map +1 -1
- package/dist/types/helpers.d.ts +42 -0
- package/dist/types/helpers.js +113 -0
- package/dist/types/helpers.js.map +1 -0
- package/dist/util.d.ts +68 -15
- package/dist/util.js +193 -45
- package/dist/util.js.map +1 -1
- package/dist/validators/ClassValidator.d.ts +5 -1
- package/dist/validators/ClassValidator.js +31 -17
- package/dist/validators/ClassValidator.js.map +1 -1
- package/package.json +1 -1
- package/dist/FunctionScope.d.ts +0 -27
- package/dist/FunctionScope.js +0 -49
- package/dist/FunctionScope.js.map +0 -1
package/dist/Scope.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Scope = void 0;
|
|
4
|
-
const path = require("path");
|
|
5
4
|
const vscode_languageserver_1 = require("vscode-languageserver");
|
|
6
5
|
const chalk_1 = require("chalk");
|
|
7
6
|
const DiagnosticMessages_1 = require("./DiagnosticMessages");
|
|
@@ -13,6 +12,11 @@ const Cache_1 = require("./Cache");
|
|
|
13
12
|
const vscode_uri_1 = require("vscode-uri");
|
|
14
13
|
const Logger_1 = require("./Logger");
|
|
15
14
|
const reflection_1 = require("./astUtils/reflection");
|
|
15
|
+
const SymbolTable_1 = require("./SymbolTable");
|
|
16
|
+
const UninitializedType_1 = require("./types/UninitializedType");
|
|
17
|
+
const ObjectType_1 = require("./types/ObjectType");
|
|
18
|
+
const BscType_1 = require("./types/BscType");
|
|
19
|
+
const DynamicType_1 = require("./types/DynamicType");
|
|
16
20
|
/**
|
|
17
21
|
* A class to keep track of all declarations within a given scope (like source scope, component scope)
|
|
18
22
|
*/
|
|
@@ -50,6 +54,14 @@ class Scope {
|
|
|
50
54
|
var _a;
|
|
51
55
|
return (_a = this.getClassFileLink(className, containingNamespace)) === null || _a === void 0 ? void 0 : _a.item;
|
|
52
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* A cache of a map of tokens -> TokenSymbolLookups, which are the result of getSymbolTypeFromToken()
|
|
59
|
+
* Sometimes the lookup of symbols may take a while if there are lazyTypes or multiple tokens in a chain
|
|
60
|
+
* By caching the result of this lookup, subsequent lookups of the same tokens are quicker
|
|
61
|
+
*/
|
|
62
|
+
get symbolCache() {
|
|
63
|
+
return this.cache.getOrAdd('symbolCache', () => new Map());
|
|
64
|
+
}
|
|
53
65
|
/**
|
|
54
66
|
* Get a class and its containing file by the class name
|
|
55
67
|
* @param className - The class name, including the namespace of the class if possible
|
|
@@ -66,9 +78,24 @@ class Scope {
|
|
|
66
78
|
return cls;
|
|
67
79
|
}
|
|
68
80
|
/**
|
|
81
|
+
* Gets the parent class of the given class
|
|
82
|
+
* @param klass - The class to get the parent of, if possible
|
|
83
|
+
*/
|
|
84
|
+
getParentClass(klass) {
|
|
85
|
+
if (klass === null || klass === void 0 ? void 0 : klass.hasParentClass()) {
|
|
86
|
+
const lowerParentClassNames = klass.getPossibleFullParentNames().map(name => name.toLowerCase());
|
|
87
|
+
for (const lowerParentClassName of lowerParentClassNames) {
|
|
88
|
+
const foundParent = this.getClassMap().get(lowerParentClassName);
|
|
89
|
+
if (foundParent) {
|
|
90
|
+
return foundParent.item;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
69
96
|
* Tests if a class exists with the specified name
|
|
70
97
|
* @param className - the all-lower-case namespace-included class name
|
|
71
|
-
* @param
|
|
98
|
+
* @param namespaceName - the current namespace name
|
|
72
99
|
*/
|
|
73
100
|
hasClass(className, namespaceName) {
|
|
74
101
|
return !!this.getClass(className, namespaceName);
|
|
@@ -95,6 +122,20 @@ class Scope {
|
|
|
95
122
|
return map;
|
|
96
123
|
});
|
|
97
124
|
}
|
|
125
|
+
getAncestorTypeList(className, namespaceName) {
|
|
126
|
+
var _a;
|
|
127
|
+
const ancestors = [];
|
|
128
|
+
let currentClass = (_a = this.getClassFileLink(className, namespaceName)) === null || _a === void 0 ? void 0 : _a.item;
|
|
129
|
+
if (currentClass) {
|
|
130
|
+
ancestors.push(currentClass === null || currentClass === void 0 ? void 0 : currentClass.getCustomType());
|
|
131
|
+
}
|
|
132
|
+
while (currentClass === null || currentClass === void 0 ? void 0 : currentClass.hasParentClass()) {
|
|
133
|
+
currentClass = this.getParentClass(currentClass);
|
|
134
|
+
ancestors.push(currentClass === null || currentClass === void 0 ? void 0 : currentClass.getCustomType());
|
|
135
|
+
}
|
|
136
|
+
// TODO TYPES: this should probably be cached
|
|
137
|
+
return ancestors;
|
|
138
|
+
}
|
|
98
139
|
onDependenciesChanged(event) {
|
|
99
140
|
this.logDebug('invalidated because dependency graph said [', event.sourceKey, '] changed');
|
|
100
141
|
this.invalidate();
|
|
@@ -129,7 +170,7 @@ class Scope {
|
|
|
129
170
|
*/
|
|
130
171
|
getParentScope() {
|
|
131
172
|
let scope;
|
|
132
|
-
//use the global scope if we didn't find a
|
|
173
|
+
//use the global scope if we didn't find a scope and this is not the global scope
|
|
133
174
|
if (this.program.globalScope !== this) {
|
|
134
175
|
scope = this.program.globalScope;
|
|
135
176
|
}
|
|
@@ -153,12 +194,16 @@ class Scope {
|
|
|
153
194
|
}
|
|
154
195
|
/**
|
|
155
196
|
* Get the file with the specified pkgPath
|
|
197
|
+
* @param filePath can be a srcPath, a pkgPath, or a destPath (same as pkgPath but without `pkg:/`)
|
|
198
|
+
* @param normalizePath should this function repair and standardize the path? Passing false should have a performance boost if you can guarantee your path is already sanitized
|
|
156
199
|
*/
|
|
157
|
-
getFile(
|
|
158
|
-
|
|
200
|
+
getFile(srcPath, normalizePath = true) {
|
|
201
|
+
if (normalizePath) {
|
|
202
|
+
srcPath = (0, util_1.standardizePath) `${srcPath}`;
|
|
203
|
+
}
|
|
159
204
|
let files = this.getAllFiles();
|
|
160
205
|
for (let file of files) {
|
|
161
|
-
if (file.
|
|
206
|
+
if (file.srcPath === srcPath) {
|
|
162
207
|
return file;
|
|
163
208
|
}
|
|
164
209
|
}
|
|
@@ -188,7 +233,7 @@ class Scope {
|
|
|
188
233
|
}
|
|
189
234
|
}
|
|
190
235
|
else {
|
|
191
|
-
let file = this.program.
|
|
236
|
+
let file = this.program.getFile(dependency, false);
|
|
192
237
|
if (file) {
|
|
193
238
|
result.push(file);
|
|
194
239
|
}
|
|
@@ -312,7 +357,8 @@ class Scope {
|
|
|
312
357
|
namespaces: {},
|
|
313
358
|
classStatements: {},
|
|
314
359
|
functionStatements: {},
|
|
315
|
-
statements: []
|
|
360
|
+
statements: [],
|
|
361
|
+
symbolTable: new SymbolTable_1.SymbolTable(this.symbolTable)
|
|
316
362
|
};
|
|
317
363
|
}
|
|
318
364
|
let ns = namespaceLookup[name.toLowerCase()];
|
|
@@ -325,6 +371,9 @@ class Scope {
|
|
|
325
371
|
ns.functionStatements[statement.name.text.toLowerCase()] = statement;
|
|
326
372
|
}
|
|
327
373
|
}
|
|
374
|
+
// Merges all the symbol tables of the namespace statements into the new symbol table created above.
|
|
375
|
+
// Set those symbol tables to have this new merged table as a parent
|
|
376
|
+
ns.symbolTable.mergeSymbolTable(namespace.symbolTable);
|
|
328
377
|
}
|
|
329
378
|
//associate child namespaces with their parents
|
|
330
379
|
for (let key in namespaceLookup) {
|
|
@@ -350,37 +399,32 @@ class Scope {
|
|
|
350
399
|
logDebug(...args) {
|
|
351
400
|
this.program.logger.debug(this._debugLogComponentName, ...args);
|
|
352
401
|
}
|
|
353
|
-
validate(
|
|
354
|
-
//if this scope is already validated, no need to revalidate
|
|
355
|
-
if (this.isValidated === true && !force) {
|
|
356
|
-
this.logDebug('validate(): already validated');
|
|
357
|
-
return;
|
|
358
|
-
}
|
|
402
|
+
validate() {
|
|
359
403
|
this.program.logger.time(Logger_1.LogLevel.debug, [this._debugLogComponentName, 'validate()'], () => {
|
|
360
404
|
let parentScope = this.getParentScope();
|
|
361
405
|
//validate our parent before we validate ourself
|
|
362
|
-
if (parentScope
|
|
406
|
+
if ((parentScope === null || parentScope === void 0 ? void 0 : parentScope.isValidated) === false) {
|
|
363
407
|
this.logDebug('validate(): validating parent first');
|
|
364
|
-
parentScope.validate(
|
|
408
|
+
parentScope.validate();
|
|
365
409
|
}
|
|
366
410
|
//clear the scope's errors list (we will populate them from this method)
|
|
367
411
|
this.diagnostics = [];
|
|
412
|
+
// link the symbol table
|
|
413
|
+
this.linkSymbolTable();
|
|
368
414
|
let callables = this.getAllCallables();
|
|
369
415
|
//sort the callables by filepath and then method name, so the errors will be consistent
|
|
370
416
|
callables = callables.sort((a, b) => {
|
|
371
417
|
return (
|
|
372
418
|
//sort by path
|
|
373
|
-
a.callable.file.
|
|
419
|
+
a.callable.file.srcPath.localeCompare(b.callable.file.srcPath) ||
|
|
374
420
|
//then sort by method name
|
|
375
421
|
a.callable.name.localeCompare(b.callable.name));
|
|
376
422
|
});
|
|
377
423
|
//get a list of all callables, indexed by their lower case names
|
|
378
424
|
let callableContainerMap = util_1.util.getCallableContainersByLowerName(callables);
|
|
379
|
-
let files = this.getOwnFiles();
|
|
380
|
-
this.program.plugins.emit('beforeScopeValidate', this, files, callableContainerMap);
|
|
381
425
|
this._validate(callableContainerMap);
|
|
382
|
-
|
|
383
|
-
this.
|
|
426
|
+
// unlink the symbol table so it can't be accessed from the wrong scope
|
|
427
|
+
this.unlinkSymbolTable();
|
|
384
428
|
});
|
|
385
429
|
}
|
|
386
430
|
_validate(callableContainerMap) {
|
|
@@ -388,16 +432,15 @@ class Scope {
|
|
|
388
432
|
this.diagnosticFindDuplicateFunctionDeclarations(callableContainerMap);
|
|
389
433
|
//detect missing and incorrect-case script imports
|
|
390
434
|
this.diagnosticValidateScriptImportPaths();
|
|
391
|
-
//enforce a series of checks on the bodies of class methods
|
|
392
|
-
this.validateClasses();
|
|
393
435
|
//do many per-file checks
|
|
394
436
|
this.enumerateBrsFiles((file) => {
|
|
395
|
-
|
|
396
|
-
this.
|
|
437
|
+
//enforce a series of checks on the bodies of class methods
|
|
438
|
+
this.validateClasses(file);
|
|
397
439
|
this.diagnosticDetectShadowedLocalVars(file, callableContainerMap);
|
|
398
440
|
this.diagnosticDetectFunctionCollisions(file);
|
|
399
441
|
this.detectVariableNamespaceCollisions(file);
|
|
400
442
|
this.diagnosticDetectInvalidFunctionExpressionTypes(file);
|
|
443
|
+
this.diagnosticDetectInvalidFunctionCalls(file, callableContainerMap);
|
|
401
444
|
});
|
|
402
445
|
}
|
|
403
446
|
/**
|
|
@@ -407,6 +450,84 @@ class Scope {
|
|
|
407
450
|
this.isValidated = false;
|
|
408
451
|
//clear out various lookups (they'll get regenerated on demand the next time they're requested)
|
|
409
452
|
this.cache.clear();
|
|
453
|
+
this.clearSymbolTable();
|
|
454
|
+
this.symbolCache.clear();
|
|
455
|
+
}
|
|
456
|
+
get symbolTable() {
|
|
457
|
+
var _a, _b;
|
|
458
|
+
if (!this._symbolTable) {
|
|
459
|
+
this._symbolTable = new SymbolTable_1.SymbolTable((_a = this.getParentScope()) === null || _a === void 0 ? void 0 : _a.symbolTable);
|
|
460
|
+
this._symbolTable.addSymbol('m', null, new ObjectType_1.ObjectType(this.memberTable));
|
|
461
|
+
for (let file of this.getOwnFiles()) {
|
|
462
|
+
if ((0, reflection_1.isBrsFile)(file)) {
|
|
463
|
+
this._symbolTable.mergeSymbolTable((_b = file.parser) === null || _b === void 0 ? void 0 : _b.symbolTable);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return this._symbolTable;
|
|
468
|
+
}
|
|
469
|
+
get memberTable() {
|
|
470
|
+
var _a;
|
|
471
|
+
if (!this._memberTable) {
|
|
472
|
+
this._memberTable = new SymbolTable_1.SymbolTable((_a = this.getParentScope()) === null || _a === void 0 ? void 0 : _a.memberTable);
|
|
473
|
+
if (!this.getParentScope()) {
|
|
474
|
+
this._memberTable.addSymbol('global', null, new ObjectType_1.ObjectType());
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return this._memberTable;
|
|
478
|
+
}
|
|
479
|
+
clearSymbolTable() {
|
|
480
|
+
this._symbolTable = null;
|
|
481
|
+
this._memberTable = null;
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Builds the current symbol table for the scope, by merging the tables for all the files in this scope.
|
|
485
|
+
* Also links all file symbols tables to this new table
|
|
486
|
+
* This will only rebuilt if the symbol table has not been built before
|
|
487
|
+
*/
|
|
488
|
+
linkSymbolTable() {
|
|
489
|
+
var _a;
|
|
490
|
+
for (const file of this.getAllFiles()) {
|
|
491
|
+
if ((0, reflection_1.isBrsFile)(file)) {
|
|
492
|
+
file.parser.symbolTable.setParent(this.symbolTable);
|
|
493
|
+
for (const namespace of file.parser.references.namespaceStatements) {
|
|
494
|
+
const namespaceNameLower = namespace.nameExpression.getName(parser_1.ParseMode.BrighterScript).toLowerCase();
|
|
495
|
+
const namespaceSymbolTable = this.namespaceLookup[namespaceNameLower].symbolTable;
|
|
496
|
+
namespace.symbolTable.setParent(namespaceSymbolTable);
|
|
497
|
+
}
|
|
498
|
+
//TODO TYPES: build symbol tables for dotted set assignments using actual values
|
|
499
|
+
// Currently this is prone to call-stack issues.
|
|
500
|
+
// eg. m.key = "value"
|
|
501
|
+
for (const dotSetStmt of file.parser.references.dottedSetStatements) {
|
|
502
|
+
if ((0, reflection_1.isVariableExpression)(dotSetStmt.obj)) {
|
|
503
|
+
if (dotSetStmt.obj.getName(parser_1.ParseMode.BrighterScript).toLowerCase() === 'm') {
|
|
504
|
+
this.memberTable.addSymbol(dotSetStmt.name.text, dotSetStmt.range, new DynamicType_1.DynamicType());
|
|
505
|
+
// TODO TYPES: get actual types: getBscTypeFromExpression(dotSetStmt.value, file.parser.references.getContainingFunctionExpression(dotSetStmt.name)));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
// TODO TYPES: What other types of expressions could these be?
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
// also link classes
|
|
515
|
+
const classMap = this.getClassMap();
|
|
516
|
+
for (const pair of classMap) {
|
|
517
|
+
const classStmt = (_a = pair[1]) === null || _a === void 0 ? void 0 : _a.item;
|
|
518
|
+
classStmt === null || classStmt === void 0 ? void 0 : classStmt.buildSymbolTable(this.getParentClass(classStmt));
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
unlinkSymbolTable() {
|
|
522
|
+
var _a;
|
|
523
|
+
for (let file of this.getOwnFiles()) {
|
|
524
|
+
if ((0, reflection_1.isBrsFile)(file)) {
|
|
525
|
+
(_a = file.parser) === null || _a === void 0 ? void 0 : _a.symbolTable.setParent(null);
|
|
526
|
+
for (const namespace of file.parser.references.namespaceStatements) {
|
|
527
|
+
namespace.symbolTable.setParent(null);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
410
531
|
}
|
|
411
532
|
detectVariableNamespaceCollisions(file) {
|
|
412
533
|
//find all function parameters
|
|
@@ -418,7 +539,7 @@ class Scope {
|
|
|
418
539
|
if (namespace) {
|
|
419
540
|
this.diagnostics.push(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.parameterMayNotHaveSameNameAsNamespace(param.name.text)), { range: param.name.range, relatedInformation: [{
|
|
420
541
|
message: 'Namespace declared here',
|
|
421
|
-
location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(namespace.file.
|
|
542
|
+
location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(namespace.file.srcPath).toString(), namespace.nameRange)
|
|
422
543
|
}] }));
|
|
423
544
|
}
|
|
424
545
|
}
|
|
@@ -430,7 +551,7 @@ class Scope {
|
|
|
430
551
|
if (namespace) {
|
|
431
552
|
this.diagnostics.push(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.variableMayNotHaveSameNameAsNamespace(assignment.name.text)), { range: assignment.name.range, relatedInformation: [{
|
|
432
553
|
message: 'Namespace declared here',
|
|
433
|
-
location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(namespace.file.
|
|
554
|
+
location: vscode_languageserver_1.Location.create(vscode_uri_1.URI.file(namespace.file.srcPath).toString(), namespace.nameRange)
|
|
434
555
|
}] }));
|
|
435
556
|
}
|
|
436
557
|
}
|
|
@@ -458,20 +579,22 @@ class Scope {
|
|
|
458
579
|
* Find function parameters and function return types that are neither built-in types or known Class references
|
|
459
580
|
*/
|
|
460
581
|
diagnosticDetectInvalidFunctionExpressionTypes(file) {
|
|
461
|
-
var _a, _b;
|
|
582
|
+
var _a, _b, _c, _d;
|
|
462
583
|
for (let func of file.parser.references.functionExpressions) {
|
|
463
|
-
|
|
584
|
+
const returnType = (0, BscType_1.getTypeFromContext)(func.returnType, { file: file, scope: this, position: (_a = func.range) === null || _a === void 0 ? void 0 : _a.start });
|
|
585
|
+
if (!returnType && func.returnTypeToken) {
|
|
464
586
|
// check if this custom type is in our class map
|
|
465
|
-
const returnTypeName = func.
|
|
466
|
-
const currentNamespaceName = (
|
|
587
|
+
const returnTypeName = func.returnTypeToken.text;
|
|
588
|
+
const currentNamespaceName = (_b = func.namespaceName) === null || _b === void 0 ? void 0 : _b.getName(parser_1.ParseMode.BrighterScript);
|
|
467
589
|
if (!this.hasClass(returnTypeName, currentNamespaceName)) {
|
|
468
590
|
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.invalidFunctionReturnType(returnTypeName)), { range: func.returnTypeToken.range, file: file }));
|
|
469
591
|
}
|
|
470
592
|
}
|
|
471
593
|
for (let param of func.parameters) {
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
const
|
|
594
|
+
const paramType = (0, BscType_1.getTypeFromContext)(param.type, { file: file, scope: this, position: (_c = param.range) === null || _c === void 0 ? void 0 : _c.start });
|
|
595
|
+
if (!paramType && param.typeToken) {
|
|
596
|
+
const paramTypeName = param.typeToken.text;
|
|
597
|
+
const currentNamespaceName = (_d = func.namespaceName) === null || _d === void 0 ? void 0 : _d.getName(parser_1.ParseMode.BrighterScript);
|
|
475
598
|
if (!this.hasClass(paramTypeName, currentNamespaceName)) {
|
|
476
599
|
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionParameterTypeIsInvalid(param.name.text, paramTypeName)), { range: param.typeToken.range, file: file }));
|
|
477
600
|
}
|
|
@@ -479,6 +602,92 @@ class Scope {
|
|
|
479
602
|
}
|
|
480
603
|
}
|
|
481
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* Find functions with either the wrong type of parameters, or the wrong number of parameters
|
|
607
|
+
*/
|
|
608
|
+
diagnosticDetectInvalidFunctionCalls(file, callableContainersByLowerName) {
|
|
609
|
+
var _a, _b, _c, _d, _e;
|
|
610
|
+
if ((0, reflection_1.isBrsFile)(file)) {
|
|
611
|
+
for (let expCall of file.functionCalls) {
|
|
612
|
+
const symbolTypeInfo = file.getSymbolTypeFromToken(expCall.name, expCall.functionExpression, this);
|
|
613
|
+
let funcType = symbolTypeInfo.type;
|
|
614
|
+
if (!(0, reflection_1.isFunctionType)(funcType) && !(0, reflection_1.isDynamicType)(funcType)) {
|
|
615
|
+
// We don't know if this is a function. Try seeing if it is a global
|
|
616
|
+
const callableContainer = util_1.util.getCallableContainerByFunctionCall(callableContainersByLowerName, expCall);
|
|
617
|
+
if (callableContainer) {
|
|
618
|
+
// We found a global callable with correct number of params - use that
|
|
619
|
+
funcType = (_a = callableContainer.callable) === null || _a === void 0 ? void 0 : _a.type;
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
const allowedParamCount = util_1.util.getMinMaxParamCountByFunctionCall(callableContainersByLowerName, expCall);
|
|
623
|
+
if (allowedParamCount) {
|
|
624
|
+
// We found a global callable, but it needs a different number of args
|
|
625
|
+
this.addMismatchParamCountDiagnostic(allowedParamCount, expCall, file);
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
if ((0, reflection_1.isFunctionType)(funcType)) {
|
|
631
|
+
// Check for Argument count mismatch.
|
|
632
|
+
//get min/max parameter count for callable
|
|
633
|
+
let paramCount = util_1.util.getMinMaxParamCount(funcType.params);
|
|
634
|
+
if (expCall.args.length > paramCount.max || expCall.args.length < paramCount.min) {
|
|
635
|
+
this.addMismatchParamCountDiagnostic(paramCount, expCall, file);
|
|
636
|
+
}
|
|
637
|
+
// Check for Argument type mismatch.
|
|
638
|
+
const paramTypeContext = { file: file, scope: this, position: (_b = expCall.functionExpression.range) === null || _b === void 0 ? void 0 : _b.start };
|
|
639
|
+
const argTypeContext = { file: file, scope: this, position: (_c = expCall.range) === null || _c === void 0 ? void 0 : _c.start };
|
|
640
|
+
for (let index = 0; index < funcType.params.length; index++) {
|
|
641
|
+
const param = funcType.params[index];
|
|
642
|
+
const arg = expCall.args[index];
|
|
643
|
+
if (!arg) {
|
|
644
|
+
// not enough args
|
|
645
|
+
break;
|
|
646
|
+
}
|
|
647
|
+
let argType = (_d = arg.type) !== null && _d !== void 0 ? _d : new UninitializedType_1.UninitializedType();
|
|
648
|
+
let assignable = false;
|
|
649
|
+
const paramType = (0, BscType_1.getTypeFromContext)(param.type, paramTypeContext);
|
|
650
|
+
if (!paramType) {
|
|
651
|
+
// other error - can not determine what type this parameter should be
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
argType = (0, BscType_1.getTypeFromContext)(argType, argTypeContext);
|
|
655
|
+
if ((0, reflection_1.isCustomType)(argType)) {
|
|
656
|
+
const lowerNamespaceName = (_e = expCall.functionExpression.namespaceName) === null || _e === void 0 ? void 0 : _e.getName().toLowerCase();
|
|
657
|
+
assignable = argType.isAssignableTo(paramType, argTypeContext, this.getAncestorTypeList(argType.name, lowerNamespaceName));
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
assignable = argType === null || argType === void 0 ? void 0 : argType.isAssignableTo(paramType, argTypeContext);
|
|
661
|
+
}
|
|
662
|
+
if (!assignable) {
|
|
663
|
+
// TODO TYPES: perhaps this should be a strict mode setting?
|
|
664
|
+
assignable = argType === null || argType === void 0 ? void 0 : argType.isConvertibleTo(paramType, argTypeContext);
|
|
665
|
+
}
|
|
666
|
+
if (!assignable) {
|
|
667
|
+
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.argumentTypeMismatch(argType === null || argType === void 0 ? void 0 : argType.toString(argTypeContext), paramType.toString(paramTypeContext))), { range: arg === null || arg === void 0 ? void 0 : arg.range, file: file }));
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
else if ((0, reflection_1.isInvalidType)(symbolTypeInfo.type)) {
|
|
672
|
+
// TODO TYPES: standard member functions like integer.ToStr() are not detectable yet.
|
|
673
|
+
}
|
|
674
|
+
else if ((0, reflection_1.isDynamicType)(symbolTypeInfo.type)) {
|
|
675
|
+
// maybe this is a function? who knows
|
|
676
|
+
}
|
|
677
|
+
else {
|
|
678
|
+
const functionNameText = symbolTypeInfo.expandedTokenText;
|
|
679
|
+
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.callToUnknownFunction(functionNameText, this.name)), { range: expCall.nameRange,
|
|
680
|
+
//TODO detect end of expression call
|
|
681
|
+
file: file }));
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
addMismatchParamCountDiagnostic(paramCount, expCall, file) {
|
|
687
|
+
const minMaxParamsText = paramCount.min === paramCount.max ? paramCount.max : `${paramCount.min}-${paramCount.max}`;
|
|
688
|
+
const expCallArgCount = expCall.args.length;
|
|
689
|
+
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.mismatchArgumentCount(minMaxParamsText, expCallArgCount)), { range: expCall.nameRange, file: file }));
|
|
690
|
+
}
|
|
482
691
|
getNewExpressions() {
|
|
483
692
|
let result = [];
|
|
484
693
|
this.enumerateBrsFiles((file) => {
|
|
@@ -490,131 +699,54 @@ class Scope {
|
|
|
490
699
|
});
|
|
491
700
|
return result;
|
|
492
701
|
}
|
|
493
|
-
validateClasses() {
|
|
702
|
+
validateClasses(file) {
|
|
494
703
|
let validator = new ClassValidator_1.BsClassValidator();
|
|
495
|
-
validator.validate(this);
|
|
704
|
+
validator.validate(this, file);
|
|
496
705
|
this.diagnostics.push(...validator.diagnostics);
|
|
497
706
|
}
|
|
498
707
|
/**
|
|
499
|
-
* Detect
|
|
500
|
-
* @param file
|
|
501
|
-
* @param callableContainersByLowerName
|
|
502
|
-
*/
|
|
503
|
-
diagnosticDetectFunctionCallsWithWrongParamCount(file, callableContainersByLowerName) {
|
|
504
|
-
//validate all function calls
|
|
505
|
-
for (let expCall of file.functionCalls) {
|
|
506
|
-
let callableContainersWithThisName = callableContainersByLowerName.get(expCall.name.toLowerCase());
|
|
507
|
-
//use the first item from callablesByLowerName, because if there are more, that's a separate error
|
|
508
|
-
let knownCallableContainer = callableContainersWithThisName ? callableContainersWithThisName[0] : undefined;
|
|
509
|
-
if (knownCallableContainer) {
|
|
510
|
-
//get min/max parameter count for callable
|
|
511
|
-
let minParams = 0;
|
|
512
|
-
let maxParams = 0;
|
|
513
|
-
for (let param of knownCallableContainer.callable.params) {
|
|
514
|
-
maxParams++;
|
|
515
|
-
//optional parameters must come last, so we can assume that minParams won't increase once we hit
|
|
516
|
-
//the first isOptional
|
|
517
|
-
if (param.isOptional === false) {
|
|
518
|
-
minParams++;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
let expCallArgCount = expCall.args.length;
|
|
522
|
-
if (expCall.args.length > maxParams || expCall.args.length < minParams) {
|
|
523
|
-
let minMaxParamsText = minParams === maxParams ? maxParams : `${minParams}-${maxParams}`;
|
|
524
|
-
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.mismatchArgumentCount(minMaxParamsText, expCallArgCount)), { range: expCall.nameRange,
|
|
525
|
-
//TODO detect end of expression call
|
|
526
|
-
file: file }));
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
/**
|
|
532
|
-
* Detect local variables (function scope) that have the same name as scope calls
|
|
708
|
+
* Detect local variables (vars declared within a function expression) that have the same name as scope calls
|
|
533
709
|
* @param file
|
|
534
710
|
* @param callableContainerMap
|
|
535
711
|
*/
|
|
536
712
|
diagnosticDetectShadowedLocalVars(file, callableContainerMap) {
|
|
537
|
-
var _a;
|
|
538
713
|
const classMap = this.getClassMap();
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const varName = varDeclaration.name;
|
|
544
|
-
const lowerVarName = varName.toLowerCase();
|
|
714
|
+
for (let func of file.parser.references.functionExpressions) {
|
|
715
|
+
//every var declaration in this function expression
|
|
716
|
+
for (let symbol of func.symbolTable.getOwnSymbols()) {
|
|
717
|
+
const symbolNameLower = symbol.name.toLowerCase();
|
|
545
718
|
//if the var is a function
|
|
546
|
-
if ((0, reflection_1.isFunctionType)(
|
|
719
|
+
if ((0, reflection_1.isFunctionType)(symbol.type)) {
|
|
547
720
|
//local var function with same name as stdlib function
|
|
548
721
|
if (
|
|
549
722
|
//has same name as stdlib
|
|
550
|
-
globalCallables_1.globalCallableMap.has(
|
|
551
|
-
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib')), { range:
|
|
723
|
+
globalCallables_1.globalCallableMap.has(symbolNameLower)) {
|
|
724
|
+
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib')), { range: symbol.range, file: file }));
|
|
552
725
|
//this check needs to come after the stdlib one, because the stdlib functions are included
|
|
553
726
|
//in the scope function list
|
|
554
727
|
}
|
|
555
728
|
else if (
|
|
556
729
|
//has same name as scope function
|
|
557
|
-
callableContainerMap.has(
|
|
558
|
-
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('scope')), { range:
|
|
730
|
+
callableContainerMap.has(symbolNameLower)) {
|
|
731
|
+
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('scope')), { range: symbol.range, file: file }));
|
|
559
732
|
}
|
|
560
733
|
//var is not a function
|
|
561
734
|
}
|
|
562
735
|
else if (
|
|
563
736
|
//is NOT a callable from stdlib (because non-function local vars can have same name as stdlib names)
|
|
564
|
-
!globalCallables_1.globalCallableMap.has(
|
|
737
|
+
!globalCallables_1.globalCallableMap.has(symbolNameLower)) {
|
|
565
738
|
//is same name as a callable
|
|
566
|
-
if (callableContainerMap.has(
|
|
567
|
-
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarShadowedByScopedFunction()), { range:
|
|
739
|
+
if (callableContainerMap.has(symbolNameLower)) {
|
|
740
|
+
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarShadowedByScopedFunction()), { range: symbol.range, file: file }));
|
|
568
741
|
//has the same name as an in-scope class
|
|
569
742
|
}
|
|
570
|
-
else if (classMap.has(
|
|
571
|
-
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarSameNameAsClass(
|
|
743
|
+
else if (classMap.has(symbolNameLower)) {
|
|
744
|
+
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarSameNameAsClass(classMap.get(symbolNameLower).item.getName(parser_1.ParseMode.BrighterScript))), { range: symbol.range, file: file }));
|
|
572
745
|
}
|
|
573
746
|
}
|
|
574
747
|
}
|
|
575
748
|
}
|
|
576
749
|
}
|
|
577
|
-
/**
|
|
578
|
-
* Detect calls to functions that are not defined in this scope
|
|
579
|
-
* @param file
|
|
580
|
-
* @param callablesByLowerName
|
|
581
|
-
*/
|
|
582
|
-
diagnosticDetectCallsToUnknownFunctions(file, callablesByLowerName) {
|
|
583
|
-
//validate all expression calls
|
|
584
|
-
for (let expCall of file.functionCalls) {
|
|
585
|
-
const lowerName = expCall.name.toLowerCase();
|
|
586
|
-
//for now, skip validation on any method named "super" within `.bs` contexts.
|
|
587
|
-
//TODO revise this logic so we know if this function call resides within a class constructor function
|
|
588
|
-
if (file.extension === '.bs' && lowerName === 'super') {
|
|
589
|
-
continue;
|
|
590
|
-
}
|
|
591
|
-
//get the local scope for this expression
|
|
592
|
-
let scope = file.getFunctionScopeAtPosition(expCall.nameRange.start);
|
|
593
|
-
//if we don't already have a variable with this name.
|
|
594
|
-
if (!(scope === null || scope === void 0 ? void 0 : scope.getVariableByName(lowerName))) {
|
|
595
|
-
let callablesWithThisName;
|
|
596
|
-
if (expCall.functionScope.func.namespaceName) {
|
|
597
|
-
// prefer namespaced function
|
|
598
|
-
const potentialNamespacedCallable = expCall.functionScope.func.namespaceName.getName(parser_1.ParseMode.BrightScript).toLowerCase() + '_' + lowerName;
|
|
599
|
-
callablesWithThisName = callablesByLowerName.get(potentialNamespacedCallable.toLowerCase());
|
|
600
|
-
}
|
|
601
|
-
if (!callablesWithThisName) {
|
|
602
|
-
// just try it as is
|
|
603
|
-
callablesWithThisName = callablesByLowerName.get(lowerName);
|
|
604
|
-
}
|
|
605
|
-
//use the first item from callablesByLowerName, because if there are more, that's a separate error
|
|
606
|
-
let knownCallable = callablesWithThisName ? callablesWithThisName[0] : undefined;
|
|
607
|
-
//detect calls to unknown functions
|
|
608
|
-
if (!knownCallable) {
|
|
609
|
-
this.diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.callToUnknownFunction(expCall.name, this.name)), { range: expCall.nameRange, file: file }));
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
else {
|
|
613
|
-
//if we found a variable with the same name as the function, assume the call is "known".
|
|
614
|
-
//If the variable is a different type, some other check should add a diagnostic for that.
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
750
|
/**
|
|
619
751
|
* Create diagnostics for any duplicate function declarations
|
|
620
752
|
* @param callablesByLowerName
|
|
@@ -691,7 +823,7 @@ class Scope {
|
|
|
691
823
|
//if we can't find the file
|
|
692
824
|
if (!referencedFile) {
|
|
693
825
|
//skip the default bslib file, it will exist at transpile time but should not show up in the program during validation cycle
|
|
694
|
-
if (scriptImport.pkgPath === `source
|
|
826
|
+
if (scriptImport.pkgPath === `pkg:/source/bslib.brs`) {
|
|
695
827
|
continue;
|
|
696
828
|
}
|
|
697
829
|
let dInfo;
|