From d48c3908b27854775aac942d819aca6f92264887 Mon Sep 17 00:00:00 2001 From: Joshua Goldberg Date: Tue, 6 Feb 2018 10:36:11 -0800 Subject: [PATCH 1/6] Excluded native JSX elements from no-unsafe-any --- src/rules/noUnsafeAnyRule.ts | 26 +++++++++++++++++++++++++- test/rules/no-unsafe-any/test.ts.lint | 11 +++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/rules/noUnsafeAnyRule.ts b/src/rules/noUnsafeAnyRule.ts index 32b8b159bd0..857ae895111 100644 --- a/src/rules/noUnsafeAnyRule.ts +++ b/src/rules/noUnsafeAnyRule.ts @@ -15,9 +15,10 @@ * limitations under the License. */ -import { isReassignmentTarget, isSymbolFlagSet, isTokenKind, isTypeFlagSet, isTypeNodeKind } from "tsutils"; +import { isIdentifier, isReassignmentTarget, isSymbolFlagSet, isTokenKind, isTypeFlagSet, isTypeNodeKind } from "tsutils"; import * as ts from "typescript"; import * as Lint from "../index"; +import { isLowerCase } from '../utils'; export class Rule extends Lint.Rules.TypedRule { /* tslint:disable:object-literal-sort-keys */ @@ -378,9 +379,32 @@ function isNodeAny(node: ts.Node, checker: ts.TypeChecker): boolean { return isAny(checker.getDeclaredTypeOfSymbol(symbol)); } } + + // Lowercase JSX elements are assumed to be allowed by design + if (isJsxNativeElement(node)) { + return false; + } + return isAny(checker.getTypeAtLocation(node)); } +const jsxElementTypes = new Set([ + ts.SyntaxKind.JsxClosingElement, + ts.SyntaxKind.JsxOpeningElement, + ts.SyntaxKind.JsxSelfClosingElement, +]); + +function isJsxNativeElement(node: ts.Node): boolean { + if (!isIdentifier(node)) { + return false; + } + + return isIdentifier(node) + && node.parent !== undefined + && jsxElementTypes.has(node.parent.kind) + && isLowerCase(node.text); +} + function isStringLike(expr: ts.Expression, checker: ts.TypeChecker): boolean { return isTypeFlagSet(checker.getTypeAtLocation(expr), ts.TypeFlags.StringLike); } diff --git a/test/rules/no-unsafe-any/test.ts.lint b/test/rules/no-unsafe-any/test.ts.lint index 3746de2495c..3f18f868633 100644 --- a/test/rules/no-unsafe-any/test.ts.lint +++ b/test/rules/no-unsafe-any/test.ts.lint @@ -345,4 +345,15 @@ namespace TestNS2 { class Test6 implements NS.ITest {} } +const nativeJsxFull = ; +const nativeJsxShort = ; + +function render(AnyComponent: any) { + const componentJsxFull = ; + ~~~~~~~~~~~~ [0] + ~~~~~~~~~~~~ [0] + const componentJsxShort = ; + ~~~~~~~~~~~~ [0] +} + [0]: Unsafe use of expression of type 'any'. From 04a461d8ee5b72e62ae33c923102cb6e103ac3f6 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 6 Feb 2018 10:51:15 -0800 Subject: [PATCH 2/6] Lint fix: double-apostrophe Interesting how TypeScript auto-added single-apostrophes... --- src/rules/noUnsafeAnyRule.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rules/noUnsafeAnyRule.ts b/src/rules/noUnsafeAnyRule.ts index 857ae895111..aca12d26fac 100644 --- a/src/rules/noUnsafeAnyRule.ts +++ b/src/rules/noUnsafeAnyRule.ts @@ -18,7 +18,7 @@ import { isIdentifier, isReassignmentTarget, isSymbolFlagSet, isTokenKind, isTypeFlagSet, isTypeNodeKind } from "tsutils"; import * as ts from "typescript"; import * as Lint from "../index"; -import { isLowerCase } from '../utils'; +import { isLowerCase } from "../utils"; export class Rule extends Lint.Rules.TypedRule { /* tslint:disable:object-literal-sort-keys */ From 74ec46c73337e59c867b7f888a27a8fd068eff62 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 6 Feb 2018 11:10:42 -0800 Subject: [PATCH 3/6] ...almost working.. --- src/rules/noUnsafeAnyRule.ts | 6 +----- test/rules/no-unsafe-any/test.ts.lint | 14 +++++++------- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/rules/noUnsafeAnyRule.ts b/src/rules/noUnsafeAnyRule.ts index aca12d26fac..0d40671128a 100644 --- a/src/rules/noUnsafeAnyRule.ts +++ b/src/rules/noUnsafeAnyRule.ts @@ -395,14 +395,10 @@ const jsxElementTypes = new Set([ ]); function isJsxNativeElement(node: ts.Node): boolean { - if (!isIdentifier(node)) { - return false; - } - return isIdentifier(node) && node.parent !== undefined && jsxElementTypes.has(node.parent.kind) - && isLowerCase(node.text); + && isLowerCase(node.text[0]); } function isStringLike(expr: ts.Expression, checker: ts.TypeChecker): boolean { diff --git a/test/rules/no-unsafe-any/test.ts.lint b/test/rules/no-unsafe-any/test.ts.lint index 3f18f868633..6430f39187b 100644 --- a/test/rules/no-unsafe-any/test.ts.lint +++ b/test/rules/no-unsafe-any/test.ts.lint @@ -345,15 +345,15 @@ namespace TestNS2 { class Test6 implements NS.ITest {} } +deckare const AnyComponent: any; + const nativeJsxFull = ; const nativeJsxShort = ; -function render(AnyComponent: any) { - const componentJsxFull = ; - ~~~~~~~~~~~~ [0] - ~~~~~~~~~~~~ [0] - const componentJsxShort = ; - ~~~~~~~~~~~~ [0] -} +const componentJsxFull = ; + ~~~~~~~~~~~~ [0] + ~~~~~~~~~~~~ [0] +const componentJsxShort = ; + ~~~~~~~~~~~~ [0] [0]: Unsafe use of expression of type 'any'. From db71b407bd5eae4a33d54b3a48258b44d9be9e26 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 6 Feb 2018 13:05:48 -0800 Subject: [PATCH 4/6] Split no-unsafe-any into separate tests --- .../no-unsafe-any/{ => default}/commonjsModule.ts | 0 test/rules/no-unsafe-any/{ => default}/es6Module.ts | 0 test/rules/no-unsafe-any/{ => default}/test.ts.lint | 11 ----------- test/rules/no-unsafe-any/{ => default}/tsconfig.json | 0 test/rules/no-unsafe-any/{ => default}/tslint.json | 0 test/rules/no-unsafe-any/jsx/test.tsx.lint | 12 ++++++++++++ test/rules/no-unsafe-any/jsx/tsconfig.json | 8 ++++++++ test/rules/no-unsafe-any/jsx/tslint.json | 5 +++++ 8 files changed, 25 insertions(+), 11 deletions(-) rename test/rules/no-unsafe-any/{ => default}/commonjsModule.ts (100%) rename test/rules/no-unsafe-any/{ => default}/es6Module.ts (100%) rename test/rules/no-unsafe-any/{ => default}/test.ts.lint (95%) rename test/rules/no-unsafe-any/{ => default}/tsconfig.json (100%) rename test/rules/no-unsafe-any/{ => default}/tslint.json (100%) create mode 100644 test/rules/no-unsafe-any/jsx/test.tsx.lint create mode 100644 test/rules/no-unsafe-any/jsx/tsconfig.json create mode 100644 test/rules/no-unsafe-any/jsx/tslint.json diff --git a/test/rules/no-unsafe-any/commonjsModule.ts b/test/rules/no-unsafe-any/default/commonjsModule.ts similarity index 100% rename from test/rules/no-unsafe-any/commonjsModule.ts rename to test/rules/no-unsafe-any/default/commonjsModule.ts diff --git a/test/rules/no-unsafe-any/es6Module.ts b/test/rules/no-unsafe-any/default/es6Module.ts similarity index 100% rename from test/rules/no-unsafe-any/es6Module.ts rename to test/rules/no-unsafe-any/default/es6Module.ts diff --git a/test/rules/no-unsafe-any/test.ts.lint b/test/rules/no-unsafe-any/default/test.ts.lint similarity index 95% rename from test/rules/no-unsafe-any/test.ts.lint rename to test/rules/no-unsafe-any/default/test.ts.lint index 6430f39187b..3746de2495c 100644 --- a/test/rules/no-unsafe-any/test.ts.lint +++ b/test/rules/no-unsafe-any/default/test.ts.lint @@ -345,15 +345,4 @@ namespace TestNS2 { class Test6 implements NS.ITest {} } -deckare const AnyComponent: any; - -const nativeJsxFull = ; -const nativeJsxShort = ; - -const componentJsxFull = ; - ~~~~~~~~~~~~ [0] - ~~~~~~~~~~~~ [0] -const componentJsxShort = ; - ~~~~~~~~~~~~ [0] - [0]: Unsafe use of expression of type 'any'. diff --git a/test/rules/no-unsafe-any/tsconfig.json b/test/rules/no-unsafe-any/default/tsconfig.json similarity index 100% rename from test/rules/no-unsafe-any/tsconfig.json rename to test/rules/no-unsafe-any/default/tsconfig.json diff --git a/test/rules/no-unsafe-any/tslint.json b/test/rules/no-unsafe-any/default/tslint.json similarity index 100% rename from test/rules/no-unsafe-any/tslint.json rename to test/rules/no-unsafe-any/default/tslint.json diff --git a/test/rules/no-unsafe-any/jsx/test.tsx.lint b/test/rules/no-unsafe-any/jsx/test.tsx.lint new file mode 100644 index 00000000000..d8c9c3022bf --- /dev/null +++ b/test/rules/no-unsafe-any/jsx/test.tsx.lint @@ -0,0 +1,12 @@ +declare const AnyComponent: any; + +const nativeJsxFull = ; +const nativeJsxShort = ; + +const componentJsxFull = ; + ~~~~~~~~~~~~ [0] + ~~~~~~~~~~~~ [0] +const componentJsxShort = ; + ~~~~~~~~~~~~ [0] + +[0]: Unsafe use of expression of type 'any'. diff --git a/test/rules/no-unsafe-any/jsx/tsconfig.json b/test/rules/no-unsafe-any/jsx/tsconfig.json new file mode 100644 index 00000000000..0b75885e6e8 --- /dev/null +++ b/test/rules/no-unsafe-any/jsx/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "jsx": "react", + "module": "commonjs", + "target": "es6", + "experimentalDecorators": true + } +} diff --git a/test/rules/no-unsafe-any/jsx/tslint.json b/test/rules/no-unsafe-any/jsx/tslint.json new file mode 100644 index 00000000000..b44ff572bc8 --- /dev/null +++ b/test/rules/no-unsafe-any/jsx/tslint.json @@ -0,0 +1,5 @@ +{ + "rules": { + "no-unsafe-any": true + } +} From a37b276ee56ddfb402ceb73a7eed7e607db2bca2 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Thu, 3 May 2018 11:29:25 -0400 Subject: [PATCH 5/6] Added test cases for fragments --- test/rules/no-unsafe-any/jsx/test.tsx.lint | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/rules/no-unsafe-any/jsx/test.tsx.lint b/test/rules/no-unsafe-any/jsx/test.tsx.lint index d8c9c3022bf..edd49f1002c 100644 --- a/test/rules/no-unsafe-any/jsx/test.tsx.lint +++ b/test/rules/no-unsafe-any/jsx/test.tsx.lint @@ -3,6 +3,9 @@ declare const AnyComponent: any; const nativeJsxFull = ; const nativeJsxShort = ; +const nativeFragmentEmpty = <>; +const nativeFragmentFull = <>; + const componentJsxFull = ; ~~~~~~~~~~~~ [0] ~~~~~~~~~~~~ [0] From ab6483395b52c8caac7af7f0a85201783aef00a4 Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Thu, 3 May 2018 11:43:36 -0400 Subject: [PATCH 6/6] Added case for TS2.1 and fragments --- src/rules/noUnsafeAnyRule.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/rules/noUnsafeAnyRule.ts b/src/rules/noUnsafeAnyRule.ts index 33650268aff..a497011a4cb 100644 --- a/src/rules/noUnsafeAnyRule.ts +++ b/src/rules/noUnsafeAnyRule.ts @@ -404,10 +404,16 @@ const jsxElementTypes = new Set([ ]); function isJsxNativeElement(node: ts.Node): boolean { - return isIdentifier(node) - && node.parent !== undefined - && jsxElementTypes.has(node.parent.kind) - && isLowerCase(node.text[0]); + if (!isIdentifier(node) || node.parent === undefined) { + return false; + } + + // TypeScript <=2.1 incorrectly parses JSX fragments + if (node.text === "") { + return true; + } + + return jsxElementTypes.has(node.parent.kind) && isLowerCase(node.text[0]); } function isStringLike(expr: ts.Expression, checker: ts.TypeChecker): boolean {