From a7c402a8dd1df1d6a1d67284c7892887328b69f2 Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Mon, 16 Sep 2024 10:08:29 -0300 Subject: [PATCH 1/2] Adds -allow-implicit for tyep annotations --- README.md | 5 + src/index.ts | 2 +- src/plugins/codeStyle/index.spec.ts | 154 +++++++++++------- src/plugins/codeStyle/index.ts | 18 +- .../source/type-annotations-implicit.brs | 19 +++ test/project1/source/type-annotations.brs | 4 + 6 files changed, 143 insertions(+), 59 deletions(-) create mode 100644 test/project1/source/type-annotations-implicit.brs diff --git a/README.md b/README.md index 7e27449..8935eca 100644 --- a/README.md +++ b/README.md @@ -263,8 +263,13 @@ Default rules: - `all`: enforce both arguments and return type annotations - `return`: enforce return type annotations - `args`: enforce arguments type annotations + - `all-allow-implicit`: enforce argument and return type annotations, but allows arguments with default values to not have an annotation + - `args-allow-implicit`: enforce arguments type annotations, but allows arguments with default values to not have an annotation - `off`: do not validate (**default**) +- `name-shadowing`: validation of using the same name for different entities (`error | warn | info | off`) + + ### Code flow rules Valid values for the rules severity are: `error | warn | info | off`. diff --git a/src/index.ts b/src/index.ts index b2767b7..da78db6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -11,7 +11,7 @@ export type RuleBlockIf = 'no-then' | 'then' | 'off'; export type RuleCondition = 'no-group' | 'group' | 'off'; export type RuleFunction = 'no-function' | 'no-sub' | 'auto' | 'off'; export type RuleAAComma = 'always' | 'no-dangling' | 'never' | 'off'; -export type RuleTypeAnnotations = 'all' | 'return' | 'args' | 'off'; +export type RuleTypeAnnotations = 'all' | 'return' | 'args' | 'off' | 'all-allow-implicit' | 'args-allow-implicit'; export type RuleEolLast = 'always' | 'never' | 'off'; export type RuleColorFormat = 'hash-hex' | 'quoted-numeric-hex' | 'never' | 'off'; export type RuleColorCase = 'upper' | 'lower' | 'off'; diff --git a/src/plugins/codeStyle/index.spec.ts b/src/plugins/codeStyle/index.spec.ts index c619df8..3c2818a 100644 --- a/src/plugins/codeStyle/index.spec.ts +++ b/src/plugins/codeStyle/index.spec.ts @@ -335,67 +335,111 @@ describe('codeStyle', () => { const expected = []; expect(actual).deep.equal(expected); }); - }); - it('enforce return type only', async () => { - const diagnostics = await linter.run({ - ...project1, - files: ['source/type-annotations.brs'], - rules: { - 'named-function-style': 'off', - 'anon-function-style': 'off', - 'type-annotations': 'return', - 'no-print': 'off' - } + it('enforce return type only', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/type-annotations.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'type-annotations': 'return', + 'no-print': 'off' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `05:LINT3010:Strictness: function should declare the return type` + ]; + expect(actual).deep.equal(expected); + // should only highlight the function name + expect(diagnostics[0].location.range).to.eql( + util.createRange(4, 0, 4, 8) + ); }); - const actual = fmtDiagnostics(diagnostics); - const expected = [ - `05:LINT3010:Strictness: function should declare the return type` - ]; - expect(actual).deep.equal(expected); - // should only highlight the function name - expect(diagnostics[0].location.range).to.eql( - util.createRange(4, 0, 4, 8) - ); - }); - it('enforce arguments type only', async () => { - const diagnostics = await linter.run({ - ...project1, - files: ['source/type-annotations.brs'], - rules: { - 'named-function-style': 'off', - 'anon-function-style': 'off', - 'type-annotations': 'args', - 'no-print': 'off' - } + it('enforce arguments type only', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/type-annotations.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'type-annotations': 'args', + 'no-print': 'off' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `01:LINT3011:Strictness: type annotation required`, + `05:LINT3011:Strictness: type annotation required`, + `13:LINT3011:Strictness: type annotation required` + ]; + expect(actual).deep.equal(expected); }); - const actual = fmtDiagnostics(diagnostics); - const expected = [ - `01:LINT3011:Strictness: type annotation required`, - `05:LINT3011:Strictness: type annotation required` - ]; - expect(actual).deep.equal(expected); - }); - it('enforce all annotations', async () => { - const diagnostics = await linter.run({ - ...project1, - files: ['source/type-annotations.brs'], - rules: { - 'named-function-style': 'off', - 'anon-function-style': 'off', - 'type-annotations': 'all', - 'no-print': 'off' - } + it('enforce all annotations', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/type-annotations.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'type-annotations': 'all', + 'no-print': 'off' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `01:LINT3011:Strictness: type annotation required`, + `05:LINT3010:Strictness: function should declare the return type`, + `05:LINT3011:Strictness: type annotation required`, + `13:LINT3011:Strictness: type annotation required` + ]; + expect(actual).deep.equal(expected); }); - const actual = fmtDiagnostics(diagnostics); - const expected = [ - `01:LINT3011:Strictness: type annotation required`, - `05:LINT3010:Strictness: function should declare the return type`, - `05:LINT3011:Strictness: type annotation required` - ]; - expect(actual).deep.equal(expected); + + it('allows implicit type definitions with default values', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/type-annotations-implicit.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'type-annotations': 'all-allow-implicit', + 'no-print': 'off' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `01:LINT3011:Strictness: type annotation required`, + `05:LINT3010:Strictness: function should declare the return type`, + `05:LINT3011:Strictness: type annotation required`, + `17:LINT3010:Strictness: function should declare the return type` + ]; + expect(actual).deep.equal(expected); + }); + + + it('allows implicit type definitions with default values for args only', async () => { + const diagnostics = await linter.run({ + ...project1, + files: ['source/type-annotations-implicit.brs'], + rules: { + 'named-function-style': 'off', + 'anon-function-style': 'off', + 'type-annotations': 'args-allow-implicit', + 'no-print': 'off' + } + }); + const actual = fmtDiagnostics(diagnostics); + const expected = [ + `01:LINT3011:Strictness: type annotation required`, + `05:LINT3011:Strictness: type annotation required` + ]; + expect(actual).deep.equal(expected); + }); + }); it('enforce no print', async () => { diff --git a/src/plugins/codeStyle/index.ts b/src/plugins/codeStyle/index.ts index 2e01e46..2ac0aba 100644 --- a/src/plugins/codeStyle/index.ts +++ b/src/plugins/codeStyle/index.ts @@ -343,7 +343,11 @@ export default class CodeStyle implements CompilerPlugin { // type annotations if (typeAnnotations !== 'off') { - if (typeAnnotations !== 'args') { + const needsArgType = typeAnnotations.startsWith('args') || typeAnnotations.startsWith('all'); + const needsReturnType = typeAnnotations.startsWith('return') || typeAnnotations.startsWith('all'); + const allowImplicit = typeAnnotations.includes('allow-implicit'); + + if (needsReturnType) { if (hasReturnedValue && !fun.returnTypeExpression) { diagnostics.push(messages.expectedReturnTypeAnnotation( // add the error to the function keyword (or just highlight the whole function if that's somehow missing) @@ -351,8 +355,16 @@ export default class CodeStyle implements CompilerPlugin { )); } } - if (typeAnnotations !== 'return') { - const missingAnnotation = fun.parameters.find(arg => !arg.typeExpression); + if (needsArgType) { + const missingAnnotation = fun.parameters.find(arg => { + if (!arg.typeExpression) { + if (allowImplicit && arg.defaultValue) { + return false; + } + return true; + } + return false; + }); if (missingAnnotation) { // only report 1st missing arg annotation to avoid error overload diagnostics.push(messages.expectedTypeAnnotation(missingAnnotation.location)); diff --git a/test/project1/source/type-annotations-implicit.brs b/test/project1/source/type-annotations-implicit.brs new file mode 100644 index 0000000..a0331e7 --- /dev/null +++ b/test/project1/source/type-annotations-implicit.brs @@ -0,0 +1,19 @@ +function test1(arg1, arg2) + print "noop" +end function + +function test2(arg1, arg2) + return arg1 + arg2 +end function + +function test3(arg1 as string, arg2 as string) as string + return arg1 + arg2 +end function + +function test4(arg1 = 0, arg2 = 1) as integer + return arg1 + arg2 +end function + +function test5(arg1 = 0 as float, arg2 = 1 as float) + return arg1 + arg2 +end function diff --git a/test/project1/source/type-annotations.brs b/test/project1/source/type-annotations.brs index b0ba142..045e6fe 100644 --- a/test/project1/source/type-annotations.brs +++ b/test/project1/source/type-annotations.brs @@ -9,3 +9,7 @@ end function function test3(arg1 as string, arg2 as string) as string return arg1 + arg2 end function + +function test4(arg1 = "hello", arg2 = "world") as string + return arg1 + arg2 +end function From 43007934c54f80b91d3e07da9da9cf3ca40fd60a Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Mon, 16 Sep 2024 10:15:00 -0300 Subject: [PATCH 2/2] changed readme --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 8935eca..8f4c60a 100644 --- a/README.md +++ b/README.md @@ -267,8 +267,7 @@ Default rules: - `args-allow-implicit`: enforce arguments type annotations, but allows arguments with default values to not have an annotation - `off`: do not validate (**default**) -- `name-shadowing`: validation of using the same name for different entities (`error | warn | info | off`) - +- `name-shadowing`: enforces that no two items have the same name (`error | warn | info | off`) ### Code flow rules