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.
Files changed (168) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/README.md +1 -1
  3. package/dist/AstValidationSegmenter.d.ts +12 -2
  4. package/dist/AstValidationSegmenter.js +74 -16
  5. package/dist/AstValidationSegmenter.js.map +1 -1
  6. package/dist/DependencyGraph.d.ts +4 -0
  7. package/dist/DependencyGraph.js +19 -0
  8. package/dist/DependencyGraph.js.map +1 -1
  9. package/dist/DiagnosticFilterer.d.ts +7 -4
  10. package/dist/DiagnosticFilterer.js +67 -37
  11. package/dist/DiagnosticFilterer.js.map +1 -1
  12. package/dist/DiagnosticMessages.d.ts +1 -1
  13. package/dist/PluginInterface.js +1 -1
  14. package/dist/PluginInterface.js.map +1 -1
  15. package/dist/Program.d.ts +19 -15
  16. package/dist/Program.js +153 -88
  17. package/dist/Program.js.map +1 -1
  18. package/dist/Scope.d.ts +27 -28
  19. package/dist/Scope.js +174 -361
  20. package/dist/Scope.js.map +1 -1
  21. package/dist/Stopwatch.d.ts +4 -0
  22. package/dist/Stopwatch.js +7 -0
  23. package/dist/Stopwatch.js.map +1 -1
  24. package/dist/SymbolTable.d.ts +2 -1
  25. package/dist/SymbolTable.js +26 -0
  26. package/dist/SymbolTable.js.map +1 -1
  27. package/dist/{SymbolTableFlag.js → SymbolTypeFlag.js} +1 -1
  28. package/dist/SymbolTypeFlag.js.map +1 -0
  29. package/dist/XmlScope.d.ts +0 -8
  30. package/dist/XmlScope.js +0 -77
  31. package/dist/XmlScope.js.map +1 -1
  32. package/dist/astUtils/CachedLookups.js +4 -8
  33. package/dist/astUtils/CachedLookups.js.map +1 -1
  34. package/dist/astUtils/creators.d.ts +1 -0
  35. package/dist/astUtils/creators.js +3 -2
  36. package/dist/astUtils/creators.js.map +1 -1
  37. package/dist/astUtils/creators.spec.js +0 -10
  38. package/dist/astUtils/creators.spec.js.map +1 -1
  39. package/dist/astUtils/reflection.d.ts +4 -4
  40. package/dist/astUtils/reflection.js +8 -7
  41. package/dist/astUtils/reflection.js.map +1 -1
  42. package/dist/astUtils/reflection.spec.js +10 -15
  43. package/dist/astUtils/reflection.spec.js.map +1 -1
  44. package/dist/astUtils/visitors.d.ts +1 -2
  45. package/dist/astUtils/visitors.js.map +1 -1
  46. package/dist/astUtils/visitors.spec.js +1 -5
  47. package/dist/astUtils/visitors.spec.js.map +1 -1
  48. package/dist/bscPlugin/BscPlugin.d.ts +3 -1
  49. package/dist/bscPlugin/BscPlugin.js +10 -0
  50. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  51. package/dist/bscPlugin/SignatureHelpUtil.js +4 -3
  52. package/dist/bscPlugin/SignatureHelpUtil.js.map +1 -1
  53. package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +1 -0
  54. package/dist/bscPlugin/completions/CompletionsProcessor.js +31 -11
  55. package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -1
  56. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js +39 -0
  57. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js.map +1 -1
  58. package/dist/bscPlugin/hover/HoverProcessor.js +5 -5
  59. package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -1
  60. package/dist/bscPlugin/hover/HoverProcessor.spec.js +51 -5
  61. package/dist/bscPlugin/hover/HoverProcessor.spec.js.map +1 -1
  62. package/dist/bscPlugin/references/ReferencesProvider.d.ts +12 -0
  63. package/dist/bscPlugin/references/ReferencesProvider.js +56 -0
  64. package/dist/bscPlugin/references/ReferencesProvider.js.map +1 -0
  65. package/dist/bscPlugin/references/ReferencesProvider.spec.d.ts +1 -0
  66. package/dist/bscPlugin/references/ReferencesProvider.spec.js +51 -0
  67. package/dist/bscPlugin/references/ReferencesProvider.spec.js.map +1 -0
  68. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js +1 -1
  69. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +1 -1
  70. package/dist/bscPlugin/validation/BrsFileAfterValidatior.d.ts +7 -0
  71. package/dist/bscPlugin/validation/BrsFileAfterValidatior.js +18 -0
  72. package/dist/bscPlugin/validation/BrsFileAfterValidatior.js.map +1 -0
  73. package/dist/bscPlugin/validation/BrsFileValidator.d.ts +1 -0
  74. package/dist/bscPlugin/validation/BrsFileValidator.js +17 -10
  75. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -1
  76. package/dist/bscPlugin/validation/ScopeValidator.d.ts +37 -1
  77. package/dist/bscPlugin/validation/ScopeValidator.js +434 -25
  78. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -1
  79. package/dist/bscPlugin/validation/ScopeValidator.spec.js +91 -4
  80. package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +1 -1
  81. package/dist/files/BrsFile.Class.spec.js +11 -4
  82. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  83. package/dist/files/BrsFile.d.ts +23 -5
  84. package/dist/files/BrsFile.js +189 -51
  85. package/dist/files/BrsFile.js.map +1 -1
  86. package/dist/files/BrsFile.spec.js +589 -97
  87. package/dist/files/BrsFile.spec.js.map +1 -1
  88. package/dist/files/BscFile.d.ts +2 -1
  89. package/dist/files/BscFile.js.map +1 -1
  90. package/dist/files/XmlFile.d.ts +2 -2
  91. package/dist/files/XmlFile.js +2 -2
  92. package/dist/files/XmlFile.js.map +1 -1
  93. package/dist/index.d.ts +1 -0
  94. package/dist/index.js +1 -0
  95. package/dist/index.js.map +1 -1
  96. package/dist/interfaces.d.ts +43 -4
  97. package/dist/interfaces.js.map +1 -1
  98. package/dist/lexer/Lexer.d.ts +9 -3
  99. package/dist/lexer/Lexer.js +36 -15
  100. package/dist/lexer/Lexer.js.map +1 -1
  101. package/dist/lexer/Lexer.spec.js +76 -38
  102. package/dist/lexer/Lexer.spec.js.map +1 -1
  103. package/dist/lexer/Token.js +1 -1
  104. package/dist/lexer/Token.js.map +1 -1
  105. package/dist/lexer/TokenKind.d.ts +1 -0
  106. package/dist/lexer/TokenKind.js +4 -1
  107. package/dist/lexer/TokenKind.js.map +1 -1
  108. package/dist/parser/AstNode.d.ts +1 -2
  109. package/dist/parser/AstNode.js +0 -1
  110. package/dist/parser/AstNode.js.map +1 -1
  111. package/dist/parser/BrsTranspileState.d.ts +1 -1
  112. package/dist/parser/Expression.d.ts +77 -47
  113. package/dist/parser/Expression.js +162 -88
  114. package/dist/parser/Expression.js.map +1 -1
  115. package/dist/parser/Parser.d.ts +7 -2
  116. package/dist/parser/Parser.js +40 -90
  117. package/dist/parser/Parser.js.map +1 -1
  118. package/dist/parser/Parser.spec.js +21 -44
  119. package/dist/parser/Parser.spec.js.map +1 -1
  120. package/dist/parser/SGTypes.js +5 -5
  121. package/dist/parser/SGTypes.js.map +1 -1
  122. package/dist/parser/Statement.d.ts +92 -84
  123. package/dist/parser/Statement.js +199 -133
  124. package/dist/parser/Statement.js.map +1 -1
  125. package/dist/parser/Statement.spec.js +0 -13
  126. package/dist/parser/Statement.spec.js.map +1 -1
  127. package/dist/parser/TranspileState.d.ts +17 -8
  128. package/dist/parser/TranspileState.js +67 -8
  129. package/dist/parser/TranspileState.js.map +1 -1
  130. package/dist/parser/tests/Parser.spec.d.ts +1 -1
  131. package/dist/parser/tests/Parser.spec.js +1 -2
  132. package/dist/parser/tests/Parser.spec.js.map +1 -1
  133. package/dist/parser/tests/controlFlow/If.spec.js +1 -1
  134. package/dist/parser/tests/controlFlow/If.spec.js.map +1 -1
  135. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js +1 -3
  136. package/dist/parser/tests/expression/AssociativeArrayLiterals.spec.js.map +1 -1
  137. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +44 -0
  138. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  139. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +6 -6
  140. package/dist/parser/tests/expression/TernaryExpression.spec.js +47 -0
  141. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  142. package/dist/parser/tests/statement/ConstStatement.spec.js +2 -2
  143. package/dist/parser/tests/statement/InterfaceStatement.spec.js +8 -1
  144. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  145. package/dist/parser/tests/statement/Misc.spec.js +25 -5
  146. package/dist/parser/tests/statement/Misc.spec.js.map +1 -1
  147. package/dist/preprocessor/Chunk.js +1 -2
  148. package/dist/preprocessor/Chunk.js.map +1 -1
  149. package/dist/preprocessor/PreprocessorParser.js +2 -1
  150. package/dist/preprocessor/PreprocessorParser.js.map +1 -1
  151. package/dist/types/AssociativeArrayType.d.ts +3 -0
  152. package/dist/types/AssociativeArrayType.js +9 -0
  153. package/dist/types/AssociativeArrayType.js.map +1 -1
  154. package/dist/types/BscType.d.ts +1 -1
  155. package/dist/types/BscType.js +1 -0
  156. package/dist/types/BscType.js.map +1 -1
  157. package/dist/types/ComponentType.d.ts +1 -1
  158. package/dist/types/ReferenceType.d.ts +9 -1
  159. package/dist/types/ReferenceType.js +45 -1
  160. package/dist/types/ReferenceType.js.map +1 -1
  161. package/dist/types/ReferenceType.spec.js +15 -0
  162. package/dist/types/ReferenceType.spec.js.map +1 -1
  163. package/dist/util.d.ts +23 -9
  164. package/dist/util.js +115 -21
  165. package/dist/util.js.map +1 -1
  166. package/package.json +6 -3
  167. package/dist/SymbolTableFlag.js.map +0 -1
  168. /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 ignoreUnresolvedAssignmentLHS;
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
- if (hasChangeInfo && !thisFileRequiresChangedSymbol && !thisFileHasChanges) {
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
- return;
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
- ignoreUnresolvedAssignmentLHS(expression, exprType, definingNode) {
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 (!(expectedLHSType === null || expectedLHSType === void 0 ? void 0 : expectedLHSType.isTypeCompatible(actualRHSType, compatibilityData))) {
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 shouldIgnoreLHS = this.ignoreUnresolvedAssignmentLHS(expression, exprType, typeData === null || typeData === void 0 ? void 0 : typeData.definingNode);
472
- if (!this.isTypeKnown(exprType) && !shouldIgnoreLHS) {
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} -${diagnostic.message} -${util_1.default.rangeToString(diagnostic.range)} `, () => {
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} -${diagnostic.code} -${diagnostic.message} -${util_1.default.rangeToString(diagnostic.range)} `, () => {
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
  }