diff --git a/README.md b/README.md index 4a724356b..971ed7e35 100644 --- a/README.md +++ b/README.md @@ -542,12 +542,6 @@ Type: `boolean` Allow BrighterScript features (classes, interfaces, etc...) to be included in BrightScript (`.brs`) files, and force those files to be transpiled. -#### `enableTypeValidation` - -Type: `boolean` - -Performs additional validation on all declared and inferred types, such as checking member accesses in Classes and Interfaces, verifying that Namespace members exist when accessed, etc. - ## Ignore errors and warnings on a per-line basis In addition to disabling an entire class of errors in `bsconfig.json` by using `ignoreErrorCodes`, you may also disable errors for a subset of the complier rules within a file with the following comment flags: - `bs:disable-next-line` diff --git a/benchmarks/index.ts b/benchmarks/index.ts index 341fa8a4a..12c8d00a1 100644 --- a/benchmarks/index.ts +++ b/benchmarks/index.ts @@ -209,7 +209,7 @@ let options = yargs }) .option('config', { type: 'string', - description: 'add additional BsConfig settings as JSON - eg. \'{"enableTypeValidation":true}\'', + description: 'add additional BsConfig settings as JSON - eg. \'{"removeParameterTypes":true}\'', default: '{}' }) .strict() diff --git a/benchmarks/targets/validate.ts b/benchmarks/targets/validate.ts index ca60f66e9..163d9ece2 100644 --- a/benchmarks/targets/validate.ts +++ b/benchmarks/targets/validate.ts @@ -10,7 +10,6 @@ module.exports = async (options: TargetOptions) => { cwd: projectPath, createPackage: false, copyToStaging: false, - enableTypeValidation: false, //disable diagnostic reporting (they still get collected) diagnosticFilters: ['**/*'], logLevel: 'error', diff --git a/docs/union-types.md b/docs/union-types.md index f6d9aa9cb..3452b670f 100644 --- a/docs/union-types.md +++ b/docs/union-types.md @@ -41,7 +41,7 @@ end function ## Validation -When the `enableTypeValidation` option flag is enabled, a diagnostic error will be raised when a member is accessed that is not a member of ALL of the types of a union. +A diagnostic error will be raised when a member is accessed that is not a member of ALL of the types of a union. In the example above, accessing `response.status` is fine, because it is in all of the types of the union. Accessing `response.data` would raise an error, because it's not in the `Error` interface. diff --git a/src/BsConfig.ts b/src/BsConfig.ts index 940bf1bbc..9fe4389bf 100644 --- a/src/BsConfig.ts +++ b/src/BsConfig.ts @@ -169,10 +169,4 @@ export interface BsConfig { * @default false */ allowBrighterScriptInBrightScript?: boolean; - - /** - * Do full type checking & validation based on declared and inferred types. - * @default false - */ - enableTypeValidation?: boolean; } diff --git a/src/Scope.spec.ts b/src/Scope.spec.ts index a705aea9d..e08130942 100644 --- a/src/Scope.spec.ts +++ b/src/Scope.spec.ts @@ -401,7 +401,7 @@ describe('Scope', () => { `); program.validate(); expectDiagnostics(program, [{ - ...DiagnosticMessages.itemCannotBeUsedAsVariable('namespace'), + ...DiagnosticMessages.cannotFindName('Charlie'), range: util.createRange(6, 32, 6, 39) }, { ...DiagnosticMessages.itemCannotBeUsedAsVariable('namespace'), @@ -446,7 +446,7 @@ describe('Scope', () => { program.validate(); expectDiagnostics(program, [{ //print |lineHeight| - message: DiagnosticMessages.itemCannotBeUsedAsVariable('namespace').message, + message: DiagnosticMessages.cannotFindName('lineHeight').message, range: util.createRange(3, 30, 3, 40) }]); }); @@ -818,7 +818,7 @@ describe('Scope', () => { sayMyName = function(name as string) end function - sayMyName() + sayMyName("John Doe") end sub` ); program.validate(); @@ -1571,14 +1571,10 @@ describe('Scope', () => { expectZeroDiagnostics(program); }); }); - describe('enhanced typing', () => { - beforeEach(() => { - program.options.enableTypeValidation = true; - }); - describe('runtime vs typetime', () => { - it('detects invalidly using a class member as a parameter type', () => { - program.setFile(`source/main.bs`, ` + describe('runtime vs typetime', () => { + it('detects invalidly using a class member as a parameter type', () => { + program.setFile(`source/main.bs`, ` sub a(num as myClass.member) end sub @@ -1587,14 +1583,14 @@ describe('Scope', () => { end class `); - program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.itemCannotBeUsedAsType('myClass.member').message - ]); - }); + program.validate(); + expectDiagnostics(program, [ + DiagnosticMessages.itemCannotBeUsedAsType('myClass.member').message + ]); + }); - it('detects invalidly using an EnumMember as a parameter type', () => { - program.setFile(`source/main.bs`, ` + it('detects invalidly using an EnumMember as a parameter type', () => { + program.setFile(`source/main.bs`, ` sub a(num as MyNameSpace.SomeEnum.memberA) end sub @@ -1605,14 +1601,14 @@ describe('Scope', () => { end enum end namespace `); - program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.itemCannotBeUsedAsType('MyNameSpace.SomeEnum.memberA').message - ]); - }); + program.validate(); + expectDiagnostics(program, [ + DiagnosticMessages.itemCannotBeUsedAsType('MyNameSpace.SomeEnum.memberA').message + ]); + }); - it('detects a member of a nested namespace', () => { - program.setFile(`source/main.bs`, ` + it('detects a member of a nested namespace', () => { + program.setFile(`source/main.bs`, ` sub a(num as NSExistsA.NSExistsB.Klass) end sub @@ -1621,12 +1617,12 @@ describe('Scope', () => { end class end namespace `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('detects an unknown member of a nested namespace', () => { - program.setFile(`source/main.bs`, ` + it('detects an unknown member of a nested namespace', () => { + program.setFile(`source/main.bs`, ` sub a(num as NSExistsA.NSExistsB.NSDoesNotExistC.Klass) end sub @@ -1635,15 +1631,15 @@ describe('Scope', () => { end class end namespace `); - program.validate(); + program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.cannotFindName('NSDoesNotExistC', 'NSExistsA.NSExistsB.NSDoesNotExistC').message - ]); - }); + expectDiagnostics(program, [ + DiagnosticMessages.cannotFindName('NSDoesNotExistC', 'NSExistsA.NSExistsB.NSDoesNotExistC').message + ]); + }); - it('allows a class to extend from a class in another namespace and file', () => { - program.setFile(`source/main.bs`, ` + it('allows a class to extend from a class in another namespace and file', () => { + program.setFile(`source/main.bs`, ` sub fn(myFace as Villain) print myFace.coin end sub @@ -1652,20 +1648,20 @@ describe('Scope', () => { name as string end class `); - program.setFile(`source/extra.bs`, ` + program.setFile(`source/extra.bs`, ` namespace MyKlasses class twoFace coin as string end class end namespace `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('resolves a const in a namespace', () => { - program.setFile(`source/main.bs`, ` + it('resolves a const in a namespace', () => { + program.setFile(`source/main.bs`, ` sub a() print NSExistsA.SOME_CONST end sub @@ -1674,12 +1670,12 @@ describe('Scope', () => { const SOME_CONST = 3.14 end namespace `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('resolves namespaces with relative references', () => { - program.setFile(`source/main.bs`, ` + it('resolves namespaces with relative references', () => { + program.setFile(`source/main.bs`, ` namespace NameA sub fn1() 'fully qualified-relative references are allowed @@ -1691,12 +1687,12 @@ describe('Scope', () => { const API_URL = "http://some.url.com" end namespace `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('resolves nested namespaces with relative references', () => { - program.setFile(`source/main.bs`, ` + it('resolves nested namespaces with relative references', () => { + program.setFile(`source/main.bs`, ` sub main() print NameA.A_VAL print NameA.NameB.B_VAL @@ -1726,12 +1722,12 @@ describe('Scope', () => { end namespace const SOME_CONST = "hello" `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('resolves namespaces defined in different locations', () => { - program.setFile(`source/main.bs`, ` + it('resolves namespaces defined in different locations', () => { + program.setFile(`source/main.bs`, ` sub main() print NameA.A_VAL print NameA.funcA() @@ -1756,12 +1752,12 @@ describe('Scope', () => { end class end namespace `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('resolves deep namespaces defined in different locations', () => { - program.setFile(`source/main.bs`, ` + it('resolves deep namespaces defined in different locations', () => { + program.setFile(`source/main.bs`, ` sub main() print NameA.NameB.B_VAL print NameA.NameB.funcB() @@ -1796,32 +1792,32 @@ describe('Scope', () => { end class end namespace `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('allows dot-references to properties on results of global callables', () => { - program.setFile(`source/main.bs`, ` + it('allows dot-references to properties on results of global callables', () => { + program.setFile(`source/main.bs`, ` sub fn() print CreateObject("roSgNode", "Node").id end sub `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('allows dot-references to functions on results of global callables', () => { - program.setFile(`source/main.bs`, ` + it('allows dot-references to functions on results of global callables', () => { + program.setFile(`source/main.bs`, ` sub fn() print CreateObject("roDateTime").asSeconds() end sub `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('finds unknown members of primitive types', () => { - program.setFile(`source/main.bs`, ` + it('finds unknown members of primitive types', () => { + program.setFile(`source/main.bs`, ` sub fn(input as SomeKlass) piValue = input.getPi().noMethod() end sub @@ -1832,15 +1828,15 @@ describe('Scope', () => { end function end class `); - program.validate(); - //TODO: ideally, if this is a primitive type, we should know all the possible members - // This *SHOULD* be an error, but currently, during Runtime, an unknown member (from DottedtGetExpression) is returned as Dynamic.instance - expectZeroDiagnostics(program); - }); + program.validate(); + //TODO: ideally, if this is a primitive type, we should know all the possible members + // This *SHOULD* be an error, but currently, during Runtime, an unknown member (from DottedtGetExpression) is returned as Dynamic.instance + expectZeroDiagnostics(program); + }); - it('finds members of arrays', () => { - program.setFile(`source/main.bs`, ` + it('finds members of arrays', () => { + program.setFile(`source/main.bs`, ` sub fn(input as SomeKlass) numValue = input.getOtherKlasses()[2].num print numValue @@ -1860,16 +1856,16 @@ describe('Scope', () => { end function end class `); - program.validate(); - //TODO: When array types are available, check that `numValue` is an integer - expectZeroDiagnostics(program); - }); - + program.validate(); + //TODO: When array types are available, check that `numValue` is an integer + expectZeroDiagnostics(program); }); - describe('interfaces', () => { - it('allows using interfaces as types', () => { - program.setFile(`source/main.bs`, ` + }); + + describe('interfaces', () => { + it('allows using interfaces as types', () => { + program.setFile(`source/main.bs`, ` sub fn(myFace as iFace) end sub @@ -1877,12 +1873,12 @@ describe('Scope', () => { name as string end interface `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('disallows using interface members as types', () => { - program.setFile(`source/main.bs`, ` + it('disallows using interface members as types', () => { + program.setFile(`source/main.bs`, ` sub fn(myFaceName as iFace.name) end sub @@ -1890,14 +1886,14 @@ describe('Scope', () => { name as string end interface `); - program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.itemCannotBeUsedAsType('iFace.name').message - ]); - }); + program.validate(); + expectDiagnostics(program, [ + DiagnosticMessages.itemCannotBeUsedAsType('iFace.name').message + ]); + }); - it('allows accessing interface members in code', () => { - program.setFile(`source/main.bs`, ` + it('allows accessing interface members in code', () => { + program.setFile(`source/main.bs`, ` sub fn(myFace as iFace) print myFace.name end sub @@ -1906,12 +1902,12 @@ describe('Scope', () => { name as string end interface `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('allows accessing an interface member from a super interface', () => { - program.setFile(`source/main.bs`, ` + it('allows accessing an interface member from a super interface', () => { + program.setFile(`source/main.bs`, ` sub fn(myFace as iFace) print myFace.coin end sub @@ -1924,12 +1920,12 @@ describe('Scope', () => { coin as string end interface `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('allows an interface to extend from an interface in another namespace and file', () => { - program.setFile(`source/main.bs`, ` + it('allows an interface to extend from an interface in another namespace and file', () => { + program.setFile(`source/main.bs`, ` sub fn(myFace as iFace) print myFace.coin end sub @@ -1938,22 +1934,22 @@ describe('Scope', () => { name as string end interface `); - program.setFile(`source/interfaces.bs`, ` + program.setFile(`source/interfaces.bs`, ` namespace MyInterfaces interface twoFace coin as string end interface end namespace `); - program.validate(); - expectZeroDiagnostics(program); - }); - + program.validate(); + expectZeroDiagnostics(program); }); + }); - it('should accept global callables returning objects', () => { - program.setFile(`source/main.brs`, ` + + it('should accept global callables returning objects', () => { + program.setFile(`source/main.brs`, ` sub main() screen = CreateObject("roSGScreen") port = CreateObject("roMessagePort") @@ -1976,16 +1972,16 @@ describe('Scope', () => { end while end sub `); - program.setFile('components/MyMainScene.xml', trim` + program.setFile('components/MyMainScene.xml', trim` `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); }); + describe('inheritance', () => { it('inherits callables from parent', () => { program = new Program({ rootDir: rootDir }); @@ -2107,9 +2103,7 @@ describe('Scope', () => { }); describe('symbolTable lookups with enhanced typing', () => { - beforeEach(() => { - program.options.enableTypeValidation = true; - }); + const mainFileContents = ` sub main() population = Animals.getPopulation() diff --git a/src/Scope.ts b/src/Scope.ts index 454b5bd85..5b7aeff68 100644 --- a/src/Scope.ts +++ b/src/Scope.ts @@ -752,10 +752,6 @@ export class Scope { //do many per-file checks this.enumerateBrsFiles((file) => { - if (!this.program.options.enableTypeValidation) { - //TODO: this is replaced by ScopeValidator.validateFunctionCalls() when enableTypeValidation is true - this.diagnosticDetectFunctionCallsWithWrongParamCount(file, callableContainerMap); - } this.diagnosticDetectShadowedLocalVars(file, callableContainerMap); this.diagnosticDetectFunctionCollisions(file); this.detectVariableNamespaceCollisions(file); @@ -964,43 +960,6 @@ export class Scope { this.diagnostics.push(...validator.diagnostics); } - /** - * Detect calls to functions with the incorrect number of parameters - */ - private diagnosticDetectFunctionCallsWithWrongParamCount(file: BscFile, callableContainersByLowerName: CallableContainerMap) { - //validate all function calls - for (let expCall of file.functionCalls) { - let callableContainersWithThisName = callableContainersByLowerName.get(expCall.name.toLowerCase()); - - //use the first item from callablesByLowerName, because if there are more, that's a separate error - let knownCallableContainer = callableContainersWithThisName ? callableContainersWithThisName[0] : undefined; - - if (knownCallableContainer) { - //get min/max parameter count for callable - let minParams = 0; - let maxParams = 0; - for (let param of knownCallableContainer.callable.params) { - maxParams++; - //optional parameters must come last, so we can assume that minParams won't increase once we hit - //the first isOptional - if (param.isOptional !== true) { - minParams++; - } - } - let expCallArgCount = expCall.args.length; - if (expCall.args.length > maxParams || expCall.args.length < minParams) { - let minMaxParamsText = minParams === maxParams ? maxParams : `${minParams}-${maxParams}`; - this.diagnostics.push({ - ...DiagnosticMessages.mismatchArgumentCount(minMaxParamsText, expCallArgCount), - range: expCall.nameRange, - //TODO detect end of expression call - file: file - }); - } - } - } - } - /** * Detect local variables (function scope) that have the same name as scope calls */ diff --git a/src/bscPlugin/hover/HoverProcessor.spec.ts b/src/bscPlugin/hover/HoverProcessor.spec.ts index 66861bd34..b0913df1c 100644 --- a/src/bscPlugin/hover/HoverProcessor.spec.ts +++ b/src/bscPlugin/hover/HoverProcessor.spec.ts @@ -81,304 +81,6 @@ describe('HoverProcessor', () => { let hover = program.getHover(file.srcPath, util.createPosition(1, 28))[0]; expect(hover).to.exist; - expect(hover.range).to.eql(util.createRange(1, 25, 1, 29)); - expect(hover.contents).to.eql(fence('function Main(count? as integer) as dynamic')); - }); - - it('finds variable function hover in same scope', () => { - let file = program.setFile('source/main.brs', ` - sub Main() - sayMyName = sub(name as string) - end sub - - sayMyName() - end sub - `); - program.validate(); - let hover = program.getHover(file.srcPath, util.createPosition(5, 24))[0]; - - expect(hover.range).to.eql(util.createRange(5, 20, 5, 29)); - expect(hover.contents).to.eql(fence('sub sayMyName(name as string) as void')); - }); - - it('finds function hover in file scope', () => { - let file = program.setFile('source/main.brs', ` - sub Main() - sayMyName() - end sub - - sub sayMyName() - - end sub - `); - - let hover = program.getHover(file.srcPath, util.createPosition(2, 25))[0]; - - expect(hover.range).to.eql(util.createRange(2, 20, 2, 29)); - expect(hover.contents).to.eql(fence('sub sayMyName() as void')); - }); - - it('finds function hover in scope', () => { - let rootDir = process.cwd(); - program = new Program({ - rootDir: rootDir - }); - - let mainFile = program.setFile('source/main.brs', ` - sub Main() - sayMyName() - end sub - `); - - program.setFile('source/lib.brs', ` - sub sayMyName(name as string) - - end sub - `); - program.validate(); - - let hover = program.getHover(mainFile.srcPath, util.createPosition(2, 25))[0]; - expect(hover?.range).to.eql(util.createRange(2, 20, 2, 29)); - expect(hover?.contents).to.eql(fence('sub sayMyName(name as string) as void')); - }); - - it('finds top-level constant value', () => { - program.setFile('source/main.bs', ` - sub main() - print SOME_VALUE - end sub - const SOME_VALUE = true - `); - program.validate(); - // print SOM|E_VALUE - let hover = program.getHover('source/main.bs', util.createPosition(2, 29))[0]; - expect(hover?.range).to.eql(util.createRange(2, 26, 2, 36)); - expect(hover?.contents).to.eql(fence('const SOME_VALUE = true')); - }); - - it('finds top-level constant in assignment expression', () => { - program.setFile('source/main.bs', ` - sub main() - value = "" - value += SOME_VALUE - end sub - const SOME_VALUE = "value" - `); - program.validate(); - // value += SOME|_VALUE - let hover = program.getHover('source/main.bs', util.createPosition(3, 33))[0]; - expect(hover?.range).to.eql(util.createRange(3, 29, 3, 39)); - expect(hover?.contents).to.eql(fence('const SOME_VALUE = "value"')); - }); - - it('finds namespaced constant in assignment expression', () => { - program.setFile('source/main.bs', ` - sub main() - value = "" - value += someNamespace.SOME_VALUE - end sub - namespace someNamespace - const SOME_VALUE = "value" - end namespace - `); - program.validate(); - // value += SOME|_VALUE - let hover = program.getHover('source/main.bs', util.createPosition(3, 47))[0]; - expect(hover?.range).to.eql(util.createRange(3, 43, 3, 53)); - expect(hover?.contents).to.eql(fence('const someNamespace.SOME_VALUE = "value"')); - }); - - it('finds namespaced constant value', () => { - program.setFile('source/main.bs', ` - sub main() - print name.SOME_VALUE - end sub - namespace name - const SOME_VALUE = true - end namespace - `); - program.validate(); - // print name.SOM|E_VALUE - let hover = program.getHover('source/main.bs', util.createPosition(2, 34))[0]; - expect(hover?.range).to.eql(util.createRange(2, 31, 2, 41)); - expect(hover?.contents).to.eql(fence('const name.SOME_VALUE = true')); - }); - - it('finds deep namespaced constant value', () => { - program.setFile('source/main.bs', ` - sub main() - print name.sp.a.c.e.SOME_VALUE - end sub - namespace name.sp.a.c.e - const SOME_VALUE = true - end namespace - `); - program.validate(); - // print name.sp.a.c.e.SOM|E_VALUE - let hover = program.getHover('source/main.bs', util.createPosition(2, 43))[0]; - expect(hover?.range).to.eql(util.createRange(2, 40, 2, 50)); - expect(hover?.contents).to.eql(fence('const name.sp.a.c.e.SOME_VALUE = true')); - }); - - it('finds namespaced class types', () => { - program.setFile('source/main.bs', ` - sub main() - myKlass = new name.Klass() - runNoop(myKlass) - end sub - - sub runNoop(myKlass as name.Klass) - myKlass.noop() - end sub - - namespace name - class Klass - sub noop() - end sub - end class - end namespace - `); - program.validate(); - // run|Noop(myKlass) - let hover = program.getHover('source/main.bs', util.createPosition(3, 24))[0]; - expect(hover?.range).to.eql(util.createRange(3, 20, 3, 27)); - expect(hover?.contents).to.eql(fence('sub runNoop(myKlass as name.Klass) as void')); - // myKl|ass.noop() - hover = program.getHover('source/main.bs', util.createPosition(7, 25))[0]; - expect(hover?.range).to.eql(util.createRange(7, 20, 7, 27)); - expect(hover?.contents).to.eql(fence('myKlass as name.Klass')); - // sub no|op() - hover = program.getHover('source/main.bs', util.createPosition(12, 31))[0]; - // Unfortunately, we can't get hover details on class members yet - // TODO: Add hover ability on class members - expect(hover).to.be.undefined; - }); - - it('finds types properly', () => { - program.setFile('source/main.bs', ` - class Person - end class - - sub doWork(age as integer, name as string, guy as Person) - end sub - `); - program.validate(); - // a|ge as integer - let hover = program.getHover('source/main.bs', util.createPosition(4, 29))[0]; - expect(hover?.range).to.eql(util.createRange(4, 27, 4, 30)); - expect(hover?.contents).to.eql(fence('age as integer')); - // age as int|eger - hover = program.getHover('source/main.bs', util.createPosition(4, 39))[0]; - // no hover on base types - expect(hover).to.be.undefined; - // n|ame as string - hover = program.getHover('source/main.bs', util.createPosition(4, 46))[0]; - expect(hover?.range).to.eql(util.createRange(4, 43, 4, 47)); - expect(hover?.contents).to.eql(fence('name as string')); - // name as st|ring - hover = program.getHover('source/main.bs', util.createPosition(4, 54))[0]; - // no hover on base types - expect(hover).to.be.undefined; - // gu|y as Person - hover = program.getHover('source/main.bs', util.createPosition(4, 60))[0]; - expect(hover?.range).to.eql(util.createRange(4, 59, 4, 62)); - expect(hover?.contents).to.eql(fence('guy as Person')); - // guy as Pe|rson - hover = program.getHover('source/main.bs', util.createPosition(4, 69))[0]; - //TODO: Add hover on custom types (classes, interfaces, enums, etc.) - expect(hover).to.be.undefined; - }); - - - }); - - describe('BrsFile - enableTypeValidation', () => { - beforeEach(() => { - program.options.enableTypeValidation = true; - }); - - it('finds types from assignments defined in different file', () => { - program.setFile(`source/main.bs`, ` - sub main() - thing = new MyKlass() - useKlass(thing) - someVal = getValue() - print someVal - end sub - - sub useKlass(thing as MyKlass) - print thing - end sub - `); - program.setFile(`source/MyKlass.bs`, ` - class MyKlass - end class - `); - - program.setFile(`source/util.bs`, ` - function getValue() as string - return "hello" - end function - `); - program.validate(); - //th|ing = new MyKlass() - let hover = program.getHover('source/main.bs', util.createPosition(2, 24))[0]; - expect(hover?.range).to.eql(util.createRange(2, 20, 2, 25)); - expect(hover?.contents).to.eql([fence('thing as MyKlass')]); - //print some|Val - hover = program.getHover('source/main.bs', util.createPosition(5, 31))[0]; - expect(hover?.range).to.eql(util.createRange(5, 26, 5, 33)); - expect(hover?.contents).to.eql([fence('someVal as string')]); - }); - - it('works for param types', () => { - const file = program.setFile('source/main.brs', ` - sub DoSomething(name as string) - name = 1 - sayMyName = function(name as string) - end function - end sub - `); - program.validate(); - //hover over the `name = 1` line - let hover = program.getHover(file.srcPath, util.createPosition(2, 24))[0]; - expect(hover).to.exist; - expect(hover.range).to.eql(util.createRange(2, 20, 2, 24)); - - //hover over the `name` parameter declaration - hover = program.getHover(file.srcPath, util.createPosition(1, 34))[0]; - expect(hover).to.exist; - expect(hover.range).to.eql(util.createRange(1, 32, 1, 36)); - }); - - //ignore this for now...it's not a huge deal - it('does not match on keywords or data types', () => { - let file = program.setFile('source/main.brs', ` - sub Main(name as string) - end sub - sub as() - end sub - `); - program.validate(); - //hover over the `as` - expect(program.getHover(file.srcPath, util.createPosition(1, 31))).to.be.empty; - //hover over the `string` - expect(program.getHover(file.srcPath, util.createPosition(1, 36))).to.be.empty; - }); - - it('finds declared function', () => { - let file = program.setFile('source/main.brs', ` - function Main(count = 1) - firstName = "bob" - age = 21 - shoeSize = 10 - end function - `); - program.validate(); - - let hover = program.getHover(file.srcPath, util.createPosition(1, 28))[0]; - expect(hover).to.exist; - expect(hover.range).to.eql(util.createRange(1, 25, 1, 29)); expect(hover.contents).to.eql([fence('function Main(count? as integer) as dynamic')]); }); @@ -406,10 +108,10 @@ describe('HoverProcessor', () => { end sub sub sayMyName() - end sub `); program.validate(); + //sayM|yName() let hover = program.getHover(file.srcPath, util.createPosition(2, 25))[0]; expect(hover.range).to.eql(util.createRange(2, 20, 2, 29)); @@ -431,8 +133,8 @@ describe('HoverProcessor', () => { program.validate(); let hover = program.getHover(mainFile.srcPath, util.createPosition(2, 25))[0]; - expect(hover?.range).to.eql(util.createRange(2, 20, 2, 29)); - expect(hover?.contents).to.eql([fence('sub sayMyName(name as string) as void')]); + expect(hover.range).to.eql(util.createRange(2, 20, 2, 29)); + expect(hover.contents).to.eql([fence('sub sayMyName(name as string) as void')]); }); it('finds top-level constant value', () => { @@ -590,12 +292,10 @@ describe('HoverProcessor', () => { sub useKlass(thing as MyKlass) print thing - print thing.myNumber end sub `); program.setFile(`source/MyKlass.bs`, ` class MyKlass - myNumber as integer end class `); @@ -605,25 +305,14 @@ describe('HoverProcessor', () => { end function `); program.validate(); + //th|ing = new MyKlass() let hover = program.getHover('source/main.bs', util.createPosition(2, 24))[0]; expect(hover?.range).to.eql(util.createRange(2, 20, 2, 25)); expect(hover?.contents).to.eql([fence('thing as MyKlass')]); - //thing = new MyK|lass() - hover = program.getHover('source/main.bs', util.createPosition(2, 35))[0]; - expect(hover?.range).to.eql(util.createRange(2, 32, 2, 39)); - expect(hover?.contents).to.eql([fence('class MyKlass')]); //print some|Val hover = program.getHover('source/main.bs', util.createPosition(5, 31))[0]; expect(hover?.range).to.eql(util.createRange(5, 26, 5, 33)); expect(hover?.contents).to.eql([fence('someVal as string')]); - //print thi|ng - hover = program.getHover('source/main.bs', util.createPosition(9, 29))[0]; - expect(hover?.range).to.eql(util.createRange(9, 26, 9, 31)); - expect(hover?.contents).to.eql([fence('thing as MyKlass')]); - //print thing.myI|nteger - hover = program.getHover('source/main.bs', util.createPosition(10, 34))[0]; - expect(hover?.range).to.eql(util.createRange(10, 32, 10, 40)); - expect(hover?.contents).to.eql([fence('MyKlass.myNumber as integer')]); }); it('hovers of functions include comments', () => { diff --git a/src/bscPlugin/hover/HoverProcessor.ts b/src/bscPlugin/hover/HoverProcessor.ts index 4130224eb..485fcc454 100644 --- a/src/bscPlugin/hover/HoverProcessor.ts +++ b/src/bscPlugin/hover/HoverProcessor.ts @@ -29,9 +29,7 @@ export class HoverProcessor { public process() { let hover: Hover; if (isBrsFile(this.event.file)) { - hover = this.event.program.options.enableTypeValidation - ? this.getTypedBrsFileHover(this.event.file) - : this.getBrsFileHover(this.event.file); + hover = this.getBrsFileHover(this.event.file); } else if (isXmlFile(this.event.file)) { hover = this.getXmlFileHover(this.event.file); } @@ -86,84 +84,6 @@ export class HoverProcessor { } } - private getBrsFileHover(file: BrsFile): Hover { - const scope = this.event.scopes[0]; - try { - scope.linkSymbolTable(); - - //get the token at the position - let token = file.getTokenAt(this.event.position); - - if (!this.isValidTokenForHover(token)) { - return null; - } - - const expression = file.getClosestExpression(this.event.position); - if (expression) { - const constHover = this.getConstHover(token, file, scope, expression); - if (constHover) { - return { - contents: constHover, - range: token.range - }; - } - } - - let lowerTokenText = token.text.toLowerCase(); - - //look through local variables first - { - //get the function scope for this position (if exists) - let functionScope = file.getFunctionScopeAtPosition(this.event.position); - if (functionScope) { - //find any variable with this name - for (const varDeclaration of functionScope.variableDeclarations) { - //we found a variable declaration with this token text! - if (varDeclaration.name.toLowerCase() === lowerTokenText) { - let typeText: string; - const varDeclarationType = varDeclaration.getType(); - if (isFunctionType(varDeclarationType)) { - varDeclarationType.setName(varDeclaration.name); - typeText = varDeclarationType.toString(); - } else { - typeText = `${varDeclaration.name} as ${varDeclarationType.toString()}`; - } - return { - range: token.range, - //append the variable name to the front for scope - contents: fence(typeText) - }; - } - } - const labelHover = this.getLabelHover(token, functionScope); - if (labelHover) { - return { - range: token.range, - contents: labelHover - }; - } - } - } - - //look through all callables in relevant scopes - if (!expression?.findAncestor(isTypeExpression)) { - // only look for callables when they aren't inside a type expression - // this was a problem for the function `string()` as it is a type AND a function https://developer.roku.com/en-ca/docs/references/brightscript/language/global-string-functions.md#stringn-as-integer-str-as-string--as-string - for (let scope of this.event.scopes) { - let callable = scope.getCallableByName(lowerTokenText); - if (callable) { - return { - range: token.range, - contents: this.buildContentsWithDocs(fence(callable.type.toString()), callable.functionStatement?.func?.functionType) - }; - } - } - } - } finally { - scope?.unlinkSymbolTable(); - } - } - private getFunctionTypeHover(token: Token, expression: Expression, expressionType: FunctionType, scope: Scope) { const lowerTokenText = token.text.toLowerCase(); let result = fence(expressionType.toString()); @@ -202,7 +122,7 @@ export class HoverProcessor { return result; } - private getTypedBrsFileHover(file: BrsFile): Hover { + private getBrsFileHover(file: BrsFile): Hover { //get the token at the position let token = file.getTokenAt(this.event.position); diff --git a/src/bscPlugin/validation/BrsFileValidator.ts b/src/bscPlugin/validation/BrsFileValidator.ts index 18be4ac6d..90d3988d9 100644 --- a/src/bscPlugin/validation/BrsFileValidator.ts +++ b/src/bscPlugin/validation/BrsFileValidator.ts @@ -2,14 +2,13 @@ import { isBody, isClassStatement, isCommentStatement, isConstStatement, isDotte import { createVisitor, WalkMode } from '../../astUtils/visitors'; import { DiagnosticMessages } from '../../DiagnosticMessages'; import type { BrsFile } from '../../files/BrsFile'; -import type { GetTypeOptions, OnFileValidateEvent } from '../../interfaces'; +import type { OnFileValidateEvent } from '../../interfaces'; import { TokenKind } from '../../lexer/TokenKind'; import type { AstNode, Expression, Statement } from '../../parser/AstNode'; import type { LiteralExpression } from '../../parser/Expression'; import { ParseMode } from '../../parser/Parser'; import type { ContinueStatement, EnumMemberStatement, EnumStatement, ForEachStatement, ForStatement, ImportStatement, LibraryStatement, WhileStatement } from '../../parser/Statement'; import { SymbolTypeFlag } from '../../SymbolTable'; -import type { BscType } from '../../types/BscType'; import { DynamicType } from '../../types/DynamicType'; import util from '../../util'; import type { Range } from 'vscode-languageserver'; @@ -30,17 +29,6 @@ export class BrsFileValidator { } } - /** - * Wrapper for getting the type from an expression, so we can use a program option to just return a default Dynamic type - */ - private getTypeFromNode(node: AstNode, options?: GetTypeOptions): BscType { - if (this.event.program.options.enableTypeValidation) { - return node.getType(options); - } - return DynamicType.instance; - } - - /** * Walk the full AST */ @@ -48,14 +36,10 @@ export class BrsFileValidator { const visitor = createVisitor({ MethodStatement: (node) => { //add the `super` symbol to class methods - let superType: BscType = DynamicType.instance; if (isClassStatement(node.parent) && node.parent.hasParentClass()) { - if (this.event.program.options.enableTypeValidation) { - const parentClassType = node.parent.parentClassName.getType({ flags: SymbolTypeFlag.typetime }); - const methodName = node.getName(ParseMode.BrighterScript); - superType = parentClassType.getMemberType(methodName, { flags: SymbolTypeFlag.runtime }); - } - node.func.body.symbolTable.addSymbol('super', undefined, superType, SymbolTypeFlag.runtime); + //Todo: get the actual type of the parent class + // Maybe? const parentClassType = node.parent.parentClassName.getType({ flags: SymbolTypeFlag.typetime }); + node.func.body.symbolTable.addSymbol('super', undefined, DynamicType.instance, SymbolTypeFlag.runtime); } }, CallfuncExpression: (node) => { @@ -72,7 +56,7 @@ export class BrsFileValidator { this.validateEnumDeclaration(node); //register this enum declaration - const nodeType = this.getTypeFromNode(node, { flags: SymbolTypeFlag.typetime }); + const nodeType = node.getType({ flags: SymbolTypeFlag.typetime }); // eslint-disable-next-line no-bitwise node.parent.getSymbolTable()?.addSymbol(node.tokens.name.text, node.tokens.name.range, nodeType, SymbolTypeFlag.typetime | SymbolTypeFlag.runtime); }, @@ -80,13 +64,13 @@ export class BrsFileValidator { this.validateDeclarationLocations(node, 'class', () => util.createBoundingRange(node.classKeyword, node.name)); //register this class - const nodeType = this.getTypeFromNode(node, { flags: SymbolTypeFlag.typetime }); + const nodeType = node.getType({ flags: SymbolTypeFlag.typetime }); // eslint-disable-next-line no-bitwise node.parent.getSymbolTable()?.addSymbol(node.name.text, node.name.range, nodeType, SymbolTypeFlag.typetime | SymbolTypeFlag.runtime); }, AssignmentStatement: (node) => { //register this variable - const nodeType = this.getTypeFromNode(node, { flags: SymbolTypeFlag.runtime }); + const nodeType = node.getType({ flags: SymbolTypeFlag.runtime }); node.parent.getSymbolTable()?.addSymbol(node.name.text, node.name.range, nodeType, SymbolTypeFlag.runtime); }, DottedSetStatement: (node) => { @@ -105,7 +89,7 @@ export class BrsFileValidator { }, FunctionStatement: (node) => { this.validateDeclarationLocations(node, 'function', () => util.createBoundingRange(node.func.functionType, node.name)); - const funcType = this.getTypeFromNode(node, { flags: SymbolTypeFlag.typetime }); + const funcType = node.getType({ flags: SymbolTypeFlag.typetime }); if (node.name?.text) { node.parent.getSymbolTable().addSymbol( @@ -144,19 +128,19 @@ export class BrsFileValidator { FunctionParameterExpression: (node) => { const paramName = node.name?.text; const symbolTable = node.getSymbolTable(); - const nodeType = this.getTypeFromNode(node, { flags: SymbolTypeFlag.typetime }); + const nodeType = node.getType({ flags: SymbolTypeFlag.typetime }); symbolTable?.addSymbol(paramName, node.name.range, nodeType, SymbolTypeFlag.runtime); }, InterfaceStatement: (node) => { this.validateDeclarationLocations(node, 'interface', () => util.createBoundingRange(node.tokens.interface, node.tokens.name)); - const nodeType = this.getTypeFromNode(node, { flags: SymbolTypeFlag.typetime }); + const nodeType = node.getType({ flags: SymbolTypeFlag.typetime }); // eslint-disable-next-line no-bitwise node.parent.getSymbolTable().addSymbol(node.tokens.name.text, node.tokens.name.range, nodeType, SymbolTypeFlag.runtime | SymbolTypeFlag.typetime); }, ConstStatement: (node) => { this.validateDeclarationLocations(node, 'const', () => util.createBoundingRange(node.tokens.const, node.tokens.name)); - const nodeType = this.getTypeFromNode(node, { flags: SymbolTypeFlag.runtime }); + const nodeType = node.getType({ flags: SymbolTypeFlag.runtime }); node.parent.getSymbolTable().addSymbol(node.tokens.name.text, node.tokens.name.range, nodeType, SymbolTypeFlag.runtime); }, CatchStatement: (node) => { diff --git a/src/bscPlugin/validation/ScopeValidator.spec.ts b/src/bscPlugin/validation/ScopeValidator.spec.ts index d622e78a1..777693978 100644 --- a/src/bscPlugin/validation/ScopeValidator.spec.ts +++ b/src/bscPlugin/validation/ScopeValidator.spec.ts @@ -20,29 +20,23 @@ describe('ScopeValidator', () => { program.dispose(); }); - describe('function call validation with enableTypeValidation', () => { - - beforeEach(() => { - program.options.enableTypeValidation = true; - }); - - describe('mismatchArgumentCount', () => { - it('detects calling functions with too many arguments', () => { - program.setFile('source/file.brs', ` + describe('mismatchArgumentCount', () => { + it('detects calling functions with too many arguments', () => { + program.setFile('source/file.brs', ` sub a() end sub sub b() a(1) end sub `); - program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.mismatchArgumentCount(0, 1).message - ]); - }); - - it('detects calling class constructors with too many arguments', () => { - program.setFile('source/main.bs', ` + program.validate(); + expectDiagnostics(program, [ + DiagnosticMessages.mismatchArgumentCount(0, 1).message + ]); + }); + + it('detects calling class constructors with too many arguments', () => { + program.setFile('source/main.bs', ` function noop0() end function @@ -55,121 +49,121 @@ describe('ScopeValidator', () => { noop1() end sub `); - program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.mismatchArgumentCount(0, 1), - DiagnosticMessages.mismatchArgumentCount(1, 2), - DiagnosticMessages.mismatchArgumentCount(1, 0) - ]); - }); - - it('detects calling functions with too few arguments', () => { - program.setFile('source/file.brs', ` + program.validate(); + expectDiagnostics(program, [ + DiagnosticMessages.mismatchArgumentCount(0, 1), + DiagnosticMessages.mismatchArgumentCount(1, 2), + DiagnosticMessages.mismatchArgumentCount(1, 0) + ]); + }); + + it('detects calling functions with too few arguments', () => { + program.setFile('source/file.brs', ` sub a(name) end sub sub b() a() end sub `); - program.validate(); - expectDiagnostics(program, [ - DiagnosticMessages.mismatchArgumentCount(1, 0) - ]); - }); - - it('allows skipping optional parameter', () => { - program.setFile('source/file.brs', ` + program.validate(); + expectDiagnostics(program, [ + DiagnosticMessages.mismatchArgumentCount(1, 0) + ]); + }); + + it('allows skipping optional parameter', () => { + program.setFile('source/file.brs', ` sub a(name="Bob") end sub sub b() a() end sub `); - program.validate(); - //should have an error - expectZeroDiagnostics(program); - }); + program.validate(); + //should have an error + expectZeroDiagnostics(program); + }); - it('shows expected parameter range in error message', () => { - program.setFile('source/file.brs', ` + it('shows expected parameter range in error message', () => { + program.setFile('source/file.brs', ` sub a(age, name="Bob") end sub sub b() a() end sub `); - program.validate(); - //should have an error - expectDiagnostics(program, [ - DiagnosticMessages.mismatchArgumentCount('1-2', 0) - ]); - }); - - it('handles expressions as arguments to a function', () => { - program.setFile('source/file.brs', ` + program.validate(); + //should have an error + expectDiagnostics(program, [ + DiagnosticMessages.mismatchArgumentCount('1-2', 0) + ]); + }); + + it('handles expressions as arguments to a function', () => { + program.setFile('source/file.brs', ` sub a(age, name="Bob") end sub sub b() a("cat" + "dog" + "mouse") end sub `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('Catches extra arguments for expressions as arguments to a function', () => { - program.setFile('source/file.brs', ` + it('Catches extra arguments for expressions as arguments to a function', () => { + program.setFile('source/file.brs', ` sub a(age) end sub sub b() a(m.lib.movies[0], 1) end sub `); - program.validate(); - //should have an error - expectDiagnostics(program, [ - DiagnosticMessages.mismatchArgumentCount(1, 2) - ]); - }); + program.validate(); + //should have an error + expectDiagnostics(program, [ + DiagnosticMessages.mismatchArgumentCount(1, 2) + ]); }); + }); - describe('argumentTypeMismatch', () => { - it('Catches argument type mismatches on function calls', () => { - program.setFile('source/file.brs', ` + describe('argumentTypeMismatch', () => { + it('Catches argument type mismatches on function calls', () => { + program.setFile('source/file.brs', ` sub a(age as integer) end sub sub b() a("hello") end sub `); - program.validate(); - //should have an error - expect(program.getDiagnostics().map(x => x.message)).to.include( - DiagnosticMessages.argumentTypeMismatch('string', 'integer').message - ); - }); - - it('Catches argument type mismatches on function calls for functions defined in another file', () => { - program.setFile('source/file.brs', ` + program.validate(); + //should have an error + expect(program.getDiagnostics().map(x => x.message)).to.include( + DiagnosticMessages.argumentTypeMismatch('string', 'integer').message + ); + }); + + it('Catches argument type mismatches on function calls for functions defined in another file', () => { + program.setFile('source/file.brs', ` sub a(age as integer) end sub `); - program.setFile('source/file2.brs', ` + program.setFile('source/file2.brs', ` sub b() a("hello") foo = "foo" a(foo) end sub `); - program.validate(); - //should have an error - expect(program.getDiagnostics().map(x => x.message)).to.include( - DiagnosticMessages.argumentTypeMismatch('string', 'integer').message - ); - }); - - it('catches argument type mismatches on function calls within namespaces', () => { - program.setFile('source/file.bs', ` + program.validate(); + //should have an error + expect(program.getDiagnostics().map(x => x.message)).to.include( + DiagnosticMessages.argumentTypeMismatch('string', 'integer').message + ); + }); + + it('catches argument type mismatches on function calls within namespaces', () => { + program.setFile('source/file.bs', ` namespace Name.Space sub a(param as integer) print param @@ -182,15 +176,15 @@ describe('ScopeValidator', () => { end sub end namespace `); - program.validate(); - //should have an error - expect(program.getDiagnostics().map(x => x.message)).to.include( - DiagnosticMessages.argumentTypeMismatch('string', 'integer').message - ); - }); - - it('catches argument type mismatches on function calls as arguments', () => { - program.setFile('source/file1.bs', ` + program.validate(); + //should have an error + expect(program.getDiagnostics().map(x => x.message)).to.include( + DiagnosticMessages.argumentTypeMismatch('string', 'integer').message + ); + }); + + it('catches argument type mismatches on function calls as arguments', () => { + program.setFile('source/file1.bs', ` sub a(param as string) print param end sub @@ -203,16 +197,16 @@ describe('ScopeValidator', () => { a(getNum()) end sub `); - program.validate(); - //should have an error - expect(program.getDiagnostics().map(x => x.message)).to.include( - DiagnosticMessages.argumentTypeMismatch('integer', 'string').message - ); - }); + program.validate(); + //should have an error + expect(program.getDiagnostics().map(x => x.message)).to.include( + DiagnosticMessages.argumentTypeMismatch('integer', 'string').message + ); + }); - it('catches argument type mismatches on function calls within namespaces across files', () => { - program.setFile('source/file1.bs', ` + it('catches argument type mismatches on function calls within namespaces across files', () => { + program.setFile('source/file1.bs', ` namespace Name.Space function getNum() as integer return 1 @@ -223,7 +217,7 @@ describe('ScopeValidator', () => { end function end namespace `); - program.setFile('source/file2.bs', ` + program.setFile('source/file2.bs', ` namespace Name.Space sub needsInt(param as integer) print param @@ -235,16 +229,16 @@ describe('ScopeValidator', () => { end sub end namespace `); - program.validate(); - //should have an error - expect(program.getDiagnostics().length).to.equal(1); - expect(program.getDiagnostics().map(x => x.message)).to.include( - DiagnosticMessages.argumentTypeMismatch('string', 'integer').message - ); - }); - - it('correctly validates correct parameters that are class members', () => { - program.setFile('source/main.bs', ` + program.validate(); + //should have an error + expect(program.getDiagnostics().length).to.equal(1); + expect(program.getDiagnostics().map(x => x.message)).to.include( + DiagnosticMessages.argumentTypeMismatch('string', 'integer').message + ); + }); + + it('correctly validates correct parameters that are class members', () => { + program.setFile('source/main.bs', ` class PiHolder pi = 3.14 function getPi() as float @@ -260,13 +254,13 @@ describe('ScopeValidator', () => { takesFloat(holder.pi) takesFloat(holder.getPI()) end sub`); - program.validate(); - //should have no error - expectZeroDiagnostics(program); - }); + program.validate(); + //should have no error + expectZeroDiagnostics(program); + }); - it('correctly validates wrong parameters that are class members', () => { - program.setFile('source/main.bs', ` + it('correctly validates wrong parameters that are class members', () => { + program.setFile('source/main.bs', ` class PiHolder pi = 3.14 name = "hello" @@ -283,16 +277,16 @@ describe('ScopeValidator', () => { takesFloat(holder.name) takesFloat(Str(holder.getPI())) end sub`); - program.validate(); - //should have error: holder.name is string - expect(program.getDiagnostics().length).to.equal(2); - expect(program.getDiagnostics().map(x => x.message)).to.include( - DiagnosticMessages.argumentTypeMismatch('string', 'float').message - ); - }); - - it('correctly validates correct parameters that are interface members', () => { - program.setFile('source/main.bs', ` + program.validate(); + //should have error: holder.name is string + expect(program.getDiagnostics().length).to.equal(2); + expect(program.getDiagnostics().map(x => x.message)).to.include( + DiagnosticMessages.argumentTypeMismatch('string', 'float').message + ); + }); + + it('correctly validates correct parameters that are interface members', () => { + program.setFile('source/main.bs', ` interface IPerson height as float name as string @@ -307,13 +301,13 @@ describe('ScopeValidator', () => { takesFloat(person.height) takesFloat(person.getWeight()) end sub`); - program.validate(); - //should have no error - expectZeroDiagnostics(program); - }); + program.validate(); + //should have no error + expectZeroDiagnostics(program); + }); - it('correctly validates wrong parameters that are interface members', () => { - program.setFile('source/main.bs', ` + it('correctly validates wrong parameters that are interface members', () => { + program.setFile('source/main.bs', ` interface IPerson isAlive as boolean function getAddress() as string @@ -327,16 +321,16 @@ describe('ScopeValidator', () => { takesFloat(person.getAddress()) end sub `); - program.validate(); - //should have 2 errors: person.name is string (not float) and person.getAddress() is object (not float) - expectDiagnostics(program, [ - DiagnosticMessages.argumentTypeMismatch('boolean', 'float').message, - DiagnosticMessages.argumentTypeMismatch('string', 'float').message - ]); - }); - - it('`as object` param allows all types', () => { - program.setFile('source/main.bs', ` + program.validate(); + //should have 2 errors: person.name is string (not float) and person.getAddress() is object (not float) + expectDiagnostics(program, [ + DiagnosticMessages.argumentTypeMismatch('boolean', 'float').message, + DiagnosticMessages.argumentTypeMismatch('string', 'float').message + ]); + }); + + it('`as object` param allows all types', () => { + program.setFile('source/main.bs', ` sub takesObject(obj as Object) end sub @@ -350,25 +344,25 @@ describe('ScopeValidator', () => { takesObject([]) end sub `); - program.validate(); - expectZeroDiagnostics(program); - }); + program.validate(); + expectZeroDiagnostics(program); + }); - it('allows conversions for arguments', () => { - program.setFile('source/main.bs', ` + it('allows conversions for arguments', () => { + program.setFile('source/main.bs', ` sub takesFloat(fl as float) end sub sub someFunc() takesFloat(1) end sub`); - program.validate(); - //should have no error - expectZeroDiagnostics(program); - }); + program.validate(); + //should have no error + expectZeroDiagnostics(program); + }); - it('allows subclasses as arguments', () => { - program.setFile('source/main.bs', ` + it('allows subclasses as arguments', () => { + program.setFile('source/main.bs', ` class Animal end class @@ -389,13 +383,13 @@ describe('ScopeValidator', () => { fido = new Lab() takesAnimal(fido) end sub`); - program.validate(); - //should have no error - expectZeroDiagnostics(program); - }); + program.validate(); + //should have no error + expectZeroDiagnostics(program); + }); - it('allows subclasses from namespaces as arguments', () => { - program.setFile('source/main.bs', ` + it('allows subclasses from namespaces as arguments', () => { + program.setFile('source/main.bs', ` class Outside end class @@ -460,13 +454,13 @@ describe('ScopeValidator', () => { child.methodTakesInside(new NS.ChildInExtendsInside()) child.methodTakesInside(new ChildOutExtendsInside()) end sub`); - program.validate(); - //should have no error - expectZeroDiagnostics(program); - }); + program.validate(); + //should have no error + expectZeroDiagnostics(program); + }); - it('respects union types', () => { - program.setFile('source/main.bs', ` + it('respects union types', () => { + program.setFile('source/main.bs', ` sub takesStringOrKlass(p as string or Klass) end sub @@ -479,17 +473,17 @@ describe('ScopeValidator', () => { takesStringOrKlass(myKlass) takesStringOrKlass(1) end sub`); - program.validate(); - //should have error when passed an integer - expect(program.getDiagnostics().length).to.equal(1); - expectDiagnostics(program, [ - DiagnosticMessages.argumentTypeMismatch('integer', 'string or Klass').message - ]); - }); + program.validate(); + //should have error when passed an integer + expect(program.getDiagnostics().length).to.equal(1); + expectDiagnostics(program, [ + DiagnosticMessages.argumentTypeMismatch('integer', 'string or Klass').message + ]); + }); - it('validates functions assigned to variables', () => { - program.setFile('source/main.bs', ` + it('validates functions assigned to variables', () => { + program.setFile('source/main.bs', ` sub someFunc() myFunc = function(i as integer, s as string) print i+1 @@ -497,13 +491,12 @@ describe('ScopeValidator', () => { end function myFunc("hello", 2) end sub`); - program.validate(); - //should have error when passed incorrect types - expectDiagnostics(program, [ - DiagnosticMessages.argumentTypeMismatch('string', 'integer').message, - DiagnosticMessages.argumentTypeMismatch('integer', 'string').message - ]); - }); + program.validate(); + //should have error when passed incorrect types + expectDiagnostics(program, [ + DiagnosticMessages.argumentTypeMismatch('string', 'integer').message, + DiagnosticMessages.argumentTypeMismatch('integer', 'string').message + ]); }); }); }); diff --git a/src/bscPlugin/validation/ScopeValidator.ts b/src/bscPlugin/validation/ScopeValidator.ts index 85d7710e8..dafbee04d 100644 --- a/src/bscPlugin/validation/ScopeValidator.ts +++ b/src/bscPlugin/validation/ScopeValidator.ts @@ -52,9 +52,7 @@ export class ScopeValidator { if (isBrsFile(file)) { this.iterateFileExpressions(file); this.validateCreateObjectCalls(file); - if (this.event.program.options.enableTypeValidation) { - this.validateFunctionCalls(file); - } + this.validateFunctionCalls(file); } }); } @@ -118,63 +116,45 @@ export class ScopeValidator { oppositeSymbolType = SymbolTypeFlag.runtime; } - if (scope.program.options.enableTypeValidation) { - // Do a complete type check on all DottedGet and Variable expressions - // this will create a diagnostic if an invalid member is accessed - const typeChain = []; - let exprType = info.expression.getType({ - flags: symbolType, - typeChain: typeChain - }); + // Do a complete type check on all DottedGet and Variable expressions + // this will create a diagnostic if an invalid member is accessed + const typeChain = []; + let exprType = info.expression.getType({ + flags: symbolType, + typeChain: typeChain + }); - if (!exprType || !exprType.isResolvable()) { - if (info.expression.getType({ flags: oppositeSymbolType })?.isResolvable()) { - const oppoSiteTypeChain = []; - const invalidlyUsedResolvedType = info.expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain }); - const typeChainScan = util.processTypeChain(oppoSiteTypeChain); - if (info.isUsedAsType) { - this.addMultiScopeDiagnostic({ - ...DiagnosticMessages.itemCannotBeUsedAsType(typeChainScan.fullChainName), - range: info.expression.range, - file: file - }, 'When used in scope'); - } else { - this.addMultiScopeDiagnostic({ - ...DiagnosticMessages.itemCannotBeUsedAsVariable(invalidlyUsedResolvedType.toString()), - range: info.expression.range, - file: file - }, 'When used in scope'); - } - continue; + if (!exprType || !exprType.isResolvable()) { + if (info.expression.getType({ flags: oppositeSymbolType })?.isResolvable()) { + const oppoSiteTypeChain = []; + const invalidlyUsedResolvedType = info.expression.getType({ flags: oppositeSymbolType, typeChain: oppoSiteTypeChain }); + const typeChainScan = util.processTypeChain(oppoSiteTypeChain); + if (info.isUsedAsType) { + this.addMultiScopeDiagnostic({ + ...DiagnosticMessages.itemCannotBeUsedAsType(typeChainScan.fullChainName), + range: info.expression.range, + file: file + }, 'When used in scope'); + } else { + this.addMultiScopeDiagnostic({ + ...DiagnosticMessages.itemCannotBeUsedAsVariable(invalidlyUsedResolvedType.toString()), + range: info.expression.range, + file: file + }, 'When used in scope'); } - - const typeChainScan = util.processTypeChain(typeChain); - this.addMultiScopeDiagnostic({ - file: file as BscFile, - ...DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem), - range: typeChainScan.range - }); - //skip to the next expression - continue; - } - } else { - //flag all unknown left-most variables only - const symbolTable = info.expression.getSymbolTable(); - const firstPart = info.parts[0]; - if ( - !symbolTable?.hasSymbol(firstPart.name?.text, symbolType) && - !namespaceContainer - ) { - this.addMultiScopeDiagnostic({ - file: file as BscFile, - ...DiagnosticMessages.cannotFindName(firstPart.name?.text), - range: firstPart.name.range - }); - //skip to the next expression continue; } - } + const typeChainScan = util.processTypeChain(typeChain); + this.addMultiScopeDiagnostic({ + file: file as BscFile, + ...DiagnosticMessages.cannotFindName(typeChainScan.itemName, typeChainScan.fullNameOfItem), + range: typeChainScan.range + }); + //skip to the next expression + continue; + + } const enumStatement = scope.getEnum(firstNamespacePartLower, info.enclosingNamespaceNameLower); //if this isn't a namespace, skip it @@ -396,8 +376,8 @@ export class ScopeValidator { } /** - * Detect calls to functions with the incorrect number of parameters, or wrong types of arguments - */ + * Detect calls to functions with the incorrect number of parameters, or wrong types of arguments + */ private validateFunctionCalls(file: BscFile) { const diagnostics: BsDiagnostic[] = []; diff --git a/src/files/BrsFile.Class.spec.ts b/src/files/BrsFile.Class.spec.ts index c70910474..eedfd9ab1 100644 --- a/src/files/BrsFile.Class.spec.ts +++ b/src/files/BrsFile.Class.spec.ts @@ -473,6 +473,9 @@ describe('BrsFile BrighterScript classes', () => { class Animal sub new(name as string) end sub + + sub DoSomething() + end sub end class class Duck extends Animal @@ -486,6 +489,8 @@ describe('BrsFile BrighterScript classes', () => { instance = {} instance.new = sub(name as string) end sub + instance.DoSomething = sub() + end sub return instance end function function Animal(name as string) diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts index 9758b6a28..7a4172af9 100644 --- a/src/files/BrsFile.spec.ts +++ b/src/files/BrsFile.spec.ts @@ -1812,322 +1812,6 @@ describe('BrsFile', () => { expect(mainFile.getDiagnostics()).to.be.lengthOf(0); }); - describe('getHover', () => { - it('works for param types', () => { - let file = program.setFile('source/main.brs', ` - sub DoSomething(name as string) - name = 1 - sayMyName = function(name as string) - end function - end sub - `); - - //hover over the `name = 1` line - let hover = program.getHover(file.srcPath, Position.create(2, 24))[0]; - expect(hover).to.exist; - expect(hover.range).to.eql(Range.create(2, 20, 2, 24)); - - //hover over the `name` parameter declaration - hover = program.getHover(file.srcPath, Position.create(1, 34))[0]; - expect(hover).to.exist; - expect(hover.range).to.eql(Range.create(1, 32, 1, 36)); - }); - - //ignore this for now...it's not a huge deal - it('does not match on keywords or data types', () => { - let file = program.setFile('source/main.brs', ` - sub Main(name as string) - end sub - sub as() - end sub - `); - //hover over the `as` - expect(program.getHover(file.srcPath, Position.create(1, 31))).to.be.empty; - //hover over the `string` - expect(program.getHover(file.srcPath, Position.create(1, 36))).to.be.empty; - }); - - it('finds declared function', () => { - let file = program.setFile('source/main.brs', ` - function Main(count = 1) - firstName = "bob" - age = 21 - shoeSize = 10 - end function - `); - - let hover = program.getHover(file.srcPath, Position.create(1, 28))[0]; - expect(hover).to.exist; - - expect(hover.range).to.eql(Range.create(1, 25, 1, 29)); - expect(hover.contents).to.equal([ - '```brightscript', - 'function Main(count? as integer) as dynamic', - '```' - ].join('\n')); - }); - - it('finds declared namespace function', () => { - let file = program.setFile('source/main.brs', ` - namespace mySpace - function Main(count = 1) - firstName = "bob" - age = 21 - shoeSize = 10 - end function - end namespace - `); - - let hover = program.getHover(file.srcPath, Position.create(2, 28))[0]; - expect(hover).to.exist; - - expect(hover.range).to.eql(Range.create(2, 25, 2, 29)); - expect(hover.contents).to.equal([ - '```brightscript', - 'function Main(count? as integer) as dynamic', - '```' - ].join('\n')); - }); - - it('finds variable function hover in same scope', () => { - let file = program.setFile('source/main.brs', ` - sub Main() - sayMyName = sub(name as string) - end sub - - sayMyName() - end sub - `); - - let hover = program.getHover(file.srcPath, Position.create(5, 24))[0]; - - expect(hover.range).to.eql(Range.create(5, 20, 5, 29)); - expect(hover.contents).to.equal([ - '```brightscript', - 'sub sayMyName(name as string) as void', - '```' - ].join('\n')); - }); - - it('does not crash when hovering on built-in functions', () => { - let file = program.setFile('source/main.brs', ` - function doUcase(text) - return ucase(text) - end function - `); - - expect( - program.getHover(file.srcPath, Position.create(2, 30))[0].contents - ).to.equal([ - '```brightscript', - 'function UCase(s as string) as string', - '```' - ].join('\n')); - }); - - it('does not crash when hovering on object method call', () => { - let file = program.setFile('source/main.brs', ` - function getInstr(url, text) - return url.instr(text) - end function - `); - - expect( - program.getHover(file.srcPath, Position.create(2, 35))[0].contents - ).to.equal([ - '```brightscript', - //TODO this really shouldn't be returning the global function, but it does...so make sure it doesn't crash right now. - 'function Instr(start as integer, text as string, substring as string) as integer', - '```' - ].join('\n')); - }); - - it('finds function hover in file scope', () => { - let file = program.setFile('source/main.brs', ` - sub Main() - sayMyName() - end sub - - sub sayMyName() - - end sub - `); - - let hover = program.getHover(file.srcPath, Position.create(2, 25))[0]; - - expect(hover.range).to.eql(Range.create(2, 20, 2, 29)); - expect(hover.contents).to.equal([ - '```brightscript', - 'sub sayMyName() as void', - '```' - ].join('\n')); - }); - - it('finds namespace function hover in file scope', () => { - let file = program.setFile('source/main.brs', ` - namespace mySpace - sub Main() - sayMyName() - end sub - - sub sayMyName() - - end sub - end namespace - `); - - let hover = program.getHover(file.srcPath, Position.create(3, 25))[0]; - - expect(hover.range).to.eql(Range.create(3, 20, 3, 29)); - expect(hover.contents).to.equal([ - '```brightscript', - 'sub sayMyName() as void', - '```' - ].join('\n')); - }); - - it('finds function hover in scope', () => { - let rootDir = process.cwd(); - program = new Program({ - rootDir: rootDir - }); - - let mainFile = program.setFile('source/main.brs', ` - sub Main() - sayMyName() - end sub - `); - - program.setFile('source/lib.brs', ` - sub sayMyName(name as string) - - end sub - `); - - let hover = program.getHover(mainFile.srcPath, Position.create(2, 25))[0]; - expect(hover).to.exist; - - expect(hover.range).to.eql(Range.create(2, 20, 2, 29)); - expect(hover.contents).to.equal([ - '```brightscript', - 'sub sayMyName(name as string) as void', - '```' - ].join('\n')); - }); - - it('finds namespace function hover in scope', () => { - let rootDir = process.cwd(); - program = new Program({ - rootDir: rootDir - }); - - let mainFile = program.setFile('source/main.brs', ` - sub Main() - mySpace.sayMyName() - end sub - `); - - program.setFile('source/lib.brs', ` - namespace mySpace - sub sayMyName(name as string) - end sub - end namespace - `); - - let hover = program.getHover(mainFile.srcPath, Position.create(2, 34))[0]; - expect(hover).to.exist; - - expect(hover.range).to.eql(Range.create(2, 28, 2, 37)); - expect(hover.contents).to.equal([ - '```brightscript', - 'sub sayMyName(name as string) as void', - '```' - ].join('\n')); - }); - - it('includes markdown comments in hover.', () => { - let rootDir = process.cwd(); - program = new Program({ - rootDir: rootDir - }); - - const file = program.setFile('source/lib.brs', ` - ' - ' The main function - ' - sub main() - writeToLog("hello") - end sub - - ' - ' Prints a message to the log. - ' Works with *markdown* **content** - ' - sub writeToLog(message as string) - print message - end sub - `); - - //hover over log("hello") - expect( - program.getHover(file.srcPath, Position.create(5, 22))[0].contents - ).to.equal([ - '```brightscript', - 'sub writeToLog(message as string) as void', - '```', - '***', - '', - ' Prints a message to the log.', - ' Works with *markdown* **content**', - '' - ].join('\n')); - - //hover over sub ma|in() - expect( - trim( - program.getHover(file.srcPath, Position.create(4, 22))[0].contents.toString() - ) - ).to.equal(trim` - \`\`\`brightscript - sub main() as void - \`\`\` - *** - - The main function - ` - ); - }); - - it('handles mixed case `then` partions of conditionals', () => { - let mainFile = program.setFile('source/main.brs', ` - sub Main() - if true then - print "works" - end if - end sub - `); - - expectZeroDiagnostics(mainFile); - mainFile = program.setFile('source/main.brs', ` - sub Main() - if true Then - print "works" - end if - end sub - `); - expectZeroDiagnostics(mainFile); - - mainFile = program.setFile('source/main.brs', ` - sub Main() - if true THEN - print "works" - end if - end sub - `); - expectZeroDiagnostics(mainFile); - }); - }); - it('does not throw when encountering incomplete import statement', () => { program.setFile('source/main.brs', ` import diff --git a/src/files/BrsFile.ts b/src/files/BrsFile.ts index 807544ed4..1fa77c3e3 100644 --- a/src/files/BrsFile.ts +++ b/src/files/BrsFile.ts @@ -13,8 +13,8 @@ import type { Token } from '../lexer/Token'; import { Lexer } from '../lexer/Lexer'; import { TokenKind, AllowedLocalIdentifiers, Keywords } from '../lexer/TokenKind'; import { Parser, ParseMode } from '../parser/Parser'; -import type { FunctionExpression, TypeExpression, VariableExpression } from '../parser/Expression'; -import type { ClassStatement, FunctionStatement, NamespaceStatement, MethodStatement, FieldStatement, AssignmentStatement } from '../parser/Statement'; +import type { FunctionExpression, VariableExpression } from '../parser/Expression'; +import type { ClassStatement, FunctionStatement, NamespaceStatement, MethodStatement, FieldStatement } from '../parser/Statement'; import type { Program } from '../Program'; import { DynamicType } from '../types/DynamicType'; import { standardizePath as s, util } from '../util'; @@ -22,18 +22,13 @@ import { BrsTranspileState } from '../parser/BrsTranspileState'; import { Preprocessor } from '../preprocessor/Preprocessor'; import { LogLevel } from '../Logger'; import { serializeError } from 'serialize-error'; -import { isMethodStatement, isClassStatement, isDottedGetExpression, isFunctionStatement, isFunctionType, isLiteralExpression, isNamespaceStatement, isStringType, isVariableExpression, isXmlFile, isImportStatement, isFieldStatement, isEnumStatement, isConstStatement, isCallExpression, isFunctionExpression } from '../astUtils/reflection'; +import { isMethodStatement, isClassStatement, isDottedGetExpression, isFunctionStatement, isFunctionType, isLiteralExpression, isNamespaceStatement, isStringType, isVariableExpression, isXmlFile, isImportStatement, isFieldStatement, isEnumStatement, isConstStatement, isFunctionExpression } from '../astUtils/reflection'; import { createVisitor, WalkMode } from '../astUtils/visitors'; import type { DependencyGraph } from '../DependencyGraph'; import { CommentFlagProcessor } from '../CommentFlagProcessor'; import { URI } from 'vscode-uri'; import { type AstNode, type Expression, type Statement } from '../parser/AstNode'; import { SymbolTypeFlag } from '../SymbolTable'; -import type { BscType } from '../types/BscType'; -import { ClassType } from '../types/ClassType'; -import { createIdentifier } from '../astUtils/creators'; -import { FunctionType } from '../types/FunctionType'; -import { VoidType } from '../types/VoidType'; /** * Holds all details about this file within the scope of the whole program */ @@ -446,17 +441,12 @@ export class BrsFile { //add every parameter for (let param of func.parameters) { - let paramType; scope.variableDeclarations.push({ nameRange: param.name.range, lineIndex: param.name.range.start.line, name: param.name.text, getType: () => { - if (this.program.options.enableTypeValidation) { - return param.getType({ flags: SymbolTypeFlag.typetime }); - } - paramType = paramType ?? this.getBscTypeFromTypeExpression(param.typeExpression); - return paramType; + return param.getType({ flags: SymbolTypeFlag.typetime }); } }); } @@ -501,106 +491,25 @@ export class BrsFile { //skip variable declarations that are outside of any scope if (scope) { - let assignmentType; scope.variableDeclarations.push({ nameRange: statement.name.range, lineIndex: statement.name.range.start.line, name: statement.name.text, getType: () => { - if (this.program.options.enableTypeValidation) { - return statement.getType({ flags: SymbolTypeFlag.runtime }); - } - assignmentType = assignmentType ?? this.getBscTypeFromAssignment(statement, scope); - return assignmentType; + return statement.getType({ flags: SymbolTypeFlag.runtime }); } }); } } } - /** - * Short circuit full `node.getType()` calls - used when enableTypeValidation is false - */ - private getBscTypeFromTypeExpression(typeExpr: TypeExpression, defaultValue?: Expression): BscType { - const typeAsString = typeExpr?.getName(); - if (typeAsString) { - return util.tokenToBscType(createIdentifier(typeAsString, typeExpr.range)) ?? new ClassType(typeAsString); - } - if (isLiteralExpression(defaultValue)) { - return defaultValue.getType({ flags: SymbolTypeFlag.typetime }); - } - return DynamicType.instance; - } - - private getFunctionTypeFromFuncExpr(expression: FunctionExpression, name: string): FunctionType { - let functionType = new FunctionType(this.getBscTypeFromTypeExpression(expression.returnTypeExpression)); - functionType.isSub = expression.functionType.text === 'sub'; - if (functionType.isSub) { - functionType.returnType = new VoidType(); - } - - functionType.setName(name); - for (let param of expression.parameters) { - let isOptional = !!param.defaultValue; - functionType.addParameter(param.name.text, this.getBscTypeFromTypeExpression(param.typeExpression, param.defaultValue), isOptional); - } - return functionType; - } - - /** - * Short circuit full `node.getType()` calls - used when enableTypeValidation is false - */ - private getBscTypeFromAssignment(assignment: AssignmentStatement, scope: FunctionScope): BscType { - const getTypeOptions = { flags: SymbolTypeFlag.runtime }; - try { - //function - if (isFunctionExpression(assignment.value)) { - return this.getFunctionTypeFromFuncExpr(assignment.value, assignment.name.text); - //literal - } else if (isLiteralExpression(assignment.value)) { - return assignment.value.getType(getTypeOptions); - //function call - } else if (isCallExpression(assignment.value)) { - let calleeName = (assignment.value.callee as any)?.name?.text; - if (calleeName) { - let func = this.getCallableByName(calleeName); - if (func) { - return func.type.returnType; - } - } - } else if (isVariableExpression(assignment.value)) { - let variableName = assignment.value?.name?.text; - let variable = scope.getVariableByName(variableName); - return variable.getType(); - } - } catch (e) { - //do nothing. Just return dynamic - } - //fallback to dynamic - return DynamicType.instance; - } - - private getCallableByName(name: string) { - name = name ? name.toLowerCase() : undefined; - if (!name) { - return; - } - for (let func of this.callables) { - if (func.name.toLowerCase() === name) { - return func; - } - } - } - private findCallables() { for (let statement of this.parser.references.functionStatements ?? []) { //extract the parameters let params = [] as CallableParam[]; for (let param of statement.func.parameters) { - const paramType = this.program.options.enableTypeValidation - ? param.getType({ flags: SymbolTypeFlag.typetime }) - : this.getBscTypeFromTypeExpression(param.typeExpression, param.defaultValue); + const paramType = param.getType({ flags: SymbolTypeFlag.typetime }); let callableParam = { name: param.name.text, @@ -610,11 +519,7 @@ export class BrsFile { }; params.push(callableParam); } - - const funcType = this.program.options.enableTypeValidation - ? statement.getType({ flags: SymbolTypeFlag.typetime }) - : this.getFunctionTypeFromFuncExpr(statement.func, statement.name.text); - + const funcType = statement.getType({ flags: SymbolTypeFlag.typetime }); this.callables.push({ isSub: statement.func.functionType.text.toLowerCase() === 'sub', name: statement.name.text, diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts index af1a23d74..48fc2d28b 100644 --- a/src/parser/Statement.ts +++ b/src/parser/Statement.ts @@ -2639,11 +2639,11 @@ export class EnumStatement extends Statement implements TypedefProvider { } } - getType() { + getType(options: GetTypeOptions) { const resultType = new EnumType(this.fullName); resultType.pushMemberProvider(() => this.getSymbolTable()); for (const statement of this.getMembers()) { - resultType.addMember(statement?.tokens?.name?.text, statement?.range, statement.getType(), SymbolTypeFlag.runtime); + resultType.addMember(statement?.tokens?.name?.text, statement?.range, statement.getType(options), SymbolTypeFlag.runtime); } return resultType; @@ -2703,7 +2703,7 @@ export class EnumMemberStatement extends Statement implements TypedefProvider { } } - getType() { + getType(options: GetTypeOptions) { return new EnumMemberType((this.parent as EnumStatement)?.fullName, this.tokens?.name?.text); } } diff --git a/src/util.ts b/src/util.ts index 25a97b879..c55cd70ea 100644 --- a/src/util.ts +++ b/src/util.ts @@ -346,7 +346,6 @@ export class Util { config.allowBrighterScriptInBrightScript = config.allowBrighterScriptInBrightScript === true ? true : false; config.emitDefinitions = config.emitDefinitions === true ? true : false; config.removeParameterTypes = config.removeParameterTypes === true ? true : false; - config.enableTypeValidation = config.enableTypeValidation === true ? true : false; if (typeof config.logLevel === 'string') { config.logLevel = LogLevel[(config.logLevel as string).toLowerCase()]; }