brighterscript 0.66.0-alpha.6 → 0.66.0-alpha.8

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 (288) hide show
  1. package/CHANGELOG.md +88 -10
  2. package/README.md +16 -0
  3. package/bsconfig.schema.json +15 -0
  4. package/dist/ActionPipeline.d.ts +10 -0
  5. package/dist/ActionPipeline.js +40 -0
  6. package/dist/ActionPipeline.js.map +1 -0
  7. package/dist/AstValidationSegmenter.d.ts +25 -0
  8. package/dist/AstValidationSegmenter.js +150 -0
  9. package/dist/AstValidationSegmenter.js.map +1 -0
  10. package/dist/BsConfig.d.ts +15 -1
  11. package/dist/CommentFlagProcessor.d.ts +4 -3
  12. package/dist/CommentFlagProcessor.js.map +1 -1
  13. package/dist/DiagnosticMessages.d.ts +8 -1
  14. package/dist/DiagnosticMessages.js +30 -13
  15. package/dist/DiagnosticMessages.js.map +1 -1
  16. package/dist/LanguageServer.js +7 -1
  17. package/dist/LanguageServer.js.map +1 -1
  18. package/dist/PluginInterface.d.ts +11 -2
  19. package/dist/PluginInterface.js +69 -10
  20. package/dist/PluginInterface.js.map +1 -1
  21. package/dist/Program.d.ts +107 -38
  22. package/dist/Program.js +502 -270
  23. package/dist/Program.js.map +1 -1
  24. package/dist/ProgramBuilder.d.ts +10 -4
  25. package/dist/ProgramBuilder.js +44 -54
  26. package/dist/ProgramBuilder.js.map +1 -1
  27. package/dist/Scope.d.ts +26 -38
  28. package/dist/Scope.js +153 -174
  29. package/dist/Scope.js.map +1 -1
  30. package/dist/SymbolTable.d.ts +4 -1
  31. package/dist/SymbolTable.js +19 -7
  32. package/dist/SymbolTable.js.map +1 -1
  33. package/dist/XmlScope.d.ts +5 -4
  34. package/dist/XmlScope.js +16 -14
  35. package/dist/XmlScope.js.map +1 -1
  36. package/dist/astUtils/{AstEditor.d.ts → Editor.d.ts} +6 -1
  37. package/dist/astUtils/{AstEditor.js → Editor.js} +9 -3
  38. package/dist/astUtils/Editor.js.map +1 -0
  39. package/dist/astUtils/{AstEditor.spec.js → Editor.spec.js} +10 -6
  40. package/dist/astUtils/Editor.spec.js.map +1 -0
  41. package/dist/astUtils/reflection.d.ts +9 -4
  42. package/dist/astUtils/reflection.js +23 -7
  43. package/dist/astUtils/reflection.js.map +1 -1
  44. package/dist/astUtils/reflection.spec.js +2 -2
  45. package/dist/astUtils/reflection.spec.js.map +1 -1
  46. package/dist/astUtils/visitors.d.ts +14 -3
  47. package/dist/astUtils/visitors.js +22 -2
  48. package/dist/astUtils/visitors.js.map +1 -1
  49. package/dist/astUtils/visitors.spec.js +58 -7
  50. package/dist/astUtils/visitors.spec.js.map +1 -1
  51. package/dist/bscPlugin/BscPlugin.d.ts +10 -2
  52. package/dist/bscPlugin/BscPlugin.js +24 -4
  53. package/dist/bscPlugin/BscPlugin.js.map +1 -1
  54. package/dist/bscPlugin/FileWriter.d.ts +6 -0
  55. package/dist/bscPlugin/FileWriter.js +24 -0
  56. package/dist/bscPlugin/FileWriter.js.map +1 -0
  57. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js +8 -8
  58. package/dist/bscPlugin/codeActions/CodeActionsProcessor.js.map +1 -1
  59. package/dist/bscPlugin/codeActions/CodeActionsProcessor.spec.js.map +1 -1
  60. package/dist/bscPlugin/completions/CompletionsProcessor.d.ts +7 -2
  61. package/dist/bscPlugin/completions/CompletionsProcessor.js +112 -44
  62. package/dist/bscPlugin/completions/CompletionsProcessor.js.map +1 -1
  63. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js +212 -6
  64. package/dist/bscPlugin/completions/CompletionsProcessor.spec.js.map +1 -1
  65. package/dist/bscPlugin/fileProviders/FileProvider.d.ts +9 -0
  66. package/dist/bscPlugin/fileProviders/FileProvider.js +51 -0
  67. package/dist/bscPlugin/fileProviders/FileProvider.js.map +1 -0
  68. package/dist/bscPlugin/hover/HoverProcessor.d.ts +1 -7
  69. package/dist/bscPlugin/hover/HoverProcessor.js +10 -8
  70. package/dist/bscPlugin/hover/HoverProcessor.js.map +1 -1
  71. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.d.ts +1 -0
  72. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js +43 -0
  73. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.js.map +1 -1
  74. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js +22 -0
  75. package/dist/bscPlugin/semanticTokens/BrsFileSemanticTokensProcessor.spec.js.map +1 -1
  76. package/dist/bscPlugin/serialize/BslibInjector.spec.js +19 -0
  77. package/dist/bscPlugin/serialize/BslibInjector.spec.js.map +1 -0
  78. package/dist/bscPlugin/serialize/BslibManager.d.ts +9 -0
  79. package/dist/bscPlugin/serialize/BslibManager.js +40 -0
  80. package/dist/bscPlugin/serialize/BslibManager.js.map +1 -0
  81. package/dist/bscPlugin/serialize/FileSerializer.d.ts +9 -0
  82. package/dist/bscPlugin/serialize/FileSerializer.js +72 -0
  83. package/dist/bscPlugin/serialize/FileSerializer.js.map +1 -0
  84. package/dist/bscPlugin/transpile/{BrsFilePreTranspileProcessor.d.ts → BrsFileTranspileProcessor.d.ts} +4 -2
  85. package/dist/bscPlugin/transpile/{BrsFilePreTranspileProcessor.js → BrsFileTranspileProcessor.js} +29 -5
  86. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.js.map +1 -0
  87. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.d.ts +1 -0
  88. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js +41 -0
  89. package/dist/bscPlugin/transpile/BrsFileTranspileProcessor.spec.js.map +1 -0
  90. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.d.ts +2 -2
  91. package/dist/bscPlugin/transpile/XmlFilePreTranspileProcessor.js.map +1 -1
  92. package/dist/bscPlugin/validation/BrsFileValidator.js +8 -3
  93. package/dist/bscPlugin/validation/BrsFileValidator.js.map +1 -1
  94. package/dist/bscPlugin/validation/BrsFileValidator.spec.js +1 -1
  95. package/dist/bscPlugin/validation/BrsFileValidator.spec.js.map +1 -1
  96. package/dist/bscPlugin/validation/ScopeValidator.d.ts +5 -9
  97. package/dist/bscPlugin/validation/ScopeValidator.js +214 -222
  98. package/dist/bscPlugin/validation/ScopeValidator.js.map +1 -1
  99. package/dist/bscPlugin/validation/ScopeValidator.spec.js +669 -0
  100. package/dist/bscPlugin/validation/ScopeValidator.spec.js.map +1 -1
  101. package/dist/bscPlugin/validation/XmlFileValidator.js +2 -2
  102. package/dist/bscPlugin/validation/XmlFileValidator.js.map +1 -1
  103. package/dist/cli.js +1 -0
  104. package/dist/cli.js.map +1 -1
  105. package/dist/deferred.d.ts +2 -2
  106. package/dist/deferred.js.map +1 -1
  107. package/dist/diagnosticUtils.d.ts +1 -0
  108. package/dist/diagnosticUtils.js +4 -3
  109. package/dist/diagnosticUtils.js.map +1 -1
  110. package/dist/examples/plugins/removePrint.js +1 -1
  111. package/dist/examples/plugins/removePrint.js.map +1 -1
  112. package/dist/files/AssetFile.d.ts +26 -0
  113. package/dist/files/AssetFile.js +26 -0
  114. package/dist/files/AssetFile.js.map +1 -0
  115. package/dist/files/BrsFile.Class.spec.js +241 -40
  116. package/dist/files/BrsFile.Class.spec.js.map +1 -1
  117. package/dist/files/BrsFile.d.ts +66 -16
  118. package/dist/files/BrsFile.js +330 -80
  119. package/dist/files/BrsFile.js.map +1 -1
  120. package/dist/files/BrsFile.spec.js +1134 -167
  121. package/dist/files/BrsFile.spec.js.map +1 -1
  122. package/dist/files/Factory.d.ts +25 -0
  123. package/dist/files/Factory.js +22 -0
  124. package/dist/files/Factory.js.map +1 -0
  125. package/dist/files/File.d.ts +106 -0
  126. package/dist/files/File.js +16 -0
  127. package/dist/files/File.js.map +1 -0
  128. package/dist/files/LazyFileData.d.ts +20 -0
  129. package/dist/files/LazyFileData.js +54 -0
  130. package/dist/files/LazyFileData.js.map +1 -0
  131. package/dist/files/LazyFileData.spec.d.ts +1 -0
  132. package/dist/files/LazyFileData.spec.js +27 -0
  133. package/dist/files/LazyFileData.spec.js.map +1 -0
  134. package/dist/files/XmlFile.d.ts +55 -17
  135. package/dist/files/XmlFile.js +88 -47
  136. package/dist/files/XmlFile.js.map +1 -1
  137. package/dist/files/XmlFile.spec.js +64 -57
  138. package/dist/files/XmlFile.spec.js.map +1 -1
  139. package/dist/files/tests/imports.spec.js +21 -8
  140. package/dist/files/tests/imports.spec.js.map +1 -1
  141. package/dist/files/tests/optionalChaning.spec.js +14 -14
  142. package/dist/files/tests/optionalChaning.spec.js.map +1 -1
  143. package/dist/globalCallables.js +1 -1
  144. package/dist/globalCallables.js.map +1 -1
  145. package/dist/index.d.ts +6 -1
  146. package/dist/index.js +6 -1
  147. package/dist/index.js.map +1 -1
  148. package/dist/interfaces.d.ts +357 -89
  149. package/dist/interfaces.js +10 -2
  150. package/dist/interfaces.js.map +1 -1
  151. package/dist/lexer/Lexer.js +1 -1
  152. package/dist/lexer/TokenKind.d.ts +1 -0
  153. package/dist/lexer/TokenKind.js +4 -1
  154. package/dist/lexer/TokenKind.js.map +1 -1
  155. package/dist/parser/AstNode.d.ts +2 -2
  156. package/dist/parser/AstNode.js +1 -1
  157. package/dist/parser/AstNode.js.map +1 -1
  158. package/dist/parser/BrsTranspileState.d.ts +3 -2
  159. package/dist/parser/BrsTranspileState.js +3 -2
  160. package/dist/parser/BrsTranspileState.js.map +1 -1
  161. package/dist/parser/Expression.d.ts +2 -2
  162. package/dist/parser/Expression.js +23 -19
  163. package/dist/parser/Expression.js.map +1 -1
  164. package/dist/parser/Parser.Class.spec.js +103 -0
  165. package/dist/parser/Parser.Class.spec.js.map +1 -1
  166. package/dist/parser/Parser.js +61 -13
  167. package/dist/parser/Parser.js.map +1 -1
  168. package/dist/parser/Parser.spec.js +227 -1
  169. package/dist/parser/Parser.spec.js.map +1 -1
  170. package/dist/parser/SGParser.d.ts +2 -2
  171. package/dist/parser/SGParser.js +3 -3
  172. package/dist/parser/SGParser.js.map +1 -1
  173. package/dist/parser/SGParser.spec.js +2 -2
  174. package/dist/parser/SGParser.spec.js.map +1 -1
  175. package/dist/parser/SGTypes.d.ts +1 -1
  176. package/dist/parser/Statement.d.ts +12 -5
  177. package/dist/parser/Statement.js +56 -26
  178. package/dist/parser/Statement.js.map +1 -1
  179. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js +16 -16
  180. package/dist/parser/tests/expression/NullCoalescenceExpression.spec.js.map +1 -1
  181. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js +10 -10
  182. package/dist/parser/tests/expression/RegexLiteralExpression.spec.js.map +1 -1
  183. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js +24 -24
  184. package/dist/parser/tests/expression/SourceLiteralExpression.spec.js.map +1 -1
  185. package/dist/parser/tests/expression/TemplateStringExpression.spec.js +64 -36
  186. package/dist/parser/tests/expression/TemplateStringExpression.spec.js.map +1 -1
  187. package/dist/parser/tests/expression/TernaryExpression.spec.js +34 -34
  188. package/dist/parser/tests/expression/TernaryExpression.spec.js.map +1 -1
  189. package/dist/parser/tests/expression/UnaryExpression.spec.d.ts +1 -0
  190. package/dist/parser/tests/expression/UnaryExpression.spec.js +52 -0
  191. package/dist/parser/tests/expression/UnaryExpression.spec.js.map +1 -0
  192. package/dist/parser/tests/statement/ConstStatement.spec.js +90 -16
  193. package/dist/parser/tests/statement/ConstStatement.spec.js.map +1 -1
  194. package/dist/parser/tests/statement/Continue.spec.js +2 -2
  195. package/dist/parser/tests/statement/Continue.spec.js.map +1 -1
  196. package/dist/parser/tests/statement/Enum.spec.js +35 -26
  197. package/dist/parser/tests/statement/Enum.spec.js.map +1 -1
  198. package/dist/parser/tests/statement/For.spec.js +6 -6
  199. package/dist/parser/tests/statement/For.spec.js.map +1 -1
  200. package/dist/parser/tests/statement/ForEach.spec.js +4 -4
  201. package/dist/parser/tests/statement/ForEach.spec.js.map +1 -1
  202. package/dist/parser/tests/statement/InterfaceStatement.spec.js +20 -12
  203. package/dist/parser/tests/statement/InterfaceStatement.spec.js.map +1 -1
  204. package/dist/parser/tests/statement/PrintStatement.spec.js +10 -10
  205. package/dist/parser/tests/statement/PrintStatement.spec.js.map +1 -1
  206. package/dist/preprocessor/Manifest.d.ts +1 -1
  207. package/dist/preprocessor/Manifest.js +2 -2
  208. package/dist/preprocessor/Manifest.js.map +1 -1
  209. package/dist/roku-types/data.json +98 -193
  210. package/dist/roku-types/index.d.ts +15 -11
  211. package/dist/types/ArrayType.d.ts +1 -1
  212. package/dist/types/ArrayType.js +4 -0
  213. package/dist/types/ArrayType.js.map +1 -1
  214. package/dist/types/ArrayType.spec.js +1 -1
  215. package/dist/types/ArrayType.spec.js.map +1 -1
  216. package/dist/types/AssociativeArrayType.d.ts +1 -1
  217. package/dist/types/AssociativeArrayType.js +1 -1
  218. package/dist/types/AssociativeArrayType.js.map +1 -1
  219. package/dist/types/BooleanType.d.ts +1 -1
  220. package/dist/types/BooleanType.js +2 -1
  221. package/dist/types/BooleanType.js.map +1 -1
  222. package/dist/types/BscType.d.ts +2 -2
  223. package/dist/types/BscType.js +30 -9
  224. package/dist/types/BscType.js.map +1 -1
  225. package/dist/types/BuiltInInterfaceAdder.d.ts +3 -0
  226. package/dist/types/BuiltInInterfaceAdder.js +37 -16
  227. package/dist/types/BuiltInInterfaceAdder.js.map +1 -1
  228. package/dist/types/BuiltInInterfaceAdder.spec.js +7 -0
  229. package/dist/types/BuiltInInterfaceAdder.spec.js.map +1 -1
  230. package/dist/types/ClassType.d.ts +4 -3
  231. package/dist/types/ClassType.js +6 -3
  232. package/dist/types/ClassType.js.map +1 -1
  233. package/dist/types/ClassType.spec.js +5 -3
  234. package/dist/types/ClassType.spec.js.map +1 -1
  235. package/dist/types/ComponentType.d.ts +1 -1
  236. package/dist/types/ComponentType.js +3 -0
  237. package/dist/types/ComponentType.js.map +1 -1
  238. package/dist/types/DoubleType.js +3 -1
  239. package/dist/types/DoubleType.js.map +1 -1
  240. package/dist/types/EnumType.d.ts +1 -1
  241. package/dist/types/EnumType.js +7 -2
  242. package/dist/types/EnumType.js.map +1 -1
  243. package/dist/types/FloatType.js +3 -1
  244. package/dist/types/FloatType.js.map +1 -1
  245. package/dist/types/InheritableType.d.ts +7 -4
  246. package/dist/types/InheritableType.js +67 -3
  247. package/dist/types/InheritableType.js.map +1 -1
  248. package/dist/types/IntegerType.js +3 -1
  249. package/dist/types/IntegerType.js.map +1 -1
  250. package/dist/types/InterfaceType.d.ts +5 -4
  251. package/dist/types/InterfaceType.js +5 -12
  252. package/dist/types/InterfaceType.js.map +1 -1
  253. package/dist/types/InterfaceType.spec.js +23 -0
  254. package/dist/types/InterfaceType.spec.js.map +1 -1
  255. package/dist/types/LongIntegerType.js +3 -1
  256. package/dist/types/LongIntegerType.js.map +1 -1
  257. package/dist/types/NamespaceType.d.ts +2 -1
  258. package/dist/types/NamespaceType.js +3 -0
  259. package/dist/types/NamespaceType.js.map +1 -1
  260. package/dist/types/ObjectType.d.ts +1 -1
  261. package/dist/types/ReferenceType.js +40 -6
  262. package/dist/types/ReferenceType.js.map +1 -1
  263. package/dist/types/StringType.js +2 -2
  264. package/dist/types/StringType.js.map +1 -1
  265. package/dist/types/TypedFunctionType.d.ts +6 -1
  266. package/dist/types/TypedFunctionType.js +46 -16
  267. package/dist/types/TypedFunctionType.js.map +1 -1
  268. package/dist/types/TypedFunctionType.spec.js +99 -0
  269. package/dist/types/TypedFunctionType.spec.js.map +1 -1
  270. package/dist/types/UnionType.js +8 -0
  271. package/dist/types/UnionType.js.map +1 -1
  272. package/dist/types/helper.spec.js +15 -0
  273. package/dist/types/helper.spec.js.map +1 -1
  274. package/dist/types/helpers.d.ts +3 -0
  275. package/dist/types/helpers.js +33 -1
  276. package/dist/types/helpers.js.map +1 -1
  277. package/dist/util.d.ts +25 -9
  278. package/dist/util.js +165 -72
  279. package/dist/util.js.map +1 -1
  280. package/dist/validators/ClassValidator.js.map +1 -1
  281. package/package.json +2 -2
  282. package/dist/astUtils/AstEditor.js.map +0 -1
  283. package/dist/astUtils/AstEditor.spec.js.map +0 -1
  284. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.js.map +0 -1
  285. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.spec.js +0 -31
  286. package/dist/bscPlugin/transpile/BrsFilePreTranspileProcessor.spec.js.map +0 -1
  287. /package/dist/astUtils/{AstEditor.spec.d.ts → Editor.spec.d.ts} +0 -0
  288. /package/dist/bscPlugin/{transpile/BrsFilePreTranspileProcessor.spec.d.ts → serialize/BslibInjector.spec.d.ts} +0 -0
@@ -5,12 +5,14 @@ const vscode_uri_1 = require("vscode-uri");
5
5
  const reflection_1 = require("../../astUtils/reflection");
6
6
  const Cache_1 = require("../../Cache");
7
7
  const DiagnosticMessages_1 = require("../../DiagnosticMessages");
8
+ const interfaces_1 = require("../../interfaces");
8
9
  const SymbolTable_1 = require("../../SymbolTable");
9
10
  const util_1 = require("../../util");
10
11
  const roku_types_1 = require("../../roku-types");
11
- const Parser_1 = require("../../parser/Parser");
12
- const TokenKind_1 = require("../../lexer/TokenKind");
12
+ const Expression_1 = require("../../parser/Expression");
13
13
  const visitors_1 = require("../../astUtils/visitors");
14
+ const AstValidationSegmenter_1 = require("../../AstValidationSegmenter");
15
+ const TokenKind_1 = require("../../lexer/TokenKind");
14
16
  /**
15
17
  * The lower-case names of all platform-included scenegraph nodes
16
18
  */
@@ -24,12 +26,14 @@ const platformComponentNames = roku_types_1.components ? new Set(Object.values(r
24
26
  */
25
27
  class ScopeValidator {
26
28
  constructor() {
27
- this.expressionsByFile = new Cache_1.Cache();
28
29
  this.onceCache = new Cache_1.Cache();
29
30
  this.multiScopeCache = new Cache_1.Cache();
30
31
  }
31
32
  processEvent(event) {
32
33
  this.event = event;
34
+ if (this.event.program.globalScope === this.event.scope) {
35
+ return;
36
+ }
33
37
  this.walkFiles();
34
38
  this.detectDuplicateEnums();
35
39
  }
@@ -41,11 +45,32 @@ class ScopeValidator {
41
45
  walkFiles() {
42
46
  this.event.scope.enumerateOwnFiles((file) => {
43
47
  if ((0, reflection_1.isBrsFile)(file)) {
44
- this.iterateFileExpressions(file);
45
- this.validateCreateObjectCalls(file);
46
- file.ast.walk((0, visitors_1.createVisitor)({
48
+ const hasChangeInfo = this.event.changedFiles && this.event.changedSymbols;
49
+ let thisFileRequiresChangedSymbol = false;
50
+ for (let requiredSymbol of file.requiredSymbols) {
51
+ const changeSymbolSetForFlag = this.event.changedSymbols.get(requiredSymbol.flags);
52
+ if (util_1.default.setContainsUnresolvedSymbol(changeSymbolSetForFlag, requiredSymbol)) {
53
+ thisFileRequiresChangedSymbol = true;
54
+ }
55
+ }
56
+ const thisFileHasChanges = this.event.changedFiles.includes(file);
57
+ if (hasChangeInfo && !thisFileRequiresChangedSymbol && !thisFileHasChanges) {
58
+ // this file does not require a symbol that has changed, and this file has not changed
59
+ return;
60
+ }
61
+ if (thisFileHasChanges) {
62
+ this.event.scope.clearAstSegmentDiagnosticsByFile(file);
63
+ }
64
+ const validationVisitor = (0, visitors_1.createVisitor)({
65
+ VariableExpression: (varExpr) => {
66
+ this.validateVariableAndDottedGetExpressions(file, varExpr);
67
+ },
68
+ DottedGetExpression: (dottedGet) => {
69
+ this.validateVariableAndDottedGetExpressions(file, dottedGet);
70
+ },
47
71
  CallExpression: (functionCall) => {
48
72
  this.validateFunctionCall(file, functionCall);
73
+ this.validateCreateObjectCall(file, functionCall);
49
74
  },
50
75
  ReturnStatement: (returnStatement) => {
51
76
  this.validateReturnStatement(file, returnStatement);
@@ -59,27 +84,21 @@ class ScopeValidator {
59
84
  UnaryExpression: (unaryExpr) => {
60
85
  this.validateUnaryExpression(file, unaryExpr);
61
86
  }
62
- }), {
63
- walkMode: visitors_1.WalkMode.visitAllRecursive
64
87
  });
88
+ const segmentsToWalkForValidation = (thisFileHasChanges || !hasChangeInfo)
89
+ ? file.validationSegmenter.segmentsForValidation // validate everything in the file
90
+ : file.getValidationSegments(this.event.changedSymbols); // validate only what's needed in the file
91
+ for (const segment of segmentsToWalkForValidation) {
92
+ this.currentSegmentBeingValidated = segment;
93
+ this.event.scope.clearAstSegmentDiagnostics(segment);
94
+ segment.walk(validationVisitor, {
95
+ walkMode: AstValidationSegmenter_1.InsideSegmentWalkMode
96
+ });
97
+ file.markSegmentAsValidated(segment);
98
+ }
65
99
  }
66
100
  });
67
101
  }
68
- checkIfUsedAsTypeExpression(expression) {
69
- //TODO: this is much faster than node.findAncestor(), but will not work for "complicated" type expressions like UnionTypes
70
- if ((0, reflection_1.isTypeExpression)(expression) ||
71
- (0, reflection_1.isTypeExpression)(expression.parent)) {
72
- return true;
73
- }
74
- if ((0, reflection_1.isBinaryExpression)(expression.parent)) {
75
- let currentExpr = expression.parent;
76
- while ((0, reflection_1.isBinaryExpression)(currentExpr) && currentExpr.operator.kind === TokenKind_1.TokenKind.Or) {
77
- currentExpr = currentExpr.parent;
78
- }
79
- return (0, reflection_1.isTypeExpression)(currentExpr);
80
- }
81
- return false;
82
- }
83
102
  isTypeKnown(exprType) {
84
103
  let isKnownType = exprType === null || exprType === void 0 ? void 0 : exprType.isResolvable();
85
104
  return isKnownType;
@@ -87,136 +106,21 @@ class ScopeValidator {
87
106
  /**
88
107
  * If this is the lhs of an assignment, we don't need to flag it as unresolved
89
108
  */
90
- ignoreUnresolvedAssignmentLHS(expression, exprType) {
109
+ ignoreUnresolvedAssignmentLHS(expression, exprType, definingNode) {
110
+ var _a, _b;
91
111
  if (!(0, reflection_1.isVariableExpression)(expression)) {
92
112
  return false;
93
113
  }
94
- const assignmentAncestor = expression === null || expression === void 0 ? void 0 : expression.findAncestor(reflection_1.isAssignmentStatement);
95
- return (assignmentAncestor === null || assignmentAncestor === void 0 ? void 0 : assignmentAncestor.name) === (expression === null || expression === void 0 ? void 0 : expression.name) && (0, reflection_1.isUnionType)(exprType); // the left hand side is not a union, which means it was never assigned
96
- }
97
- iterateFileExpressions(file) {
98
- var _a, _b, _c;
99
- const { scope } = this.event;
100
- //build an expression collection ONCE per file
101
- const expressionInfos = this.expressionsByFile.getOrAdd(file, () => {
102
- var _a, _b;
103
- const result = [];
104
- const expressions = [...file.parser.references.expressions];
105
- for (let expression of expressions) {
106
- if (!expression) {
107
- continue;
108
- }
109
- //walk left-to-right on every expression, only keep the ones that start with VariableExpression, and then keep subsequent DottedGet parts
110
- const parts = util_1.default.getDottedGetPath(expression);
111
- if (parts.length > 0) {
112
- result.push({
113
- parts: parts,
114
- expression: expression,
115
- isUsedAsType: this.checkIfUsedAsTypeExpression(expression),
116
- enclosingNamespaceNameLower: (_b = (_a = expression.findAncestor(reflection_1.isNamespaceStatement)) === null || _a === void 0 ? void 0 : _a.getName(Parser_1.ParseMode.BrighterScript)) === null || _b === void 0 ? void 0 : _b.toLowerCase()
117
- });
118
- }
119
- }
120
- return result;
121
- });
122
- outer: for (const info of expressionInfos) {
123
- const firstNamespacePart = info.parts[0].name.text;
124
- const firstNamespacePartLower = firstNamespacePart === null || firstNamespacePart === void 0 ? void 0 : firstNamespacePart.toLowerCase();
125
- //get the namespace container (accounting for namespace-relative as well)
126
- const namespaceContainer = scope.getNamespace(firstNamespacePartLower, info.enclosingNamespaceNameLower);
127
- let symbolType = SymbolTable_1.SymbolTypeFlag.runtime;
128
- let oppositeSymbolType = SymbolTable_1.SymbolTypeFlag.typetime;
129
- if (info.isUsedAsType) {
130
- // This is used in a TypeExpression - only look up types from SymbolTable
131
- symbolType = SymbolTable_1.SymbolTypeFlag.typetime;
132
- oppositeSymbolType = SymbolTable_1.SymbolTypeFlag.runtime;
133
- }
134
- // Do a complete type check on all DottedGet and Variable expressions
135
- // this will create a diagnostic if an invalid member is accessed
136
- const typeChain = [];
137
- let exprType = info.expression.getType({
138
- flags: symbolType,
139
- typeChain: typeChain
140
- });
141
- if (!this.isTypeKnown(exprType) && !this.ignoreUnresolvedAssignmentLHS(info.expression, exprType)) {
142
- if ((_a = info.expression.getType({ flags: oppositeSymbolType })) === null || _a === void 0 ? void 0 : _a.isResolvable()) {
143
- const oppoSiteTypeChain = [];
144
- const invalidlyUsedResolvedType = info.expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain });
145
- const typeChainScan = util_1.default.processTypeChain(oppoSiteTypeChain);
146
- if (info.isUsedAsType) {
147
- this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsType(typeChainScan.fullChainName)), { range: info.expression.range, file: file }), 'When used in scope');
148
- }
149
- else {
150
- this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable(invalidlyUsedResolvedType.toString())), { range: info.expression.range, file: file }), 'When used in scope');
151
- }
152
- continue;
153
- }
154
- const typeChainScan = util_1.default.processTypeChain(typeChain);
155
- this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem)), { range: typeChainScan.range }));
156
- //skip to the next expression
157
- continue;
158
- }
159
- const enumStatement = scope.getEnum(firstNamespacePartLower, info.enclosingNamespaceNameLower);
160
- //if this isn't a namespace, skip it
161
- if (!namespaceContainer && !enumStatement) {
162
- continue;
163
- }
164
- //catch unknown namespace items
165
- let entityName = firstNamespacePart;
166
- let entityNameLower = firstNamespacePart.toLowerCase();
167
- for (let i = 1; i < info.parts.length; i++) {
168
- const part = info.parts[i];
169
- entityName += '.' + part.name.text;
170
- entityNameLower += '.' + part.name.text.toLowerCase();
171
- //if this is an enum member, stop validating here to prevent errors further down the chain
172
- if (scope.getEnumMemberFileLink(entityName, info.enclosingNamespaceNameLower)) {
173
- break;
174
- }
175
- if (!scope.getEnumMap().has(entityNameLower) &&
176
- !scope.getClassMap().has(entityNameLower) &&
177
- !scope.getInterfaceMap().has(entityNameLower) &&
178
- !scope.getConstMap().has(entityNameLower) &&
179
- !scope.getCallableByName(entityNameLower) &&
180
- !scope.getNamespace(entityNameLower, info.enclosingNamespaceNameLower)) {
181
- //if this looks like an enum, provide a nicer error message
182
- const theEnum = (_b = this.getEnum(scope, entityNameLower)) === null || _b === void 0 ? void 0 : _b.item;
183
- if (theEnum) {
184
- this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.unknownEnumValue((_c = part.name.text) === null || _c === void 0 ? void 0 : _c.split('.').pop(), theEnum.fullName)), { range: part.name.range, relatedInformation: [{
185
- message: 'Enum declared here',
186
- location: util_1.default.createLocation(vscode_uri_1.URI.file(file.srcPath).toString(), theEnum.tokens.name.range)
187
- }] }));
188
- }
189
- else {
190
- this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.cannotFindName(part.name.text, entityName)), { range: part.name.range, file: file }));
191
- }
192
- //no need to add another diagnostic for future unknown items
193
- continue outer;
194
- }
195
- }
196
- //if the full expression is just an enum name, this is an illegal statement because enums don't exist at runtime
197
- if (!info.isUsedAsType && enumStatement && info.parts.length === 1) {
198
- this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable('enum')), { range: info.expression.range, file: file }), 'When used in scope');
199
- }
200
- //if the full expression is a namespace path, this is an illegal statement because namespaces don't exist at runtme
201
- if (scope.getNamespace(entityNameLower, info.enclosingNamespaceNameLower)) {
202
- this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable('namespace')), { range: info.expression.range, file: file }), 'When used in scope');
203
- }
114
+ let assignmentAncestor;
115
+ if ((0, reflection_1.isAssignmentStatement)(definingNode) && definingNode.equals.kind === TokenKind_1.TokenKind.Equal) {
116
+ // this symbol was defined in a "normal" assignment (eg. not a compound assignment)
117
+ assignmentAncestor = definingNode;
118
+ return ((_a = assignmentAncestor === null || assignmentAncestor === void 0 ? void 0 : assignmentAncestor.name) === null || _a === void 0 ? void 0 : _a.text.toLowerCase()) === ((_b = expression === null || expression === void 0 ? void 0 : expression.name) === null || _b === void 0 ? void 0 : _b.text.toLowerCase());
204
119
  }
205
- }
206
- /**
207
- * Given a string optionally separated by dots, find an enum related to it.
208
- * For example, all of these would return the enum: `SomeNamespace.SomeEnum.SomeMember`, SomeEnum.SomeMember, `SomeEnum`
209
- */
210
- getEnum(scope, name) {
211
- //look for the enum directly
212
- let result = scope.getEnumMap().get(name);
213
- //assume we've been given the enum.member syntax, so pop the member and try again
214
- if (!result) {
215
- const parts = name.split('.');
216
- parts.pop();
217
- result = scope.getEnumMap().get(parts.join('.'));
218
- }
219
- return result;
120
+ else {
121
+ assignmentAncestor = expression === null || expression === void 0 ? void 0 : expression.findAncestor(reflection_1.isAssignmentStatement);
122
+ }
123
+ return (assignmentAncestor === null || assignmentAncestor === void 0 ? void 0 : assignmentAncestor.name) === (expression === null || expression === void 0 ? void 0 : expression.name) && (0, reflection_1.isUnionType)(exprType);
220
124
  }
221
125
  /**
222
126
  * Flag duplicate enums
@@ -257,7 +161,7 @@ class ScopeValidator {
257
161
  diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.duplicateEnumDeclaration(this.event.scope.name, fullName)), { file: duplicateEnumInfo.file, range: duplicateEnumInfo.statement.tokens.name.range, relatedInformation: [{
258
162
  message: 'Enum declared here',
259
163
  location: util_1.default.createLocation(vscode_uri_1.URI.file(primaryEnum.file.srcPath).toString(), primaryEnum.statement.tokens.name.range)
260
- }] }));
164
+ }], origin: interfaces_1.DiagnosticOrigin.Scope }));
261
165
  }
262
166
  }
263
167
  this.event.scope.addDiagnostics(diagnostics);
@@ -268,64 +172,60 @@ class ScopeValidator {
268
172
  * what these calls are supposed to look like, and this is a very common thing for brs devs to do, so just
269
173
  * do this manually for now.
270
174
  */
271
- validateCreateObjectCalls(file) {
272
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
273
- const diagnostics = [];
274
- for (const call of file.functionCalls) {
275
- //skip non CreateObject function calls
276
- if (((_a = call.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) !== 'createobject' || !(0, reflection_1.isLiteralExpression)((_b = call === null || call === void 0 ? void 0 : call.args[0]) === null || _b === void 0 ? void 0 : _b.expression)) {
277
- continue;
175
+ validateCreateObjectCall(file, call) {
176
+ var _a, _b, _c, _d, _e, _f;
177
+ //skip non CreateObject function calls
178
+ const callName = (_a = util_1.default.getAllDottedGetPartsAsString(call.callee)) === null || _a === void 0 ? void 0 : _a.toLowerCase();
179
+ if (callName !== 'createobject' || !(0, reflection_1.isLiteralExpression)(call === null || call === void 0 ? void 0 : call.args[0])) {
180
+ return;
181
+ }
182
+ const firstParamToken = (_b = call === null || call === void 0 ? void 0 : call.args[0]) === null || _b === void 0 ? void 0 : _b.token;
183
+ const firstParamStringValue = (_c = firstParamToken === null || firstParamToken === void 0 ? void 0 : firstParamToken.text) === null || _c === void 0 ? void 0 : _c.replace(/"/g, '');
184
+ //if this is a `createObject('roSGNode'` call, only support known sg node types
185
+ if ((firstParamStringValue === null || firstParamStringValue === void 0 ? void 0 : firstParamStringValue.toLowerCase()) === 'rosgnode' && (0, reflection_1.isLiteralExpression)(call === null || call === void 0 ? void 0 : call.args[1])) {
186
+ const componentName = (_d = call === null || call === void 0 ? void 0 : call.args[1]) === null || _d === void 0 ? void 0 : _d.token;
187
+ //don't validate any components with a colon in their name (probably component libraries, but regular components can have them too).
188
+ if ((_e = componentName === null || componentName === void 0 ? void 0 : componentName.text) === null || _e === void 0 ? void 0 : _e.includes(':')) {
189
+ return;
278
190
  }
279
- const firstParamToken = (_d = (_c = call === null || call === void 0 ? void 0 : call.args[0]) === null || _c === void 0 ? void 0 : _c.expression) === null || _d === void 0 ? void 0 : _d.token;
280
- const firstParamStringValue = (_e = firstParamToken === null || firstParamToken === void 0 ? void 0 : firstParamToken.text) === null || _e === void 0 ? void 0 : _e.replace(/"/g, '');
281
- //if this is a `createObject('roSGNode'` call, only support known sg node types
282
- if ((firstParamStringValue === null || firstParamStringValue === void 0 ? void 0 : firstParamStringValue.toLowerCase()) === 'rosgnode' && (0, reflection_1.isLiteralExpression)((_f = call === null || call === void 0 ? void 0 : call.args[1]) === null || _f === void 0 ? void 0 : _f.expression)) {
283
- const componentName = (_h = (_g = call === null || call === void 0 ? void 0 : call.args[1]) === null || _g === void 0 ? void 0 : _g.expression) === null || _h === void 0 ? void 0 : _h.token;
284
- //don't validate any components with a colon in their name (probably component libraries, but regular components can have them too).
285
- if ((_j = componentName === null || componentName === void 0 ? void 0 : componentName.text) === null || _j === void 0 ? void 0 : _j.includes(':')) {
286
- continue;
287
- }
288
- //add diagnostic for unknown components
289
- const unquotedComponentName = (_k = componentName === null || componentName === void 0 ? void 0 : componentName.text) === null || _k === void 0 ? void 0 : _k.replace(/"/g, '');
290
- if (unquotedComponentName && !platformNodeNames.has(unquotedComponentName.toLowerCase()) && !this.event.program.getComponent(unquotedComponentName)) {
291
- this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.unknownRoSGNode(unquotedComponentName)), { range: componentName.range }));
292
- }
293
- else if ((call === null || call === void 0 ? void 0 : call.args.length) !== 2) {
294
- // roSgNode should only ever have 2 args in `createObject`
295
- this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.mismatchCreateObjectArgumentCount(firstParamStringValue, [2], call === null || call === void 0 ? void 0 : call.args.length)), { range: call.range }));
296
- }
191
+ //add diagnostic for unknown components
192
+ const unquotedComponentName = (_f = componentName === null || componentName === void 0 ? void 0 : componentName.text) === null || _f === void 0 ? void 0 : _f.replace(/"/g, '');
193
+ if (unquotedComponentName && !platformNodeNames.has(unquotedComponentName.toLowerCase()) && !this.event.program.getComponent(unquotedComponentName)) {
194
+ this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.unknownRoSGNode(unquotedComponentName)), { range: componentName.range }));
297
195
  }
298
- else if (!platformComponentNames.has(firstParamStringValue.toLowerCase())) {
299
- this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.unknownBrightScriptComponent(firstParamStringValue)), { range: firstParamToken.range }));
196
+ else if ((call === null || call === void 0 ? void 0 : call.args.length) !== 2) {
197
+ // roSgNode should only ever have 2 args in `createObject`
198
+ this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.mismatchCreateObjectArgumentCount(firstParamStringValue, [2], call === null || call === void 0 ? void 0 : call.args.length)), { range: call.range }));
300
199
  }
301
- else {
302
- // This is valid brightscript component
303
- // Test for invalid arg counts
304
- const brightScriptComponent = roku_types_1.components[firstParamStringValue.toLowerCase()];
305
- // Valid arg counts for createObject are 1+ number of args for constructor
306
- let validArgCounts = brightScriptComponent.constructors.map(cnstr => cnstr.params.length + 1);
307
- if (validArgCounts.length === 0) {
308
- // no constructors for this component, so createObject only takes 1 arg
309
- validArgCounts = [1];
310
- }
311
- if (!validArgCounts.includes(call === null || call === void 0 ? void 0 : call.args.length)) {
312
- // Incorrect number of arguments included in `createObject()`
313
- this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.mismatchCreateObjectArgumentCount(firstParamStringValue, validArgCounts, call === null || call === void 0 ? void 0 : call.args.length)), { range: call.range }));
314
- }
315
- // Test for deprecation
316
- if (brightScriptComponent.isDeprecated) {
317
- this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.deprecatedBrightScriptComponent(firstParamStringValue, brightScriptComponent.deprecatedDescription)), { range: call.range }));
318
- }
200
+ }
201
+ else if (!platformComponentNames.has(firstParamStringValue.toLowerCase())) {
202
+ this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.unknownBrightScriptComponent(firstParamStringValue)), { range: firstParamToken.range }));
203
+ }
204
+ else {
205
+ // This is valid brightscript component
206
+ // Test for invalid arg counts
207
+ const brightScriptComponent = roku_types_1.components[firstParamStringValue.toLowerCase()];
208
+ // Valid arg counts for createObject are 1+ number of args for constructor
209
+ let validArgCounts = brightScriptComponent.constructors.map(cnstr => cnstr.params.length + 1);
210
+ if (validArgCounts.length === 0) {
211
+ // no constructors for this component, so createObject only takes 1 arg
212
+ validArgCounts = [1];
213
+ }
214
+ if (!validArgCounts.includes(call === null || call === void 0 ? void 0 : call.args.length)) {
215
+ // Incorrect number of arguments included in `createObject()`
216
+ this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.mismatchCreateObjectArgumentCount(firstParamStringValue, validArgCounts, call === null || call === void 0 ? void 0 : call.args.length)), { range: call.range }));
217
+ }
218
+ // Test for deprecation
219
+ if (brightScriptComponent.isDeprecated) {
220
+ this.addDiagnosticOnce(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.deprecatedBrightScriptComponent(firstParamStringValue, brightScriptComponent.deprecatedDescription)), { range: call.range }));
319
221
  }
320
222
  }
321
- this.event.scope.addDiagnostics(diagnostics);
322
223
  }
323
224
  /**
324
225
  * Detect calls to functions with the incorrect number of parameters, or wrong types of arguments
325
226
  */
326
227
  validateFunctionCall(file, expression) {
327
228
  var _a, _b;
328
- const diagnostics = [];
329
229
  const getTypeOptions = { flags: SymbolTable_1.SymbolTypeFlag.runtime };
330
230
  let funcType = (_a = expression === null || expression === void 0 ? void 0 : expression.callee) === null || _a === void 0 ? void 0 : _a.getType(getTypeOptions);
331
231
  if ((funcType === null || funcType === void 0 ? void 0 : funcType.isResolvable()) && (0, reflection_1.isClassType)(funcType)) {
@@ -345,10 +245,14 @@ class ScopeValidator {
345
245
  minParams++;
346
246
  }
347
247
  }
248
+ if (funcType.isVariadic) {
249
+ // function accepts variable number of arguments
250
+ maxParams = Expression_1.CallExpression.MaximumArguments;
251
+ }
348
252
  let expCallArgCount = expression.args.length;
349
253
  if (expCallArgCount > maxParams || expCallArgCount < minParams) {
350
254
  let minMaxParamsText = minParams === maxParams ? maxParams : `${minParams}-${maxParams}`;
351
- diagnostics.push(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.mismatchArgumentCount(minMaxParamsText, expCallArgCount)), { range: expression.callee.range,
255
+ this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.mismatchArgumentCount(minMaxParamsText, expCallArgCount)), { range: expression.callee.range,
352
256
  //TODO detect end of expression call
353
257
  file: file }));
354
258
  }
@@ -369,14 +273,12 @@ class ScopeValidator {
369
273
  paramIndex++;
370
274
  }
371
275
  }
372
- this.event.scope.addDiagnostics(diagnostics);
373
276
  }
374
277
  /**
375
278
  * Detect return statements with incompatible types vs. declared return type
376
279
  */
377
280
  validateReturnStatement(file, returnStmt) {
378
281
  var _a;
379
- const diagnostics = [];
380
282
  const getTypeOptions = { flags: SymbolTable_1.SymbolTypeFlag.runtime };
381
283
  let funcType = returnStmt.findAncestor(reflection_1.isFunctionExpression).getType({ flags: SymbolTable_1.SymbolTypeFlag.typetime });
382
284
  if ((0, reflection_1.isTypedFunctionType)(funcType)) {
@@ -386,14 +288,12 @@ class ScopeValidator {
386
288
  this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.returnTypeMismatch(actualReturnType.toString(), funcType.returnType.toString(), compatibilityData)), { range: returnStmt.value.range, file: file }));
387
289
  }
388
290
  }
389
- this.event.scope.addDiagnostics(diagnostics);
390
291
  }
391
292
  /**
392
293
  * Detect return statements with incompatible types vs. declared return type
393
294
  */
394
295
  validateDottedSetStatement(file, dottedSetStmt) {
395
296
  var _a, _b, _c;
396
- const diagnostics = [];
397
297
  const getTypeOpts = { flags: SymbolTable_1.SymbolTypeFlag.runtime };
398
298
  const expectedLHSType = (_b = (_a = dottedSetStmt === null || dottedSetStmt === void 0 ? void 0 : dottedSetStmt.obj) === null || _a === void 0 ? void 0 : _a.getType(getTypeOpts)) === null || _b === void 0 ? void 0 : _b.getMemberType(dottedSetStmt.name.text, getTypeOpts);
399
299
  const actualRHSType = (_c = dottedSetStmt === null || dottedSetStmt === void 0 ? void 0 : dottedSetStmt.value) === null || _c === void 0 ? void 0 : _c.getType(getTypeOpts);
@@ -401,15 +301,13 @@ class ScopeValidator {
401
301
  if ((expectedLHSType === null || expectedLHSType === void 0 ? void 0 : expectedLHSType.isResolvable()) && !(expectedLHSType === null || expectedLHSType === void 0 ? void 0 : expectedLHSType.isTypeCompatible(actualRHSType, compatibilityData))) {
402
302
  this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.assignmentTypeMismatch(actualRHSType.toString(), expectedLHSType.toString(), compatibilityData)), { range: dottedSetStmt.range, file: file }));
403
303
  }
404
- this.event.scope.addDiagnostics(diagnostics);
405
304
  }
406
305
  /**
407
306
  * Detect invalid use of a binary operator
408
307
  */
409
308
  validateBinaryExpression(file, binaryExpr) {
410
- const diagnostics = [];
411
309
  const getTypeOpts = { flags: SymbolTable_1.SymbolTypeFlag.runtime };
412
- if (this.checkIfUsedAsTypeExpression(binaryExpr)) {
310
+ if (util_1.default.isInTypeExpression(binaryExpr)) {
413
311
  return;
414
312
  }
415
313
  let leftType = binaryExpr.left.getType(getTypeOpts);
@@ -448,13 +346,11 @@ class ScopeValidator {
448
346
  // if the result was dynamic, that means there wasn't a valid operation
449
347
  this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.operatorTypeMismatch(binaryExpr.operator.text, leftType.toString(), rightType.toString())), { range: binaryExpr.range, file: file }));
450
348
  }
451
- this.event.scope.addDiagnostics(diagnostics);
452
349
  }
453
350
  /**
454
351
  * Detect invalid use of a Unary operator
455
352
  */
456
353
  validateUnaryExpression(file, unaryExpr) {
457
- const diagnostics = [];
458
354
  const getTypeOpts = { flags: SymbolTable_1.SymbolTypeFlag.runtime };
459
355
  let rightType = unaryExpr.right.getType(getTypeOpts);
460
356
  if (!rightType.isResolvable()) {
@@ -468,11 +364,9 @@ class ScopeValidator {
468
364
  if ((0, reflection_1.isUnionType)(rightTypeToTest)) {
469
365
  // TODO: it is possible to validate based on innerTypes, but more complicated
470
366
  // Because you need to verify each combination of types
471
- return;
472
367
  }
473
368
  else if ((0, reflection_1.isDynamicType)(rightTypeToTest) || (0, reflection_1.isObjectType)(rightTypeToTest)) {
474
369
  // operand is basically "any" type... ignore;
475
- return;
476
370
  }
477
371
  else if ((0, reflection_1.isPrimitiveType)(rightType)) {
478
372
  const opResult = util_1.default.unaryOperatorResultType(unaryExpr.operator, rightTypeToTest);
@@ -484,7 +378,82 @@ class ScopeValidator {
484
378
  // rhs is not a primitive, so no binary operator is allowed
485
379
  this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.operatorTypeMismatch(unaryExpr.operator.text, rightType.toString())), { range: unaryExpr.range, file: file }));
486
380
  }
487
- this.event.scope.addDiagnostics(diagnostics);
381
+ }
382
+ validateVariableAndDottedGetExpressions(file, expression) {
383
+ var _a, _b;
384
+ if ((0, reflection_1.isDottedGetExpression)(expression.parent)) {
385
+ // We validate dottedGetExpressions at the top-most level
386
+ return;
387
+ }
388
+ if ((0, reflection_1.isVariableExpression)(expression)) {
389
+ if ((0, reflection_1.isAssignmentStatement)(expression.parent) && expression.parent.name === expression.name) {
390
+ // Don't validate LHS of assignments
391
+ return;
392
+ }
393
+ else if ((0, reflection_1.isNamespaceStatement)(expression.parent)) {
394
+ return;
395
+ }
396
+ }
397
+ let symbolType = SymbolTable_1.SymbolTypeFlag.runtime;
398
+ let oppositeSymbolType = SymbolTable_1.SymbolTypeFlag.typetime;
399
+ const isUsedAsType = util_1.default.isInTypeExpression(expression);
400
+ if (isUsedAsType) {
401
+ // This is used in a TypeExpression - only look up types from SymbolTable
402
+ symbolType = SymbolTable_1.SymbolTypeFlag.typetime;
403
+ oppositeSymbolType = SymbolTable_1.SymbolTypeFlag.runtime;
404
+ }
405
+ // Do a complete type check on all DottedGet and Variable expressions
406
+ // this will create a diagnostic if an invalid member is accessed
407
+ const typeChain = [];
408
+ const typeData = {};
409
+ let exprType = expression.getType({
410
+ flags: symbolType,
411
+ typeChain: typeChain,
412
+ data: typeData
413
+ });
414
+ const shouldIgnoreLHS = this.ignoreUnresolvedAssignmentLHS(expression, exprType, typeData === null || typeData === void 0 ? void 0 : typeData.definingNode);
415
+ if (!this.isTypeKnown(exprType) && !shouldIgnoreLHS) {
416
+ if ((_a = expression.getType({ flags: oppositeSymbolType })) === null || _a === void 0 ? void 0 : _a.isResolvable()) {
417
+ const oppoSiteTypeChain = [];
418
+ const invalidlyUsedResolvedType = expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain });
419
+ const typeChainScan = util_1.default.processTypeChain(oppoSiteTypeChain);
420
+ if (isUsedAsType) {
421
+ this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsType(typeChainScan.fullChainName)), { range: expression.range, file: file }));
422
+ }
423
+ else {
424
+ this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable(invalidlyUsedResolvedType.toString())), { range: expression.range, file: file }));
425
+ }
426
+ }
427
+ else {
428
+ const typeChainScan = util_1.default.processTypeChain(typeChain);
429
+ this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem)), { range: typeChainScan.range }));
430
+ }
431
+ }
432
+ if (isUsedAsType) {
433
+ return;
434
+ }
435
+ const lastTypeInfo = typeChain[typeChain.length - 1];
436
+ const parentTypeInfo = typeChain[typeChain.length - 2];
437
+ if ((0, reflection_1.isNamespaceType)(exprType)) {
438
+ this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable('namespace')), { range: expression.range, file: file }));
439
+ }
440
+ else if ((0, reflection_1.isEnumType)(exprType)) {
441
+ const enumStatement = this.event.scope.getEnum(util_1.default.getAllDottedGetPartsAsString(expression));
442
+ if (enumStatement) {
443
+ // there's an enum with this name
444
+ this.addMultiScopeDiagnostic(Object.assign(Object.assign({}, DiagnosticMessages_1.DiagnosticMessages.itemCannotBeUsedAsVariable('enum')), { range: expression.range, file: file }));
445
+ }
446
+ }
447
+ else if ((0, reflection_1.isDynamicType)(exprType) && (0, reflection_1.isEnumType)(parentTypeInfo === null || parentTypeInfo === void 0 ? void 0 : parentTypeInfo.type) && (0, reflection_1.isDottedGetExpression)(expression)) {
448
+ const enumFileLink = this.event.scope.getEnumFileLink(util_1.default.getAllDottedGetPartsAsString(expression.obj));
449
+ const typeChainScanForParent = util_1.default.processTypeChain(typeChain.slice(0, -1));
450
+ if (enumFileLink) {
451
+ this.addMultiScopeDiagnostic(Object.assign(Object.assign({ file: file }, DiagnosticMessages_1.DiagnosticMessages.unknownEnumValue(lastTypeInfo === null || lastTypeInfo === void 0 ? void 0 : lastTypeInfo.name, typeChainScanForParent.fullChainName)), { range: lastTypeInfo === null || lastTypeInfo === void 0 ? void 0 : lastTypeInfo.range, relatedInformation: [{
452
+ message: 'Enum declared here',
453
+ location: util_1.default.createLocation(vscode_uri_1.URI.file(enumFileLink === null || enumFileLink === void 0 ? void 0 : enumFileLink.file.srcPath).toString(), (_b = enumFileLink === null || enumFileLink === void 0 ? void 0 : enumFileLink.item) === null || _b === void 0 ? void 0 : _b.tokens.name.range)
454
+ }] }));
455
+ }
456
+ }
488
457
  }
489
458
  /**
490
459
  * Adds a diagnostic to the first scope for this key. Prevents duplicate diagnostics
@@ -492,35 +461,58 @@ class ScopeValidator {
492
461
  */
493
462
  addDiagnosticOnce(diagnostic) {
494
463
  this.onceCache.getOrAdd(`${diagnostic.code}-${diagnostic.message}-${util_1.default.rangeToString(diagnostic.range)}`, () => {
495
- this.event.scope.addDiagnostics([diagnostic]);
464
+ const diagnosticWithOrigin = Object.assign({}, diagnostic);
465
+ if (!diagnosticWithOrigin.origin) {
466
+ // diagnostic does not have origin.
467
+ // set the origin to the current astSegment
468
+ diagnosticWithOrigin.origin = interfaces_1.DiagnosticOrigin.ASTSegment;
469
+ diagnosticWithOrigin.astSegment = this.currentSegmentBeingValidated;
470
+ }
471
+ this.event.scope.addDiagnostics([diagnosticWithOrigin]);
496
472
  return true;
497
473
  });
498
474
  }
499
475
  addDiagnostic(diagnostic) {
500
- this.event.scope.addDiagnostics([diagnostic]);
476
+ const diagnosticWithOrigin = Object.assign({}, diagnostic);
477
+ if (!diagnosticWithOrigin.origin) {
478
+ // diagnostic does not have origin.
479
+ // set the origin to the current astSegment
480
+ diagnosticWithOrigin.origin = interfaces_1.DiagnosticOrigin.ASTSegment;
481
+ diagnosticWithOrigin.astSegment = this.currentSegmentBeingValidated;
482
+ }
483
+ this.event.scope.addDiagnostics([diagnosticWithOrigin]);
501
484
  }
502
485
  /**
503
486
  * Add a diagnostic (to the first scope) that will have `relatedInformation` for each affected scope
504
487
  */
505
- addMultiScopeDiagnostic(diagnostic, message = 'Not defined in scope') {
506
- var _a, _b, _c, _d, _e, _f, _g, _h;
488
+ addMultiScopeDiagnostic(diagnostic) {
489
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
507
490
  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)}`, () => {
508
491
  if (!diagnostic.relatedInformation) {
509
492
  diagnostic.relatedInformation = [];
510
493
  }
511
- this.addDiagnostic(diagnostic);
512
- return diagnostic;
494
+ const diagnosticWithOrigin = Object.assign({}, diagnostic);
495
+ if (!diagnosticWithOrigin.origin) {
496
+ // diagnostic does not have origin.
497
+ // set the origin to the current astSegment
498
+ diagnosticWithOrigin.origin = interfaces_1.DiagnosticOrigin.ASTSegment;
499
+ diagnosticWithOrigin.astSegment = this.currentSegmentBeingValidated;
500
+ }
501
+ this.addDiagnostic(diagnosticWithOrigin);
502
+ return diagnosticWithOrigin;
513
503
  });
514
- const info = {
515
- message: `${message} '${this.event.scope.name}'`
516
- };
517
504
  if ((0, reflection_1.isXmlScope)(this.event.scope) && ((_b = this.event.scope.xmlFile) === null || _b === void 0 ? void 0 : _b.srcPath)) {
518
- info.location = util_1.default.createLocation(vscode_uri_1.URI.file(this.event.scope.xmlFile.srcPath).toString(), (_h = (_g = (_f = (_e = (_d = (_c = this.event.scope) === null || _c === void 0 ? void 0 : _c.xmlFile) === null || _d === void 0 ? void 0 : _d.ast) === null || _e === void 0 ? void 0 : _e.componentElement) === null || _f === void 0 ? void 0 : _f.getAttribute('name')) === null || _g === void 0 ? void 0 : _g.range) !== null && _h !== void 0 ? _h : util_1.default.createRange(0, 0, 0, 10));
505
+ diagnostic.relatedInformation.push({
506
+ message: `In component scope '${(_e = (_d = (_c = this.event.scope) === null || _c === void 0 ? void 0 : _c.xmlFile) === null || _d === void 0 ? void 0 : _d.componentName) === null || _e === void 0 ? void 0 : _e.text}'`,
507
+ location: util_1.default.createLocation(vscode_uri_1.URI.file(this.event.scope.xmlFile.srcPath).toString(), (_o = (_m = (_l = (_k = (_j = (_h = (_g = (_f = this.event.scope) === null || _f === void 0 ? void 0 : _f.xmlFile) === null || _g === void 0 ? void 0 : _g.ast) === null || _h === void 0 ? void 0 : _h.componentElement) === null || _j === void 0 ? void 0 : _j.getAttribute('name')) === null || _k === void 0 ? void 0 : _k.tokens) === null || _l === void 0 ? void 0 : _l.value) === null || _m === void 0 ? void 0 : _m.range) !== null && _o !== void 0 ? _o : util_1.default.createRange(0, 0, 0, 10))
508
+ });
519
509
  }
520
510
  else {
521
- info.location = util_1.default.createLocation(vscode_uri_1.URI.file(diagnostic.file.srcPath).toString(), diagnostic.range);
511
+ diagnostic.relatedInformation.push({
512
+ message: `In scope '${this.event.scope.name}'`,
513
+ location: util_1.default.createLocation(vscode_uri_1.URI.file(diagnostic.file.srcPath).toString(), diagnostic.range)
514
+ });
522
515
  }
523
- diagnostic.relatedInformation.push(info);
524
516
  }
525
517
  }
526
518
  exports.ScopeValidator = ScopeValidator;