From 4598943ed3d47731ef867a0ff67aeb7158492b38 Mon Sep 17 00:00:00 2001 From: Ryan Clarke Date: Thu, 1 Nov 2018 06:11:55 -0400 Subject: [PATCH] Add codefix for 'convert to unknown' diagnostic Codefix for diagnostic error 2352: "Conversion of type '{0}' to type '{1}' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first." Add codefix for both AsExpressions and TypeAssertions --- src/compiler/diagnosticMessages.json | 8 ++++ ...dConvertToUnknownForNonOverlappingTypes.ts | 42 +++++++++++++++++++ src/services/tsconfig.json | 1 + ...ConvertToUnknownForNonOverlappingTypes1.ts | 8 ++++ ...ConvertToUnknownForNonOverlappingTypes2.ts | 8 ++++ ...ConvertToUnknownForNonOverlappingTypes3.ts | 8 ++++ ...ConvertToUnknownForNonOverlappingTypes4.ts | 8 ++++ ...ConvertToUnknownForNonOverlappingTypes5.ts | 8 ++++ ...ConvertToUnknownForNonOverlappingTypes6.ts | 8 ++++ ...ConvertToUnknownForNonOverlappingTypes7.ts | 8 ++++ ...ConvertToUnknownForNonOverlappingTypes8.ts | 8 ++++ ...vertToUnknownForNonOverlappingTypes_all.ts | 22 ++++++++++ 12 files changed, 137 insertions(+) create mode 100644 src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes1.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes2.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes3.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes4.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes5.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes6.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes7.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes8.ts create mode 100644 tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes_all.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 01aa861cabb..07c1da2f775 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4724,5 +4724,13 @@ "Generate types for all packages without types": { "category": "Message", "code": 95068 + }, + "Add 'unknown' conversion for non-overlapping types": { + "category": "Message", + "code": 95069 + }, + "Add 'unknown' to all conversions of non-overlapping types": { + "category": "Message", + "code": 95070 } } diff --git a/src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts b/src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts new file mode 100644 index 00000000000..2860c956387 --- /dev/null +++ b/src/services/codefixes/addConvertToUnknownForNonOverlappingTypes.ts @@ -0,0 +1,42 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "addConvertToUnknownForNonOverlappingTypes"; + const errorCodes = [Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first.code]; + registerCodeFix({ + errorCodes, + getCodeActions: (context) => { + const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); + return [createCodeFixAction(fixId, changes, Diagnostics.Add_unknown_conversion_for_non_overlapping_types, fixId, Diagnostics.Add_unknown_to_all_conversions_of_non_overlapping_types)]; + }, + fixIds: [fixId], + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag.start)), + }); + + function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number) { + const token = getTokenAtPosition(sourceFile, pos); + + const asExpression = findAncestor(token, isAsExpression)!; + if (!!asExpression) { + Debug.assert(!!asExpression, "Expected position to be owned by an as-expression."); + + const nodeBeingCast = asExpression.getChildAt(0); + const expressionBeingCast = findAncestor(nodeBeingCast, isExpression)!; + Debug.assert(!!expressionBeingCast, "Expected position to be owned by an expression."); + + const replacement = createAsExpression(expressionBeingCast, createKeywordTypeNode(SyntaxKind.UnknownKeyword)); + changeTracker.replaceNode(sourceFile, expressionBeingCast, replacement); + } + + const typeAssertion = findAncestor(token, isTypeAssertion)!; + if (!!typeAssertion) { + Debug.assert(!!typeAssertion, "Expected position to be owned by a type assertion."); + + const nodeBeingCast = typeAssertion.getLastToken(); + const expressionBeingCast = findAncestor(nodeBeingCast, isExpression)!; + Debug.assert(!!expressionBeingCast, "Expected position to be owned by an expression."); + + const replacement = createTypeAssertion(createKeywordTypeNode(SyntaxKind.UnknownKeyword), expressionBeingCast); + changeTracker.replaceNode(sourceFile, expressionBeingCast, replacement); + } + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 66b1977ddc5..74be7988d2d 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -43,6 +43,7 @@ "textChanges.ts", "codeFixProvider.ts", "refactorProvider.ts", + "codefixes/addConvertToUnknownForNonOverlappingTypes.ts", "codefixes/addMissingInvocationForDecorator.ts", "codefixes/annotateWithTypeFromJSDoc.ts", "codefixes/convertFunctionToEs6Class.ts", diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes1.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes1.ts new file mode 100644 index 00000000000..2e67f62288b --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes1.ts @@ -0,0 +1,8 @@ +/// + +////[|0 as string|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `0 as unknown as string` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes2.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes2.ts new file mode 100644 index 00000000000..ce53304b22e --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes2.ts @@ -0,0 +1,8 @@ +/// + +////[|0 * (4 + 3) / 100 as string|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `0 * (4 + 3) / 100 as unknown as string` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes3.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes3.ts new file mode 100644 index 00000000000..1412121e3c8 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes3.ts @@ -0,0 +1,8 @@ +/// + +////[|["words"] as string|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `["words"] as unknown as string` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes4.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes4.ts new file mode 100644 index 00000000000..6781dad3fb2 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes4.ts @@ -0,0 +1,8 @@ +/// + +////[|"words" as object|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `"words" as unknown as object` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes5.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes5.ts new file mode 100644 index 00000000000..bfcebbbadf8 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes5.ts @@ -0,0 +1,8 @@ +/// + +////[|0|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `0` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes6.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes6.ts new file mode 100644 index 00000000000..74e8df3de5f --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes6.ts @@ -0,0 +1,8 @@ +/// + +////[|0 * (4 + 3) / 100|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `0 * (4 + 3) / 100` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes7.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes7.ts new file mode 100644 index 00000000000..43f9e91c1f8 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes7.ts @@ -0,0 +1,8 @@ +/// + +////[|["words"]|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `["words"]` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes8.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes8.ts new file mode 100644 index 00000000000..177e1237bd7 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes8.ts @@ -0,0 +1,8 @@ +/// + +////[|"words"|] + +verify.codeFix({ + description: "Add 'unknown' conversion for non-overlapping types", + newRangeContent: `"words"` +}); diff --git a/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes_all.ts b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes_all.ts new file mode 100644 index 00000000000..816761c6516 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddConvertToUnknownForNonOverlappingTypes_all.ts @@ -0,0 +1,22 @@ +/// + +////class C { +//// const s1 = 1 as string; +//// const o1 = s + " word" as object; +//// +//// const s2 = 2; +//// const o2 = s2; +////} + +verify.codeFixAll({ + fixId: "addConvertToUnknownForNonOverlappingTypes", + fixAllDescription: "Add 'unknown' to all conversions of non-overlapping types", + newFileContent: +`class C { + const s1 = 1 as unknown as string; + const o1 = s + " word" as unknown as object; + + const s2 = 2; + const o2 = s2; +}` +}); \ No newline at end of file