brighterscript 1.0.0-alpha.27 → 1.0.0-alpha.29
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 +71 -0
- package/README.md +1 -1
- package/dist/AstValidationSegmenter.d.ts +12 -2
- package/dist/AstValidationSegmenter.js +74 -16
- package/dist/AstValidationSegmenter.js.map +1 -1
- package/dist/DependencyGraph.d.ts +4 -0
- package/dist/DependencyGraph.js +19 -0
- package/dist/DependencyGraph.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 -1
- package/dist/PluginInterface.js +1 -1
- package/dist/PluginInterface.js.map +1 -1
- package/dist/Program.d.ts +19 -15
- package/dist/Program.js +153 -88
- package/dist/Program.js.map +1 -1
- package/dist/Scope.d.ts +27 -28
- package/dist/Scope.js +174 -361
- package/dist/Scope.js.map +1 -1
- package/dist/Stopwatch.d.ts +4 -0
- package/dist/Stopwatch.js +7 -0
- package/dist/Stopwatch.js.map +1 -1
- package/dist/SymbolTable.d.ts +2 -1
- package/dist/SymbolTable.js +26 -0
- package/dist/SymbolTable.js.map +1 -1
- package/dist/{SymbolTableFlag.js → SymbolTypeFlag.js} +1 -1
- package/dist/SymbolTypeFlag.js.map +1 -0
- package/dist/XmlScope.d.ts +0 -8
- package/dist/XmlScope.js +0 -77
- package/dist/XmlScope.js.map +1 -1
- package/dist/astUtils/CachedLookups.js +4 -8
- package/dist/astUtils/CachedLookups.js.map +1 -1
- package/dist/astUtils/creators.d.ts +1 -0
- package/dist/astUtils/creators.js +3 -2
- 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 +4 -4
- package/dist/astUtils/reflection.js +8 -7
- package/dist/astUtils/reflection.js.map +1 -1
- package/dist/astUtils/reflection.spec.js +10 -15
- 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 +3 -1
- package/dist/bscPlugin/BscPlugin.js +10 -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 +31 -11
- 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.js +5 -5
- package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -1
- package/dist/bscPlugin/hover/HoverProcessor.spec.js +51 -5
- 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/transpile/BrsFileTranspileProcessor.js +1 -1
- package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +1 -1
- package/dist/bscPlugin/validation/BrsFileAfterValidatior.d.ts +7 -0
- package/dist/bscPlugin/validation/BrsFileAfterValidatior.js +18 -0
- package/dist/bscPlugin/validation/BrsFileAfterValidatior.js.map +1 -0
- package/dist/bscPlugin/validation/BrsFileValidator.d.ts +1 -0
- package/dist/bscPlugin/validation/BrsFileValidator.js +17 -10
- package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -1
- package/dist/bscPlugin/validation/ScopeValidator.d.ts +37 -1
- package/dist/bscPlugin/validation/ScopeValidator.js +434 -25
- package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -1
- package/dist/bscPlugin/validation/ScopeValidator.spec.js +91 -4
- package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +1 -1
- package/dist/files/BrsFile.Class.spec.js +11 -4
- package/dist/files/BrsFile.Class.spec.js.map +1 -1
- package/dist/files/BrsFile.d.ts +23 -5
- package/dist/files/BrsFile.js +189 -51
- package/dist/files/BrsFile.js.map +1 -1
- package/dist/files/BrsFile.spec.js +589 -97
- package/dist/files/BrsFile.spec.js.map +1 -1
- package/dist/files/BscFile.d.ts +2 -1
- package/dist/files/BscFile.js.map +1 -1
- package/dist/files/XmlFile.d.ts +2 -2
- package/dist/files/XmlFile.js +2 -2
- 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 +43 -4
- 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 +77 -47
- package/dist/parser/Expression.js +162 -88
- package/dist/parser/Expression.js.map +1 -1
- package/dist/parser/Parser.d.ts +7 -2
- package/dist/parser/Parser.js +40 -90
- package/dist/parser/Parser.js.map +1 -1
- package/dist/parser/Parser.spec.js +21 -44
- package/dist/parser/Parser.spec.js.map +1 -1
- package/dist/parser/SGTypes.js +5 -5
- package/dist/parser/SGTypes.js.map +1 -1
- package/dist/parser/Statement.d.ts +92 -84
- package/dist/parser/Statement.js +199 -133
- 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 +67 -8
- 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/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/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/types/AssociativeArrayType.d.ts +3 -0
- package/dist/types/AssociativeArrayType.js +9 -0
- package/dist/types/AssociativeArrayType.js.map +1 -1
- package/dist/types/BscType.d.ts +1 -1
- package/dist/types/BscType.js +1 -0
- package/dist/types/BscType.js.map +1 -1
- package/dist/types/ComponentType.d.ts +1 -1
- package/dist/types/ReferenceType.d.ts +9 -1
- package/dist/types/ReferenceType.js +45 -1
- package/dist/types/ReferenceType.js.map +1 -1
- package/dist/types/ReferenceType.spec.js +15 -0
- package/dist/types/ReferenceType.spec.js.map +1 -1
- package/dist/util.d.ts +23 -9
- package/dist/util.js +115 -21
- package/dist/util.js.map +1 -1
- package/package.json +6 -3
- package/dist/SymbolTableFlag.js.map +0 -1
- /package/dist/{SymbolTableFlag.d.ts → SymbolTypeFlag.d.ts} +0 -0
|
@@ -1,7 +1,11 @@
|
|
|
1
|
+
import type { Range } from 'vscode-languageserver';
|
|
1
2
|
import type { BrsFile } from '../../files/BrsFile';
|
|
2
3
|
import type { OnScopeValidateEvent } from '../../interfaces';
|
|
4
|
+
import type { Token } from '../../lexer/Token';
|
|
5
|
+
import type { AstNode } from '../../parser/AstNode';
|
|
3
6
|
import type { VariableExpression, DottedGetExpression } from '../../parser/Expression';
|
|
4
7
|
import { CallExpression } from '../../parser/Expression';
|
|
8
|
+
import type { BscType } from '../../types/BscType';
|
|
5
9
|
/**
|
|
6
10
|
* A validator that handles all scope validations for a program validation cycle.
|
|
7
11
|
* You should create ONE of these to handle all scope events between beforeProgramValidate and afterProgramValidate,
|
|
@@ -15,12 +19,15 @@ export declare class ScopeValidator {
|
|
|
15
19
|
processEvent(event: OnScopeValidateEvent): void;
|
|
16
20
|
reset(): void;
|
|
17
21
|
private walkFiles;
|
|
22
|
+
private doesFileRequireChangedSymbol;
|
|
23
|
+
private doesFileProvideChangedSymbol;
|
|
24
|
+
private doesFileAssignChangedSymbol;
|
|
18
25
|
private currentSegmentBeingValidated;
|
|
19
26
|
private isTypeKnown;
|
|
20
27
|
/**
|
|
21
28
|
* If this is the lhs of an assignment, we don't need to flag it as unresolved
|
|
22
29
|
*/
|
|
23
|
-
private
|
|
30
|
+
private hasValidDeclaration;
|
|
24
31
|
/**
|
|
25
32
|
* Flag duplicate enums
|
|
26
33
|
*/
|
|
@@ -72,6 +79,35 @@ export declare class ScopeValidator {
|
|
|
72
79
|
* and make sure we can find a class with that name
|
|
73
80
|
*/
|
|
74
81
|
private validateNewExpression;
|
|
82
|
+
/**
|
|
83
|
+
* Create diagnostics for any duplicate function declarations
|
|
84
|
+
*/
|
|
85
|
+
private flagDuplicateFunctionDeclarations;
|
|
86
|
+
/**
|
|
87
|
+
* Verify that all of the scripts imported by each file in this scope actually exist, and have the correct case
|
|
88
|
+
*/
|
|
89
|
+
private validateScriptImportPaths;
|
|
90
|
+
/**
|
|
91
|
+
* Validate all classes defined in this scope
|
|
92
|
+
*/
|
|
93
|
+
private validateClasses;
|
|
94
|
+
/**
|
|
95
|
+
* Find various function collisions
|
|
96
|
+
*/
|
|
97
|
+
private diagnosticDetectFunctionCollisions;
|
|
98
|
+
private detectNameCollisions;
|
|
99
|
+
validateNameCollision(file: BrsFile, node: AstNode, nameIdentifier: Token): void;
|
|
100
|
+
detectShadowedLocalVar(file: BrsFile, varDeclaration: {
|
|
101
|
+
name: string;
|
|
102
|
+
type: BscType;
|
|
103
|
+
nameRange: Range;
|
|
104
|
+
}): void;
|
|
105
|
+
private detectVariableNamespaceCollisions;
|
|
106
|
+
private validateXmlInterface;
|
|
107
|
+
/**
|
|
108
|
+
* Detect when a child has imported a script that an ancestor also imported
|
|
109
|
+
*/
|
|
110
|
+
private diagnosticDetectDuplicateAncestorScriptImports;
|
|
75
111
|
/**
|
|
76
112
|
* Adds a diagnostic to the first scope for this key. Prevents duplicate diagnostics
|
|
77
113
|
* for diagnostics where scope isn't important. (i.e. CreateObject validations)
|
|
@@ -14,6 +14,9 @@ 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,26 +56,30 @@ class ScopeValidator {
|
|
|
44
56
|
this.multiScopeCache.clear();
|
|
45
57
|
}
|
|
46
58
|
walkFiles() {
|
|
59
|
+
const hasChangeInfo = this.event.changedFiles && this.event.changedSymbols;
|
|
60
|
+
//do many per-file checks for every file in this (and parent) scopes
|
|
61
|
+
this.event.scope.enumerateBrsFiles((file) => {
|
|
62
|
+
if (!(0, reflection_1.isBrsFile)(file)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const thisFileHasChanges = this.event.changedFiles.includes(file);
|
|
66
|
+
this.detectVariableNamespaceCollisions(file);
|
|
67
|
+
if (thisFileHasChanges || this.doesFileProvideChangedSymbol(file, this.event.changedSymbols)) {
|
|
68
|
+
this.diagnosticDetectFunctionCollisions(file);
|
|
69
|
+
this.detectNameCollisions(file);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
47
72
|
this.event.scope.enumerateOwnFiles((file) => {
|
|
48
73
|
if ((0, reflection_1.isBrsFile)(file)) {
|
|
49
|
-
const hasChangeInfo = this.event.changedFiles && this.event.changedSymbols;
|
|
50
|
-
let thisFileRequiresChangedSymbol = false;
|
|
51
|
-
for (let requiredSymbol of file.requiredSymbols) {
|
|
52
|
-
// eslint-disable-next-line no-bitwise
|
|
53
|
-
for (const flag of [1 /* SymbolTypeFlag.runtime */, 2 /* SymbolTypeFlag.typetime */]) {
|
|
54
|
-
// eslint-disable-next-line no-bitwise
|
|
55
|
-
if (flag & requiredSymbol.flags) {
|
|
56
|
-
const changeSymbolSetForFlag = this.event.changedSymbols.get(flag);
|
|
57
|
-
if (util_1.default.setContainsUnresolvedSymbol(changeSymbolSetForFlag, requiredSymbol)) {
|
|
58
|
-
thisFileRequiresChangedSymbol = true;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
74
|
const thisFileHasChanges = this.event.changedFiles.includes(file);
|
|
64
|
-
|
|
75
|
+
const thisFileRequiresChangedSymbol = this.doesFileRequireChangedSymbol(file);
|
|
76
|
+
const hasUnvalidatedSegments = file.validationSegmenter.hasUnvalidatedSegments();
|
|
77
|
+
if (hasChangeInfo && !thisFileRequiresChangedSymbol && !thisFileHasChanges && !hasUnvalidatedSegments) {
|
|
65
78
|
// this file does not require a symbol that has changed, and this file has not changed
|
|
66
|
-
|
|
79
|
+
if (!this.doesFileAssignChangedSymbol(file)) {
|
|
80
|
+
// this file does not have a variable assignment that needs to be checked
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
67
83
|
}
|
|
68
84
|
if (thisFileHasChanges) {
|
|
69
85
|
this.event.scope.clearAstSegmentDiagnosticsByFile(file);
|
|
@@ -93,15 +109,38 @@ class ScopeValidator {
|
|
|
93
109
|
},
|
|
94
110
|
AssignmentStatement: (assignStmt) => {
|
|
95
111
|
this.validateAssignmentStatement(file, assignStmt);
|
|
112
|
+
// Note: this also includes For statements
|
|
113
|
+
this.detectShadowedLocalVar(file, {
|
|
114
|
+
name: assignStmt.tokens.name.text,
|
|
115
|
+
type: assignStmt.getType({ flags: 1 /* SymbolTypeFlag.runtime */ }),
|
|
116
|
+
nameRange: assignStmt.tokens.name.range
|
|
117
|
+
});
|
|
96
118
|
},
|
|
97
119
|
NewExpression: (newExpr) => {
|
|
98
120
|
this.validateNewExpression(file, newExpr);
|
|
121
|
+
},
|
|
122
|
+
ForEachStatement: (forEachStmt) => {
|
|
123
|
+
this.detectShadowedLocalVar(file, {
|
|
124
|
+
name: forEachStmt.tokens.item.text,
|
|
125
|
+
type: forEachStmt.getType({ flags: 1 /* SymbolTypeFlag.runtime */ }),
|
|
126
|
+
nameRange: forEachStmt.tokens.item.range
|
|
127
|
+
});
|
|
128
|
+
},
|
|
129
|
+
FunctionParameterExpression: (funcParam) => {
|
|
130
|
+
this.detectShadowedLocalVar(file, {
|
|
131
|
+
name: funcParam.tokens.name.text,
|
|
132
|
+
type: funcParam.getType({ flags: 1 /* SymbolTypeFlag.runtime */ }),
|
|
133
|
+
nameRange: funcParam.tokens.name.range
|
|
134
|
+
});
|
|
99
135
|
}
|
|
100
136
|
});
|
|
101
137
|
const segmentsToWalkForValidation = (thisFileHasChanges || !hasChangeInfo)
|
|
102
138
|
? file.validationSegmenter.segmentsForValidation // validate everything in the file
|
|
103
139
|
: file.getValidationSegments(this.event.changedSymbols); // validate only what's needed in the file
|
|
104
140
|
for (const segment of segmentsToWalkForValidation) {
|
|
141
|
+
if (!file.validationSegmenter.checkIfSegmentNeedRevalidation(segment)) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
105
144
|
this.currentSegmentBeingValidated = segment;
|
|
106
145
|
this.event.scope.clearAstSegmentDiagnostics(segment);
|
|
107
146
|
segment.walk(validationVisitor, {
|
|
@@ -112,6 +151,49 @@ class ScopeValidator {
|
|
|
112
151
|
}
|
|
113
152
|
});
|
|
114
153
|
}
|
|
154
|
+
doesFileRequireChangedSymbol(file) {
|
|
155
|
+
let thisFileRequiresChangedSymbol = false;
|
|
156
|
+
for (let requiredSymbol of file.requiredSymbols) {
|
|
157
|
+
// eslint-disable-next-line no-bitwise
|
|
158
|
+
for (const flag of [1 /* SymbolTypeFlag.runtime */, 2 /* SymbolTypeFlag.typetime */]) {
|
|
159
|
+
// eslint-disable-next-line no-bitwise
|
|
160
|
+
if (flag & requiredSymbol.flags) {
|
|
161
|
+
const changeSymbolSetForFlag = this.event.changedSymbols.get(flag);
|
|
162
|
+
if (util_1.default.setContainsUnresolvedSymbol(changeSymbolSetForFlag, requiredSymbol)) {
|
|
163
|
+
thisFileRequiresChangedSymbol = true;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return thisFileRequiresChangedSymbol;
|
|
170
|
+
}
|
|
171
|
+
doesFileProvideChangedSymbol(file, changedSymbols) {
|
|
172
|
+
if (!changedSymbols) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
for (const flag of [1 /* SymbolTypeFlag.runtime */, 2 /* SymbolTypeFlag.typetime */]) {
|
|
176
|
+
const providedSymbolKeysFlag = file.providedSymbols.symbolMap.get(flag).keys();
|
|
177
|
+
const changedSymbolSetForFlag = changedSymbols.get(flag);
|
|
178
|
+
for (let providedKey of providedSymbolKeysFlag) {
|
|
179
|
+
if (changedSymbolSetForFlag.has(providedKey)) {
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
doesFileAssignChangedSymbol(file) {
|
|
187
|
+
let thisFileAssignsChangedSymbol = false;
|
|
188
|
+
const runTimeChangedSymbolSet = this.event.changedSymbols.get(1 /* SymbolTypeFlag.runtime */);
|
|
189
|
+
for (let assignedSymbol of file.assignedSymbols) {
|
|
190
|
+
if (runTimeChangedSymbolSet.has(assignedSymbol.token.text.toLowerCase())) {
|
|
191
|
+
thisFileAssignsChangedSymbol = true;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return thisFileAssignsChangedSymbol;
|
|
196
|
+
}
|
|
115
197
|
isTypeKnown(exprType) {
|
|
116
198
|
let isKnownType = exprType === null || exprType === void 0 ? void 0 : exprType.isResolvable();
|
|
117
199
|
return isKnownType;
|
|
@@ -119,7 +201,7 @@ class ScopeValidator {
|
|
|
119
201
|
/**
|
|
120
202
|
* If this is the lhs of an assignment, we don't need to flag it as unresolved
|
|
121
203
|
*/
|
|
122
|
-
|
|
204
|
+
hasValidDeclaration(expression, exprType, definingNode) {
|
|
123
205
|
var _a, _b;
|
|
124
206
|
if (!(0, reflection_1.isVariableExpression)(expression)) {
|
|
125
207
|
return false;
|
|
@@ -130,6 +212,10 @@ class ScopeValidator {
|
|
|
130
212
|
assignmentAncestor = definingNode;
|
|
131
213
|
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());
|
|
132
214
|
}
|
|
215
|
+
else if ((0, reflection_1.isFunctionParameterExpression)(definingNode)) {
|
|
216
|
+
// this symbol was defined in a function param
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
133
219
|
else {
|
|
134
220
|
assignmentAncestor = expression === null || expression === void 0 ? void 0 : expression.findAncestor(reflection_1.isAssignmentStatement);
|
|
135
221
|
}
|
|
@@ -355,7 +441,10 @@ class ScopeValidator {
|
|
|
355
441
|
const expectedLHSType = assignStmt.typeExpression.getType(Object.assign(Object.assign({}, getTypeOpts), { data: {}, typeChain: typeChainExpectedLHS }));
|
|
356
442
|
const actualRHSType = (_a = assignStmt.value) === null || _a === void 0 ? void 0 : _a.getType(getTypeOpts);
|
|
357
443
|
const compatibilityData = {};
|
|
358
|
-
if (!
|
|
444
|
+
if (!expectedLHSType || !expectedLHSType.isResolvable()) {
|
|
445
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.cannotFindName(assignStmt.typeExpression.getName(Parser_1.ParseMode.BrighterScript))), { range: assignStmt.typeExpression.range, file: file }));
|
|
446
|
+
}
|
|
447
|
+
else if (!(expectedLHSType === null || expectedLHSType === void 0 ? void 0 : expectedLHSType.isTypeCompatible(actualRHSType, compatibilityData))) {
|
|
359
448
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.assignmentTypeMismatch(actualRHSType.toString(), expectedLHSType.toString(), compatibilityData)), { range: assignStmt.range, file: file }));
|
|
360
449
|
}
|
|
361
450
|
}
|
|
@@ -468,18 +557,22 @@ class ScopeValidator {
|
|
|
468
557
|
typeChain: typeChain,
|
|
469
558
|
data: typeData
|
|
470
559
|
});
|
|
471
|
-
const
|
|
472
|
-
if (!this.isTypeKnown(exprType) && !
|
|
473
|
-
if ((_a = expression.getType({ flags: oppositeSymbolType })) === null || _a === void 0 ? void 0 : _a.isResolvable()) {
|
|
560
|
+
const hasValidDeclaration = this.hasValidDeclaration(expression, exprType, typeData === null || typeData === void 0 ? void 0 : typeData.definingNode);
|
|
561
|
+
if (!this.isTypeKnown(exprType) && !hasValidDeclaration) {
|
|
562
|
+
if ((_a = expression.getType({ flags: oppositeSymbolType, isExistenceTest: true })) === null || _a === void 0 ? void 0 : _a.isResolvable()) {
|
|
474
563
|
const oppoSiteTypeChain = [];
|
|
475
|
-
const invalidlyUsedResolvedType = expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain });
|
|
564
|
+
const invalidlyUsedResolvedType = expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain, isExistenceTest: true });
|
|
476
565
|
const typeChainScan = util_1.default.processTypeChain(oppoSiteTypeChain);
|
|
477
566
|
if (isUsedAsType) {
|
|
478
567
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsType(typeChainScan.fullChainName)), { range: expression.range, file: file }));
|
|
479
568
|
}
|
|
480
|
-
else {
|
|
569
|
+
else if (invalidlyUsedResolvedType && !(0, reflection_1.isReferenceType)(invalidlyUsedResolvedType)) {
|
|
481
570
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable(invalidlyUsedResolvedType.toString())), { range: expression.range, file: file }));
|
|
482
571
|
}
|
|
572
|
+
else {
|
|
573
|
+
const typeChainScan = util_1.default.processTypeChain(typeChain);
|
|
574
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem)), { range: typeChainScan.range }));
|
|
575
|
+
}
|
|
483
576
|
}
|
|
484
577
|
else {
|
|
485
578
|
const typeChainScan = util_1.default.processTypeChain(typeChain);
|
|
@@ -604,12 +697,328 @@ class ScopeValidator {
|
|
|
604
697
|
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.expressionIsNotConstructable(fullName)), { file: file, range: newExpression.className.range }));
|
|
605
698
|
}
|
|
606
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* Create diagnostics for any duplicate function declarations
|
|
702
|
+
*/
|
|
703
|
+
flagDuplicateFunctionDeclarations() {
|
|
704
|
+
//for each list of callables with the same name
|
|
705
|
+
for (let [lowerName, callableContainers] of this.event.scope.getCallableContainerMap()) {
|
|
706
|
+
let globalCallables = [];
|
|
707
|
+
let nonGlobalCallables = [];
|
|
708
|
+
let ownCallables = [];
|
|
709
|
+
let ancestorNonGlobalCallables = [];
|
|
710
|
+
for (let container of callableContainers) {
|
|
711
|
+
if (container.scope === this.event.program.globalScope) {
|
|
712
|
+
globalCallables.push(container);
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
nonGlobalCallables.push(container);
|
|
716
|
+
if (container.scope === this.event.scope) {
|
|
717
|
+
ownCallables.push(container);
|
|
718
|
+
}
|
|
719
|
+
else {
|
|
720
|
+
ancestorNonGlobalCallables.push(container);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
//add info diagnostics about child shadowing parent functions
|
|
725
|
+
if (ownCallables.length > 0 && ancestorNonGlobalCallables.length > 0) {
|
|
726
|
+
for (let container of ownCallables) {
|
|
727
|
+
//skip the init function (because every component will have one of those){
|
|
728
|
+
if (lowerName !== 'init') {
|
|
729
|
+
let shadowedCallable = ancestorNonGlobalCallables[ancestorNonGlobalCallables.length - 1];
|
|
730
|
+
if (!!shadowedCallable && shadowedCallable.callable.file === container.callable.file) {
|
|
731
|
+
//same file: skip redundant imports
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.overridesAncestorFunction(container.callable.name, container.scope.name, shadowedCallable.callable.file.destPath,
|
|
735
|
+
//grab the last item in the list, which should be the closest ancestor's version
|
|
736
|
+
shadowedCallable.scope.name)), { range: container.callable.nameRange, file: container.callable.file, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
//add error diagnostics about duplicate functions in the same scope
|
|
741
|
+
if (ownCallables.length > 1) {
|
|
742
|
+
for (let callableContainer of ownCallables) {
|
|
743
|
+
let callable = callableContainer.callable;
|
|
744
|
+
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 }));
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Verify that all of the scripts imported by each file in this scope actually exist, and have the correct case
|
|
751
|
+
*/
|
|
752
|
+
validateScriptImportPaths() {
|
|
753
|
+
let scriptImports = this.event.scope.getOwnScriptImports();
|
|
754
|
+
//verify every script import
|
|
755
|
+
for (let scriptImport of scriptImports) {
|
|
756
|
+
let referencedFile = this.event.scope.getFileByRelativePath(scriptImport.destPath);
|
|
757
|
+
//if we can't find the file
|
|
758
|
+
if (!referencedFile) {
|
|
759
|
+
//skip the default bslib file, it will exist at transpile time but should not show up in the program during validation cycle
|
|
760
|
+
if (scriptImport.destPath === this.event.program.bslibPkgPath) {
|
|
761
|
+
continue;
|
|
762
|
+
}
|
|
763
|
+
let dInfo;
|
|
764
|
+
if (scriptImport.text.trim().length === 0) {
|
|
765
|
+
dInfo = DiagnosticMessages_1.DiagnosticMessages.scriptSrcCannotBeEmpty();
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
dInfo = DiagnosticMessages_1.DiagnosticMessages.referencedFileDoesNotExist();
|
|
769
|
+
}
|
|
770
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, dInfo), { range: scriptImport.filePathRange, file: scriptImport.sourceFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
771
|
+
//if the character casing of the script import path does not match that of the actual path
|
|
772
|
+
}
|
|
773
|
+
else if (scriptImport.destPath !== referencedFile.destPath) {
|
|
774
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.scriptImportCaseMismatch(referencedFile.destPath)), { range: scriptImport.filePathRange, file: scriptImport.sourceFile, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
/**
|
|
779
|
+
* Validate all classes defined in this scope
|
|
780
|
+
*/
|
|
781
|
+
validateClasses() {
|
|
782
|
+
let validator = new ClassValidator_1.BsClassValidator(this.event.scope);
|
|
783
|
+
validator.validate();
|
|
784
|
+
for (const diagnostic of validator.diagnostics) {
|
|
785
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, diagnostic), { origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Find various function collisions
|
|
790
|
+
*/
|
|
791
|
+
diagnosticDetectFunctionCollisions(file) {
|
|
792
|
+
for (let func of file.callables) {
|
|
793
|
+
const funcName = func.getName(Parser_1.ParseMode.BrighterScript);
|
|
794
|
+
const lowerFuncName = funcName === null || funcName === void 0 ? void 0 : funcName.toLowerCase();
|
|
795
|
+
if (lowerFuncName) {
|
|
796
|
+
//find function declarations with the same name as a stdlib function
|
|
797
|
+
if (globalCallables_1.globalCallableMap.has(lowerFuncName)) {
|
|
798
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.scopeFunctionShadowedByBuiltInFunction()), { range: func.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
799
|
+
}
|
|
800
|
+
//find any functions that have the same name as a class
|
|
801
|
+
const klassLink = this.event.scope.getClassFileLink(lowerFuncName);
|
|
802
|
+
if (klassLink) {
|
|
803
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.functionCannotHaveSameNameAsClass(funcName)), { range: func.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope, relatedInformation: [{
|
|
804
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(klassLink.file.srcPath).toString(), klassLink.item.tokens.name.range),
|
|
805
|
+
message: 'Original class declared here'
|
|
806
|
+
}] }));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
detectNameCollisions(file) {
|
|
812
|
+
var _a;
|
|
813
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
814
|
+
for (let nsStmt of file['_cachedLookups'].namespaceStatements) {
|
|
815
|
+
this.validateNameCollision(file, nsStmt, (_a = nsStmt.getNameParts()) === null || _a === void 0 ? void 0 : _a[0]);
|
|
816
|
+
}
|
|
817
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
818
|
+
for (let classStmt of file['_cachedLookups'].classStatements) {
|
|
819
|
+
this.validateNameCollision(file, classStmt, classStmt.tokens.name);
|
|
820
|
+
}
|
|
821
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
822
|
+
for (let ifaceStmt of file['_cachedLookups'].interfaceStatements) {
|
|
823
|
+
this.validateNameCollision(file, ifaceStmt, ifaceStmt.tokens.name);
|
|
824
|
+
}
|
|
825
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
826
|
+
for (let constStmt of file['_cachedLookups'].constStatements) {
|
|
827
|
+
this.validateNameCollision(file, constStmt, constStmt.tokens.name);
|
|
828
|
+
}
|
|
829
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
830
|
+
for (let enumStmt of file['_cachedLookups'].enumStatements) {
|
|
831
|
+
this.validateNameCollision(file, enumStmt, enumStmt.tokens.name);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
validateNameCollision(file, node, nameIdentifier) {
|
|
835
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
836
|
+
const name = nameIdentifier === null || nameIdentifier === void 0 ? void 0 : nameIdentifier.text;
|
|
837
|
+
if (!name || !node) {
|
|
838
|
+
return;
|
|
839
|
+
}
|
|
840
|
+
const nameRange = nameIdentifier.range;
|
|
841
|
+
const containingNamespace = (_a = node.findAncestor(reflection_1.isNamespaceStatement)) === null || _a === void 0 ? void 0 : _a.getName(Parser_1.ParseMode.BrighterScript);
|
|
842
|
+
const containingNamespaceLower = containingNamespace === null || containingNamespace === void 0 ? void 0 : containingNamespace.toLowerCase();
|
|
843
|
+
const links = this.event.scope.getAllFileLinks(name, containingNamespace, !(0, reflection_1.isNamespaceStatement)(node));
|
|
844
|
+
for (let link of links) {
|
|
845
|
+
if (!link || link.item === node) {
|
|
846
|
+
// refers to same node
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
if ((0, reflection_1.isNamespaceStatement)(link.item) && (0, reflection_1.isNamespaceStatement)(node)) {
|
|
850
|
+
// namespace can be declared multiple times
|
|
851
|
+
continue;
|
|
852
|
+
}
|
|
853
|
+
if ((0, reflection_1.isFunctionStatement)(link.item) || ((_b = link.file) === null || _b === void 0 ? void 0 : _b.destPath) === 'global') {
|
|
854
|
+
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();
|
|
855
|
+
if (!(containingNamespaceLower && linkItemNamespaceLower) || linkItemNamespaceLower !== containingNamespaceLower) {
|
|
856
|
+
// the thing found is a function OR from global (which is also a function)
|
|
857
|
+
if ((0, reflection_1.isNamespaceStatement)(node) ||
|
|
858
|
+
(0, reflection_1.isEnumStatement)(node) ||
|
|
859
|
+
(0, reflection_1.isConstStatement)(node) ||
|
|
860
|
+
(0, reflection_1.isInterfaceStatement)(node)) {
|
|
861
|
+
// these are not callable functions in transpiled code - ignore them
|
|
862
|
+
continue;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
const thisNodeKindName = util_1.default.getAstNodeFriendlyName(node);
|
|
867
|
+
const thatNodeKindName = link.file.srcPath === 'global' ? 'Global Function' : (_f = util_1.default.getAstNodeFriendlyName(link.item)) !== null && _f !== void 0 ? _f : '';
|
|
868
|
+
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;
|
|
869
|
+
if ((0, reflection_1.isNamespaceStatement)(link.item)) {
|
|
870
|
+
thatNameRange = (_o = (_m = link.item.getNameParts()) === null || _m === void 0 ? void 0 : _m[0]) === null || _o === void 0 ? void 0 : _o.range;
|
|
871
|
+
}
|
|
872
|
+
const relatedInformation = thatNameRange ? [{
|
|
873
|
+
message: `${thatNodeKindName} declared here`,
|
|
874
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file((_p = link.file) === null || _p === void 0 ? void 0 : _p.srcPath).toString(), thatNameRange)
|
|
875
|
+
}] : undefined;
|
|
876
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.nameCollision(thisNodeKindName, thatNodeKindName, name)), { origin: interfaces_1.DiagnosticOrigin.Scope, range: nameRange, relatedInformation: relatedInformation }));
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
detectShadowedLocalVar(file, varDeclaration) {
|
|
880
|
+
var _a;
|
|
881
|
+
const varName = varDeclaration.name;
|
|
882
|
+
const lowerVarName = varName.toLowerCase();
|
|
883
|
+
const callableContainerMap = this.event.scope.getCallableContainerMap();
|
|
884
|
+
const varIsFunction = () => {
|
|
885
|
+
return (0, reflection_1.isCallableType)(varDeclaration.type);
|
|
886
|
+
};
|
|
887
|
+
if (
|
|
888
|
+
//has same name as stdlib
|
|
889
|
+
globalCallables_1.globalCallableMap.has(lowerVarName)) {
|
|
890
|
+
//local var function with same name as stdlib function
|
|
891
|
+
if (varIsFunction()) {
|
|
892
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('stdlib')), { range: varDeclaration.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
else if (callableContainerMap.has(lowerVarName)) {
|
|
896
|
+
const callable = callableContainerMap.get(lowerVarName);
|
|
897
|
+
//is same name as a callable
|
|
898
|
+
if (varIsFunction()) {
|
|
899
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarFunctionShadowsParentFunction('scope')), { range: varDeclaration.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope, relatedInformation: [{
|
|
900
|
+
message: 'Function declared here',
|
|
901
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(callable[0].callable.file.srcPath).toString(), callable[0].callable.nameRange)
|
|
902
|
+
}] }));
|
|
903
|
+
}
|
|
904
|
+
else {
|
|
905
|
+
this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.localVarShadowedByScopedFunction()), { range: varDeclaration.nameRange, file: file, origin: interfaces_1.DiagnosticOrigin.Scope, relatedInformation: [{
|
|
906
|
+
message: 'Function declared here',
|
|
907
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(callable[0].callable.file.srcPath).toString(), callable[0].callable.nameRange)
|
|
908
|
+
}] }));
|
|
909
|
+
}
|
|
910
|
+
//has the same name as an in-scope class
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
const classStmtLink = this.event.scope.getClassFileLink(lowerVarName);
|
|
914
|
+
if (classStmtLink) {
|
|
915
|
+
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: [{
|
|
916
|
+
message: 'Class declared here',
|
|
917
|
+
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)
|
|
918
|
+
}] }));
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
detectVariableNamespaceCollisions(file) {
|
|
923
|
+
var _a, _b;
|
|
924
|
+
//find all function parameters
|
|
925
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
926
|
+
for (let func of file['_cachedLookups'].functionExpressions) {
|
|
927
|
+
for (let param of func.parameters) {
|
|
928
|
+
let lowerParamName = param.tokens.name.text.toLowerCase();
|
|
929
|
+
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());
|
|
930
|
+
//see if the param matches any starting namespace part
|
|
931
|
+
if (namespace) {
|
|
932
|
+
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: [{
|
|
933
|
+
message: 'Namespace declared here',
|
|
934
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(namespace.file.srcPath).toString(), namespace.nameRange)
|
|
935
|
+
}] }));
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
940
|
+
for (let assignment of file['_cachedLookups'].assignmentStatements) {
|
|
941
|
+
let lowerAssignmentName = assignment.tokens.name.text.toLowerCase();
|
|
942
|
+
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());
|
|
943
|
+
//see if the param matches any starting namespace part
|
|
944
|
+
if (namespace) {
|
|
945
|
+
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: [{
|
|
946
|
+
message: 'Namespace declared here',
|
|
947
|
+
location: util_1.default.createLocation(vscode_uri_1.URI.file(namespace.file.srcPath).toString(), namespace.nameRange)
|
|
948
|
+
}] }));
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
validateXmlInterface(scope) {
|
|
953
|
+
var _a, _b, _c, _d, _e;
|
|
954
|
+
if (!((_b = (_a = scope.xmlFile.parser.ast) === null || _a === void 0 ? void 0 : _a.componentElement) === null || _b === void 0 ? void 0 : _b.interfaceElement)) {
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
const iface = scope.xmlFile.parser.ast.componentElement.interfaceElement;
|
|
958
|
+
const callableContainerMap = scope.getCallableContainerMap();
|
|
959
|
+
//validate functions
|
|
960
|
+
for (const func of iface.functions) {
|
|
961
|
+
const name = func.name;
|
|
962
|
+
if (!name) {
|
|
963
|
+
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 }));
|
|
964
|
+
}
|
|
965
|
+
else if (!callableContainerMap.has(name.toLowerCase())) {
|
|
966
|
+
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 }));
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
//validate fields
|
|
970
|
+
for (const field of iface.fields) {
|
|
971
|
+
const { id, type, onChange } = field;
|
|
972
|
+
if (!id) {
|
|
973
|
+
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 }));
|
|
974
|
+
}
|
|
975
|
+
if (!type) {
|
|
976
|
+
if (!field.alias) {
|
|
977
|
+
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 }));
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
else if (!SGTypes_1.SGFieldTypes.includes(type.toLowerCase())) {
|
|
981
|
+
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 }));
|
|
982
|
+
}
|
|
983
|
+
if (onChange) {
|
|
984
|
+
if (!callableContainerMap.has(onChange.toLowerCase())) {
|
|
985
|
+
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 }));
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
/**
|
|
991
|
+
* Detect when a child has imported a script that an ancestor also imported
|
|
992
|
+
*/
|
|
993
|
+
diagnosticDetectDuplicateAncestorScriptImports(scope) {
|
|
994
|
+
var _a, _b;
|
|
995
|
+
if (scope.xmlFile.parentComponent) {
|
|
996
|
+
//build a lookup of pkg paths -> FileReference so we can more easily look up collisions
|
|
997
|
+
let parentScriptImports = scope.xmlFile.getAncestorScriptTagImports();
|
|
998
|
+
let lookup = {};
|
|
999
|
+
for (let parentScriptImport of parentScriptImports) {
|
|
1000
|
+
//keep the first occurance of a pkgPath. Parent imports are first in the array
|
|
1001
|
+
if (!lookup[parentScriptImport.destPath]) {
|
|
1002
|
+
lookup[parentScriptImport.destPath] = parentScriptImport;
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
//add warning for every script tag that this file shares with an ancestor
|
|
1006
|
+
for (let scriptImport of scope.xmlFile.scriptTagImports) {
|
|
1007
|
+
let ancestorScriptImport = lookup[scriptImport.destPath];
|
|
1008
|
+
if (ancestorScriptImport) {
|
|
1009
|
+
let ancestorComponent = ancestorScriptImport.sourceFile;
|
|
1010
|
+
let ancestorComponentName = (_b = (_a = ancestorComponent.componentName) === null || _a === void 0 ? void 0 : _a.text) !== null && _b !== void 0 ? _b : ancestorComponent.destPath;
|
|
1011
|
+
this.addDiagnostic(Object.assign(Object.assign({ file: scope.xmlFile, range: scriptImport.filePathRange }, DiagnosticMessages_1.DiagnosticMessages.unnecessaryScriptImportInChildFromParent(ancestorComponentName)), { origin: interfaces_1.DiagnosticOrigin.Scope }));
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
607
1016
|
/**
|
|
608
1017
|
* Adds a diagnostic to the first scope for this key. Prevents duplicate diagnostics
|
|
609
1018
|
* for diagnostics where scope isn't important. (i.e. CreateObject validations)
|
|
610
1019
|
*/
|
|
611
1020
|
addDiagnosticOnce(diagnostic) {
|
|
612
|
-
this.onceCache.getOrAdd(`${diagnostic.code}
|
|
1021
|
+
this.onceCache.getOrAdd(`${diagnostic.code} - ${diagnostic.message} - ${util_1.default.rangeToString(diagnostic.range)} `, () => {
|
|
613
1022
|
const diagnosticWithOrigin = Object.assign({}, diagnostic);
|
|
614
1023
|
if (!diagnosticWithOrigin.origin) {
|
|
615
1024
|
// diagnostic does not have origin.
|
|
@@ -636,7 +1045,7 @@ class ScopeValidator {
|
|
|
636
1045
|
*/
|
|
637
1046
|
addMultiScopeDiagnostic(diagnostic) {
|
|
638
1047
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
639
|
-
diagnostic = this.multiScopeCache.getOrAdd(`${(_a = diagnostic.file) === null || _a === void 0 ? void 0 : _a.srcPath}
|
|
1048
|
+
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)} `, () => {
|
|
640
1049
|
if (!diagnostic.relatedInformation) {
|
|
641
1050
|
diagnostic.relatedInformation = [];
|
|
642
1051
|
}
|