brighterscript 0.41.2 → 1.0.0-alpha.12

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