From 5d648efbb619a0ec3e34b7b88b8ac0d8ff13666b Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 24 Apr 2018 12:14:59 -0700 Subject: [PATCH] Basic groundwork for suggestioning removing non-null assertions. --- src/compiler/checker.ts | 9 ++++-- src/compiler/diagnosticMessages.json | 12 +++++++ src/harness/tsconfig.json | 1 + src/server/tsconfig.json | 1 + .../removeUnnecessaryNonNullAssertion.ts | 31 +++++++++++++++++++ src/services/tsconfig.json | 1 + 6 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/services/codefixes/removeUnnecessaryNonNullAssertion.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 00ddb416e22..fe8b278b6ce 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16551,13 +16551,18 @@ namespace ts { undefinedDiagnostic?: DiagnosticMessage, nullOrUndefinedDiagnostic?: DiagnosticMessage, ) { - return checkNonNullType( - checkExpression(node), + const originalType = checkExpression(node); + const nonNullType = checkNonNullType( + originalType, node, nullDiagnostic, undefinedDiagnostic, nullOrUndefinedDiagnostic ); + if (strictNullChecks && originalType === nonNullType) { + error(node, Diagnostics.This_non_null_assertion_operator_is_unnecessary_for_type_1, typeToString(originalType)); + } + return nonNullType } function checkNonNullType( diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2c376da7bc9..bc2776b7137 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3894,6 +3894,10 @@ "category": "Suggestion", "code": 80004 }, + "This non-null assertion operator is unnecessary for type '{1}'": { + "category": "Suggestion", + "code": 80005 + }, "Add missing 'super()' call": { "category": "Message", @@ -4170,5 +4174,13 @@ "Generate 'get' and 'set' accessors": { "category": "Message", "code": 95046 + }, + "Remove unnecessary non-null assertion operator": { + "category": "Message", + "code": 95047 + }, + "Remove all unnecessary non-null assertion operators": { + "category": "Message", + "code": 95048 } } diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index a4ad8cb3797..dda6723c08a 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -113,6 +113,7 @@ "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", "../services/codefixes/useDefaultImport.ts", + "../services/codefixes/removeUnnecessaryNonNullAssertion.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", "../services/sourcemaps.ts", diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json index e6768f4edee..6ed7a5ced83 100644 --- a/src/server/tsconfig.json +++ b/src/server/tsconfig.json @@ -109,6 +109,7 @@ "../services/codefixes/fixInvalidImportSyntax.ts", "../services/codefixes/fixStrictClassInitialization.ts", "../services/codefixes/useDefaultImport.ts", + "../services/codefixes/removeUnnecessaryNonNullAssertion.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", "../services/sourcemaps.ts", diff --git a/src/services/codefixes/removeUnnecessaryNonNullAssertion.ts b/src/services/codefixes/removeUnnecessaryNonNullAssertion.ts new file mode 100644 index 00000000000..36d7c76d350 --- /dev/null +++ b/src/services/codefixes/removeUnnecessaryNonNullAssertion.ts @@ -0,0 +1,31 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "removeUnnecessaryNonNullAssertion"; + const errorCodes = [Diagnostics.This_non_null_assertion_operator_is_unnecessary_for_type_1.code]; + registerCodeFix({ + errorCodes, + getCodeActions(context) { + const qualifiedName = getNonNullAssertion(context.sourceFile, context.span.start); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName)); + return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unnecessary_non_null_assertion_operator, fixId, Diagnostics.Remove_all_unnecessary_non_null_assertion_operators)]; + }, + fixIds: [fixId], + getAllCodeActions: (context) => codeFixAll(context, errorCodes, (changes, diag) => { + const q = getNonNullAssertion(diag.file, diag.start); + if (q) { + doChange(changes, diag.file, q); + } + }), + }); + + function getNonNullAssertion(sourceFile: SourceFile, pos: number): NonNullExpression { + const qualifiedName = findAncestor(getTokenAtPosition(sourceFile, pos, /*includeJsDocComment*/ true), isNonNullExpression)!; + Debug.assert(!!qualifiedName, "Expected position to be owned by a non-null expression."); + return qualifiedName; + } + + function doChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, nonNullExpression: NonNullExpression): void { + const expr = nonNullExpression.expression; + changeTracker.replaceNode(sourceFile, nonNullExpression, expr); + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 75b46a5c49b..a7c44e2b005 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -106,6 +106,7 @@ "codefixes/fixInvalidImportSyntax.ts", "codefixes/fixStrictClassInitialization.ts", "codefixes/useDefaultImport.ts", + "codefixes/removeUnnecessaryNonNullAssertion.ts", "refactors/extractSymbol.ts", "refactors/generateGetAccessorAndSetAccessor.ts", "sourcemaps.ts",