From e89872b6a1953bb5529d4b111284d76c6d2a5d9c Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Wed, 26 Jun 2024 15:38:39 -0300 Subject: [PATCH 01/42] Took a stab at re-writing the docs --- docs/bsconfig.md | 44 +++++++++++++++++++++++++--------- src/DiagnosticFilterer.spec.ts | 17 ++++++++++--- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/docs/bsconfig.md b/docs/bsconfig.md index 01594bca5..d428e79dc 100644 --- a/docs/bsconfig.md +++ b/docs/bsconfig.md @@ -70,22 +70,44 @@ If `true`, after a successful build, the project will be deployed to the Roku sp ## `diagnosticFilters` -Type: `Array; codes?: Array}` A list of filters used to hide diagnostics. -- A `string` value should be a relative-to-root-dir or absolute file path or glob pattern of the files that should be excluded. Any file matching this pattern will have all diagnostics supressed. These file paths refer to the location of the source files pre-compilation and are relative to [`rootDir`](#rootdir). Absolute file paths may be used as well. - - A file glob may be prefixed with `!` to make it a negative pattern which "un-ignores" the files it matches. (See examples below). -- A `number` value should be a diagnostic code. This will supress all diagnostics with that code for the whole project. -- An object can also be provided to filter specific diagnostic codes for a file pattern. For example, +- A `string` value should be a diagnostic code, or diagnostic shortname. This will supress all diagnostics with that code for the whole project. For example, + ```jsonc + "diagnosticFilters": [ + "1000", + "1011", + "BS1001" + "mismatch-argument-count" + ] + ``` + +- A `number` value is treated as if it were a string, and matches all diagnostic codes that are the string representation of that number. For example, an entry of `1234` would suppress any diagnostic with code `'1234'`. + - An object can also be provided to filter specific diagnostic codes for a file pattern. If no `files` property is included, any diagnostic that matches the values in the `codes` will b e suppressed. If a `string` if given for the `files` property, it is treated as a relative-to-root-dir or absolute file path or glob pattern of the files that should be excluded. These file paths refer to the location of the source files pre-compilation and are relative to [`rootDir`](#rootdir). Absolute file paths may be used as well. For example, + + ```jsonc + "diagnosticFilters": [{ + "files": "vendor/**/*", + "codes": ["1000", "1011"] //ignore these specific codes from vendor libraries + }] + ``` +- If an object is provided, the `files` property could also be an array, providing either a series of globs, or a specific set of files that match _either_ their `src` or `dest` paths. For example, ```jsonc "diagnosticFilters": [{ - "src": "vendor/**/*", - "codes": [1000, 1011] //ignore these specific codes from vendor libraries + "files": [ + "vendor/**/*", // all vendor files will be suppressed + { "src": "themes/theme1/**/*"}, // all files coming from `themes/theme1/` will be suppressed + { "dest": "source/common/**/*"}, // all files then will be placed in from `source/common/` will be suppressed + ] + "codes": ["1000", "1011"] //ignore these specific codes }] ``` +- A file glob may be prefixed with `!` to make it a negative pattern which "un-ignores" the files it matches. (See examples below). + Defaults to `undefined`. If a child bsconfig extends from a parent bsconfig, and both bsconfigs specify `diagnosticFilters`, the parent bsconfig's `diagnosticFilters` field will be completely overwritten. @@ -96,8 +118,8 @@ A negative pattern can be used to un-ignore some files or codes which were previ ```jsonc "diagnosticFilters": [ - { "src": "vendor/**/*" }, //ignore all codes from vendor libraries - { "src": "!vendor/unreliable/**/*" } //EXCEPT do show errors from this one specific library + { "files": "vendor/**/*" }, //ignore all codes from vendor libraries + { "files": "!vendor/unreliable/**/*" } //EXCEPT do show errors from this one specific library ] ``` @@ -105,8 +127,8 @@ A specific error code can be unignored in multiple places by using a pattern whi ```jsonc "diagnosticFilters": [ - { "src": "vendor/**/*" }, //ignore all errors from vendor libraries - { "src": "!*/**/*", "codes": [1000] } //EXCEPT do show this particular code everywhere + { "files": "vendor/**/*" }, //ignore all errors from vendor libraries + { "files": "!*/**/*", "codes": [1000] } //EXCEPT do show this particular code everywhere ] ``` diff --git a/src/DiagnosticFilterer.spec.ts b/src/DiagnosticFilterer.spec.ts index 7b50657ad..2be5f0626 100644 --- a/src/DiagnosticFilterer.spec.ts +++ b/src/DiagnosticFilterer.spec.ts @@ -6,12 +6,13 @@ import { createSandbox } from 'sinon'; const sinon = createSandbox(); let rootDir = s`${process.cwd()}/rootDir`; -describe('DiagnosticFilterer', () => { +describe.only('DiagnosticFilterer', () => { let filterer: DiagnosticFilterer; let options = { rootDir: rootDir, diagnosticFilters: [ + 'codename', //ignore these codes globally { codes: [1, 2, 3, 'X4'] }, //ignore all codes from lib @@ -86,18 +87,28 @@ describe('DiagnosticFilterer', () => { ).to.eql([11, 12, 13, 'X14']); }); + + it.only('works with single file src glob', () => { + expect( + filterer.filter(options, [ + getDiagnostic('codename', `${rootDir}/source/main.brs`) //remove + ]).map(x => x.code) + ).to.eql([]); + }); + describe('with negative globs', () => { let optionsWithNegatives = { rootDir: rootDir, diagnosticFilters: [ //ignore these codes globally - { codes: [1, 2] }, + { codes: [1, 2, 'codename'] }, 3, 4, + 'codename', //ignore all codes from lib { src: 'lib/**/*.brs' }, //un-ignore specific errors from lib/special - { src: '!lib/special/**/*.brs', codes: [1, 2, 3] }, + { src: '!lib/special/**/*.brs', codes: [1, 2, 3, 'codename'] }, //re-ignore errors from one extra special file { src: 'lib/special/all-reignored.brs' }, //un-ignore all codes from third special file From c7c720fc00c8a428988c81f29e8baf8b7802179b Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Thu, 27 Jun 2024 13:40:50 -0300 Subject: [PATCH 02/42] Added human readable names to diagnostics --- src/BsConfig.ts | 2 +- src/DiagnosticFilterer.spec.ts | 4 +- src/DiagnosticFilterer.ts | 77 +-- src/DiagnosticMessages.spec.ts | 21 + src/DiagnosticMessages.ts | 438 ++++++++++++------ src/DiagnosticSeverityAdjuster.ts | 4 + .../codeActions/CodeActionsProcessor.ts | 15 - src/diagnosticUtils.ts | 4 +- src/files/BrsFile.Class.spec.ts | 14 - src/files/BrsFile.spec.ts | 12 + src/interfaces.ts | 5 + src/util.ts | 8 +- 12 files changed, 394 insertions(+), 210 deletions(-) diff --git a/src/BsConfig.ts b/src/BsConfig.ts index e9718f484..c92c4a33f 100644 --- a/src/BsConfig.ts +++ b/src/BsConfig.ts @@ -128,7 +128,7 @@ export interface BsConfig { /** * A list of filters used to exclude diagnostics from the output */ - diagnosticFilters?: Array; + diagnosticFilters?: Array; codes?: Array }>; /** * Specify what diagnostic types should be printed to the console. Defaults to 'warn' diff --git a/src/DiagnosticFilterer.spec.ts b/src/DiagnosticFilterer.spec.ts index 2be5f0626..1d15e3c37 100644 --- a/src/DiagnosticFilterer.spec.ts +++ b/src/DiagnosticFilterer.spec.ts @@ -6,7 +6,7 @@ import { createSandbox } from 'sinon'; const sinon = createSandbox(); let rootDir = s`${process.cwd()}/rootDir`; -describe.only('DiagnosticFilterer', () => { +describe('DiagnosticFilterer', () => { let filterer: DiagnosticFilterer; let options = { @@ -88,7 +88,7 @@ describe.only('DiagnosticFilterer', () => { }); - it.only('works with single file src glob', () => { + it('works with single file src glob', () => { expect( filterer.filter(options, [ getDiagnostic('codename', `${rootDir}/source/main.brs`) //remove diff --git a/src/DiagnosticFilterer.ts b/src/DiagnosticFilterer.ts index a2f726389..fe31b7b0e 100644 --- a/src/DiagnosticFilterer.ts +++ b/src/DiagnosticFilterer.ts @@ -153,7 +153,7 @@ export class DiagnosticFilterer { } for (let filter of diagnosticFilters) { - if (typeof filter === 'number') { + if (typeof filter === 'number' || typeof filter === 'string') { result.push({ codes: [filter], isNegative: false @@ -161,24 +161,13 @@ export class DiagnosticFilterer { continue; } - if (typeof filter === 'string') { - const isNegative = filter.startsWith('!'); - const trimmedFilter = isNegative ? filter.slice(1) : filter; - - result.push({ - src: trimmedFilter, - isNegative: isNegative - }); - continue; - } - //filter out bad inputs if (!filter || typeof filter !== 'object') { continue; } //code-only filter - if ('codes' in filter && !('src' in filter) && Array.isArray(filter.codes)) { + if ('codes' in filter && !('files' in filter) && Array.isArray(filter.codes)) { result.push({ codes: filter.codes, isNegative: false @@ -186,24 +175,56 @@ export class DiagnosticFilterer { continue; } - if ('src' in filter) { - const isNegative = filter.src.startsWith('!'); - const trimmedFilter = isNegative ? filter.src.slice(1) : filter.src; - - if ('codes' in filter) { - result.push({ - src: trimmedFilter, - codes: filter.codes, - isNegative: isNegative - }); - } else { - result.push({ - src: trimmedFilter, - isNegative: isNegative - }); + if ('files' in filter) { + if (typeof filter.files === 'string') { + result.push(this.getNormalizedFilter(filter.files, filter)); + continue; + } + + if (Array.isArray(filter.files)) { + for (const fileIdentifier of filter.files) { + if (typeof fileIdentifier === 'string') { + const isNegative = fileIdentifier.startsWith('!'); + const trimmedFilter = isNegative ? fileIdentifier.slice(1) : fileIdentifier; + if ('codes' in filter) { + result.push({ + src: trimmedFilter, + codes: filter.codes, + isNegative: isNegative + }); + } else { + result.push({ + src: trimmedFilter, + isNegative: isNegative + }); + } + continue; + } + if (typeof fileIdentifier === 'object' && 'src' in filter) { + + } + } } } } return result; } + + + private getNormalizedFilter(fileGlob: string, filter: { files: string } | { codes?: (number | string)[] }) { + const isNegative = fileGlob.startsWith('!'); + const trimmedFilter = isNegative ? fileGlob.slice(1) : fileGlob; + if ('codes' in filter && Array.isArray(filter.codes)) { + return { + src: trimmedFilter, + codes: filter.codes, + isNegative: isNegative + }; + } else { + return { + src: trimmedFilter, + isNegative: isNegative + }; + } + } } diff --git a/src/DiagnosticMessages.spec.ts b/src/DiagnosticMessages.spec.ts index fa54bfc95..1caa39ff9 100644 --- a/src/DiagnosticMessages.spec.ts +++ b/src/DiagnosticMessages.spec.ts @@ -16,4 +16,25 @@ describe('DiagnosticMessages', () => { } } }); + it('has unique name for each message', () => { + let names = {}; + for (let key in DiagnosticMessages) { + if (key.startsWith('__unused')) { + // ignore unused diagnostics + continue; + } + let func = DiagnosticMessages[key]; + let obj = func('', '', '', '', '', '', '', '', ''); + const diagName: string = obj.name ?? ''; + expect(diagName).to.not.equal('', `Diagnostic name is empty - ${key}`); + expect(diagName.toLowerCase()).to.equal(obj.name, `Diagnostic name has capitals - ${key}`); + expect(diagName.indexOf(' ')).to.equal(-1, `Diagnostic name has space - ${key}`); + //if another message already has this name + if (!names[obj.name]) { + names[obj.name] = key; + } else { + expect(names[obj.name]).to.equal(key, 'Two diagnostic messages share the same error names'); + } + } + }); }); diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts index 382053304..edfd121a3 100644 --- a/src/DiagnosticMessages.ts +++ b/src/DiagnosticMessages.ts @@ -13,7 +13,8 @@ export let DiagnosticMessages = { genericParserMessage: (message: string) => ({ message: message, code: 1000, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'generic-parser-message' }), /** * @@ -30,125 +31,149 @@ export let DiagnosticMessages = { fullName: fullName ?? name, typeName: typeName ? typeName : undefined }, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'cannot-find-name' }), mismatchArgumentCount: (expectedCount: number | string, actualCount: number) => ({ message: `Expected ${expectedCount} arguments, but got ${actualCount}.`, code: 1002, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'mismatch-argument-count' }), duplicateFunctionImplementation: (functionName: string) => ({ message: `Duplicate function implementation for '${functionName}'.`, code: 1003, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'duplicate-function-implementation' }), referencedFileDoesNotExist: () => ({ message: `Referenced file does not exist.`, code: 1004, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'referenced-file-does-not-exist' }), xmlComponentMissingComponentDeclaration: () => ({ message: `Missing a component declaration.`, code: 1005, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'xml-missing-component-declaration' }), xmlComponentMissingNameAttribute: () => ({ message: `Component must have a name attribute.`, code: 1006, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'xml-missing-name-attribute' }), xmlComponentMissingExtendsAttribute: () => ({ message: `Component is mising "extends" attribute and will automatically extend "Group" by default`, code: 1007, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'xml-missing-extends-attribute' }), xmlGenericParseError: (message: string) => ({ //generic catchall xml parse error message: message, code: 1008, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'xml-generic-parse-error' }), unnecessaryScriptImportInChildFromParent: (parentComponentName: string) => ({ message: `Unnecessary script import: Script is already imported in ancestor component '${parentComponentName}'.`, code: 1009, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'unnecessary-script-import' }), overridesAncestorFunction: (callableName: string, currentScopeName: string, parentFilePath: string, parentScopeName: string) => ({ message: `Function '${callableName}' included in '${currentScopeName}' overrides function in '${parentFilePath}' included in '${parentScopeName}'.`, code: 1010, - severity: DiagnosticSeverity.Hint + severity: DiagnosticSeverity.Hint, + name: 'overrides-ancestor-function' }), localVarFunctionShadowsParentFunction: (scopeName: 'stdlib' | 'scope') => ({ message: `Local variable function has same name as ${scopeName} function and will never be called.`, code: 1011, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'local-var-function-shadow' }), scriptImportCaseMismatch: (correctFilePath: string) => ({ message: `Script import path does not match casing of actual file path '${correctFilePath}'.`, code: 1012, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'script-import-case-mismatch' }), fileNotReferencedByAnyOtherFile: () => ({ message: `This file is not referenced by any other file in the project.`, code: 1013, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'file-not-referenced' }), unknownDiagnosticCode: (theUnknownCode: number) => ({ message: `Unknown diagnostic code ${theUnknownCode}`, code: 1014, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'unknown-diagnostic-code' }), scriptSrcCannotBeEmpty: () => ({ message: `Script import cannot be empty or whitespace`, code: 1015, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'script-import-empty' }), expectedIdentifierAfterKeyword: (keywordText: string) => ({ message: `Expected identifier after '${keywordText}' keyword`, code: 1016, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-identifier-after-keyword' }), missingCallableKeyword: () => ({ message: `Expected 'function' or 'sub' to preceed identifier`, code: 1017, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-callable-keyword' }), expectedValidTypeToFollowAsKeyword: () => ({ message: `Expected valid type to follow 'as' keyword`, code: 1018, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-valid-type' }), bsFeatureNotSupportedInBrsFiles: (featureName) => ({ message: `BrighterScript feature '${featureName}' is not supported in standard BrightScript files`, code: 1019, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'bs-feature-not-supported' }), brsConfigJsonIsDeprecated: () => ({ message: `'brsconfig.json' is deprecated. Please rename to 'bsconfig.json'`, code: 1020, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'brsconfig-deprecated' }), bsConfigJsonHasSyntaxErrors: (message: string) => ({ message: `Encountered syntax errors in bsconfig.json: ${message}`, code: 1021, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'bsconfig-syntax-errors' }), itemIsDeprecated: () => ({ message: `Item is deprecated`, code: 1022, - severity: DiagnosticSeverity.Hint + severity: DiagnosticSeverity.Hint, + name: 'item-deprecated' }), cannotUseOverrideKeywordOnConstructorFunction: () => ({ message: 'Override keyword is not allowed on class constructor method', code: 1023, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'override-keyword-on-constructor' }), statementMustBeDeclaredAtTopOfFile: (statementKeyword: string) => ({ message: `'${statementKeyword}' statement must be declared at the top of the file`, code: 1024, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'must-be-declared-at-top' }), - methodDoesNotExistOnType: (methodName: string, className: string) => ({ + __unused8: (methodName: string, className: string) => ({ message: `Method '${methodName}' does not exist on type '${className}'`, code: 1025, severity: DiagnosticSeverity.Error @@ -156,19 +181,22 @@ export let DiagnosticMessages = { duplicateIdentifier: (memberName: string) => ({ message: `Duplicate identifier '${memberName}'`, code: 1026, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'duplicate-identifier' }), missingOverrideKeyword: (ancestorClassName: string) => ({ message: `Method has no override keyword but is declared in ancestor class '${ancestorClassName}'`, code: 1027, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'missing-override-keyword' }), nameCollision: (thisThingKind: string, thatThingKind: string, thatThingName: string) => ({ message: `${thisThingKind} has same name as ${thatThingKind} '${thatThingName}'`, code: 1028, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'name-collision' }), - classCouldNotBeFound: (className: string, scopeName: string) => ({ + __unused9: (className: string, scopeName: string) => ({ message: `Class '${className}' could not be found when this file is included in scope '${scopeName}'`, code: 1029, severity: DiagnosticSeverity.Error, @@ -179,37 +207,44 @@ export let DiagnosticMessages = { expectedClassFieldIdentifier: () => ({ message: `Expected identifier in class body`, code: 1030, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-identifier-in-body' }), expressionIsNotConstructable: (expressionType: string) => ({ message: `Cannot use the 'new' keyword here because '${expressionType}' is not a constructable type`, code: 1031, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'cannot-use-new-keyword' }), expectedKeyword: (kind: TokenKind) => ({ message: `Expected '${kind}' keyword`, code: 1032, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-keyword' }), expectedLeftParenAfterCallable: (callableType: string) => ({ message: `Expected '(' after ${callableType}`, code: 1033, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-left-paren-after-callable' }), expectedNameAfterCallableKeyword: (callableType: string) => ({ message: `Expected ${callableType} name after '${callableType}' keyword`, code: 1034, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-name-after-callable' }), expectedLeftParenAfterCallableName: (callableType: string) => ({ message: `Expected '(' after ${callableType} name`, code: 1035, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-left-paren-after-callable-name' }), tooManyCallableParameters: (actual: number, max: number) => ({ message: `Cannot have more than ${max} parameters but found ${actual})`, code: 1036, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'too-many-parameters' }), __unused: (typeText: string) => ({ message: `Function return type '${typeText}' is invalid`, @@ -219,32 +254,38 @@ export let DiagnosticMessages = { requiredParameterMayNotFollowOptionalParameter: (parameterName: string) => ({ message: `Required parameter '${parameterName}' must be declared before any optional parameters`, code: 1038, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'required-parameter-may-not-follow-optional' }), expectedNewlineOrColon: () => ({ message: `Expected newline or ':' at the end of a statement`, code: 1039, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-newline-or-colon' }), functionNameCannotEndWithTypeDesignator: (callableType: string, name: string, designator: string) => ({ message: `${callableType} name '${name}' cannot end with type designator '${designator}'`, code: 1040, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'function-name-ends-with-type' }), callableBlockMissingEndKeyword: (callableType: string) => ({ message: `Expected 'end ${callableType}' to terminate ${callableType} block`, code: 1041, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'callable-block-missing-end-keyword' }), mismatchedEndCallableKeyword: (expectedCallableType: string, actualCallableType: string) => ({ message: `Expected 'end ${expectedCallableType?.replace(/^end\s*/, '')}' to terminate ${expectedCallableType} block but found 'end ${actualCallableType?.replace(/^end\s*/, '')}' instead.`, code: 1042, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'mismatched-end-callable-keyword' }), expectedParameterNameButFound: (text: string) => ({ message: `Expected parameter name, but found '${text ?? ''}'`, code: 1043, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-parameter-name' }), __unused2: (parameterName: string, typeText: string) => ({ message: `Function parameter '${parameterName}' is of invalid type '${typeText}'`, @@ -254,65 +295,77 @@ export let DiagnosticMessages = { cannotUseReservedWordAsIdentifier: (name: string) => ({ message: `Cannot use reserved word '${name}' as an identifier`, code: 1045, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'cannot-use-reserved-word' }), expectedOperatorAfterIdentifier: (operators: TokenKind[], name: string) => { operators = Array.isArray(operators) ? operators : []; return { message: `Expected operator ('${operators.join(`', '`)}') after idenfifier '${name}'`, code: 1046, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-operator-after-identifier' }; }, expectedInlineIfStatement: () => ({ message: `If/else statement within an inline if should be also inline`, code: 1047, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-inline-if' }), expectedFinalNewline: () => ({ message: `Expected newline at the end of an inline if statement`, code: 1048, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-final-newline' }), couldNotFindMatchingEndKeyword: (keyword: string) => ({ message: `Could not find matching 'end ${keyword}'`, code: 1049, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'could-not-find-matching-end-keyword' }), expectedCatchBlockInTryCatch: () => ({ message: `Expected 'catch' block in 'try' statement`, code: 1050, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-catch' }), expectedEndForOrNextToTerminateForLoop: () => ({ message: `Expected 'end for' or 'next' to terminate 'for' loop`, code: 1051, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-end-for' }), expectedInAfterForEach: (name: string) => ({ message: `Expected 'in' after 'for each ${name}'`, code: 1052, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-in-for-each' }), expectedExpressionAfterForEachIn: () => ({ message: `Expected expression after 'in' keyword from 'for each' statement`, code: 1053, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-expression-for-each' }), unexpectedColonBeforeIfStatement: () => ({ message: `Colon before 'if' statement is not allowed`, code: 1054, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unexpected-colon-before-if' }), expectedStringLiteralAfterKeyword: (keyword: string) => ({ message: `Missing string literal after '${keyword}' keyword`, code: 1055, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-string-literal' }), keywordMustBeDeclaredAtRootLevel: (keyword: string) => ({ message: `${keyword} must be declared at the root level`, code: 1056, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'keyword-must-be-root-level' }), __unused5: () => ({ message: `'library' statement must be declared at the top of the file`, @@ -322,122 +375,146 @@ export let DiagnosticMessages = { expectedEndIfElseIfOrElseToTerminateThenBlock: () => ({ message: `Expected 'end if', 'else if', or 'else' to terminate 'then' block`, code: 1058, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-terminator-on-then' }), expectedEndTryToTerminateTryCatch: () => ({ message: `Expected 'end try' to terminate 'try-catch' statement`, code: 1059, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-terminator-on-try-catch' }), expectedEndIfToCloseIfStatement: (startingPosition: Position) => ({ message: `Expected 'end if' to close 'if' statement started at ${startingPosition?.line + 1}:${startingPosition?.character + 1}`, code: 1060, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-terminator-on-if' }), expectedStatementToFollowConditionalCondition: (conditionType: string) => ({ message: `Expected a statement to follow '${conditionType?.toLowerCase()} ...condition... then'`, code: 1061, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-statement-after-conditional' }), expectedStatementToFollowElse: () => ({ message: `Expected a statement to follow 'else'`, code: 1062, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-statement-after-else' }), consecutiveIncrementDecrementOperatorsAreNotAllowed: () => ({ message: `Consecutive increment/decrement operators are not allowed`, code: 1063, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'consecutive-increment-decrement' }), incrementDecrementOperatorsAreNotAllowedAsResultOfFunctionCall: () => ({ - message: ``, + message: `Increment/decrement operators are not allowed on function calls`, code: 1064, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'increment-decrement-on-function-call' }), xmlUnexpectedTag: (tagName: string) => ({ message: `Unexpected tag '${tagName}'`, code: 1065, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unexpected-tag' }), expectedStatementOrFunctionCallButReceivedExpression: () => ({ message: `Expected statement or function call but instead found expression`, code: 1066, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-statement-not-expression' }), xmlFunctionNotFound: (name: string) => ({ message: `Cannot find function with name '${name}' in component scope`, code: 1067, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'xml-function-not-found' }), xmlInvalidFieldType: (name: string) => ({ message: `Invalid field type ${name}`, code: 1068, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'xml-invalid-field-type' }), xmlUnexpectedChildren: (tagName: string) => ({ message: `Tag '${tagName}' should not have children`, code: 1069, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'xml-unexpected-children' }), xmlTagMissingAttribute: (tagName: string, attrName: string) => ({ message: `Tag '${tagName}' must have a '${attrName}' attribute`, code: 1070, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'xml-missing-attribute' }), expectedLabelIdentifierAfterGotoKeyword: () => ({ message: `Expected label identifier after 'goto' keyword`, code: 1071, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-label' }), expectedRightSquareBraceAfterArrayOrObjectIndex: () => ({ message: `Expected ']' after array or object index`, code: 1072, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-right-brace' }), expectedPropertyNameAfterPeriod: () => ({ message: `Expected property name after '.'`, code: 1073, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-property-name' }), tooManyCallableArguments: (actual: number, max: number) => ({ message: `Cannot have more than ${max} arguments but found ${actual}`, code: 1074, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'too-many-arguments' }), expectedRightParenAfterFunctionCallArguments: () => ({ message: `Expected ')' after function call arguments`, code: 1075, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-right-paren' }), unmatchedLeftParenAfterExpression: () => ({ message: `Unmatched '(': expected ')' after expression`, code: 1076, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unmatched-left-paren' }), unmatchedLeftSquareBraceAfterArrayLiteral: () => ({ message: `Unmatched '[': expected ']' after array literal`, code: 1077, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unmatched-left-brace' }), unexpectedAAKey: () => ({ message: `Expected identifier or string as associative array key`, code: 1078, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-identifier-as-key' }), expectedColonBetweenAAKeyAndvalue: () => ({ message: `Expected ':' between associative array key and value`, code: 1079, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-colon-after-aa-key' }), unmatchedLeftCurlyAfterAALiteral: () => ({ message: `Unmatched '{': expected '}' after associative array literal`, code: 1080, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unmatched-left-curly' }), unexpectedToken: (text: string) => ({ message: `Unexpected token '${text}'`, code: 1081, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unexpected-token' }), /** * Used in the lexer anytime we encounter an unsupported character @@ -445,167 +522,200 @@ export let DiagnosticMessages = { unexpectedCharacter: (text: string) => ({ message: `Unexpected character '${text}' (char code ${text?.charCodeAt(0)})`, code: 1082, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unexpected-character' }), unterminatedStringAtEndOfLine: () => ({ message: `Unterminated string at end of line`, code: 1083, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unterminated-string' }), unterminatedStringAtEndOfFile: () => ({ message: `Unterminated string at end of file`, code: 1084, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unterminated-string-at-end-of-file' }), fractionalHexLiteralsAreNotSupported: () => ({ message: `Fractional hex literals are not supported`, code: 1085, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'fractional-hex-literal' }), unexpectedConditionalCompilationString: () => ({ message: `Unexpected conditional-compilation string`, code: 1086, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unexpected-conditional-compilation' }), duplicateConstDeclaration: (name: string) => ({ message: `Attempting to redeclare #const with name '${name}'`, code: 1087, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'duplicate-const-declaration' }), constAliasDoesNotExist: (name: string) => ({ message: `Attempting to create #const alias of '${name}', but no such #const exists`, code: 1088, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'const-alias-does-not-exist' }), invalidHashConstValue: () => ({ message: '#const declarations can only have values of `true`, `false`, or other #const names', code: 1089, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'invalid-const-value' }), referencedConstDoesNotExist: () => ({ message: `Referenced #const does not exist`, code: 1090, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'const-does-not-exist' }), invalidHashIfValue: () => ({ message: `#if conditionals can only be 'true', 'false', or other #const names`, code: 1091, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'invalid-hash-if-value' }), hashError: (message: string) => ({ message: `#error ${message}`, code: 1092, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'hash-error' }), expectedEqualAfterConstName: () => ({ message: `Expected '=' after #const`, code: 1093, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-equal-after-const' }), expectedHashEndIfToCloseHashIf: (startingLine: number) => ({ message: `Expected '#end if' to close '#if' conditional compilation statement starting on line ${startingLine}`, code: 1094, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-terminator-on-hash-if' }), constNameCannotBeReservedWord: () => ({ message: `#const name cannot be a reserved word`, code: 1095, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'const-reservered-word' }), expectedIdentifier: () => ({ message: `Expected identifier`, code: 1096, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-identifier' }), expectedAttributeNameAfterAtSymbol: () => ({ message: `Expected xml attribute name after '@'`, code: 1097, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-attribute-name' }), childFieldTypeNotAssignableToBaseProperty: (childTypeName: string, baseTypeName: string, fieldName: string, childFieldType: string, parentFieldType: string) => ({ message: `Field '${fieldName}' in class '${childTypeName}' is not assignable to the same field in base class '${baseTypeName}'. Type '${childFieldType}' is not assignable to type '${parentFieldType}'.`, code: 1098, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'child-field-type-not-assignable' }), classChildMemberDifferentMemberTypeThanAncestor: (memberType: string, parentMemberType: string, parentClassName: string) => ({ message: `Class member is a ${memberType} here but a ${parentMemberType} in ancestor class '${parentClassName}'`, code: 1099, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'child-field-type-different' }), classConstructorMissingSuperCall: () => ({ message: `Missing "super()" call in class constructor method.`, code: 1100, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'missing-super-call' }), classConstructorIllegalUseOfMBeforeSuperCall: () => ({ message: `Illegal use of "m" before calling "super()"`, code: 1101, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'use-m-before-super' }), classFieldCannotBeOverridden: () => ({ message: `Class field cannot be overridden`, code: 1102, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'class-field-override' }), unusedAnnotation: () => ({ message: `This annotation is not attached to any statement`, code: 1103, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unused-annotation' }), localVarShadowedByScopedFunction: () => ({ message: `Declaring a local variable with same name as scoped function can result in unexpected behavior`, code: 1104, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'local-var-shadowed-by-function' }), scopeFunctionShadowedByBuiltInFunction: () => ({ message: `Scope function will not be accessible because it has the same name as a built-in function`, code: 1105, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'function-shadowed-by-built-in-function' }), localVarSameNameAsClass: (className: string) => ({ message: `Local variable has same name as class '${className}'`, code: 1106, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'local-var-same-name-as-class' }), unnecessaryCodebehindScriptImport: () => ({ message: `This import is unnecessary because compiler option 'autoImportComponentScript' is enabled`, code: 1107, - severity: DiagnosticSeverity.Warning + severity: DiagnosticSeverity.Warning, + name: 'xml-unnecessary-import' }), expectedOpenParenToFollowCallfuncIdentifier: () => ({ message: `Expected '(' to follow callfunc identifier`, code: 1108, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-left-paren-after-callfunc' }), expectedToken: (...tokenKinds: string[]) => ({ message: `Expected token '${tokenKinds.join(`' or '`)}'`, code: 1109, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-token' }), parameterMayNotHaveSameNameAsNamespace: (paramName: string) => ({ message: `Parameter '${paramName}' may not have the same name as namespace`, code: 1110, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'parameter-same-name-as-namespace' }), variableMayNotHaveSameNameAsNamespace: (variableName: string) => ({ message: `Variable '${variableName}' may not have the same name as namespace`, code: 1111, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'variable-same-name-as-namespace' }), unterminatedTemplateStringAtEndOfFile: () => ({ message: `Unterminated template string at end of file`, code: 1113, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unterminated-template-string' }), unterminatedTemplateExpression: () => ({ message: `Unterminated template string expression. '\${' must be followed by expression, then '}'`, code: 1114, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unterminated-template-expression' }), duplicateComponentName: (componentName: string) => ({ message: `There are multiple components with the name '${componentName}'`, code: 1115, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'duplicate-component-name' }), __unused6: (className: string) => ({ message: `Function has same name as class '${className}'`, @@ -615,32 +725,38 @@ export let DiagnosticMessages = { missingExceptionVarToFollowCatch: () => ({ message: `Missing exception variable after 'catch' keyword`, code: 1117, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'missing-exception-variable' }), missingExceptionExpressionAfterThrowKeyword: () => ({ message: `Missing exception expression after 'throw' keyword`, code: 1118, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'missing-exception-expression' }), missingLeftSquareBracketAfterDimIdentifier: () => ({ message: `Missing left square bracket after 'dim' identifier`, code: 1119, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'missing-left-brace-after-dim' }), missingRightSquareBracketAfterDimIdentifier: () => ({ message: `Missing right square bracket after 'dim' identifier`, code: 1120, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'missing-right-brace-after-dim' }), missingExpressionsInDimStatement: () => ({ message: `Missing expression(s) in 'dim' statement`, code: 1121, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'missing-expression-in-dim' }), mismatchedOverriddenMemberVisibility: (childClassName: string, memberName: string, childAccessModifier: string, ancestorAccessModifier: string, ancestorClassName: string) => ({ message: `Access modifier mismatch: '${memberName}' is ${childAccessModifier} in type '${childClassName}' but is ${ancestorAccessModifier} in base type '${ancestorClassName}'.`, code: 1122, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'access-modifier-mismatch' }), __unused3: (typeName: string) => ({ message: `Cannot find type with name '${typeName}'`, @@ -650,17 +766,20 @@ export let DiagnosticMessages = { enumValueMustBeType: (expectedType: string) => ({ message: `Enum value must be type '${expectedType}'`, code: 1124, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'enum-value-invalid-type' }), enumValueIsRequired: (expectedType: string) => ({ message: `Value is required for ${expectedType} enum`, code: 1125, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'enum-value-required' }), unknownEnumValue: (name: string, enumName: string) => ({ message: `Property '${name}' does not exist on enum '${enumName}'`, code: 1126, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unknown-enum-value' }), __unused7: (scopeName: string, enumName: string) => ({ message: `Scope '${scopeName}' already contains an enum with name '${enumName}'`, @@ -670,65 +789,77 @@ export let DiagnosticMessages = { unknownRoSGNode: (nodeName: string) => ({ message: `Unknown roSGNode '${nodeName}'`, code: 1128, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unknown-rosgnode' }), unknownBrightScriptComponent: (componentName: string) => ({ message: `Unknown BrightScript component '${componentName}'`, code: 1129, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unknown-brightscript-component' }), mismatchCreateObjectArgumentCount: (componentName: string, allowedArgCounts: number[], actualCount: number) => { const argCountArray = (allowedArgCounts || [1]).sort().filter((value, index, self) => self.indexOf(value) === index); return { message: `For ${componentName}, expected ${argCountArray.map(c => c.toString()).join(' or ')} total arguments, but got ${actualCount}.`, code: 1130, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'mismatch-createobject-argument-count' }; }, deprecatedBrightScriptComponent: (componentName: string, deprecatedDescription?: string) => ({ message: `${componentName} has been deprecated${deprecatedDescription ? ': ' + deprecatedDescription : ''}`, code: 1131, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'deprecated-brightscript-component' }), circularReferenceDetected: (items: string[], scopeName: string) => ({ message: `Circular reference detected between ${Array.isArray(items) ? items.join(' -> ') : ''} in scope '${scopeName}'`, code: 1132, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'circular-reference' }), unexpectedStatementOutsideFunction: () => ({ message: `Unexpected statement found outside of function body`, code: 1133, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unexpected-statement' }), detectedTooDeepFileSource: (numberOfParentDirectories: number) => ({ message: `Expected directory depth no larger than 7, but found ${numberOfParentDirectories}`, code: 1134, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'directory-depth' }), illegalContinueStatement: () => ({ message: `Continue statement must be contained within a loop statement`, code: 1135, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'illegal-continue' }), keywordMustBeDeclaredAtNamespaceLevel: (keyword: string) => ({ message: `${keyword} must be declared at the root level or within a namespace`, code: 1136, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'keyword-must-be-at-root-or-namespace' }), itemCannotBeUsedAsVariable: (itemType: string) => ({ message: `${itemType} cannot be used as a variable`, code: 1137, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'cannout-be-used-as-variable' }), callfuncHasToManyArgs: (numberOfArgs: number) => ({ message: `You can not have more than 5 arguments in a callFunc. ${numberOfArgs} found.`, code: 1138, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'callfunc-has-too-many-arguments' }), noOptionalChainingInLeftHandSideOfAssignment: () => ({ message: `Optional chaining may not be used in the left-hand side of an assignment`, code: 1139, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'otional-chaining-on-left-of-assignment' }), /** * @@ -745,70 +876,83 @@ export let DiagnosticMessages = { fullName: fullName ?? name, typeName: typeName ? typeName : undefined }, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'cannot-find-function' }), argumentTypeMismatch: (actualTypeString: string, expectedTypeString: string, data?: TypeCompatibilityData) => ({ message: `Argument of type '${actualTypeString}' is not compatible with parameter of type '${expectedTypeString}'${typeCompatibilityMessage(actualTypeString, expectedTypeString, data)}`, data: data, code: 1141, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'argument-type-mismatch' }), returnTypeMismatch: (actualTypeString: string, expectedTypeString: string, data?: TypeCompatibilityData) => ({ message: `Type '${actualTypeString}' is not compatible with declared return type '${expectedTypeString}'${typeCompatibilityMessage(actualTypeString, expectedTypeString, data)}'`, data: data, code: 1142, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'return-type-mismatch' }), assignmentTypeMismatch: (actualTypeString: string, expectedTypeString: string, data?: TypeCompatibilityData) => ({ message: `Type '${actualTypeString}' is not compatible with type '${expectedTypeString}'${typeCompatibilityMessage(actualTypeString, expectedTypeString, data)}`, data: data, code: 1143, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'assignment-type-mismatch' }), operatorTypeMismatch: (operatorString: string, firstType: string, secondType = '') => ({ message: `Operator '${operatorString}' cannot be applied to type${secondType ? 's' : ''} '${firstType}'${secondType ? ` and '${secondType}'` : ''}`, code: 1144, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'operator-type-mismatch' }), incompatibleSymbolDefinition: (symbol: string, scopeName: string) => ({ message: `'${symbol}' is incompatible across these scopes: ${scopeName}`, code: 1145, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'incompatible-definition' }), memberAccessibilityMismatch: (memberName: string, accessModifierFlag: SymbolTypeFlag, definingClassName: string) => ({ message: `Member '${memberName}' is ${accessModifierNameFromFlag(accessModifierFlag)}${accessModifierAdditionalInfo(accessModifierFlag, definingClassName)}`, // TODO: Add scopes where it was defined code: 1146, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'member-accessibility-mismatch' }), typecastStatementMustBeDeclaredAtStart: () => ({ message: `'typecast' statement must be declared at the top of the file or beginning of function or namespace`, code: 1147, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'typecast-must-be-at-start' }), invalidTypecastStatementApplication: (foundApplication: string) => ({ message: `'typecast' statement can only be applied to 'm', but was applied to '${foundApplication}'`, code: 1148, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'invalid-typecast-statement' }), itemCannotBeUsedAsType: (typeText: string) => ({ message: `'${typeText}' cannot be used as a type`, code: 1149, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'item-cannot-be-used-as-type' }), expectedNewlineInConditionalCompile: () => ({ message: `Expected newline in conditional compilation statement`, code: 1151, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-newline-in-conditional-compile' }), expectedTerminatorOnConditionalCompileBlock: () => ({ message: `Expected '#end if', '#else if', or '#else' to terminate conditional compilation block`, code: 1152, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'expected-terminator-on-conditional-compile' }), unsafeUnmatchedTerminatorInConditionalCompileBlock: (terminator: string) => ({ message: `Unsafe unmatched terminator '${terminator}' in conditional compilation block`, code: 1153, - severity: DiagnosticSeverity.Error + severity: DiagnosticSeverity.Error, + name: 'unmatched-terminator-on-conditional-compile' }) }; export const defaultMaximumTruncationLength = 160; diff --git a/src/DiagnosticSeverityAdjuster.ts b/src/DiagnosticSeverityAdjuster.ts index b651f30bf..9a5537a8e 100644 --- a/src/DiagnosticSeverityAdjuster.ts +++ b/src/DiagnosticSeverityAdjuster.ts @@ -11,6 +11,10 @@ export class DiagnosticSeverityAdjuster { if (map.has(code)) { diagnostic.severity = map.get(code); } + const name = String(diagnostic.name); + if (name && map.has(name)) { + diagnostic.severity = map.get(name); + } }); } diff --git a/src/bscPlugin/codeActions/CodeActionsProcessor.ts b/src/bscPlugin/codeActions/CodeActionsProcessor.ts index 8058441b6..1dbfee110 100644 --- a/src/bscPlugin/codeActions/CodeActionsProcessor.ts +++ b/src/bscPlugin/codeActions/CodeActionsProcessor.ts @@ -22,8 +22,6 @@ export class CodeActionsProcessor { for (const diagnostic of this.event.diagnostics) { if (diagnostic.code === DiagnosticCodeMap.cannotFindName || diagnostic.code === DiagnosticCodeMap.cannotFindFunction) { this.suggestCannotFindName(diagnostic as any); - } else if (diagnostic.code === DiagnosticCodeMap.classCouldNotBeFound) { - this.suggestClassImports(diagnostic as any); } else if (diagnostic.code === DiagnosticCodeMap.xmlComponentMissingExtendsAttribute) { this.addMissingExtends(diagnostic as any); } @@ -86,19 +84,6 @@ export class CodeActionsProcessor { ); } - private suggestClassImports(diagnostic: DiagnosticMessageType<'classCouldNotBeFound'>) { - //skip if not a BrighterScript file - if ((diagnostic.file as BrsFile).parseMode !== ParseMode.BrighterScript) { - return; - } - const lowerClassName = diagnostic.data.className.toLowerCase(); - this.suggestImports( - diagnostic, - lowerClassName, - this.event.program.findFilesForClass(lowerClassName) - ); - } - private addMissingExtends(diagnostic: DiagnosticMessageType<'xmlComponentMissingExtendsAttribute'>) { const srcPath = this.event.file.srcPath; const { componentElement } = (this.event.file as XmlFile).parser.ast; diff --git a/src/diagnosticUtils.ts b/src/diagnosticUtils.ts index 3049b7466..b391b6132 100644 --- a/src/diagnosticUtils.ts +++ b/src/diagnosticUtils.ts @@ -72,6 +72,8 @@ export function printDiagnostic( let severityText = severityTextMap[severity]; console.log(''); + const printableDiagnosticCode = (diagnostic as BsDiagnostic).name ? (diagnostic as BsDiagnostic).name : 'BS' + diagnostic.code; + console.log( chalk.cyan(filePath ?? '') + ':' + @@ -83,7 +85,7 @@ export function printDiagnostic( ' - ' + typeColor[severity](severityText) + ' ' + - chalk.grey('BS' + diagnostic.code) + + chalk.grey(printableDiagnosticCode) + ': ' + chalk.white(diagnostic.message) ); diff --git a/src/files/BrsFile.Class.spec.ts b/src/files/BrsFile.Class.spec.ts index c36fe6848..d2dfa2036 100644 --- a/src/files/BrsFile.Class.spec.ts +++ b/src/files/BrsFile.Class.spec.ts @@ -1224,20 +1224,6 @@ describe('BrsFile BrighterScript classes', () => { ]); }); - it.skip('detects calls to unknown m methods', () => { - program.setFile('source/main.bs', ` - class Animal - sub new() - m.methodThatDoesNotExist() - end sub - end class - `); - program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.methodDoesNotExistOnType('methodThatDoesNotExist', 'Animal') - ]); - }); - it('detects direct circular extends', () => { //direct program.setFile('source/Direct.bs', ` diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts index a5084e697..1f43e968e 100644 --- a/src/files/BrsFile.spec.ts +++ b/src/files/BrsFile.spec.ts @@ -435,6 +435,18 @@ describe('BrsFile', () => { }]); }); + it.only('recognizes diagnostic names', () => { + let file = program.setFile('/source/main.brs', ` + sub Main() + 'bs:disable-next-line: cannot-find-name + name = unknown + end sub + `); + expect(file.commentFlags[0]).to.exist; + program.validate(); + expectZeroDiagnostics(program); + }); + }); describe('bs:disable-line', () => { diff --git a/src/interfaces.ts b/src/interfaces.ts index d0ffd1d4e..7ba23418f 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -29,6 +29,11 @@ export interface BsDiagnostic extends Diagnostic { * A generic data container where additional details of the diagnostic can be stored. These are stripped out before being sent to a languageclient, and not printed to the console. */ data?: any; + + /** + * A human readable name for the diagnostic to be used as alternative for the code + */ + name?: string; } export interface DiagnosticContext { diff --git a/src/util.ts b/src/util.ts index 7ad10fb20..8c30987e4 100644 --- a/src/util.ts +++ b/src/util.ts @@ -872,13 +872,17 @@ export class Util { */ public diagnosticIsSuppressed(diagnostic: BsDiagnostic) { const diagnosticCode = typeof diagnostic.code === 'string' ? diagnostic.code.toLowerCase() : diagnostic.code; + const diagnosticName = typeof diagnostic.name === 'string' ? diagnostic.name.toLowerCase() : undefined; for (let flag of diagnostic.file?.commentFlags ?? []) { //this diagnostic is affected by this flag if (diagnostic.range && this.rangeContains(flag.affectedRange, diagnostic.range.start)) { //if the flag acts upon this diagnostic's code - if (flag.codes === null || (diagnosticCode !== undefined && flag.codes.includes(diagnosticCode))) { + const diagCodeSuppressed = (diagnosticCode !== undefined && flag.codes?.includes(diagnosticCode)) || + (diagnosticName !== undefined && flag.codes?.includes(diagnosticName)); + if (flag.codes === null || diagCodeSuppressed) { return true; } + } } return false; @@ -1804,7 +1808,7 @@ export class Util { return clone; //filter out null relatedInformation items }).filter((x): x is DiagnosticRelatedInformation => Boolean(x)), - code: diagnostic.code, + code: (diagnostic as BsDiagnostic).name ? (diagnostic as BsDiagnostic).name : diagnostic.code, source: 'brs' } as Diagnostic; if (diagnostic?.tags?.length > 0) { From 656804fa75b4ed4138f14d1b9699f62d354a8311 Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Fri, 28 Jun 2024 10:37:54 -0300 Subject: [PATCH 03/42] Added file.destPath diagnostics filtering --- src/DiagnosticFilterer.spec.ts | 95 +++++++++++++++++++-------- src/DiagnosticFilterer.ts | 114 +++++++++++++++++++++++---------- src/files/BrsFile.spec.ts | 2 +- 3 files changed, 151 insertions(+), 60 deletions(-) diff --git a/src/DiagnosticFilterer.spec.ts b/src/DiagnosticFilterer.spec.ts index 1d15e3c37..530622945 100644 --- a/src/DiagnosticFilterer.spec.ts +++ b/src/DiagnosticFilterer.spec.ts @@ -16,11 +16,11 @@ describe('DiagnosticFilterer', () => { //ignore these codes globally { codes: [1, 2, 3, 'X4'] }, //ignore all codes from lib - { src: 'lib/**/*.brs' }, + { files: 'lib/**/*.brs' }, //ignore all codes from `packages` with absolute path - { src: `${rootDir}/packages/**/*.brs` }, + { files: `${rootDir}/packages/**/*.brs` }, //ignore specific codes for main.brs - { src: 'source/main.brs', codes: [4] } + { files: 'source/main.brs', codes: [4] } ] }; @@ -106,15 +106,15 @@ describe('DiagnosticFilterer', () => { 4, 'codename', //ignore all codes from lib - { src: 'lib/**/*.brs' }, + { files: 'lib/**/*.brs' }, //un-ignore specific errors from lib/special - { src: '!lib/special/**/*.brs', codes: [1, 2, 3, 'codename'] }, + { files: '!lib/special/**/*.brs', codes: [1, 2, 3, 'codename'] }, //re-ignore errors from one extra special file - { src: 'lib/special/all-reignored.brs' }, + { files: 'lib/special/all-reignored.brs' }, //un-ignore all codes from third special file - { src: '!lib/special/all-unignored.brs' }, + { files: '!lib/special/all-unignored.brs' }, //un-ignore code 5 globally - { src: '!*/**/*', codes: [5] }, + { files: '!*/**/*', codes: [5] }, //re-ignore code 10 globally, overriding previous unignores { codes: [10] } ] @@ -191,7 +191,7 @@ describe('DiagnosticFilterer', () => { it('handles standard diagnostic filters', () => { expect( filterer.getDiagnosticFilters({ - diagnosticFilters: [{ src: 'file.brs', codes: [1, 2, 'X3'] }] + diagnosticFilters: [{ files: 'file.brs', codes: [1, 2, 'X3'] }] }) ).to.eql([{ src: 'file.brs', codes: [1, 2, 'X3'], isNegative: false }]); }); @@ -199,7 +199,7 @@ describe('DiagnosticFilterer', () => { it('handles string-only diagnostic filter object', () => { expect( filterer.getDiagnosticFilters({ - diagnosticFilters: [{ src: 'file.brs' }] + diagnosticFilters: [{ files: 'file.brs' }] }) ).to.eql([{ src: 'file.brs', isNegative: false }]); }); @@ -215,9 +215,9 @@ describe('DiagnosticFilterer', () => { it('handles string diagnostic filter', () => { expect( filterer.getDiagnosticFilters({ - diagnosticFilters: ['file.brs'] + diagnosticFilters: ['cannot-find-name'] }) - ).to.eql([{ src: 'file.brs', isNegative: false }]); + ).to.eql([{ codes: ['cannot-find-name'], isNegative: false }]); }); it('converts ignoreErrorCodes to diagnosticFilters', () => { @@ -228,19 +228,11 @@ describe('DiagnosticFilterer', () => { ]); }); - it('handles negative globs in bare strings', () => { - expect(filterer.getDiagnosticFilters({ - diagnosticFilters: ['!file.brs'] - })).to.eql([ - { src: 'file.brs', isNegative: true } - ]); - }); - it('handles negative globs in objects', () => { expect(filterer.getDiagnosticFilters({ diagnosticFilters: [ { - src: '!file.brs' + files: '!file.brs' } ] })).to.eql([ @@ -252,7 +244,7 @@ describe('DiagnosticFilterer', () => { expect(filterer.getDiagnosticFilters({ diagnosticFilters: [ { - src: '!file.brs', + files: '!file.brs', codes: [1, 2, 3] } ] @@ -262,6 +254,49 @@ describe('DiagnosticFilterer', () => { }); }); + describe('filtering by dest', () => { + it('will filter by a files destPath', () => { + const resultDiagnostics = filterer.filter({ + rootDir: rootDir, + diagnosticFilters: [ + { + files: [ + { dest: 'source/remove/**/*.*' } + ], + codes: ['diagCode'] + } + ] + }, [ + getDiagnostic('diagCode', `${rootDir}/source/common.brs`), //keep + getDiagnostic('diagCode', `${rootDir}/source/utils.brs`, `${rootDir}/source/remove/utils.brs`), //remove + getDiagnostic('diagCode', `${rootDir}/components/utils.brs`, `${rootDir}/source/remove/utils2.brs`) //remove + ]); + expect(resultDiagnostics.map(x => x.code)).to.eql(['diagCode']); + expect(resultDiagnostics.map(x => x.file.srcPath)).to.eql([`${rootDir}/source/common.brs`]); + }); + + it('respects order of ignores with negative globs', () => { + const resultDiagnostics = filterer.filter({ + rootDir: rootDir, + diagnosticFilters: [{ + files: [ + { dest: 'source/**/*.*' } //ignore diagCode in files with destPath in /source/remove + ], + codes: ['diagCode'] + }, { + files: '!**/*.*', //unignore diagCode everywhere + codes: ['diagCode'] + } + ] + }, [ + getDiagnostic('diagCode', `${rootDir}/source/common.brs`), //keep + getDiagnostic('diagCode', `${rootDir}/source/utils.brs`, `${rootDir}/source/remove/utils.brs`), //remove + getDiagnostic('diagCode', `${rootDir}/components/utils.brs`, `${rootDir}/source/remove/utils2.brs`) //remove + ]); + expect(resultDiagnostics.map(x => x.code)).to.eql(['diagCode', 'diagCode', 'diagCode']); + }); + }); + it('only filters by file once per unique file (case-insensitive)', () => { const stub = sinon.stub(filterer as any, 'filterFile').returns(null); filterer.filter(options, [ @@ -270,8 +305,16 @@ describe('DiagnosticFilterer', () => { getDiagnostic(3, s`${rootDir}/source/common2.brs`), getDiagnostic(4, s`${rootDir}/source/Common2.brs`) ]); - expect(stub.callCount).to.eql(2); + const expectedCallCount = options.diagnosticFilters.reduce((acc, filter) => { + if (typeof filter === 'object' && 'files' in (filter as any)) { + return acc; + } + return acc + 1; + }, 0); + expect(stub.callCount).to.eql(expectedCallCount * 2); // 2 times for 'codename', 2 times for { codes: [1, 2, 3, 'X4'] } expect(stub.getCalls().map(x => x.args[1])).to.eql([ + s`${rootDir.toLowerCase()}/source/common1.brs`, + s`${rootDir.toLowerCase()}/source/common2.brs`, s`${rootDir.toLowerCase()}/source/common1.brs`, s`${rootDir.toLowerCase()}/source/common2.brs` ]); @@ -279,10 +322,12 @@ describe('DiagnosticFilterer', () => { }); -function getDiagnostic(code: number | string, srcPath: string) { +function getDiagnostic(code: number | string, srcPath: string, destPath?: string) { + destPath = destPath ?? srcPath; return { file: { - srcPath: s`${srcPath}` + srcPath: s`${srcPath}`, + destPath: s`${destPath}` }, code: code } as BsDiagnostic; diff --git a/src/DiagnosticFilterer.ts b/src/DiagnosticFilterer.ts index fe31b7b0e..26ab89e54 100644 --- a/src/DiagnosticFilterer.ts +++ b/src/DiagnosticFilterer.ts @@ -11,18 +11,21 @@ interface DiagnosticWithSuppression { interface NormalizedFilter { src?: string; + dest?: string; codes?: (number | string)[]; isNegative: boolean; } export class DiagnosticFilterer { private byFile: Record; + private fileDestSrcMap: Record; private filters: NormalizedFilter[] | undefined; private rootDir: string | undefined; constructor() { this.byFile = {}; + this.fileDestSrcMap = {}; } /** @@ -41,6 +44,7 @@ export class DiagnosticFilterer { //clean up this.byFile = {}; + this.fileDestSrcMap = {}; delete this.rootDir; delete this.filters; @@ -71,9 +75,10 @@ export class DiagnosticFilterer { */ private groupByFile(diagnostics: BsDiagnostic[]) { this.byFile = {}; - + this.fileDestSrcMap = {}; for (let diagnostic of diagnostics) { - const srcPath = diagnostic?.file?.srcPath ?? diagnostic?.file?.srcPath; + const srcPath = diagnostic?.file?.srcPath; + //skip diagnostics that have issues if (!srcPath) { continue; @@ -88,13 +93,27 @@ export class DiagnosticFilterer { isSuppressed: false }); } + for (let diagnostic of diagnostics) { + const destPath = diagnostic?.file?.destPath; + const srcPath = diagnostic?.file?.srcPath; + + //skip diagnostics that have issues + if (!destPath || !srcPath) { + continue; + } + + const lowerDestPath = destPath.toLowerCase(); + const lowerSrcPath = srcPath.toLowerCase(); + this.fileDestSrcMap[lowerDestPath] = lowerSrcPath; + } } private filterAllFiles(filter: NormalizedFilter) { let matchedFilePaths: string[]; - //if there's a src, match against all files if (filter.src) { + //if there's a src, match against all files + //prepend rootDir to src if the filter is a relative path let src = s( path.isAbsolute(filter.src) ? filter.src : `${this.rootDir}/${filter.src}` @@ -104,8 +123,22 @@ export class DiagnosticFilterer { nocase: true }); - //there is no src; this applies to all files + } else if (filter.dest) { + // applies to file dest location + + //prepend rootDir to dest if the filter is a relative path + let dest = s( + path.isAbsolute(filter.dest) ? filter.dest : `${this.rootDir}/${filter.dest}` + ); + // search against the set of file destinations + const matchedDestFilePaths = minimatch.match(Object.keys(this.fileDestSrcMap), dest, { + nocase: true + }); + // convert to file srcs + matchedFilePaths = matchedDestFilePaths.map(destPath => this.fileDestSrcMap[destPath]); + } else { + //there is no src; this applies to all files matchedFilePaths = Object.keys(this.byFile); } @@ -120,17 +153,19 @@ export class DiagnosticFilterer { //if the filter is not negative we're turning diagnostics off const isSuppressing = !filter.isNegative; + // get correct diagnostics set + const fileDiagnostics = this.byFile[filePath]; + //if there is no code, set isSuppressed on every diagnostic in this file if (!filter.codes) { - this.byFile[filePath].forEach(diagnostic => { + fileDiagnostics.forEach(diagnostic => { diagnostic.isSuppressed = isSuppressing; }); //set isSuppressed for any diagnostics with matching codes } else { - let fileDiagnostics = this.byFile[filePath]; for (const diagnostic of fileDiagnostics) { - if (filter.codes.includes(diagnostic.diagnostic.code!)) { + if (filter.codes.includes(diagnostic.diagnostic.code!) || filter.codes.includes(diagnostic.diagnostic.name!)) { diagnostic.isSuppressed = isSuppressing; } } @@ -184,24 +219,18 @@ export class DiagnosticFilterer { if (Array.isArray(filter.files)) { for (const fileIdentifier of filter.files) { if (typeof fileIdentifier === 'string') { - const isNegative = fileIdentifier.startsWith('!'); - const trimmedFilter = isNegative ? fileIdentifier.slice(1) : fileIdentifier; - if ('codes' in filter) { - result.push({ - src: trimmedFilter, - codes: filter.codes, - isNegative: isNegative - }); - } else { - result.push({ - src: trimmedFilter, - isNegative: isNegative - }); - } + result.push(this.getNormalizedFilter(fileIdentifier, filter)); continue; } - if (typeof fileIdentifier === 'object' && 'src' in filter) { - + if (typeof fileIdentifier === 'object') { + if ('src' in fileIdentifier) { + result.push(this.getNormalizedFilter(fileIdentifier.src, filter)); + continue; + } + if ('dest' in fileIdentifier) { + result.push(this.getNormalizedFilter(fileIdentifier.dest, filter, 'dest')); + continue; + } } } } @@ -211,20 +240,37 @@ export class DiagnosticFilterer { } - private getNormalizedFilter(fileGlob: string, filter: { files: string } | { codes?: (number | string)[] }) { + private getNormalizedFilter(fileGlob: string, filter: { files: string } | { codes?: (number | string)[] }, locationKey: 'src' | 'dest' = 'src'): NormalizedFilter { const isNegative = fileGlob.startsWith('!'); const trimmedFilter = isNegative ? fileGlob.slice(1) : fileGlob; - if ('codes' in filter && Array.isArray(filter.codes)) { - return { - src: trimmedFilter, - codes: filter.codes, - isNegative: isNegative - }; + if (locationKey === 'src') { + if ('codes' in filter && Array.isArray(filter.codes)) { + return { + src: trimmedFilter, + codes: filter.codes, + isNegative: isNegative + }; + } else { + return { + src: trimmedFilter, + isNegative: isNegative + }; + } } else { - return { - src: trimmedFilter, - isNegative: isNegative - }; + // dest + if ('codes' in filter && Array.isArray(filter.codes)) { + return { + dest: trimmedFilter, + codes: filter.codes, + isNegative: isNegative + }; + } else { + return { + dest: trimmedFilter, + isNegative: isNegative + }; + } } + } } diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts index 1f43e968e..3d52ca45f 100644 --- a/src/files/BrsFile.spec.ts +++ b/src/files/BrsFile.spec.ts @@ -435,7 +435,7 @@ describe('BrsFile', () => { }]); }); - it.only('recognizes diagnostic names', () => { + it('recognizes diagnostic names', () => { let file = program.setFile('/source/main.brs', ` sub Main() 'bs:disable-next-line: cannot-find-name From db4261c9b4f77667caf1647b7bcf143f1741f3ea Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Fri, 28 Jun 2024 10:58:36 -0300 Subject: [PATCH 04/42] hopefully fixed test --- src/DiagnosticFilterer.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DiagnosticFilterer.spec.ts b/src/DiagnosticFilterer.spec.ts index 530622945..697c4b428 100644 --- a/src/DiagnosticFilterer.spec.ts +++ b/src/DiagnosticFilterer.spec.ts @@ -272,7 +272,7 @@ describe('DiagnosticFilterer', () => { getDiagnostic('diagCode', `${rootDir}/components/utils.brs`, `${rootDir}/source/remove/utils2.brs`) //remove ]); expect(resultDiagnostics.map(x => x.code)).to.eql(['diagCode']); - expect(resultDiagnostics.map(x => x.file.srcPath)).to.eql([`${rootDir}/source/common.brs`]); + expect(resultDiagnostics.map(x => x.file.srcPath)).to.eql([s`${rootDir}/source/common.brs`]); }); it('respects order of ignores with negative globs', () => { From ce50e124be6f80e302e5dbef54ca20016d777863 Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Fri, 28 Jun 2024 12:20:57 -0300 Subject: [PATCH 05/42] Is this change needed too? --- src/files/BrsFile.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts index 3d52ca45f..afb922df9 100644 --- a/src/files/BrsFile.spec.ts +++ b/src/files/BrsFile.spec.ts @@ -436,7 +436,7 @@ describe('BrsFile', () => { }); it('recognizes diagnostic names', () => { - let file = program.setFile('/source/main.brs', ` + let file = program.setFile({ src: `${rootDir}/source/main.brs`, dest: 'source/main.brs' }, ` sub Main() 'bs:disable-next-line: cannot-find-name name = unknown From 647d89d16ab81931d250ee214b93fdec45c5462e Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Fri, 28 Jun 2024 14:45:44 -0300 Subject: [PATCH 06/42] Changed BsDiagnostic so code is the human-readable version, legacyCode is the old code --- src/DiagnosticFilterer.ts | 5 +- src/DiagnosticMessages.spec.ts | 28 +- src/DiagnosticMessages.ts | 603 +++++++++++++++++---------------- src/Scope.spec.ts | 6 +- src/files/BrsFile.ts | 4 +- src/files/XmlFile.ts | 4 +- src/interfaces.ts | 4 +- src/testHelpers.spec.ts | 28 +- src/util.ts | 8 +- 9 files changed, 353 insertions(+), 337 deletions(-) diff --git a/src/DiagnosticFilterer.ts b/src/DiagnosticFilterer.ts index 26ab89e54..170baafc7 100644 --- a/src/DiagnosticFilterer.ts +++ b/src/DiagnosticFilterer.ts @@ -165,7 +165,10 @@ export class DiagnosticFilterer { //set isSuppressed for any diagnostics with matching codes } else { for (const diagnostic of fileDiagnostics) { - if (filter.codes.includes(diagnostic.diagnostic.code!) || filter.codes.includes(diagnostic.diagnostic.name!)) { + if (filter.codes.includes(diagnostic.diagnostic.code!) || + (diagnostic.diagnostic.legacyCode && + (filter.codes.includes(diagnostic.diagnostic.legacyCode) || + filter.codes.includes(diagnostic.diagnostic.legacyCode.toString())))) { diagnostic.isSuppressed = isSuppressing; } } diff --git a/src/DiagnosticMessages.spec.ts b/src/DiagnosticMessages.spec.ts index 1caa39ff9..a6a245485 100644 --- a/src/DiagnosticMessages.spec.ts +++ b/src/DiagnosticMessages.spec.ts @@ -3,21 +3,21 @@ import { expect } from './chai-config.spec'; import { DiagnosticMessages } from './DiagnosticMessages'; describe('DiagnosticMessages', () => { - it('has unique code for each message', () => { + it('has unique legacyCode for each message', () => { let codes = {}; for (let key in DiagnosticMessages) { let func = DiagnosticMessages[key]; let obj = func('', '', '', '', '', '', '', '', ''); //if another message already has this code - if (!codes[obj.code]) { - codes[obj.code] = key; + if (!codes[obj.legacyCode]) { + codes[obj.legacyCode] = key; } else { - expect(codes[obj.code]).to.equal(key, 'Two diagnostic messages share the same error code'); + expect(codes[obj.legacyCode]).to.equal(key, 'Two diagnostic messages share the same legacy Code'); } } }); - it('has unique name for each message', () => { - let names = {}; + it('has unique code for each message', () => { + let codes = {}; for (let key in DiagnosticMessages) { if (key.startsWith('__unused')) { // ignore unused diagnostics @@ -25,15 +25,15 @@ describe('DiagnosticMessages', () => { } let func = DiagnosticMessages[key]; let obj = func('', '', '', '', '', '', '', '', ''); - const diagName: string = obj.name ?? ''; - expect(diagName).to.not.equal('', `Diagnostic name is empty - ${key}`); - expect(diagName.toLowerCase()).to.equal(obj.name, `Diagnostic name has capitals - ${key}`); - expect(diagName.indexOf(' ')).to.equal(-1, `Diagnostic name has space - ${key}`); - //if another message already has this name - if (!names[obj.name]) { - names[obj.name] = key; + const diagCode: string = obj.code ?? ''; + expect(diagCode).to.not.equal('', `Diagnostic name is empty - ${key}`); + expect(diagCode.toLowerCase()).to.equal(obj.code, `Diagnostic name has capitals - ${key}`); + expect(diagCode.indexOf(' ')).to.equal(-1, `Diagnostic name has space - ${key}`); + //if another message already has this code + if (!codes[obj.code]) { + codes[obj.code] = key; } else { - expect(names[obj.name]).to.equal(key, 'Two diagnostic messages share the same error names'); + expect(codes[obj.code]).to.equal(key, 'Two diagnostic messages share the same error codes'); } } }); diff --git a/src/DiagnosticMessages.ts b/src/DiagnosticMessages.ts index edfd121a3..f6bb56609 100644 --- a/src/DiagnosticMessages.ts +++ b/src/DiagnosticMessages.ts @@ -12,9 +12,9 @@ export let DiagnosticMessages = { //this one won't be used much, we just need a catchall object for the code since we pass through the message from the parser genericParserMessage: (message: string) => ({ message: message, - code: 1000, + legacyCode: 1000, severity: DiagnosticSeverity.Error, - name: 'generic-parser-message' + code: 'generic-parser-message' }), /** * @@ -25,180 +25,180 @@ export let DiagnosticMessages = { */ cannotFindName: (name: string, fullName?: string, typeName?: string, typeDescriptor = 'type') => ({ message: `Cannot find name '${name}'${typeName ? ` for ${typeDescriptor} '${typeName}'` : ''}`, - code: 1001, + legacyCode: 1001, data: { name: name, fullName: fullName ?? name, typeName: typeName ? typeName : undefined }, severity: DiagnosticSeverity.Error, - name: 'cannot-find-name' + code: 'cannot-find-name' }), mismatchArgumentCount: (expectedCount: number | string, actualCount: number) => ({ message: `Expected ${expectedCount} arguments, but got ${actualCount}.`, - code: 1002, + legacyCode: 1002, severity: DiagnosticSeverity.Error, - name: 'mismatch-argument-count' + code: 'mismatch-argument-count' }), duplicateFunctionImplementation: (functionName: string) => ({ message: `Duplicate function implementation for '${functionName}'.`, - code: 1003, + legacyCode: 1003, severity: DiagnosticSeverity.Error, - name: 'duplicate-function-implementation' + code: 'duplicate-function-implementation' }), referencedFileDoesNotExist: () => ({ message: `Referenced file does not exist.`, - code: 1004, + legacyCode: 1004, severity: DiagnosticSeverity.Error, - name: 'referenced-file-does-not-exist' + code: 'referenced-file-does-not-exist' }), xmlComponentMissingComponentDeclaration: () => ({ message: `Missing a component declaration.`, - code: 1005, + legacyCode: 1005, severity: DiagnosticSeverity.Error, - name: 'xml-missing-component-declaration' + code: 'xml-missing-component-declaration' }), xmlComponentMissingNameAttribute: () => ({ message: `Component must have a name attribute.`, - code: 1006, + legacyCode: 1006, severity: DiagnosticSeverity.Error, - name: 'xml-missing-name-attribute' + code: 'xml-missing-name-attribute' }), xmlComponentMissingExtendsAttribute: () => ({ message: `Component is mising "extends" attribute and will automatically extend "Group" by default`, - code: 1007, + legacyCode: 1007, severity: DiagnosticSeverity.Warning, - name: 'xml-missing-extends-attribute' + code: 'xml-missing-extends-attribute' }), xmlGenericParseError: (message: string) => ({ //generic catchall xml parse error message: message, - code: 1008, + legacyCode: 1008, severity: DiagnosticSeverity.Error, - name: 'xml-generic-parse-error' + code: 'xml-generic-parse-error' }), unnecessaryScriptImportInChildFromParent: (parentComponentName: string) => ({ message: `Unnecessary script import: Script is already imported in ancestor component '${parentComponentName}'.`, - code: 1009, + legacyCode: 1009, severity: DiagnosticSeverity.Warning, - name: 'unnecessary-script-import' + code: 'unnecessary-script-import' }), overridesAncestorFunction: (callableName: string, currentScopeName: string, parentFilePath: string, parentScopeName: string) => ({ message: `Function '${callableName}' included in '${currentScopeName}' overrides function in '${parentFilePath}' included in '${parentScopeName}'.`, - code: 1010, + legacyCode: 1010, severity: DiagnosticSeverity.Hint, - name: 'overrides-ancestor-function' + code: 'overrides-ancestor-function' }), localVarFunctionShadowsParentFunction: (scopeName: 'stdlib' | 'scope') => ({ message: `Local variable function has same name as ${scopeName} function and will never be called.`, - code: 1011, + legacyCode: 1011, severity: DiagnosticSeverity.Warning, - name: 'local-var-function-shadow' + code: 'local-var-function-shadow' }), scriptImportCaseMismatch: (correctFilePath: string) => ({ message: `Script import path does not match casing of actual file path '${correctFilePath}'.`, - code: 1012, + legacyCode: 1012, severity: DiagnosticSeverity.Warning, - name: 'script-import-case-mismatch' + code: 'script-import-case-mismatch' }), fileNotReferencedByAnyOtherFile: () => ({ message: `This file is not referenced by any other file in the project.`, - code: 1013, + legacyCode: 1013, severity: DiagnosticSeverity.Warning, - name: 'file-not-referenced' + code: 'file-not-referenced' }), - unknownDiagnosticCode: (theUnknownCode: number) => ({ + unknownDiagnosticCode: (theUnknownCode: number | string) => ({ message: `Unknown diagnostic code ${theUnknownCode}`, - code: 1014, + legacyCode: 1014, severity: DiagnosticSeverity.Warning, - name: 'unknown-diagnostic-code' + code: 'unknown-diagnostic-code' }), scriptSrcCannotBeEmpty: () => ({ message: `Script import cannot be empty or whitespace`, - code: 1015, + legacyCode: 1015, severity: DiagnosticSeverity.Error, - name: 'script-import-empty' + code: 'script-import-empty' }), expectedIdentifierAfterKeyword: (keywordText: string) => ({ message: `Expected identifier after '${keywordText}' keyword`, - code: 1016, + legacyCode: 1016, severity: DiagnosticSeverity.Error, - name: 'expected-identifier-after-keyword' + code: 'expected-identifier-after-keyword' }), missingCallableKeyword: () => ({ message: `Expected 'function' or 'sub' to preceed identifier`, - code: 1017, + legacyCode: 1017, severity: DiagnosticSeverity.Error, - name: 'expected-callable-keyword' + code: 'expected-callable-keyword' }), expectedValidTypeToFollowAsKeyword: () => ({ message: `Expected valid type to follow 'as' keyword`, - code: 1018, + legacyCode: 1018, severity: DiagnosticSeverity.Error, - name: 'expected-valid-type' + code: 'expected-valid-type' }), bsFeatureNotSupportedInBrsFiles: (featureName) => ({ message: `BrighterScript feature '${featureName}' is not supported in standard BrightScript files`, - code: 1019, + legacyCode: 1019, severity: DiagnosticSeverity.Error, - name: 'bs-feature-not-supported' + code: 'bs-feature-not-supported' }), brsConfigJsonIsDeprecated: () => ({ message: `'brsconfig.json' is deprecated. Please rename to 'bsconfig.json'`, - code: 1020, + legacyCode: 1020, severity: DiagnosticSeverity.Warning, - name: 'brsconfig-deprecated' + code: 'brsconfig-deprecated' }), bsConfigJsonHasSyntaxErrors: (message: string) => ({ message: `Encountered syntax errors in bsconfig.json: ${message}`, - code: 1021, + legacyCode: 1021, severity: DiagnosticSeverity.Error, - name: 'bsconfig-syntax-errors' + code: 'bsconfig-syntax-errors' }), itemIsDeprecated: () => ({ message: `Item is deprecated`, - code: 1022, + legacyCode: 1022, severity: DiagnosticSeverity.Hint, - name: 'item-deprecated' + code: 'item-deprecated' }), cannotUseOverrideKeywordOnConstructorFunction: () => ({ message: 'Override keyword is not allowed on class constructor method', - code: 1023, + legacyCode: 1023, severity: DiagnosticSeverity.Error, - name: 'override-keyword-on-constructor' + code: 'override-keyword-on-constructor' }), statementMustBeDeclaredAtTopOfFile: (statementKeyword: string) => ({ message: `'${statementKeyword}' statement must be declared at the top of the file`, - code: 1024, + legacyCode: 1024, severity: DiagnosticSeverity.Error, - name: 'must-be-declared-at-top' + code: 'must-be-declared-at-top' }), __unused8: (methodName: string, className: string) => ({ message: `Method '${methodName}' does not exist on type '${className}'`, - code: 1025, + legacyCode: 1025, severity: DiagnosticSeverity.Error }), duplicateIdentifier: (memberName: string) => ({ message: `Duplicate identifier '${memberName}'`, - code: 1026, + legacyCode: 1026, severity: DiagnosticSeverity.Error, - name: 'duplicate-identifier' + code: 'duplicate-identifier' }), missingOverrideKeyword: (ancestorClassName: string) => ({ message: `Method has no override keyword but is declared in ancestor class '${ancestorClassName}'`, - code: 1027, + legacyCode: 1027, severity: DiagnosticSeverity.Error, - name: 'missing-override-keyword' + code: 'missing-override-keyword' }), nameCollision: (thisThingKind: string, thatThingKind: string, thatThingName: string) => ({ message: `${thisThingKind} has same name as ${thatThingKind} '${thatThingName}'`, - code: 1028, + legacyCode: 1028, severity: DiagnosticSeverity.Error, - name: 'name-collision' + code: 'name-collision' }), __unused9: (className: string, scopeName: string) => ({ message: `Class '${className}' could not be found when this file is included in scope '${scopeName}'`, - code: 1029, + legacyCode: 1029, severity: DiagnosticSeverity.Error, data: { className: className @@ -206,660 +206,660 @@ export let DiagnosticMessages = { }), expectedClassFieldIdentifier: () => ({ message: `Expected identifier in class body`, - code: 1030, + legacyCode: 1030, severity: DiagnosticSeverity.Error, - name: 'expected-identifier-in-body' + code: 'expected-identifier-in-body' }), expressionIsNotConstructable: (expressionType: string) => ({ message: `Cannot use the 'new' keyword here because '${expressionType}' is not a constructable type`, - code: 1031, + legacyCode: 1031, severity: DiagnosticSeverity.Error, - name: 'cannot-use-new-keyword' + code: 'cannot-use-new-keyword' }), expectedKeyword: (kind: TokenKind) => ({ message: `Expected '${kind}' keyword`, - code: 1032, + legacyCode: 1032, severity: DiagnosticSeverity.Error, - name: 'expected-keyword' + code: 'expected-keyword' }), expectedLeftParenAfterCallable: (callableType: string) => ({ message: `Expected '(' after ${callableType}`, - code: 1033, + legacyCode: 1033, severity: DiagnosticSeverity.Error, - name: 'expected-left-paren-after-callable' + code: 'expected-left-paren-after-callable' }), expectedNameAfterCallableKeyword: (callableType: string) => ({ message: `Expected ${callableType} name after '${callableType}' keyword`, - code: 1034, + legacyCode: 1034, severity: DiagnosticSeverity.Error, - name: 'expected-name-after-callable' + code: 'expected-name-after-callable' }), expectedLeftParenAfterCallableName: (callableType: string) => ({ message: `Expected '(' after ${callableType} name`, - code: 1035, + legacyCode: 1035, severity: DiagnosticSeverity.Error, - name: 'expected-left-paren-after-callable-name' + code: 'expected-left-paren-after-callable-name' }), tooManyCallableParameters: (actual: number, max: number) => ({ message: `Cannot have more than ${max} parameters but found ${actual})`, - code: 1036, + legacyCode: 1036, severity: DiagnosticSeverity.Error, - name: 'too-many-parameters' + code: 'too-many-parameters' }), __unused: (typeText: string) => ({ message: `Function return type '${typeText}' is invalid`, - code: 1037, + legacyCode: 1037, severity: DiagnosticSeverity.Error }), requiredParameterMayNotFollowOptionalParameter: (parameterName: string) => ({ message: `Required parameter '${parameterName}' must be declared before any optional parameters`, - code: 1038, + legacyCode: 1038, severity: DiagnosticSeverity.Error, - name: 'required-parameter-may-not-follow-optional' + code: 'required-parameter-may-not-follow-optional' }), expectedNewlineOrColon: () => ({ message: `Expected newline or ':' at the end of a statement`, - code: 1039, + legacyCode: 1039, severity: DiagnosticSeverity.Error, - name: 'expected-newline-or-colon' + code: 'expected-newline-or-colon' }), functionNameCannotEndWithTypeDesignator: (callableType: string, name: string, designator: string) => ({ message: `${callableType} name '${name}' cannot end with type designator '${designator}'`, - code: 1040, + legacyCode: 1040, severity: DiagnosticSeverity.Error, - name: 'function-name-ends-with-type' + code: 'function-name-ends-with-type' }), callableBlockMissingEndKeyword: (callableType: string) => ({ message: `Expected 'end ${callableType}' to terminate ${callableType} block`, - code: 1041, + legacyCode: 1041, severity: DiagnosticSeverity.Error, - name: 'callable-block-missing-end-keyword' + code: 'callable-block-missing-end-keyword' }), mismatchedEndCallableKeyword: (expectedCallableType: string, actualCallableType: string) => ({ message: `Expected 'end ${expectedCallableType?.replace(/^end\s*/, '')}' to terminate ${expectedCallableType} block but found 'end ${actualCallableType?.replace(/^end\s*/, '')}' instead.`, - code: 1042, + legacyCode: 1042, severity: DiagnosticSeverity.Error, - name: 'mismatched-end-callable-keyword' + code: 'mismatched-end-callable-keyword' }), expectedParameterNameButFound: (text: string) => ({ message: `Expected parameter name, but found '${text ?? ''}'`, - code: 1043, + legacyCode: 1043, severity: DiagnosticSeverity.Error, - name: 'expected-parameter-name' + code: 'expected-parameter-name' }), __unused2: (parameterName: string, typeText: string) => ({ message: `Function parameter '${parameterName}' is of invalid type '${typeText}'`, - code: 1044, + legacyCode: 1044, severity: DiagnosticSeverity.Error }), cannotUseReservedWordAsIdentifier: (name: string) => ({ message: `Cannot use reserved word '${name}' as an identifier`, - code: 1045, + legacyCode: 1045, severity: DiagnosticSeverity.Error, - name: 'cannot-use-reserved-word' + code: 'cannot-use-reserved-word' }), expectedOperatorAfterIdentifier: (operators: TokenKind[], name: string) => { operators = Array.isArray(operators) ? operators : []; return { message: `Expected operator ('${operators.join(`', '`)}') after idenfifier '${name}'`, - code: 1046, + legacyCode: 1046, severity: DiagnosticSeverity.Error, - name: 'expected-operator-after-identifier' + code: 'expected-operator-after-identifier' }; }, expectedInlineIfStatement: () => ({ message: `If/else statement within an inline if should be also inline`, - code: 1047, + legacyCode: 1047, severity: DiagnosticSeverity.Error, - name: 'expected-inline-if' + code: 'expected-inline-if' }), expectedFinalNewline: () => ({ message: `Expected newline at the end of an inline if statement`, - code: 1048, + legacyCode: 1048, severity: DiagnosticSeverity.Error, - name: 'expected-final-newline' + code: 'expected-final-newline' }), couldNotFindMatchingEndKeyword: (keyword: string) => ({ message: `Could not find matching 'end ${keyword}'`, - code: 1049, + legacyCode: 1049, severity: DiagnosticSeverity.Error, - name: 'could-not-find-matching-end-keyword' + code: 'could-not-find-matching-end-keyword' }), expectedCatchBlockInTryCatch: () => ({ message: `Expected 'catch' block in 'try' statement`, - code: 1050, + legacyCode: 1050, severity: DiagnosticSeverity.Error, - name: 'expected-catch' + code: 'expected-catch' }), expectedEndForOrNextToTerminateForLoop: () => ({ message: `Expected 'end for' or 'next' to terminate 'for' loop`, - code: 1051, + legacyCode: 1051, severity: DiagnosticSeverity.Error, - name: 'expected-end-for' + code: 'expected-end-for' }), expectedInAfterForEach: (name: string) => ({ message: `Expected 'in' after 'for each ${name}'`, - code: 1052, + legacyCode: 1052, severity: DiagnosticSeverity.Error, - name: 'expected-in-for-each' + code: 'expected-in-for-each' }), expectedExpressionAfterForEachIn: () => ({ message: `Expected expression after 'in' keyword from 'for each' statement`, - code: 1053, + legacyCode: 1053, severity: DiagnosticSeverity.Error, - name: 'expected-expression-for-each' + code: 'expected-expression-for-each' }), unexpectedColonBeforeIfStatement: () => ({ message: `Colon before 'if' statement is not allowed`, - code: 1054, + legacyCode: 1054, severity: DiagnosticSeverity.Error, - name: 'unexpected-colon-before-if' + code: 'unexpected-colon-before-if' }), expectedStringLiteralAfterKeyword: (keyword: string) => ({ message: `Missing string literal after '${keyword}' keyword`, - code: 1055, + legacyCode: 1055, severity: DiagnosticSeverity.Error, - name: 'expected-string-literal' + code: 'expected-string-literal' }), keywordMustBeDeclaredAtRootLevel: (keyword: string) => ({ message: `${keyword} must be declared at the root level`, - code: 1056, + legacyCode: 1056, severity: DiagnosticSeverity.Error, - name: 'keyword-must-be-root-level' + code: 'keyword-must-be-root-level' }), __unused5: () => ({ message: `'library' statement must be declared at the top of the file`, - code: 1057, + legacyCode: 1057, severity: DiagnosticSeverity.Error }), expectedEndIfElseIfOrElseToTerminateThenBlock: () => ({ message: `Expected 'end if', 'else if', or 'else' to terminate 'then' block`, - code: 1058, + legacyCode: 1058, severity: DiagnosticSeverity.Error, - name: 'expected-terminator-on-then' + code: 'expected-terminator-on-then' }), expectedEndTryToTerminateTryCatch: () => ({ message: `Expected 'end try' to terminate 'try-catch' statement`, - code: 1059, + legacyCode: 1059, severity: DiagnosticSeverity.Error, - name: 'expected-terminator-on-try-catch' + code: 'expected-terminator-on-try-catch' }), expectedEndIfToCloseIfStatement: (startingPosition: Position) => ({ message: `Expected 'end if' to close 'if' statement started at ${startingPosition?.line + 1}:${startingPosition?.character + 1}`, - code: 1060, + legacyCode: 1060, severity: DiagnosticSeverity.Error, - name: 'expected-terminator-on-if' + code: 'expected-terminator-on-if' }), expectedStatementToFollowConditionalCondition: (conditionType: string) => ({ message: `Expected a statement to follow '${conditionType?.toLowerCase()} ...condition... then'`, - code: 1061, + legacyCode: 1061, severity: DiagnosticSeverity.Error, - name: 'expected-statement-after-conditional' + code: 'expected-statement-after-conditional' }), expectedStatementToFollowElse: () => ({ message: `Expected a statement to follow 'else'`, - code: 1062, + legacyCode: 1062, severity: DiagnosticSeverity.Error, - name: 'expected-statement-after-else' + code: 'expected-statement-after-else' }), consecutiveIncrementDecrementOperatorsAreNotAllowed: () => ({ message: `Consecutive increment/decrement operators are not allowed`, - code: 1063, + legacyCode: 1063, severity: DiagnosticSeverity.Error, - name: 'consecutive-increment-decrement' + code: 'consecutive-increment-decrement' }), incrementDecrementOperatorsAreNotAllowedAsResultOfFunctionCall: () => ({ message: `Increment/decrement operators are not allowed on function calls`, - code: 1064, + legacyCode: 1064, severity: DiagnosticSeverity.Error, - name: 'increment-decrement-on-function-call' + code: 'increment-decrement-on-function-call' }), xmlUnexpectedTag: (tagName: string) => ({ message: `Unexpected tag '${tagName}'`, - code: 1065, + legacyCode: 1065, severity: DiagnosticSeverity.Error, - name: 'unexpected-tag' + code: 'unexpected-tag' }), expectedStatementOrFunctionCallButReceivedExpression: () => ({ message: `Expected statement or function call but instead found expression`, - code: 1066, + legacylegacyCode: 1066, severity: DiagnosticSeverity.Error, - name: 'expected-statement-not-expression' + code: 'expected-statement-not-expression' }), xmlFunctionNotFound: (name: string) => ({ message: `Cannot find function with name '${name}' in component scope`, - code: 1067, + legacyCode: 1067, severity: DiagnosticSeverity.Error, - name: 'xml-function-not-found' + code: 'xml-function-not-found' }), xmlInvalidFieldType: (name: string) => ({ message: `Invalid field type ${name}`, - code: 1068, + legacyCode: 1068, severity: DiagnosticSeverity.Error, - name: 'xml-invalid-field-type' + code: 'xml-invalid-field-type' }), xmlUnexpectedChildren: (tagName: string) => ({ message: `Tag '${tagName}' should not have children`, - code: 1069, + legacyCode: 1069, severity: DiagnosticSeverity.Error, - name: 'xml-unexpected-children' + code: 'xml-unexpected-children' }), xmlTagMissingAttribute: (tagName: string, attrName: string) => ({ message: `Tag '${tagName}' must have a '${attrName}' attribute`, - code: 1070, + legacyCode: 1070, severity: DiagnosticSeverity.Error, - name: 'xml-missing-attribute' + code: 'xml-missing-attribute' }), expectedLabelIdentifierAfterGotoKeyword: () => ({ message: `Expected label identifier after 'goto' keyword`, - code: 1071, + legacyCode: 1071, severity: DiagnosticSeverity.Error, - name: 'expected-label' + code: 'expected-label' }), expectedRightSquareBraceAfterArrayOrObjectIndex: () => ({ message: `Expected ']' after array or object index`, - code: 1072, + legacyCode: 1072, severity: DiagnosticSeverity.Error, - name: 'expected-right-brace' + code: 'expected-right-brace' }), expectedPropertyNameAfterPeriod: () => ({ message: `Expected property name after '.'`, - code: 1073, + legacyCode: 1073, severity: DiagnosticSeverity.Error, - name: 'expected-property-name' + code: 'expected-property-name' }), tooManyCallableArguments: (actual: number, max: number) => ({ message: `Cannot have more than ${max} arguments but found ${actual}`, - code: 1074, + legacyCode: 1074, severity: DiagnosticSeverity.Error, - name: 'too-many-arguments' + code: 'too-many-arguments' }), expectedRightParenAfterFunctionCallArguments: () => ({ message: `Expected ')' after function call arguments`, - code: 1075, + legacyCode: 1075, severity: DiagnosticSeverity.Error, - name: 'expected-right-paren' + code: 'expected-right-paren' }), unmatchedLeftParenAfterExpression: () => ({ message: `Unmatched '(': expected ')' after expression`, - code: 1076, + legacyCode: 1076, severity: DiagnosticSeverity.Error, - name: 'unmatched-left-paren' + code: 'unmatched-left-paren' }), unmatchedLeftSquareBraceAfterArrayLiteral: () => ({ message: `Unmatched '[': expected ']' after array literal`, - code: 1077, + legacyCode: 1077, severity: DiagnosticSeverity.Error, - name: 'unmatched-left-brace' + code: 'unmatched-left-brace' }), unexpectedAAKey: () => ({ message: `Expected identifier or string as associative array key`, - code: 1078, + legacyCode: 1078, severity: DiagnosticSeverity.Error, - name: 'expected-identifier-as-key' + code: 'expected-identifier-as-key' }), expectedColonBetweenAAKeyAndvalue: () => ({ message: `Expected ':' between associative array key and value`, - code: 1079, + legacyCode: 1079, severity: DiagnosticSeverity.Error, - name: 'expected-colon-after-aa-key' + code: 'expected-colon-after-aa-key' }), unmatchedLeftCurlyAfterAALiteral: () => ({ message: `Unmatched '{': expected '}' after associative array literal`, - code: 1080, + legacyCode: 1080, severity: DiagnosticSeverity.Error, - name: 'unmatched-left-curly' + code: 'unmatched-left-curly' }), unexpectedToken: (text: string) => ({ message: `Unexpected token '${text}'`, - code: 1081, + legacyCode: 1081, severity: DiagnosticSeverity.Error, - name: 'unexpected-token' + code: 'unexpected-token' }), /** * Used in the lexer anytime we encounter an unsupported character */ unexpectedCharacter: (text: string) => ({ message: `Unexpected character '${text}' (char code ${text?.charCodeAt(0)})`, - code: 1082, + legacyCode: 1082, severity: DiagnosticSeverity.Error, - name: 'unexpected-character' + code: 'unexpected-character' }), unterminatedStringAtEndOfLine: () => ({ message: `Unterminated string at end of line`, - code: 1083, + legacyCode: 1083, severity: DiagnosticSeverity.Error, - name: 'unterminated-string' + code: 'unterminated-string' }), unterminatedStringAtEndOfFile: () => ({ message: `Unterminated string at end of file`, - code: 1084, + legacyCode: 1084, severity: DiagnosticSeverity.Error, - name: 'unterminated-string-at-end-of-file' + code: 'unterminated-string-at-end-of-file' }), fractionalHexLiteralsAreNotSupported: () => ({ message: `Fractional hex literals are not supported`, - code: 1085, + legacyCode: 1085, severity: DiagnosticSeverity.Error, - name: 'fractional-hex-literal' + code: 'fractional-hex-literal' }), unexpectedConditionalCompilationString: () => ({ message: `Unexpected conditional-compilation string`, - code: 1086, + legacyCode: 1086, severity: DiagnosticSeverity.Error, - name: 'unexpected-conditional-compilation' + code: 'unexpected-conditional-compilation' }), duplicateConstDeclaration: (name: string) => ({ message: `Attempting to redeclare #const with name '${name}'`, - code: 1087, + legacyCode: 1087, severity: DiagnosticSeverity.Error, - name: 'duplicate-const-declaration' + code: 'duplicate-const-declaration' }), constAliasDoesNotExist: (name: string) => ({ message: `Attempting to create #const alias of '${name}', but no such #const exists`, - code: 1088, + legacyCode: 1088, severity: DiagnosticSeverity.Error, - name: 'const-alias-does-not-exist' + code: 'const-alias-does-not-exist' }), invalidHashConstValue: () => ({ message: '#const declarations can only have values of `true`, `false`, or other #const names', - code: 1089, + legacyCode: 1089, severity: DiagnosticSeverity.Error, - name: 'invalid-const-value' + code: 'invalid-const-value' }), referencedConstDoesNotExist: () => ({ message: `Referenced #const does not exist`, - code: 1090, + legacyCode: 1090, severity: DiagnosticSeverity.Error, - name: 'const-does-not-exist' + code: 'const-does-not-exist' }), invalidHashIfValue: () => ({ message: `#if conditionals can only be 'true', 'false', or other #const names`, - code: 1091, + legacyCode: 1091, severity: DiagnosticSeverity.Error, - name: 'invalid-hash-if-value' + code: 'invalid-hash-if-value' }), hashError: (message: string) => ({ message: `#error ${message}`, - code: 1092, + legacyCode: 1092, severity: DiagnosticSeverity.Error, - name: 'hash-error' + code: 'hash-error' }), expectedEqualAfterConstName: () => ({ message: `Expected '=' after #const`, - code: 1093, + legacyCode: 1093, severity: DiagnosticSeverity.Error, - name: 'expected-equal-after-const' + code: 'expected-equal-after-const' }), expectedHashEndIfToCloseHashIf: (startingLine: number) => ({ message: `Expected '#end if' to close '#if' conditional compilation statement starting on line ${startingLine}`, - code: 1094, + legacyCode: 1094, severity: DiagnosticSeverity.Error, - name: 'expected-terminator-on-hash-if' + code: 'expected-terminator-on-hash-if' }), constNameCannotBeReservedWord: () => ({ message: `#const name cannot be a reserved word`, - code: 1095, + legacyCode: 1095, severity: DiagnosticSeverity.Error, - name: 'const-reservered-word' + code: 'const-reservered-word' }), expectedIdentifier: () => ({ message: `Expected identifier`, - code: 1096, + legacyCode: 1096, severity: DiagnosticSeverity.Error, - name: 'expected-identifier' + code: 'expected-identifier' }), expectedAttributeNameAfterAtSymbol: () => ({ message: `Expected xml attribute name after '@'`, - code: 1097, + legacyCode: 1097, severity: DiagnosticSeverity.Error, - name: 'expected-attribute-name' + code: 'expected-attribute-name' }), childFieldTypeNotAssignableToBaseProperty: (childTypeName: string, baseTypeName: string, fieldName: string, childFieldType: string, parentFieldType: string) => ({ message: `Field '${fieldName}' in class '${childTypeName}' is not assignable to the same field in base class '${baseTypeName}'. Type '${childFieldType}' is not assignable to type '${parentFieldType}'.`, - code: 1098, + legacyCode: 1098, severity: DiagnosticSeverity.Error, - name: 'child-field-type-not-assignable' + code: 'child-field-type-not-assignable' }), classChildMemberDifferentMemberTypeThanAncestor: (memberType: string, parentMemberType: string, parentClassName: string) => ({ message: `Class member is a ${memberType} here but a ${parentMemberType} in ancestor class '${parentClassName}'`, - code: 1099, + legacyCode: 1099, severity: DiagnosticSeverity.Error, - name: 'child-field-type-different' + code: 'child-field-type-different' }), classConstructorMissingSuperCall: () => ({ message: `Missing "super()" call in class constructor method.`, - code: 1100, + legacyCode: 1100, severity: DiagnosticSeverity.Error, - name: 'missing-super-call' + code: 'missing-super-call' }), classConstructorIllegalUseOfMBeforeSuperCall: () => ({ message: `Illegal use of "m" before calling "super()"`, - code: 1101, + legacyCode: 1101, severity: DiagnosticSeverity.Error, - name: 'use-m-before-super' + code: 'use-m-before-super' }), classFieldCannotBeOverridden: () => ({ message: `Class field cannot be overridden`, - code: 1102, + legacyCode: 1102, severity: DiagnosticSeverity.Error, - name: 'class-field-override' + code: 'class-field-override' }), unusedAnnotation: () => ({ message: `This annotation is not attached to any statement`, - code: 1103, + legacyCode: 1103, severity: DiagnosticSeverity.Error, - name: 'unused-annotation' + code: 'unused-annotation' }), localVarShadowedByScopedFunction: () => ({ message: `Declaring a local variable with same name as scoped function can result in unexpected behavior`, - code: 1104, + legacyCode: 1104, severity: DiagnosticSeverity.Error, - name: 'local-var-shadowed-by-function' + code: 'local-var-shadowed-by-function' }), scopeFunctionShadowedByBuiltInFunction: () => ({ message: `Scope function will not be accessible because it has the same name as a built-in function`, - code: 1105, + legacyCode: 1105, severity: DiagnosticSeverity.Error, - name: 'function-shadowed-by-built-in-function' + code: 'function-shadowed-by-built-in-function' }), localVarSameNameAsClass: (className: string) => ({ message: `Local variable has same name as class '${className}'`, - code: 1106, + legacyCode: 1106, severity: DiagnosticSeverity.Error, - name: 'local-var-same-name-as-class' + code: 'local-var-same-name-as-class' }), unnecessaryCodebehindScriptImport: () => ({ message: `This import is unnecessary because compiler option 'autoImportComponentScript' is enabled`, - code: 1107, + legacyCode: 1107, severity: DiagnosticSeverity.Warning, - name: 'xml-unnecessary-import' + code: 'xml-unnecessary-import' }), expectedOpenParenToFollowCallfuncIdentifier: () => ({ message: `Expected '(' to follow callfunc identifier`, - code: 1108, + legacyCode: 1108, severity: DiagnosticSeverity.Error, - name: 'expected-left-paren-after-callfunc' + code: 'expected-left-paren-after-callfunc' }), expectedToken: (...tokenKinds: string[]) => ({ message: `Expected token '${tokenKinds.join(`' or '`)}'`, - code: 1109, + legacyCode: 1109, severity: DiagnosticSeverity.Error, - name: 'expected-token' + code: 'expected-token' }), parameterMayNotHaveSameNameAsNamespace: (paramName: string) => ({ message: `Parameter '${paramName}' may not have the same name as namespace`, - code: 1110, + legacyCode: 1110, severity: DiagnosticSeverity.Error, - name: 'parameter-same-name-as-namespace' + code: 'parameter-same-name-as-namespace' }), variableMayNotHaveSameNameAsNamespace: (variableName: string) => ({ message: `Variable '${variableName}' may not have the same name as namespace`, - code: 1111, + legacyCode: 1111, severity: DiagnosticSeverity.Error, - name: 'variable-same-name-as-namespace' + code: 'variable-same-name-as-namespace' }), unterminatedTemplateStringAtEndOfFile: () => ({ message: `Unterminated template string at end of file`, - code: 1113, + legacyCode: 1113, severity: DiagnosticSeverity.Error, - name: 'unterminated-template-string' + code: 'unterminated-template-string' }), unterminatedTemplateExpression: () => ({ message: `Unterminated template string expression. '\${' must be followed by expression, then '}'`, - code: 1114, + legacyCode: 1114, severity: DiagnosticSeverity.Error, - name: 'unterminated-template-expression' + code: 'unterminated-template-expression' }), duplicateComponentName: (componentName: string) => ({ message: `There are multiple components with the name '${componentName}'`, - code: 1115, + legacyCode: 1115, severity: DiagnosticSeverity.Error, - name: 'duplicate-component-name' + code: 'duplicate-component-name' }), __unused6: (className: string) => ({ message: `Function has same name as class '${className}'`, - code: 1116, + legacyCode: 1116, severity: DiagnosticSeverity.Error }), missingExceptionVarToFollowCatch: () => ({ message: `Missing exception variable after 'catch' keyword`, - code: 1117, + legacyCode: 1117, severity: DiagnosticSeverity.Error, - name: 'missing-exception-variable' + code: 'missing-exception-variable' }), missingExceptionExpressionAfterThrowKeyword: () => ({ message: `Missing exception expression after 'throw' keyword`, - code: 1118, + legacyCode: 1118, severity: DiagnosticSeverity.Error, - name: 'missing-exception-expression' + code: 'missing-exception-expression' }), missingLeftSquareBracketAfterDimIdentifier: () => ({ message: `Missing left square bracket after 'dim' identifier`, - code: 1119, + legacyCode: 1119, severity: DiagnosticSeverity.Error, - name: 'missing-left-brace-after-dim' + code: 'missing-left-brace-after-dim' }), missingRightSquareBracketAfterDimIdentifier: () => ({ message: `Missing right square bracket after 'dim' identifier`, - code: 1120, + legacyCode: 1120, severity: DiagnosticSeverity.Error, - name: 'missing-right-brace-after-dim' + code: 'missing-right-brace-after-dim' }), missingExpressionsInDimStatement: () => ({ message: `Missing expression(s) in 'dim' statement`, - code: 1121, + legacyCode: 1121, severity: DiagnosticSeverity.Error, - name: 'missing-expression-in-dim' + code: 'missing-expression-in-dim' }), mismatchedOverriddenMemberVisibility: (childClassName: string, memberName: string, childAccessModifier: string, ancestorAccessModifier: string, ancestorClassName: string) => ({ message: `Access modifier mismatch: '${memberName}' is ${childAccessModifier} in type '${childClassName}' but is ${ancestorAccessModifier} in base type '${ancestorClassName}'.`, - code: 1122, + legacyCode: 1122, severity: DiagnosticSeverity.Error, - name: 'access-modifier-mismatch' + code: 'access-modifier-mismatch' }), __unused3: (typeName: string) => ({ message: `Cannot find type with name '${typeName}'`, - code: 1123, + legacyCode: 1123, severity: DiagnosticSeverity.Error }), enumValueMustBeType: (expectedType: string) => ({ message: `Enum value must be type '${expectedType}'`, - code: 1124, + legacyCode: 1124, severity: DiagnosticSeverity.Error, - name: 'enum-value-invalid-type' + code: 'enum-value-invalid-type' }), enumValueIsRequired: (expectedType: string) => ({ message: `Value is required for ${expectedType} enum`, - code: 1125, + legacyCode: 1125, severity: DiagnosticSeverity.Error, - name: 'enum-value-required' + code: 'enum-value-required' }), unknownEnumValue: (name: string, enumName: string) => ({ message: `Property '${name}' does not exist on enum '${enumName}'`, - code: 1126, + legacyCode: 1126, severity: DiagnosticSeverity.Error, - name: 'unknown-enum-value' + code: 'unknown-enum-value' }), __unused7: (scopeName: string, enumName: string) => ({ message: `Scope '${scopeName}' already contains an enum with name '${enumName}'`, - code: 1127, + legacyCode: 1127, severity: DiagnosticSeverity.Error }), unknownRoSGNode: (nodeName: string) => ({ message: `Unknown roSGNode '${nodeName}'`, - code: 1128, + legacyCode: 1128, severity: DiagnosticSeverity.Error, - name: 'unknown-rosgnode' + code: 'unknown-rosgnode' }), unknownBrightScriptComponent: (componentName: string) => ({ message: `Unknown BrightScript component '${componentName}'`, - code: 1129, + legacyCode: 1129, severity: DiagnosticSeverity.Error, - name: 'unknown-brightscript-component' + code: 'unknown-brightscript-component' }), mismatchCreateObjectArgumentCount: (componentName: string, allowedArgCounts: number[], actualCount: number) => { const argCountArray = (allowedArgCounts || [1]).sort().filter((value, index, self) => self.indexOf(value) === index); return { message: `For ${componentName}, expected ${argCountArray.map(c => c.toString()).join(' or ')} total arguments, but got ${actualCount}.`, - code: 1130, + legacyCode: 1130, severity: DiagnosticSeverity.Error, - name: 'mismatch-createobject-argument-count' + code: 'mismatch-createobject-argument-count' }; }, deprecatedBrightScriptComponent: (componentName: string, deprecatedDescription?: string) => ({ message: `${componentName} has been deprecated${deprecatedDescription ? ': ' + deprecatedDescription : ''}`, - code: 1131, + legacyCode: 1131, severity: DiagnosticSeverity.Error, - name: 'deprecated-brightscript-component' + code: 'deprecated-brightscript-component' }), circularReferenceDetected: (items: string[], scopeName: string) => ({ message: `Circular reference detected between ${Array.isArray(items) ? items.join(' -> ') : ''} in scope '${scopeName}'`, - code: 1132, + legacyCode: 1132, severity: DiagnosticSeverity.Error, - name: 'circular-reference' + code: 'circular-reference' }), unexpectedStatementOutsideFunction: () => ({ message: `Unexpected statement found outside of function body`, - code: 1133, + legacyCode: 1133, severity: DiagnosticSeverity.Error, - name: 'unexpected-statement' + code: 'unexpected-statement' }), detectedTooDeepFileSource: (numberOfParentDirectories: number) => ({ message: `Expected directory depth no larger than 7, but found ${numberOfParentDirectories}`, - code: 1134, + legacyCode: 1134, severity: DiagnosticSeverity.Error, - name: 'directory-depth' + code: 'directory-depth' }), illegalContinueStatement: () => ({ message: `Continue statement must be contained within a loop statement`, - code: 1135, + legacyCode: 1135, severity: DiagnosticSeverity.Error, - name: 'illegal-continue' + code: 'illegal-continue' }), keywordMustBeDeclaredAtNamespaceLevel: (keyword: string) => ({ message: `${keyword} must be declared at the root level or within a namespace`, - code: 1136, + legacyCode: 1136, severity: DiagnosticSeverity.Error, - name: 'keyword-must-be-at-root-or-namespace' + code: 'keyword-must-be-at-root-or-namespace' }), itemCannotBeUsedAsVariable: (itemType: string) => ({ message: `${itemType} cannot be used as a variable`, - code: 1137, + legacyCode: 1137, severity: DiagnosticSeverity.Error, - name: 'cannout-be-used-as-variable' + code: 'cannout-be-used-as-variable' }), callfuncHasToManyArgs: (numberOfArgs: number) => ({ message: `You can not have more than 5 arguments in a callFunc. ${numberOfArgs} found.`, - code: 1138, + legacyCode: 1138, severity: DiagnosticSeverity.Error, - name: 'callfunc-has-too-many-arguments' + code: 'callfunc-has-too-many-arguments' }), noOptionalChainingInLeftHandSideOfAssignment: () => ({ message: `Optional chaining may not be used in the left-hand side of an assignment`, - code: 1139, + legacyCode: 1139, severity: DiagnosticSeverity.Error, - name: 'otional-chaining-on-left-of-assignment' + code: 'otional-chaining-on-left-of-assignment' }), /** * @@ -870,89 +870,89 @@ export let DiagnosticMessages = { */ cannotFindFunction: (name: string, fullName?: string, typeName?: string, typeDescriptor = 'type') => ({ message: `Cannot find function '${name}'${typeName ? ` for ${typeDescriptor} '${typeName}'` : ''}`, - code: 1140, + legacyCode: 1140, data: { name: name, fullName: fullName ?? name, typeName: typeName ? typeName : undefined }, severity: DiagnosticSeverity.Error, - name: 'cannot-find-function' + code: 'cannot-find-function' }), argumentTypeMismatch: (actualTypeString: string, expectedTypeString: string, data?: TypeCompatibilityData) => ({ message: `Argument of type '${actualTypeString}' is not compatible with parameter of type '${expectedTypeString}'${typeCompatibilityMessage(actualTypeString, expectedTypeString, data)}`, data: data, - code: 1141, + legacyCode: 1141, severity: DiagnosticSeverity.Error, - name: 'argument-type-mismatch' + code: 'argument-type-mismatch' }), returnTypeMismatch: (actualTypeString: string, expectedTypeString: string, data?: TypeCompatibilityData) => ({ message: `Type '${actualTypeString}' is not compatible with declared return type '${expectedTypeString}'${typeCompatibilityMessage(actualTypeString, expectedTypeString, data)}'`, data: data, - code: 1142, + legacyCode: 1142, severity: DiagnosticSeverity.Error, - name: 'return-type-mismatch' + code: 'return-type-mismatch' }), assignmentTypeMismatch: (actualTypeString: string, expectedTypeString: string, data?: TypeCompatibilityData) => ({ message: `Type '${actualTypeString}' is not compatible with type '${expectedTypeString}'${typeCompatibilityMessage(actualTypeString, expectedTypeString, data)}`, data: data, - code: 1143, + legacyCode: 1143, severity: DiagnosticSeverity.Error, - name: 'assignment-type-mismatch' + code: 'assignment-type-mismatch' }), operatorTypeMismatch: (operatorString: string, firstType: string, secondType = '') => ({ message: `Operator '${operatorString}' cannot be applied to type${secondType ? 's' : ''} '${firstType}'${secondType ? ` and '${secondType}'` : ''}`, - code: 1144, + legacyCode: 1144, severity: DiagnosticSeverity.Error, - name: 'operator-type-mismatch' + code: 'operator-type-mismatch' }), incompatibleSymbolDefinition: (symbol: string, scopeName: string) => ({ message: `'${symbol}' is incompatible across these scopes: ${scopeName}`, - code: 1145, + legacyCode: 1145, severity: DiagnosticSeverity.Error, - name: 'incompatible-definition' + code: 'incompatible-definition' }), memberAccessibilityMismatch: (memberName: string, accessModifierFlag: SymbolTypeFlag, definingClassName: string) => ({ message: `Member '${memberName}' is ${accessModifierNameFromFlag(accessModifierFlag)}${accessModifierAdditionalInfo(accessModifierFlag, definingClassName)}`, // TODO: Add scopes where it was defined - code: 1146, + legacyCode: 1146, severity: DiagnosticSeverity.Error, - name: 'member-accessibility-mismatch' + code: 'member-accessibility-mismatch' }), typecastStatementMustBeDeclaredAtStart: () => ({ message: `'typecast' statement must be declared at the top of the file or beginning of function or namespace`, - code: 1147, + legacyCode: 1147, severity: DiagnosticSeverity.Error, - name: 'typecast-must-be-at-start' + code: 'typecast-must-be-at-start' }), invalidTypecastStatementApplication: (foundApplication: string) => ({ message: `'typecast' statement can only be applied to 'm', but was applied to '${foundApplication}'`, - code: 1148, + legacyCode: 1148, severity: DiagnosticSeverity.Error, - name: 'invalid-typecast-statement' + code: 'invalid-typecast-statement' }), itemCannotBeUsedAsType: (typeText: string) => ({ message: `'${typeText}' cannot be used as a type`, - code: 1149, + legacyCode: 1149, severity: DiagnosticSeverity.Error, - name: 'item-cannot-be-used-as-type' + code: 'item-cannot-be-used-as-type' }), expectedNewlineInConditionalCompile: () => ({ message: `Expected newline in conditional compilation statement`, - code: 1151, + legacyCode: 1151, severity: DiagnosticSeverity.Error, - name: 'expected-newline-in-conditional-compile' + code: 'expected-newline-in-conditional-compile' }), expectedTerminatorOnConditionalCompileBlock: () => ({ message: `Expected '#end if', '#else if', or '#else' to terminate conditional compilation block`, - code: 1152, + legacyCode: 1152, severity: DiagnosticSeverity.Error, - name: 'expected-terminator-on-conditional-compile' + code: 'expected-terminator-on-conditional-compile' }), unsafeUnmatchedTerminatorInConditionalCompileBlock: (terminator: string) => ({ message: `Unsafe unmatched terminator '${terminator}' in conditional compilation block`, - code: 1153, + legacyCode: 1153, severity: DiagnosticSeverity.Error, - name: 'unmatched-terminator-on-conditional-compile' + code: 'unmatched-terminator-on-conditional-compile' }) }; export const defaultMaximumTruncationLength = 160; @@ -1006,16 +1006,19 @@ function accessModifierAdditionalInfo(accessModifierFlag: SymbolTypeFlag, classN return TokenKind.Public; } -export const DiagnosticCodeMap = {} as Record; -export let diagnosticCodes = [] as number[]; +export const DiagnosticCodeMap = {} as Record; +export const DiagnosticLegacyCodeMap = {} as Record; +export let diagnosticCodes = [] as string[]; for (let key in DiagnosticMessages) { diagnosticCodes.push(DiagnosticMessages[key]().code); + diagnosticCodes.push(DiagnosticMessages[key]().legacyCode); DiagnosticCodeMap[key] = DiagnosticMessages[key]().code; + DiagnosticLegacyCodeMap[key] = DiagnosticMessages[key]().legacyCode; } export interface DiagnosticInfo { message: string; - code: number; + legacyCode: number; severity: DiagnosticSeverity; } diff --git a/src/Scope.spec.ts b/src/Scope.spec.ts index 1639ab396..c7c5d6dab 100644 --- a/src/Scope.spec.ts +++ b/src/Scope.spec.ts @@ -1021,10 +1021,8 @@ describe('Scope', () => { program.validate(); expectDiagnostics(program, [ DiagnosticMessages.nameCollision('Function', 'Global Function', 'Str').message, - { - message: DiagnosticMessages.scopeFunctionShadowedByBuiltInFunction().message, - range: Range.create(4, 29, 4, 32) - }]); + DiagnosticMessages.scopeFunctionShadowedByBuiltInFunction().message + ]); }); }); diff --git a/src/files/BrsFile.ts b/src/files/BrsFile.ts index a51db764c..d23bb7164 100644 --- a/src/files/BrsFile.ts +++ b/src/files/BrsFile.ts @@ -5,7 +5,7 @@ import { CancellationTokenSource } from 'vscode-languageserver'; import { CompletionItemKind } from 'vscode-languageserver'; import chalk from 'chalk'; import * as path from 'path'; -import { DiagnosticCodeMap, diagnosticCodes, DiagnosticMessages } from '../DiagnosticMessages'; +import { DiagnosticCodeMap, diagnosticCodes, DiagnosticLegacyCodeMap, DiagnosticMessages } from '../DiagnosticMessages'; import { FunctionScope } from '../FunctionScope'; import type { Callable, CallableParam, CommentFlag, BsDiagnostic, FileReference, FileLink, SerializedCodeFile, NamespaceContainer } from '../interfaces'; import type { Token } from '../lexer/Token'; @@ -445,7 +445,7 @@ export class BrsFile implements BscFile { * @param tokens - an array of tokens of which to find `TokenKind.Comment` from */ public getCommentFlags(tokens: Token[]) { - const processor = new CommentFlagProcessor(this, ['rem', `'`], diagnosticCodes, [DiagnosticCodeMap.unknownDiagnosticCode]); + const processor = new CommentFlagProcessor(this, ['rem', `'`], diagnosticCodes, [DiagnosticCodeMap.unknownDiagnosticCode, DiagnosticLegacyCodeMap.unknownDiagnosticCode]); this.commentFlags = []; for (let lexerToken of tokens) { diff --git a/src/files/XmlFile.ts b/src/files/XmlFile.ts index 373593e88..a61524273 100644 --- a/src/files/XmlFile.ts +++ b/src/files/XmlFile.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import type { CodeWithSourceMap } from 'source-map'; import { SourceNode } from 'source-map'; import type { Location, Position, Range } from 'vscode-languageserver'; -import { DiagnosticCodeMap, diagnosticCodes } from '../DiagnosticMessages'; +import { DiagnosticCodeMap, DiagnosticLegacyCodeMap, diagnosticCodes } from '../DiagnosticMessages'; import type { Callable, FileReference, CommentFlag, SerializedCodeFile } from '../interfaces'; import type { Program } from '../Program'; import util from '../util'; @@ -282,7 +282,7 @@ export class XmlFile implements BscFile { * Collect all bs: comment flags */ public getCommentFlags(tokens: Array) { - const processor = new CommentFlagProcessor(this, ['