brighterscript 1.0.0-alpha.26 → 1.0.0-alpha.28
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 +82 -5
- package/README.md +1 -1
- package/dist/AstValidationSegmenter.d.ts +1 -1
- package/dist/AstValidationSegmenter.js +4 -5
- package/dist/AstValidationSegmenter.js.map +1 -1
- package/dist/DiagnosticFilterer.d.ts +7 -4
- package/dist/DiagnosticFilterer.js +67 -37
- package/dist/DiagnosticFilterer.js.map +1 -1
- package/dist/DiagnosticMessages.d.ts +1 -3
- package/dist/DiagnosticMessages.js +5 -8
- package/dist/DiagnosticMessages.js.map +1 -1
- package/dist/PluginInterface.js +1 -1
- package/dist/PluginInterface.js.map +1 -1
- package/dist/Program.d.ts +1 -1
- package/dist/Program.js +34 -31
- package/dist/Program.js.map +1 -1
- package/dist/Scope.d.ts +7 -34
- package/dist/Scope.js +45 -305
- package/dist/Scope.js.map +1 -1
- package/dist/SymbolTable.d.ts +1 -8
- package/dist/SymbolTable.js +1 -10
- package/dist/SymbolTable.js.map +1 -1
- package/dist/SymbolTypeFlag.d.ts +8 -0
- package/dist/SymbolTypeFlag.js +13 -0
- package/dist/SymbolTypeFlag.js.map +1 -0
- package/dist/XmlScope.d.ts +0 -8
- package/dist/XmlScope.js +6 -84
- package/dist/XmlScope.js.map +1 -1
- package/dist/astUtils/CachedLookups.d.ts +1 -2
- package/dist/astUtils/CachedLookups.js +4 -19
- package/dist/astUtils/CachedLookups.js.map +1 -1
- package/dist/astUtils/creators.d.ts +1 -0
- package/dist/astUtils/creators.js +48 -8
- package/dist/astUtils/creators.js.map +1 -1
- package/dist/astUtils/creators.spec.js +0 -10
- package/dist/astUtils/creators.spec.js.map +1 -1
- package/dist/astUtils/reflection.d.ts +3 -2
- package/dist/astUtils/reflection.js +11 -7
- package/dist/astUtils/reflection.js.map +1 -1
- package/dist/astUtils/reflection.spec.js +11 -16
- package/dist/astUtils/reflection.spec.js.map +1 -1
- package/dist/astUtils/visitors.d.ts +1 -2
- package/dist/astUtils/visitors.js.map +1 -1
- package/dist/astUtils/visitors.spec.js +1 -5
- package/dist/astUtils/visitors.spec.js.map +1 -1
- package/dist/bscPlugin/BscPlugin.d.ts +2 -1
- package/dist/bscPlugin/BscPlugin.js +4 -0
- package/dist/bscPlugin/BscPlugin.js.map +1 -1
- package/dist/bscPlugin/SignatureHelpUtil.js +4 -3
- package/dist/bscPlugin/SignatureHelpUtil.js.map +1 -1
- package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +1 -0
- package/dist/bscPlugin/completions/CompletionsProcessor.js +46 -29
- package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -1
- package/dist/bscPlugin/completions/CompletionsProcessor.spec.js +39 -0
- package/dist/bscPlugin/completions/CompletionsProcessor.spec.js.map +1 -1
- package/dist/bscPlugin/hover/HoverProcessor.d.ts +1 -0
- package/dist/bscPlugin/hover/HoverProcessor.js +30 -10
- package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -1
- package/dist/bscPlugin/hover/HoverProcessor.spec.js +125 -7
- package/dist/bscPlugin/hover/HoverProcessor.spec.js.map +1 -1
- package/dist/bscPlugin/references/ReferencesProvider.d.ts +12 -0
- package/dist/bscPlugin/references/ReferencesProvider.js +56 -0
- package/dist/bscPlugin/references/ReferencesProvider.js.map +1 -0
- package/dist/bscPlugin/references/ReferencesProvider.spec.d.ts +1 -0
- package/dist/bscPlugin/references/ReferencesProvider.spec.js +51 -0
- package/dist/bscPlugin/references/ReferencesProvider.spec.js.map +1 -0
- package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +2 -3
- package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -1
- package/dist/bscPlugin/validation/BrsFileValidator.js +25 -31
- package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -1
- package/dist/bscPlugin/validation/ScopeValidator.d.ts +34 -1
- package/dist/bscPlugin/validation/ScopeValidator.js +435 -27
- package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -1
- package/dist/bscPlugin/validation/ScopeValidator.spec.js +178 -10
- package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +1 -1
- package/dist/diagnosticUtils.d.ts +1 -1
- package/dist/files/BrsFile.Class.spec.js +15 -7
- package/dist/files/BrsFile.Class.spec.js.map +1 -1
- package/dist/files/BrsFile.d.ts +14 -5
- package/dist/files/BrsFile.js +83 -150
- package/dist/files/BrsFile.js.map +1 -1
- package/dist/files/BrsFile.spec.js +429 -162
- package/dist/files/BrsFile.spec.js.map +1 -1
- package/dist/files/XmlFile.d.ts +2 -3
- package/dist/files/XmlFile.js +1 -3
- package/dist/files/XmlFile.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +117 -85
- package/dist/interfaces.js +9 -9
- package/dist/interfaces.js.map +1 -1
- package/dist/lexer/Lexer.d.ts +9 -3
- package/dist/lexer/Lexer.js +36 -15
- package/dist/lexer/Lexer.js.map +1 -1
- package/dist/lexer/Lexer.spec.js +76 -38
- package/dist/lexer/Lexer.spec.js.map +1 -1
- package/dist/lexer/Token.js +1 -1
- package/dist/lexer/Token.js.map +1 -1
- package/dist/lexer/TokenKind.d.ts +1 -0
- package/dist/lexer/TokenKind.js +4 -1
- package/dist/lexer/TokenKind.js.map +1 -1
- package/dist/parser/AstNode.d.ts +1 -2
- package/dist/parser/AstNode.js +0 -1
- package/dist/parser/AstNode.js.map +1 -1
- package/dist/parser/BrsTranspileState.d.ts +1 -1
- package/dist/parser/Expression.d.ts +153 -135
- package/dist/parser/Expression.js +204 -114
- package/dist/parser/Expression.js.map +1 -1
- package/dist/parser/Parser.Class.spec.js +15 -16
- package/dist/parser/Parser.Class.spec.js.map +1 -1
- package/dist/parser/Parser.d.ts +8 -4
- package/dist/parser/Parser.js +108 -149
- package/dist/parser/Parser.js.map +1 -1
- package/dist/parser/Parser.spec.js +48 -72
- package/dist/parser/Parser.spec.js.map +1 -1
- package/dist/parser/SGParser.js +49 -35
- package/dist/parser/SGParser.js.map +1 -1
- package/dist/parser/SGTypes.d.ts +36 -24
- package/dist/parser/SGTypes.js +31 -60
- package/dist/parser/SGTypes.js.map +1 -1
- package/dist/parser/Statement.d.ts +214 -207
- package/dist/parser/Statement.js +248 -169
- package/dist/parser/Statement.js.map +1 -1
- package/dist/parser/Statement.spec.js +0 -13
- package/dist/parser/Statement.spec.js.map +1 -1
- package/dist/parser/TranspileState.d.ts +17 -8
- package/dist/parser/TranspileState.js +64 -6
- package/dist/parser/TranspileState.js.map +1 -1
- package/dist/parser/tests/Parser.spec.d.ts +1 -1
- package/dist/parser/tests/Parser.spec.js +1 -2
- package/dist/parser/tests/Parser.spec.js.map +1 -1
- package/dist/parser/tests/controlFlow/If.spec.js +1 -1
- package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
- package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +1 -3
- package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
- package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +44 -0
- package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/TemplateStringExpression.spec.js +6 -6
- package/dist/parser/tests/expression/TernaryExpression.spec.js +47 -0
- package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
- package/dist/parser/tests/expression/TypeExpression.spec.js +8 -9
- package/dist/parser/tests/expression/TypeExpression.spec.js.map +1 -1
- package/dist/parser/tests/statement/ConstStatement.spec.js +2 -2
- package/dist/parser/tests/statement/InterfaceStatement.spec.js +8 -1
- package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
- package/dist/parser/tests/statement/Misc.spec.js +25 -5
- package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
- package/dist/parser/tests/statement/TryCatch.spec.js +9 -2
- package/dist/parser/tests/statement/TryCatch.spec.js.map +1 -1
- package/dist/preprocessor/Chunk.js +1 -2
- package/dist/preprocessor/Chunk.js.map +1 -1
- package/dist/preprocessor/PreprocessorParser.js +2 -1
- package/dist/preprocessor/PreprocessorParser.js.map +1 -1
- package/dist/roku-types/data.json +70 -52
- package/dist/roku-types/index.d.ts +43 -21
- package/dist/types/ArrayType.js +1 -2
- package/dist/types/ArrayType.js.map +1 -1
- package/dist/types/ArrayType.spec.js +7 -8
- package/dist/types/ArrayType.spec.js.map +1 -1
- package/dist/types/AssociativeArrayType.d.ts +3 -0
- package/dist/types/AssociativeArrayType.js +10 -2
- package/dist/types/AssociativeArrayType.js.map +1 -1
- package/dist/types/BscType.d.ts +1 -1
- package/dist/types/BscType.js +3 -3
- package/dist/types/BscType.js.map +1 -1
- package/dist/types/BuiltInInterfaceAdder.js +7 -8
- package/dist/types/BuiltInInterfaceAdder.js.map +1 -1
- package/dist/types/BuiltInInterfaceAdder.spec.js +31 -32
- package/dist/types/BuiltInInterfaceAdder.spec.js.map +1 -1
- package/dist/types/ClassType.spec.js +10 -11
- package/dist/types/ClassType.spec.js.map +1 -1
- package/dist/types/ComponentType.d.ts +2 -1
- package/dist/types/ComponentType.js.map +1 -1
- package/dist/types/EnumType.js +2 -3
- package/dist/types/EnumType.js.map +1 -1
- package/dist/types/InheritableType.js +3 -4
- package/dist/types/InheritableType.js.map +1 -1
- package/dist/types/InterfaceType.js +2 -3
- package/dist/types/InterfaceType.js.map +1 -1
- package/dist/types/InterfaceType.spec.js +3 -4
- package/dist/types/InterfaceType.spec.js.map +1 -1
- package/dist/types/ObjectType.js +1 -2
- package/dist/types/ObjectType.js.map +1 -1
- package/dist/types/ReferenceType.d.ts +1 -1
- package/dist/types/ReferenceType.spec.js +21 -22
- package/dist/types/ReferenceType.spec.js.map +1 -1
- package/dist/types/UnionType.js +3 -3
- package/dist/types/UnionType.js.map +1 -1
- package/dist/types/UnionType.spec.js +37 -38
- package/dist/types/UnionType.spec.js.map +1 -1
- package/dist/types/helper.spec.js +4 -5
- package/dist/types/helper.spec.js.map +1 -1
- package/dist/util.d.ts +21 -11
- package/dist/util.js +116 -37
- package/dist/util.js.map +1 -1
- package/dist/validators/ClassValidator.d.ts +0 -9
- package/dist/validators/ClassValidator.js +3 -46
- package/dist/validators/ClassValidator.js.map +1 -1
- package/package.json +6 -3
|
@@ -6,14 +6,17 @@ const reflection_1 = require("../../astUtils/reflection");
|
|
|
6
6
|
const Cache_1 = require("../../Cache");
|
|
7
7
|
const DiagnosticMessages_1 = require("../../DiagnosticMessages");
|
|
8
8
|
const interfaces_1 = require("../../interfaces");
|
|
9
|
-
const SymbolTable_1 = require("../../SymbolTable");
|
|
10
9
|
const util_1 = require("../../util");
|
|
11
10
|
const roku_types_1 = require("../../roku-types");
|
|
11
|
+
const AstNode_1 = require("../../parser/AstNode");
|
|
12
12
|
const Expression_1 = require("../../parser/Expression");
|
|
13
13
|
const visitors_1 = require("../../astUtils/visitors");
|
|
14
14
|
const AstValidationSegmenter_1 = require("../../AstValidationSegmenter");
|
|
15
15
|
const TokenKind_1 = require("../../lexer/TokenKind");
|
|
16
16
|
const Parser_1 = require("../../parser/Parser");
|
|
17
|
+
const ClassValidator_1 = require("../../validators/ClassValidator");
|
|
18
|
+
const globalCallables_1 = require("../../globalCallables");
|
|
19
|
+
const SGTypes_1 = require("../../parser/SGTypes");
|
|
17
20
|
/**
|
|
18
21
|
* The lower-case names of all platform-included scenegraph nodes
|
|
19
22
|
*/
|
|
@@ -37,6 +40,15 @@ class ScopeValidator {
|
|
|
37
40
|
}
|
|
38
41
|
this.walkFiles();
|
|
39
42
|
this.detectDuplicateEnums();
|
|
43
|
+
this.flagDuplicateFunctionDeclarations();
|
|
44
|
+
this.validateScriptImportPaths();
|
|
45
|
+
this.validateClasses();
|
|
46
|
+
if ((0, reflection_1.isXmlScope)(event.scope)) {
|
|
47
|
+
//detect when the child imports a script that its ancestor also imports
|
|
48
|
+
this.diagnosticDetectDuplicateAncestorScriptImports(event.scope);
|
|
49
|
+
//validate component interface
|
|
50
|
+
this.validateXmlInterface(event.scope);
|
|
51
|
+
}
|
|
40
52
|
}
|
|
41
53
|
reset() {
|
|
42
54
|
this.event = undefined;
|
|
@@ -44,13 +56,19 @@ class ScopeValidator {
|
|
|
44
56
|
this.multiScopeCache.clear();
|
|
45
57
|
}
|
|
46
58
|
walkFiles() {
|
|
59
|
+
//do many per-file checks for every file in this (and parent) scopes
|
|
60
|
+
this.event.scope.enumerateBrsFiles((file) => {
|
|
61
|
+
this.diagnosticDetectFunctionCollisions(file);
|
|
62
|
+
this.detectVariableNamespaceCollisions(file);
|
|
63
|
+
this.detectNameCollisions(file);
|
|
64
|
+
});
|
|
47
65
|
this.event.scope.enumerateOwnFiles((file) => {
|
|
48
66
|
if ((0, reflection_1.isBrsFile)(file)) {
|
|
49
67
|
const hasChangeInfo = this.event.changedFiles && this.event.changedSymbols;
|
|
50
68
|
let thisFileRequiresChangedSymbol = false;
|
|
51
69
|
for (let requiredSymbol of file.requiredSymbols) {
|
|
52
70
|
// eslint-disable-next-line no-bitwise
|
|
53
|
-
for (const flag of [
|
|
71
|
+
for (const flag of [1 /* SymbolTypeFlag.runtime */, 2 /* SymbolTypeFlag.typetime */]) {
|
|
54
72
|
// eslint-disable-next-line no-bitwise
|
|
55
73
|
if (flag & requiredSymbol.flags) {
|
|
56
74
|
const changeSymbolSetForFlag = this.event.changedSymbols.get(flag);
|
|
@@ -93,6 +111,9 @@ class ScopeValidator {
|
|
|
93
111
|
},
|
|
94
112
|
AssignmentStatement: (assignStmt) => {
|
|
95
113
|
this.validateAssignmentStatement(file, assignStmt);
|
|
114
|
+
},
|
|
115
|
+
NewExpression: (newExpr) => {
|
|
116
|
+
this.validateNewExpression(file, newExpr);
|
|
96
117
|
}
|
|
97
118
|
});
|
|
98
119
|
const segmentsToWalkForValidation = (thisFileHasChanges || !hasChangeInfo)
|
|
@@ -116,7 +137,7 @@ class ScopeValidator {
|
|
|
116
137
|
/**
|
|
117
138
|
* If this is the lhs of an assignment, we don't need to flag it as unresolved
|
|
118
139
|
*/
|
|
119
|
-
|
|
140
|
+
hasValidDeclaration(expression, exprType, definingNode) {
|
|
120
141
|
var _a, _b;
|
|
121
142
|
if (!(0, reflection_1.isVariableExpression)(expression)) {
|
|
122
143
|
return false;
|
|
@@ -127,6 +148,10 @@ class ScopeValidator {
|
|
|
127
148
|
assignmentAncestor = definingNode;
|
|
128
149
|
return ((_a = assignmentAncestor === null || assignmentAncestor === void 0 ? void 0 : assignmentAncestor.tokens.name) === null || _a === void 0 ? void 0 : _a.text.toLowerCase()) === ((_b = expression === null || expression === void 0 ? void 0 : expression.tokens.name) === null || _b === void 0 ? void 0 : _b.text.toLowerCase());
|
|
129
150
|
}
|
|
151
|
+
else if ((0, reflection_1.isFunctionParameterExpression)(definingNode)) {
|
|
152
|
+
// this symbol was defined in a function param
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
130
155
|
else {
|
|
131
156
|
assignmentAncestor = expression === null || expression === void 0 ? void 0 : expression.findAncestor(reflection_1.isAssignmentStatement);
|
|
132
157
|
}
|
|
@@ -241,7 +266,7 @@ class ScopeValidator {
|
|
|
241
266
|
*/
|
|
242
267
|
validateFunctionCall(file, expression) {
|
|
243
268
|
var _a, _b;
|
|
244
|
-
const getTypeOptions = { flags:
|
|
269
|
+
const getTypeOptions = { flags: 1 /* SymbolTypeFlag.runtime */, data: {} };
|
|
245
270
|
let funcType = (_a = expression === null || expression === void 0 ? void 0 : expression.callee) === null || _a === void 0 ? void 0 : _a.getType(getTypeOptions);
|
|
246
271
|
if ((funcType === null || funcType === void 0 ? void 0 : funcType.isResolvable()) && (0, reflection_1.isClassType)(funcType)) {
|
|
247
272
|
// We're calling a class - get the constructor
|
|
@@ -274,7 +299,7 @@ class ScopeValidator {
|
|
|
274
299
|
let paramIndex = 0;
|
|
275
300
|
for (let arg of expression.args) {
|
|
276
301
|
const data = {};
|
|
277
|
-
let argType = arg.getType({ flags:
|
|
302
|
+
let argType = arg.getType({ flags: 1 /* SymbolTypeFlag.runtime */, data: data });
|
|
278
303
|
const paramType = (_b = funcType.params[paramIndex]) === null || _b === void 0 ? void 0 : _b.type;
|
|
279
304
|
if (!paramType) {
|
|
280
305
|
// unable to find a paramType -- maybe there are more args than params
|
|
@@ -302,8 +327,8 @@ class ScopeValidator {
|
|
|
302
327
|
*/
|
|
303
328
|
validateReturnStatement(file, returnStmt) {
|
|
304
329
|
var _a;
|
|
305
|
-
const getTypeOptions = { flags:
|
|
306
|
-
let funcType = returnStmt.findAncestor(reflection_1.isFunctionExpression).getType({ flags:
|
|
330
|
+
const getTypeOptions = { flags: 1 /* SymbolTypeFlag.runtime */ };
|
|
331
|
+
let funcType = returnStmt.findAncestor(reflection_1.isFunctionExpression).getType({ flags: 2 /* SymbolTypeFlag.typetime */ });
|
|
307
332
|
if ((0, reflection_1.isTypedFunctionType)(funcType)) {
|
|
308
333
|
const actualReturnType = (_a = returnStmt.value) === null || _a === void 0 ? void 0 : _a.getType(getTypeOptions);
|
|
309
334
|
const compatibilityData = {};
|
|
@@ -318,7 +343,7 @@ class ScopeValidator {
|
|
|
318
343
|
validateDottedSetStatement(file, dottedSetStmt) {
|
|
319
344
|
var _a;
|
|
320
345
|
const typeChainExpectedLHS = [];
|
|
321
|
-
const getTypeOpts = { flags:
|
|
346
|
+
const getTypeOpts = { flags: 1 /* SymbolTypeFlag.runtime */ };
|
|
322
347
|
const expectedLHSType = dottedSetStmt === null || dottedSetStmt === void 0 ? void 0 : dottedSetStmt.getType(Object.assign(Object.assign({}, getTypeOpts), { data: {}, typeChain: typeChainExpectedLHS }));
|
|
323
348
|
const actualRHSType = (_a = dottedSetStmt === null || dottedSetStmt === void 0 ? void 0 : dottedSetStmt.value) === null || _a === void 0 ? void 0 : _a.getType(getTypeOpts);
|
|
324
349
|
const compatibilityData = {};
|
|
@@ -348,11 +373,14 @@ class ScopeValidator {
|
|
|
348
373
|
return;
|
|
349
374
|
}
|
|
350
375
|
const typeChainExpectedLHS = [];
|
|
351
|
-
const getTypeOpts = { flags:
|
|
376
|
+
const getTypeOpts = { flags: 1 /* SymbolTypeFlag.runtime */ };
|
|
352
377
|
const expectedLHSType = assignStmt.typeExpression.getType(Object.assign(Object.assign({}, getTypeOpts), { data: {}, typeChain: typeChainExpectedLHS }));
|
|
353
378
|
const actualRHSType = (_a = assignStmt.value) === null || _a === void 0 ? void 0 : _a.getType(getTypeOpts);
|
|
354
379
|
const compatibilityData = {};
|
|
355
|
-
if (!
|
|
380
|
+
if (!expectedLHSType || !expectedLHSType.isResolvable()) {
|
|
381
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.cannotFindName(assignStmt.typeExpression.getName(Parser_1.ParseMode.BrighterScript))), { range: assignStmt.typeExpression.range, file: file }));
|
|
382
|
+
}
|
|
383
|
+
else if (!(expectedLHSType === null || expectedLHSType === void 0 ? void 0 : expectedLHSType.isTypeCompatible(actualRHSType, compatibilityData))) {
|
|
356
384
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.assignmentTypeMismatch(actualRHSType.toString(), expectedLHSType.toString(), compatibilityData)), { range: assignStmt.range, file: file }));
|
|
357
385
|
}
|
|
358
386
|
}
|
|
@@ -360,7 +388,7 @@ class ScopeValidator {
|
|
|
360
388
|
* Detect invalid use of a binary operator
|
|
361
389
|
*/
|
|
362
390
|
validateBinaryExpression(file, binaryExpr) {
|
|
363
|
-
const getTypeOpts = { flags:
|
|
391
|
+
const getTypeOpts = { flags: 1 /* SymbolTypeFlag.runtime */ };
|
|
364
392
|
if (util_1.default.isInTypeExpression(binaryExpr)) {
|
|
365
393
|
return;
|
|
366
394
|
}
|
|
@@ -405,7 +433,7 @@ class ScopeValidator {
|
|
|
405
433
|
* Detect invalid use of a Unary operator
|
|
406
434
|
*/
|
|
407
435
|
validateUnaryExpression(file, unaryExpr) {
|
|
408
|
-
const getTypeOpts = { flags:
|
|
436
|
+
const getTypeOpts = { flags: 1 /* SymbolTypeFlag.runtime */ };
|
|
409
437
|
let rightType = unaryExpr.right.getType(getTypeOpts);
|
|
410
438
|
if (!rightType.isResolvable()) {
|
|
411
439
|
// Can not find the type. error handled elsewhere
|
|
@@ -434,7 +462,7 @@ class ScopeValidator {
|
|
|
434
462
|
}
|
|
435
463
|
}
|
|
436
464
|
validateVariableAndDottedGetExpressions(file, expression) {
|
|
437
|
-
var _a, _b;
|
|
465
|
+
var _a, _b, _c, _d;
|
|
438
466
|
if ((0, reflection_1.isDottedGetExpression)(expression.parent)) {
|
|
439
467
|
// We validate dottedGetExpressions at the top-most level
|
|
440
468
|
return;
|
|
@@ -448,13 +476,13 @@ class ScopeValidator {
|
|
|
448
476
|
return;
|
|
449
477
|
}
|
|
450
478
|
}
|
|
451
|
-
let symbolType =
|
|
452
|
-
let oppositeSymbolType =
|
|
479
|
+
let symbolType = 1 /* SymbolTypeFlag.runtime */;
|
|
480
|
+
let oppositeSymbolType = 2 /* SymbolTypeFlag.typetime */;
|
|
453
481
|
const isUsedAsType = util_1.default.isInTypeExpression(expression);
|
|
454
482
|
if (isUsedAsType) {
|
|
455
483
|
// This is used in a TypeExpression - only look up types from SymbolTable
|
|
456
|
-
symbolType =
|
|
457
|
-
oppositeSymbolType =
|
|
484
|
+
symbolType = 2 /* SymbolTypeFlag.typetime */;
|
|
485
|
+
oppositeSymbolType = 1 /* SymbolTypeFlag.runtime */;
|
|
458
486
|
}
|
|
459
487
|
// Do a complete type check on all DottedGet and Variable expressions
|
|
460
488
|
// this will create a diagnostic if an invalid member is accessed
|
|
@@ -465,18 +493,22 @@ class ScopeValidator {
|
|
|
465
493
|
typeChain: typeChain,
|
|
466
494
|
data: typeData
|
|
467
495
|
});
|
|
468
|
-
const
|
|
469
|
-
if (!this.isTypeKnown(exprType) && !
|
|
470
|
-
if ((_a = expression.getType({ flags: oppositeSymbolType })) === null || _a === void 0 ? void 0 : _a.isResolvable()) {
|
|
496
|
+
const hasValidDeclaration = this.hasValidDeclaration(expression, exprType, typeData === null || typeData === void 0 ? void 0 : typeData.definingNode);
|
|
497
|
+
if (!this.isTypeKnown(exprType) && !hasValidDeclaration) {
|
|
498
|
+
if ((_a = expression.getType({ flags: oppositeSymbolType, isExistenceTest: true })) === null || _a === void 0 ? void 0 : _a.isResolvable()) {
|
|
471
499
|
const oppoSiteTypeChain = [];
|
|
472
|
-
const invalidlyUsedResolvedType = expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain });
|
|
500
|
+
const invalidlyUsedResolvedType = expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain, isExistenceTest: true });
|
|
473
501
|
const typeChainScan = util_1.default.processTypeChain(oppoSiteTypeChain);
|
|
474
502
|
if (isUsedAsType) {
|
|
475
503
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsType(typeChainScan.fullChainName)), { range: expression.range, file: file }));
|
|
476
504
|
}
|
|
477
|
-
else {
|
|
505
|
+
else if (invalidlyUsedResolvedType && !(0, reflection_1.isReferenceType)(invalidlyUsedResolvedType)) {
|
|
478
506
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable(invalidlyUsedResolvedType.toString())), { range: expression.range, file: file }));
|
|
479
507
|
}
|
|
508
|
+
else {
|
|
509
|
+
const typeChainScan = util_1.default.processTypeChain(typeChain);
|
|
510
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem)), { range: typeChainScan.range }));
|
|
511
|
+
}
|
|
480
512
|
}
|
|
481
513
|
else {
|
|
482
514
|
const typeChainScan = util_1.default.processTypeChain(typeChain);
|
|
@@ -486,6 +518,14 @@ class ScopeValidator {
|
|
|
486
518
|
if (isUsedAsType) {
|
|
487
519
|
return;
|
|
488
520
|
}
|
|
521
|
+
const containingNamespaceName = (_b = expression.findAncestor(reflection_1.isNamespaceStatement)) === null || _b === void 0 ? void 0 : _b.getName(Parser_1.ParseMode.BrighterScript);
|
|
522
|
+
if (!((0, reflection_1.isCallExpression)(expression.parent) && (0, reflection_1.isNewExpression)((_c = expression.parent) === null || _c === void 0 ? void 0 : _c.parent))) {
|
|
523
|
+
const classUsedAsVarEntry = this.checkTypeChainForClassUsedAsVar(typeChain, containingNamespaceName);
|
|
524
|
+
if (classUsedAsVarEntry) {
|
|
525
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable(classUsedAsVarEntry.toString())), { range: expression.range, file: file }));
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
489
529
|
const lastTypeInfo = typeChain[typeChain.length - 1];
|
|
490
530
|
const parentTypeInfo = typeChain[typeChain.length - 2];
|
|
491
531
|
this.checkMemberAccessibility(file, expression, typeChain);
|
|
@@ -505,11 +545,35 @@ class ScopeValidator {
|
|
|
505
545
|
if (enumFileLink) {
|
|
506
546
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.unknownEnumValue(lastTypeInfo === null || lastTypeInfo === void 0 ? void 0 : lastTypeInfo.name, typeChainScanForParent.fullChainName)), { range: lastTypeInfo === null || lastTypeInfo === void 0 ? void 0 : lastTypeInfo.range, relatedInformation: [{
|
|
507
547
|
message: 'Enum declared here',
|
|
508
|
-
location: util_1.default.createLocation(vscode_uri_1.URI.file(enumFileLink === null || enumFileLink === void 0 ? void 0 : enumFileLink.file.srcPath).toString(), (
|
|
548
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(enumFileLink === null || enumFileLink === void 0 ? void 0 : enumFileLink.file.srcPath).toString(), (_d = enumFileLink === null || enumFileLink === void 0 ? void 0 : enumFileLink.item) === null || _d === void 0 ? void 0 : _d.tokens.name.range)
|
|
509
549
|
}] }));
|
|
510
550
|
}
|
|
511
551
|
}
|
|
512
552
|
}
|
|
553
|
+
checkTypeChainForClassUsedAsVar(typeChain, containingNamespaceName) {
|
|
554
|
+
const ignoreKinds = [AstNode_1.AstNodeKind.TypeCastExpression, AstNode_1.AstNodeKind.NewExpression];
|
|
555
|
+
let lowerNameSoFar = '';
|
|
556
|
+
let classUsedAsVar;
|
|
557
|
+
let isFirst = true;
|
|
558
|
+
for (let i = 0; i < typeChain.length - 1; i++) { // do not look at final entry - we CAN use the constructor as a variable
|
|
559
|
+
const tce = typeChain[i];
|
|
560
|
+
lowerNameSoFar += `${lowerNameSoFar ? '.' : ''}${tce.name.toLowerCase()}`;
|
|
561
|
+
if (!(0, reflection_1.isNamespaceType)(tce.type)) {
|
|
562
|
+
if (isFirst && containingNamespaceName) {
|
|
563
|
+
lowerNameSoFar = `${containingNamespaceName.toLowerCase()}.${lowerNameSoFar}`;
|
|
564
|
+
}
|
|
565
|
+
if (!tce.kind || ignoreKinds.includes(tce.kind)) {
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
else if ((0, reflection_1.isClassType)(tce.type) && lowerNameSoFar.toLowerCase() === tce.type.name.toLowerCase()) {
|
|
569
|
+
classUsedAsVar = tce.type;
|
|
570
|
+
}
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
isFirst = false;
|
|
574
|
+
}
|
|
575
|
+
return classUsedAsVar;
|
|
576
|
+
}
|
|
513
577
|
/**
|
|
514
578
|
* Adds diagnostics for accibility mismatches
|
|
515
579
|
*
|
|
@@ -530,7 +594,7 @@ class ScopeValidator {
|
|
|
530
594
|
const definingClassName = classStmtThatDefinesChildMember.getName(Parser_1.ParseMode.BrighterScript);
|
|
531
595
|
const inMatchingClassStmt = (containingClassStmt === null || containingClassStmt === void 0 ? void 0 : containingClassStmt.getName(Parser_1.ParseMode.BrighterScript).toLowerCase()) === parentChainItem.type.name.toLowerCase();
|
|
532
596
|
// eslint-disable-next-line no-bitwise
|
|
533
|
-
if (childChainItem.data.flags &
|
|
597
|
+
if (childChainItem.data.flags & 8 /* SymbolTypeFlag.private */) {
|
|
534
598
|
if (!inMatchingClassStmt || childChainItem.data.memberOfAncestor) {
|
|
535
599
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.memberAccessibilityMismatch(childChainItem.name, childChainItem.data.flags, definingClassName)), { range: expression.range, file: file }));
|
|
536
600
|
// there's an error... don't worry about the rest of the chain
|
|
@@ -538,7 +602,7 @@ class ScopeValidator {
|
|
|
538
602
|
}
|
|
539
603
|
}
|
|
540
604
|
// eslint-disable-next-line no-bitwise
|
|
541
|
-
if (childChainItem.data.flags &
|
|
605
|
+
if (childChainItem.data.flags & 16 /* SymbolTypeFlag.protected */) {
|
|
542
606
|
const containingClassName = containingClassStmt === null || containingClassStmt === void 0 ? void 0 : containingClassStmt.getName(Parser_1.ParseMode.BrighterScript);
|
|
543
607
|
const containingNamespaceName = (_c = expression.findAncestor(reflection_1.isNamespaceStatement)) === null || _c === void 0 ? void 0 : _c.getName(Parser_1.ParseMode.BrighterScript);
|
|
544
608
|
const ancestorClasses = this.event.scope.getClassHierarchy(containingClassName, containingNamespaceName).map(link => link.item);
|
|
@@ -554,12 +618,356 @@ class ScopeValidator {
|
|
|
554
618
|
}
|
|
555
619
|
return true;
|
|
556
620
|
}
|
|
621
|
+
/**
|
|
622
|
+
* Find all "new" statements in the program,
|
|
623
|
+
* and make sure we can find a class with that name
|
|
624
|
+
*/
|
|
625
|
+
validateNewExpression(file, newExpression) {
|
|
626
|
+
var _a;
|
|
627
|
+
let potentialClassName = newExpression.className.getName(Parser_1.ParseMode.BrighterScript);
|
|
628
|
+
const namespaceName = (_a = newExpression.findAncestor(reflection_1.isNamespaceStatement)) === null || _a === void 0 ? void 0 : _a.getName(Parser_1.ParseMode.BrighterScript);
|
|
629
|
+
let newableClass = this.event.scope.getClass(potentialClassName, namespaceName);
|
|
630
|
+
if (!newableClass) {
|
|
631
|
+
//try and find functions with this name.
|
|
632
|
+
let fullName = util_1.default.getFullyQualifiedClassName(potentialClassName, namespaceName);
|
|
633
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.expressionIsNotConstructable(fullName)), { file: file, range: newExpression.className.range }));
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Create diagnostics for any duplicate function declarations
|
|
638
|
+
*/
|
|
639
|
+
flagDuplicateFunctionDeclarations() {
|
|
640
|
+
//for each list of callables with the same name
|
|
641
|
+
for (let [lowerName, callableContainers] of this.event.scope.getCallableContainerMap()) {
|
|
642
|
+
let globalCallables = [];
|
|
643
|
+
let nonGlobalCallables = [];
|
|
644
|
+
let ownCallables = [];
|
|
645
|
+
let ancestorNonGlobalCallables = [];
|
|
646
|
+
for (let container of callableContainers) {
|
|
647
|
+
if (container.scope === this.event.program.globalScope) {
|
|
648
|
+
globalCallables.push(container);
|
|
649
|
+
}
|
|
650
|
+
else {
|
|
651
|
+
nonGlobalCallables.push(container);
|
|
652
|
+
if (container.scope === this.event.scope) {
|
|
653
|
+
ownCallables.push(container);
|
|
654
|
+
}
|
|
655
|
+
else {
|
|
656
|
+
ancestorNonGlobalCallables.push(container);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
//add info diagnostics about child shadowing parent functions
|
|
661
|
+
if (ownCallables.length > 0 && ancestorNonGlobalCallables.length > 0) {
|
|
662
|
+
for (let container of ownCallables) {
|
|
663
|
+
//skip the init function (because every component will have one of those){
|
|
664
|
+
if (lowerName !== 'init') {
|
|
665
|
+
let shadowedCallable = ancestorNonGlobalCallables[ancestorNonGlobalCallables.length - 1];
|
|
666
|
+
if (!!shadowedCallable && shadowedCallable.callable.file === container.callable.file) {
|
|
667
|
+
//same file: skip redundant imports
|
|
668
|
+
continue;
|
|
669
|
+
}
|
|
670
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.overridesAncestorFunction(container.callable.name, container.scope.name, shadowedCallable.callable.file.destPath,
|
|
671
|
+
//grab the last item in the list, which should be the closest ancestor's version
|
|
672
|
+
shadowedCallable.scope.name)), { range: container.callable.nameRange, file: container.callable.file, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
//add error diagnostics about duplicate functions in the same scope
|
|
677
|
+
if (ownCallables.length > 1) {
|
|
678
|
+
for (let callableContainer of ownCallables) {
|
|
679
|
+
let callable = callableContainer.callable;
|
|
680
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.duplicateFunctionImplementation(callable.name, callableContainer.scope.name)), { range: callable.nameRange, file: callable.file, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Verify that all of the scripts imported by each file in this scope actually exist, and have the correct case
|
|
687
|
+
*/
|
|
688
|
+
validateScriptImportPaths() {
|
|
689
|
+
let scriptImports = this.event.scope.getOwnScriptImports();
|
|
690
|
+
//verify every script import
|
|
691
|
+
for (let scriptImport of scriptImports) {
|
|
692
|
+
let referencedFile = this.event.scope.getFileByRelativePath(scriptImport.destPath);
|
|
693
|
+
//if we can't find the file
|
|
694
|
+
if (!referencedFile) {
|
|
695
|
+
//skip the default bslib file, it will exist at transpile time but should not show up in the program during validation cycle
|
|
696
|
+
if (scriptImport.destPath === this.event.program.bslibPkgPath) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
let dInfo;
|
|
700
|
+
if (scriptImport.text.trim().length === 0) {
|
|
701
|
+
dInfo = DiagnosticMessages_1.DiagnosticMessages.scriptSrcCannotBeEmpty();
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
dInfo = DiagnosticMessages_1.DiagnosticMessages.referencedFileDoesNotExist();
|
|
705
|
+
}
|
|
706
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, dInfo), { range: scriptImport.filePathRange, file: scriptImport.sourceFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
707
|
+
//if the character casing of the script import path does not match that of the actual path
|
|
708
|
+
}
|
|
709
|
+
else if (scriptImport.destPath !== referencedFile.destPath) {
|
|
710
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.scriptImportCaseMismatch(referencedFile.destPath)), { range: scriptImport.filePathRange, file: scriptImport.sourceFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
/**
|
|
715
|
+
* Validate all classes defined in this scope
|
|
716
|
+
*/
|
|
717
|
+
validateClasses() {
|
|
718
|
+
let validator = new ClassValidator_1.BsClassValidator(this.event.scope);
|
|
719
|
+
validator.validate();
|
|
720
|
+
for (const diagnostic of validator.diagnostics) {
|
|
721
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, diagnostic), { origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Find various function collisions
|
|
726
|
+
*/
|
|
727
|
+
diagnosticDetectFunctionCollisions(file) {
|
|
728
|
+
for (let func of file.callables) {
|
|
729
|
+
const funcName = func.getName(Parser_1.ParseMode.BrighterScript);
|
|
730
|
+
const lowerFuncName = funcName === null || funcName === void 0 ? void 0 : funcName.toLowerCase();
|
|
731
|
+
if (lowerFuncName) {
|
|
732
|
+
//find function declarations with the same name as a stdlib function
|
|
733
|
+
if (globalCallables_1.globalCallableMap.has(lowerFuncName)) {
|
|
734
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.scopeFunctionShadowedByBuiltInFunction()), { range: func.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
735
|
+
}
|
|
736
|
+
//find any functions that have the same name as a class
|
|
737
|
+
const klassLink = this.event.scope.getClassFileLink(lowerFuncName);
|
|
738
|
+
if (klassLink) {
|
|
739
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionCannotHaveSameNameAsClass(funcName)), { range: func.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope, relatedInformation: [{
|
|
740
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(klassLink.file.srcPath).toString(), klassLink.item.tokens.name.range),
|
|
741
|
+
message: 'Original class declared here'
|
|
742
|
+
}] }));
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
detectNameCollisions(file) {
|
|
748
|
+
file.ast.walk((0, visitors_1.createVisitor)({
|
|
749
|
+
NamespaceStatement: (nsStmt) => {
|
|
750
|
+
var _a;
|
|
751
|
+
this.validateNameCollision(file, nsStmt, (_a = nsStmt.getNameParts()) === null || _a === void 0 ? void 0 : _a[0]);
|
|
752
|
+
},
|
|
753
|
+
ClassStatement: (classStmt) => {
|
|
754
|
+
this.validateNameCollision(file, classStmt, classStmt.tokens.name);
|
|
755
|
+
},
|
|
756
|
+
InterfaceStatement: (ifaceStmt) => {
|
|
757
|
+
this.validateNameCollision(file, ifaceStmt, ifaceStmt.tokens.name);
|
|
758
|
+
},
|
|
759
|
+
ConstStatement: (constStmt) => {
|
|
760
|
+
this.validateNameCollision(file, constStmt, constStmt.tokens.name);
|
|
761
|
+
},
|
|
762
|
+
EnumStatement: (enumStmt) => {
|
|
763
|
+
this.validateNameCollision(file, enumStmt, enumStmt.tokens.name);
|
|
764
|
+
},
|
|
765
|
+
AssignmentStatement: (assignStmt) => {
|
|
766
|
+
// Note: this also includes For statements
|
|
767
|
+
this.detectShadowedLocalVar(file, {
|
|
768
|
+
name: assignStmt.tokens.name.text,
|
|
769
|
+
type: assignStmt.getType({ flags: 1 /* SymbolTypeFlag.runtime */ }),
|
|
770
|
+
nameRange: assignStmt.tokens.name.range
|
|
771
|
+
});
|
|
772
|
+
},
|
|
773
|
+
ForEachStatement: (forEachStmt) => {
|
|
774
|
+
this.detectShadowedLocalVar(file, {
|
|
775
|
+
name: forEachStmt.tokens.item.text,
|
|
776
|
+
type: forEachStmt.getType({ flags: 1 /* SymbolTypeFlag.runtime */ }),
|
|
777
|
+
nameRange: forEachStmt.tokens.item.range
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
}), {
|
|
781
|
+
walkMode: visitors_1.WalkMode.visitAllRecursive
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
validateNameCollision(file, node, nameIdentifier) {
|
|
785
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
786
|
+
const name = nameIdentifier === null || nameIdentifier === void 0 ? void 0 : nameIdentifier.text;
|
|
787
|
+
if (!name || !node) {
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
const nameRange = nameIdentifier.range;
|
|
791
|
+
const containingNamespace = (_a = node.findAncestor(reflection_1.isNamespaceStatement)) === null || _a === void 0 ? void 0 : _a.getName(Parser_1.ParseMode.BrighterScript);
|
|
792
|
+
const containingNamespaceLower = containingNamespace === null || containingNamespace === void 0 ? void 0 : containingNamespace.toLowerCase();
|
|
793
|
+
const links = this.event.scope.getAllFileLinks(name, containingNamespace);
|
|
794
|
+
for (let link of links) {
|
|
795
|
+
if (!link || link.item === node) {
|
|
796
|
+
// refers to same node
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
799
|
+
if ((0, reflection_1.isNamespaceStatement)(link.item) && (0, reflection_1.isNamespaceStatement)(node)) {
|
|
800
|
+
// namespace can be declared multiple times
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
if ((0, reflection_1.isFunctionStatement)(link.item) || ((_b = link.file) === null || _b === void 0 ? void 0 : _b.destPath) === 'global') {
|
|
804
|
+
const linkItemNamespaceLower = (_e = (_d = (_c = link.item) === null || _c === void 0 ? void 0 : _c.findAncestor(reflection_1.isNamespaceStatement)) === null || _d === void 0 ? void 0 : _d.getName(Parser_1.ParseMode.BrighterScript)) === null || _e === void 0 ? void 0 : _e.toLowerCase();
|
|
805
|
+
if (!(containingNamespaceLower && linkItemNamespaceLower) || linkItemNamespaceLower !== containingNamespaceLower) {
|
|
806
|
+
// the thing found is a function OR from global (which is also a function)
|
|
807
|
+
if ((0, reflection_1.isNamespaceStatement)(node) ||
|
|
808
|
+
(0, reflection_1.isEnumStatement)(node) ||
|
|
809
|
+
(0, reflection_1.isConstStatement)(node) ||
|
|
810
|
+
(0, reflection_1.isInterfaceStatement)(node)) {
|
|
811
|
+
// these are not callable functions in transpiled code - ignore them
|
|
812
|
+
continue;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
const thisNodeKindName = util_1.default.getAstNodeFriendlyName(node);
|
|
817
|
+
const thatNodeKindName = link.file.srcPath === 'global' ? 'Global Function' : (_f = util_1.default.getAstNodeFriendlyName(link.item)) !== null && _f !== void 0 ? _f : '';
|
|
818
|
+
let thatNameRange = (_k = (_j = (_h = (_g = link.item) === null || _g === void 0 ? void 0 : _g.tokens) === null || _h === void 0 ? void 0 : _h.name) === null || _j === void 0 ? void 0 : _j.range) !== null && _k !== void 0 ? _k : (_l = link.item) === null || _l === void 0 ? void 0 : _l.range;
|
|
819
|
+
if ((0, reflection_1.isNamespaceStatement)(link.item)) {
|
|
820
|
+
thatNameRange = (_o = (_m = link.item.getNameParts()) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.range;
|
|
821
|
+
}
|
|
822
|
+
const relatedInformation = thatNameRange ? [{
|
|
823
|
+
message: `${thatNodeKindName} declared here`,
|
|
824
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file((_p = link.file) === null || _p === void 0 ? void 0 : _p.srcPath).toString(), thatNameRange)
|
|
825
|
+
}] : undefined;
|
|
826
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.nameCollision(thisNodeKindName, thatNodeKindName, name)), { origin: interfaces_1.DiagnosticOrigin.Scope, range: nameRange, relatedInformation: relatedInformation }));
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
detectShadowedLocalVar(file, varDeclaration) {
|
|
830
|
+
var _a;
|
|
831
|
+
const varName = varDeclaration.name;
|
|
832
|
+
const lowerVarName = varName.toLowerCase();
|
|
833
|
+
const classMap = this.event.scope.getClassMap();
|
|
834
|
+
const callableContainerMap = this.event.scope.getCallableContainerMap();
|
|
835
|
+
const varIsFunction = () => {
|
|
836
|
+
return (0, reflection_1.isCallableType)(varDeclaration.type);
|
|
837
|
+
};
|
|
838
|
+
if (
|
|
839
|
+
//has same name as stdlib
|
|
840
|
+
globalCallables_1.globalCallableMap.has(lowerVarName)) {
|
|
841
|
+
//local var function with same name as stdlib function
|
|
842
|
+
if (varIsFunction()) {
|
|
843
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib')), { range: varDeclaration.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
else if (callableContainerMap.has(lowerVarName)) {
|
|
847
|
+
const callable = callableContainerMap.get(lowerVarName);
|
|
848
|
+
//is same name as a callable
|
|
849
|
+
if (varIsFunction()) {
|
|
850
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('scope')), { range: varDeclaration.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope, relatedInformation: [{
|
|
851
|
+
message: 'Function declared here',
|
|
852
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(callable[0].callable.file.srcPath).toString(), callable[0].callable.nameRange)
|
|
853
|
+
}] }));
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarShadowedByScopedFunction()), { range: varDeclaration.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope, relatedInformation: [{
|
|
857
|
+
message: 'Function declared here',
|
|
858
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(callable[0].callable.file.srcPath).toString(), callable[0].callable.nameRange)
|
|
859
|
+
}] }));
|
|
860
|
+
}
|
|
861
|
+
//has the same name as an in-scope class
|
|
862
|
+
}
|
|
863
|
+
else if (classMap.has(lowerVarName)) {
|
|
864
|
+
const classStmtLink = classMap.get(lowerVarName);
|
|
865
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarSameNameAsClass((_a = classStmtLink === null || classStmtLink === void 0 ? void 0 : classStmtLink.item) === null || _a === void 0 ? void 0 : _a.getName(Parser_1.ParseMode.BrighterScript))), { range: varDeclaration.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope, relatedInformation: [{
|
|
866
|
+
message: 'Class declared here',
|
|
867
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(classStmtLink.file.srcPath).toString(), classStmtLink === null || classStmtLink === void 0 ? void 0 : classStmtLink.item.tokens.name.range)
|
|
868
|
+
}] }));
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
detectVariableNamespaceCollisions(file) {
|
|
872
|
+
var _a, _b;
|
|
873
|
+
//find all function parameters
|
|
874
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
875
|
+
for (let func of file['_cachedLookups'].functionExpressions) {
|
|
876
|
+
for (let param of func.parameters) {
|
|
877
|
+
let lowerParamName = param.tokens.name.text.toLowerCase();
|
|
878
|
+
let namespace = this.event.scope.getNamespace(lowerParamName, (_a = param.findAncestor(reflection_1.isNamespaceStatement)) === null || _a === void 0 ? void 0 : _a.getName(Parser_1.ParseMode.BrighterScript).toLowerCase());
|
|
879
|
+
//see if the param matches any starting namespace part
|
|
880
|
+
if (namespace) {
|
|
881
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({ origin: interfaces_1.DiagnosticOrigin.Scope, file: file }, DiagnosticMessages_1.DiagnosticMessages.parameterMayNotHaveSameNameAsNamespace(param.tokens.name.text)), { range: param.tokens.name.range, relatedInformation: [{
|
|
882
|
+
message: 'Namespace declared here',
|
|
883
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(namespace.file.srcPath).toString(), namespace.nameRange)
|
|
884
|
+
}] }));
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
889
|
+
for (let assignment of file['_cachedLookups'].assignmentStatements) {
|
|
890
|
+
let lowerAssignmentName = assignment.tokens.name.text.toLowerCase();
|
|
891
|
+
let namespace = this.event.scope.getNamespace(lowerAssignmentName, (_b = assignment.findAncestor(reflection_1.isNamespaceStatement)) === null || _b === void 0 ? void 0 : _b.getName(Parser_1.ParseMode.BrighterScript).toLowerCase());
|
|
892
|
+
//see if the param matches any starting namespace part
|
|
893
|
+
if (namespace) {
|
|
894
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({ origin: interfaces_1.DiagnosticOrigin.Scope, file: file }, DiagnosticMessages_1.DiagnosticMessages.variableMayNotHaveSameNameAsNamespace(assignment.tokens.name.text)), { range: assignment.tokens.name.range, relatedInformation: [{
|
|
895
|
+
message: 'Namespace declared here',
|
|
896
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(namespace.file.srcPath).toString(), namespace.nameRange)
|
|
897
|
+
}] }));
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
validateXmlInterface(scope) {
|
|
902
|
+
var _a, _b, _c, _d, _e;
|
|
903
|
+
if (!((_b = (_a = scope.xmlFile.parser.ast) === null || _a === void 0 ? void 0 : _a.componentElement) === null || _b === void 0 ? void 0 : _b.interfaceElement)) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
const iface = scope.xmlFile.parser.ast.componentElement.interfaceElement;
|
|
907
|
+
const callableContainerMap = scope.getCallableContainerMap();
|
|
908
|
+
//validate functions
|
|
909
|
+
for (const func of iface.functions) {
|
|
910
|
+
const name = func.name;
|
|
911
|
+
if (!name) {
|
|
912
|
+
this.addDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.xmlTagMissingAttribute(func.tokens.startTagName.text, 'name')), { range: func.tokens.startTagName.range, file: scope.xmlFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
913
|
+
}
|
|
914
|
+
else if (!callableContainerMap.has(name.toLowerCase())) {
|
|
915
|
+
this.addDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.xmlFunctionNotFound(name)), { range: (_c = func.getAttribute('name')) === null || _c === void 0 ? void 0 : _c.tokens.value.range, file: scope.xmlFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
//validate fields
|
|
919
|
+
for (const field of iface.fields) {
|
|
920
|
+
const { id, type, onChange } = field;
|
|
921
|
+
if (!id) {
|
|
922
|
+
this.addDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.xmlTagMissingAttribute(field.tokens.startTagName.text, 'id')), { range: field.tokens.startTagName.range, file: scope.xmlFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
923
|
+
}
|
|
924
|
+
if (!type) {
|
|
925
|
+
if (!field.alias) {
|
|
926
|
+
this.addDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.xmlTagMissingAttribute(field.tokens.startTagName.text, 'type')), { range: field.tokens.startTagName.range, file: scope.xmlFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
else if (!SGTypes_1.SGFieldTypes.includes(type.toLowerCase())) {
|
|
930
|
+
this.addDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.xmlInvalidFieldType(type)), { range: (_d = field.getAttribute('type')) === null || _d === void 0 ? void 0 : _d.tokens.value.range, file: scope.xmlFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
931
|
+
}
|
|
932
|
+
if (onChange) {
|
|
933
|
+
if (!callableContainerMap.has(onChange.toLowerCase())) {
|
|
934
|
+
this.addDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.xmlFunctionNotFound(onChange)), { range: (_e = field.getAttribute('onchange')) === null || _e === void 0 ? void 0 : _e.tokens.value.range, file: scope.xmlFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Detect when a child has imported a script that an ancestor also imported
|
|
941
|
+
*/
|
|
942
|
+
diagnosticDetectDuplicateAncestorScriptImports(scope) {
|
|
943
|
+
var _a, _b;
|
|
944
|
+
if (scope.xmlFile.parentComponent) {
|
|
945
|
+
//build a lookup of pkg paths -> FileReference so we can more easily look up collisions
|
|
946
|
+
let parentScriptImports = scope.xmlFile.getAncestorScriptTagImports();
|
|
947
|
+
let lookup = {};
|
|
948
|
+
for (let parentScriptImport of parentScriptImports) {
|
|
949
|
+
//keep the first occurance of a pkgPath. Parent imports are first in the array
|
|
950
|
+
if (!lookup[parentScriptImport.destPath]) {
|
|
951
|
+
lookup[parentScriptImport.destPath] = parentScriptImport;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
//add warning for every script tag that this file shares with an ancestor
|
|
955
|
+
for (let scriptImport of scope.xmlFile.scriptTagImports) {
|
|
956
|
+
let ancestorScriptImport = lookup[scriptImport.destPath];
|
|
957
|
+
if (ancestorScriptImport) {
|
|
958
|
+
let ancestorComponent = ancestorScriptImport.sourceFile;
|
|
959
|
+
let ancestorComponentName = (_b = (_a = ancestorComponent.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : ancestorComponent.destPath;
|
|
960
|
+
this.addDiagnostic(Object.assign(Object.assign({ file: scope.xmlFile, range: scriptImport.filePathRange }, DiagnosticMessages_1.DiagnosticMessages.unnecessaryScriptImportInChildFromParent(ancestorComponentName)), { origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
557
965
|
/**
|
|
558
966
|
* Adds a diagnostic to the first scope for this key. Prevents duplicate diagnostics
|
|
559
967
|
* for diagnostics where scope isn't important. (i.e. CreateObject validations)
|
|
560
968
|
*/
|
|
561
969
|
addDiagnosticOnce(diagnostic) {
|
|
562
|
-
this.onceCache.getOrAdd(`${diagnostic.code}-${diagnostic.message}-${util_1.default.rangeToString(diagnostic.range)}`, () => {
|
|
970
|
+
this.onceCache.getOrAdd(`${diagnostic.code} -${diagnostic.message} -${util_1.default.rangeToString(diagnostic.range)} `, () => {
|
|
563
971
|
const diagnosticWithOrigin = Object.assign({}, diagnostic);
|
|
564
972
|
if (!diagnosticWithOrigin.origin) {
|
|
565
973
|
// diagnostic does not have origin.
|
|
@@ -586,7 +994,7 @@ class ScopeValidator {
|
|
|
586
994
|
*/
|
|
587
995
|
addMultiScopeDiagnostic(diagnostic) {
|
|
588
996
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
589
|
-
diagnostic = this.multiScopeCache.getOrAdd(`${(_a = diagnostic.file) === null || _a === void 0 ? void 0 : _a.srcPath}-${diagnostic.code}-${diagnostic.message}-${util_1.default.rangeToString(diagnostic.range)}`, () => {
|
|
997
|
+
diagnostic = this.multiScopeCache.getOrAdd(`${(_a = diagnostic.file) === null || _a === void 0 ? void 0 : _a.srcPath} -${diagnostic.code} -${diagnostic.message} -${util_1.default.rangeToString(diagnostic.range)} `, () => {
|
|
590
998
|
if (!diagnostic.relatedInformation) {
|
|
591
999
|
diagnostic.relatedInformation = [];
|
|
592
1000
|
}
|