From 3f59aa9871509ece6d4e688cb1eb314c2cffaa62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Fri, 10 Aug 2018 09:05:28 +0800 Subject: [PATCH 01/20] stash --- src/compiler/diagnosticMessages.json | 20 ++++++++ src/services/codefixes/returnValueSurmise.ts | 50 ++++++++++++++++++++ src/services/tsconfig.json | 1 + 3 files changed, 71 insertions(+) create mode 100644 src/services/codefixes/returnValueSurmise.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 5d4d9ae701d38..f4c03e6698823 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4563,5 +4563,25 @@ "Add all missing imports": { "category": "Message", "code": 95064 + }, + "Add a return statement": { + "category": "Message", + "code": 95065 + }, + "Remove block body braces": { + "category": "Message", + "code": 95066 + }, + "Replace braces with parentheses": { + "category": "Message", + "code": 95067 + }, + "Wrap this block with parentheses": { + "category": "Message", + "code": 95068 + }, + "Surmise all return value": { + "category": "Message", + "code": 95069 } } diff --git a/src/services/codefixes/returnValueSurmise.ts b/src/services/codefixes/returnValueSurmise.ts new file mode 100644 index 0000000000000..e3db9ccd49cd2 --- /dev/null +++ b/src/services/codefixes/returnValueSurmise.ts @@ -0,0 +1,50 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "returnValueSurmise"; + const errorCodes = [ + Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value.code, + Diagnostics.Type_0_is_not_assignable_to_type_1.code, + Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code + ]; + registerCodeFix({ + errorCodes, + getCodeActions: context => { + const { host, sourceFile, span: { start } } = context; + return packageName === undefined ? [] + : [createCodeFixAction(fixId, /*changes*/ [], [Diagnostics.Install_0, packageName], fixId, Diagnostics.Install_all_missing_types_packages, getCommand(sourceFile.fileName, packageName))]; + }, + fixIds: [fixId], + getAllCodeActions: context => codeFixAll(context, errorCodes, (_, diag, commands) => { + const pkg = getTypesPackageNameToInstall(context.host, diag.file, diag.start, diag.code); + if (pkg) { + commands.push(getCommand(diag.file.fileName, pkg)); + } + }), + }); + + interface Info { + + } + + function getInfo (checker: TypeChecker, sourceFile: SourceFile, position: number): Info | undefined { + const node = getTokenAtPosition(sourceFile, position); + if (!node.parent) return undefined; + + if (isTypeNode(node) && isFunctionLikeDeclaration(node.parent)) { + + } + else if (isDeclarationName(node) && isVariableLike(node.parent)) { + + } + else { + Debug.fail('unknow pattern'); + } + + + const functionLikeDeclaration = cast(node.parent, isFunctionLike); + Debug.assert(functionLikeDeclaration.type === node); + + const returnType = checker.getTypeFromTypeNode(cast(node, isTypeNode)); + + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 355a4d302c0e8..f5b047752440f 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -51,6 +51,7 @@ "codefixes/fixClassIncorrectlyImplementsInterface.ts", "codefixes/importFixes.ts", "codefixes/fixSpelling.ts", + "codefixes/returnValueSurmise.ts", "codefixes/fixAddMissingMember.ts", "codefixes/fixCannotFindModule.ts", "codefixes/fixClassDoesntImplementInheritedAbstractMember.ts", From 767122129ec3571ea6d7126e8e6c7cb43303cddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Tue, 14 Aug 2018 11:44:13 +0800 Subject: [PATCH 02/20] add surmise for return type --- src/compiler/checker.ts | 1 + src/compiler/types.ts | 1 + src/services/codefixes/returnValueSurmise.ts | 255 ++++++++++++++---- .../fourslash/codeFixSurmiseReturnValue1.ts | 11 + .../fourslash/codeFixSurmiseReturnValue10.ts | 9 + .../fourslash/codeFixSurmiseReturnValue2.ts | 15 ++ .../fourslash/codeFixSurmiseReturnValue3.ts | 15 ++ .../fourslash/codeFixSurmiseReturnValue4.ts | 7 + .../fourslash/codeFixSurmiseReturnValue5.ts | 7 + .../fourslash/codeFixSurmiseReturnValue6.ts | 11 + .../fourslash/codeFixSurmiseReturnValue7.ts | 10 + .../fourslash/codeFixSurmiseReturnValue8.ts | 10 + .../fourslash/codeFixSurmiseReturnValue9.ts | 9 + 13 files changed, 311 insertions(+), 50 deletions(-) create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue1.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue10.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue2.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue3.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue4.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue5.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue6.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue7.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue8.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue9.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 649979e412084..a62af2fc229a4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -293,6 +293,7 @@ namespace ts { isArrayLikeType, isTypeInvalidDueToUnionDiscriminant, getAllPossiblePropertiesOfTypes, + isTypeAssignableTo, getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type), getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning), getSuggestionForNonexistentExport: (node, target) => getSuggestionForNonexistentExport(node, target), diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 06867a94c17cb..09690da3ec2b1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3072,6 +3072,7 @@ namespace ts { * e.g. it specifies `kind: "a"` and obj has `kind: "b"`. */ /* @internal */ isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression): boolean; + /* @internal */ isTypeAssignableTo(type1: Type, type2: Type): boolean; /** * For a union, will include a property if it's defined in *any* of the member types. * So for `{ a } | { b }`, this will include both `a` and `b`. diff --git a/src/services/codefixes/returnValueSurmise.ts b/src/services/codefixes/returnValueSurmise.ts index e3db9ccd49cd2..01680ccce7d9e 100644 --- a/src/services/codefixes/returnValueSurmise.ts +++ b/src/services/codefixes/returnValueSurmise.ts @@ -1,50 +1,205 @@ -/* @internal */ -namespace ts.codefix { - const fixId = "returnValueSurmise"; - const errorCodes = [ - Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value.code, - Diagnostics.Type_0_is_not_assignable_to_type_1.code, - Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code - ]; - registerCodeFix({ - errorCodes, - getCodeActions: context => { - const { host, sourceFile, span: { start } } = context; - return packageName === undefined ? [] - : [createCodeFixAction(fixId, /*changes*/ [], [Diagnostics.Install_0, packageName], fixId, Diagnostics.Install_all_missing_types_packages, getCommand(sourceFile.fileName, packageName))]; - }, - fixIds: [fixId], - getAllCodeActions: context => codeFixAll(context, errorCodes, (_, diag, commands) => { - const pkg = getTypesPackageNameToInstall(context.host, diag.file, diag.start, diag.code); - if (pkg) { - commands.push(getCommand(diag.file.fileName, pkg)); - } - }), - }); - - interface Info { - - } - - function getInfo (checker: TypeChecker, sourceFile: SourceFile, position: number): Info | undefined { - const node = getTokenAtPosition(sourceFile, position); - if (!node.parent) return undefined; - - if (isTypeNode(node) && isFunctionLikeDeclaration(node.parent)) { - - } - else if (isDeclarationName(node) && isVariableLike(node.parent)) { - - } - else { - Debug.fail('unknow pattern'); - } - - - const functionLikeDeclaration = cast(node.parent, isFunctionLike); - Debug.assert(functionLikeDeclaration.type === node); - - const returnType = checker.getTypeFromTypeNode(cast(node, isTypeNode)); - - } -} +/* @internal */ +namespace ts.codefix { + const fixId = "returnValueSurmise"; + const fixIdAddReturnStatement = "fixAddReturnStatement"; + const fixIdRemoveBlockBodyBrace = "fixRemoveBlockBodyBrace"; + const fixIdReplaceBraceWithParen = "fixReplaceBraceWithParen"; + const fixIdWrapTheBlockWithParen = "fixWrapTheBlockWithParen"; + const errorCodes = [ + Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value.code, + Diagnostics.Type_0_is_not_assignable_to_type_1.code, + Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code + ]; + + enum FixKind { + MissingReturnStatement, + MissingParentheses + } + + interface Info { + declaration: FunctionLikeDeclaration; + fixKind: FixKind; + } + + registerCodeFix({ + errorCodes, + fixIds: [fixIdAddReturnStatement, fixIdRemoveBlockBodyBrace, fixIdReplaceBraceWithParen, fixIdWrapTheBlockWithParen], + getCodeActions: context => { + const { program, sourceFile, span: { start } } = context; + const info = getInfo(program.getTypeChecker(), sourceFile, start); + if (!info) return undefined; + + if (info.fixKind === FixKind.MissingReturnStatement) { + return [ + getActionForfixAddReturnStatement(context, info.declaration), + getActionForfixRemoveBlockBodyBrace(context, info.declaration), + getActionForfixReplaceBraceWithParen(context, info.declaration) + ]; + } + else { + return [getActionForfixWrapTheBlockWithParen(context, info.declaration)]; + } + }, + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { + const info = getInfo(context.program.getTypeChecker(), diag.file, diag.start); + if (!info) return undefined; + + switch (context.fixId) { + case fixIdAddReturnStatement: + addReturnStatement(changes, diag.file, info.declaration); + break; + case fixIdRemoveBlockBodyBrace: + removeBlockBodyBrace(changes, diag.file, info.declaration, /* withParen */ false); + break; + case fixIdReplaceBraceWithParen: + removeBlockBodyBrace(changes, diag.file, info.declaration, /* withParen */ true); + break; + case fixIdWrapTheBlockWithParen: + wrapBlockWithParen(changes, diag.file, info.declaration); + break; + default: + Debug.fail(JSON.stringify(context.fixId)); + } + }), + }); + + function updateFunctionLikeBody(declaration: FunctionLikeDeclaration, body: Block): FunctionLikeDeclaration { + switch (declaration.kind) { + case SyntaxKind.FunctionDeclaration: + return createFunctionDeclaration(declaration.decorators, declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.typeParameters, declaration.parameters, declaration.type, body); + case SyntaxKind.MethodDeclaration: + return createMethod(declaration.decorators, declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.questionToken, declaration.typeParameters, declaration.parameters, declaration.type, body); + case SyntaxKind.GetAccessor: + return createGetAccessor(declaration.decorators, declaration.modifiers, declaration.name, declaration.parameters, declaration.type, body); + case SyntaxKind.SetAccessor: + return createSetAccessor(declaration.decorators, declaration.modifiers, declaration.name, declaration.parameters, body); + case SyntaxKind.Constructor: + return createConstructor(declaration.decorators, declaration.modifiers, declaration.parameters, body); + case SyntaxKind.FunctionExpression: + return createFunctionExpression(declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.typeParameters, declaration.parameters, declaration.type, body); + case SyntaxKind.ArrowFunction: + return createArrowFunction(declaration.modifiers, declaration.typeParameters, declaration.parameters, declaration.type, declaration.equalsGreaterThanToken, body); + } + } + + function getFixInfo(checker: TypeChecker, declaration: FunctionLikeDeclaration, expectType: Type): Info | undefined { + if (!declaration.body || !isBlock(declaration.body) || length(declaration.body.statements) !== 1) return undefined; + + const firstStatement = first(declaration.body.statements); + if (isExpressionStatement(firstStatement)) { + const maybeFunction = updateFunctionLikeBody(declaration, createBlock([createReturn(firstStatement.expression)])); + const maybeType = checker.getTypeAtLocation(maybeFunction); + if (checker.isTypeAssignableTo(maybeType, expectType)) { + return { + declaration, + fixKind: FixKind.MissingReturnStatement + }; + } + } + else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { + const maybeFunction = updateFunctionLikeBody(declaration, createBlock([createReturn( + createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]) + )])); + const maybeType = checker.getTypeAtLocation(maybeFunction); + if (checker.isTypeAssignableTo(maybeType, expectType)) { + return { + declaration, + fixKind: FixKind.MissingParentheses + }; + } + } + + return undefined; + } + + function getFixInfoFromTypeAnnotation(checker: TypeChecker, declaration: FunctionLikeDeclaration, expectType: Type): Info | undefined { + if (!declaration.body || !isBlock(declaration.body) || length(declaration.body.statements) !== 1) return undefined; + + const firstStatement = first(declaration.body.statements); + if (isExpressionStatement(firstStatement)) { + const actualType = checker.getTypeAtLocation(firstStatement.expression); + if (checker.isTypeAssignableTo(actualType, expectType)) { + return { + declaration, + fixKind: FixKind.MissingReturnStatement + }; + } + } + else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { + const node = createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]); + const type = checker.getTypeAtLocation(node); + if (checker.isTypeAssignableTo(type, expectType)) { + return { + declaration, + fixKind: FixKind.MissingParentheses + }; + } + } + + return undefined; + } + + function getInfo(checker: TypeChecker, sourceFile: SourceFile, position: number): Info | undefined { + const node = getTokenAtPosition(sourceFile, position); + if (!node.parent) return undefined; + + const declaration = findAncestor(node.parent, isFunctionLikeDeclaration); + if (declaration) { + if (declaration.body && declaration.type && rangeContainsRange(declaration.type, node)) { + // check by type annotation + return getFixInfoFromTypeAnnotation(checker, declaration, checker.getTypeFromTypeNode(declaration.type)); + } + else if (isCallExpression(declaration.parent) && declaration.body) { + const pos = declaration.parent.arguments.indexOf(declaration); + const type = checker.getContextualTypeForArgumentAtIndex(declaration.parent, pos); + if (!type) return undefined; + return getFixInfo(checker, declaration, type); + } + } + else if (isDeclarationName(node) && isVariableDeclaration(node.parent) && node.parent.initializer && node.parent.type) { + const initializer = skipParentheses(node.parent.initializer); + if (!isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; + return getFixInfo(checker, initializer, checker.getTypeFromTypeNode(node.parent.type)); + } + Debug.fail("unknow pattern"); + } + + function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration) { + const body = cast(declaration.body, isBlock); + const firstStatement = cast(first(body.statements), isExpressionStatement); + changes.replaceNode(sourceFile, firstStatement, createReturn(firstStatement.expression)); + } + + function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration, withParen: boolean) { + const body = cast(declaration.body, isBlock); + const expression = cast(first(body.statements), isExpressionStatement).expression; + changes.replaceNode(sourceFile, body, withParen ? createParen(expression) : expression); + } + + function wrapBlockWithParen(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration) { + const body = cast(declaration.body, isBlock); + const labeledStatement = cast(first(body.statements), isLabeledStatement); + const expression = cast(labeledStatement.statement, isExpressionStatement).expression; + changes.replaceNode(sourceFile, body, createParen(createObjectLiteral([createPropertyAssignment(labeledStatement.label, expression)]))); + } + + function getActionForfixAddReturnStatement(context: CodeFixContext, declaration: FunctionLikeDeclaration) { + const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, declaration)); + return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Surmise_all_return_value); + } + + function getActionForfixRemoveBlockBodyBrace(context: CodeFixContext, declaration: FunctionLikeDeclaration) { + const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, /* withParen */ false)); + return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Surmise_all_return_value); + } + + function getActionForfixReplaceBraceWithParen(context: CodeFixContext, declaration: FunctionLikeDeclaration) { + const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, /* withParen */ true)); + return createCodeFixAction(fixId, changes, Diagnostics.Replace_braces_with_parentheses, fixIdReplaceBraceWithParen, Diagnostics.Surmise_all_return_value); + } + + function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: FunctionLikeDeclaration) { + const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration)); + return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_block_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Surmise_all_return_value); + } +} diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts new file mode 100644 index 0000000000000..e80ba8fd2ab3e --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts @@ -0,0 +1,11 @@ +/// + +//// function Foo (): number { +//// 1 +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts new file mode 100644 index 0000000000000..c8a94d90a7d6d --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts @@ -0,0 +1,9 @@ +/// + +//// const a: ((() => number) | (() => undefined)) = () => { 1 } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts new file mode 100644 index 0000000000000..b7e1086b08846 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts @@ -0,0 +1,15 @@ +/// + +//// interface A { +//// foo: number +//// } + +//// function Foo (): A { +//// ({ foo: 1 }) +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts new file mode 100644 index 0000000000000..0e3a845c5ec64 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts @@ -0,0 +1,15 @@ +/// + +//// interface A { +//// foo: number +//// } + +//// function Foo (): A | number { +//// 1 +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue4.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue4.ts new file mode 100644 index 0000000000000..e1270bee3f361 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue4.ts @@ -0,0 +1,7 @@ +/// + +//// function Foo (): any { +//// 1 +//// } + +verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue5.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue5.ts new file mode 100644 index 0000000000000..de457636368eb --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue5.ts @@ -0,0 +1,7 @@ +/// + +//// function Foo (): void { +//// undefined +//// } + +verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts new file mode 100644 index 0000000000000..deb5170c5f803 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts @@ -0,0 +1,11 @@ +/// + +//// function Foo (): undefined { +//// undefined +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts new file mode 100644 index 0000000000000..5a40e0a7f66dd --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts @@ -0,0 +1,10 @@ +/// + +//// function Foo (a: () => number) { a() } +//// Foo(() => { 1 }) + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts new file mode 100644 index 0000000000000..8ebc2500cabcc --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts @@ -0,0 +1,10 @@ +/// + +//// function Foo (a: (() => number) | (() => undefined) ) { a() } +//// Foo(() => { 1 }) + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts new file mode 100644 index 0000000000000..8abf79d631f7b --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts @@ -0,0 +1,9 @@ +/// + +//// const a: () => number = () => { 1 } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); From 0b1e6cc6ea15df4a599d317775bfa9a52885fb78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Tue, 14 Aug 2018 18:50:16 +0800 Subject: [PATCH 03/20] add support for more case --- src/services/codefixes/returnValueSurmise.ts | 114 +++++++++--------- .../fourslash/codeFixSurmiseReturnValue11.ts | 15 +++ .../fourslash/codeFixSurmiseReturnValue12.ts | 16 +++ .../fourslash/codeFixSurmiseReturnValue13.ts | 15 +++ .../fourslash/codeFixSurmiseReturnValue14.ts | 13 ++ .../fourslash/codeFixSurmiseReturnValue15.ts | 12 ++ 6 files changed, 129 insertions(+), 56 deletions(-) create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue11.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue12.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue13.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue14.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue15.ts diff --git a/src/services/codefixes/returnValueSurmise.ts b/src/services/codefixes/returnValueSurmise.ts index 01680ccce7d9e..75d9f82c982a0 100644 --- a/src/services/codefixes/returnValueSurmise.ts +++ b/src/services/codefixes/returnValueSurmise.ts @@ -16,11 +16,18 @@ namespace ts.codefix { MissingParentheses } - interface Info { + interface MissingReturnInfo { + kind: FixKind.MissingReturnStatement; declaration: FunctionLikeDeclaration; - fixKind: FixKind; } + interface MissingParenthesesInfo { + kind: FixKind.MissingParentheses; + declaration: ArrowFunction; + } + + type Info = MissingReturnInfo | MissingParenthesesInfo; + registerCodeFix({ errorCodes, fixIds: [fixIdAddReturnStatement, fixIdRemoveBlockBodyBrace, fixIdReplaceBraceWithParen, fixIdWrapTheBlockWithParen], @@ -29,7 +36,7 @@ namespace ts.codefix { const info = getInfo(program.getTypeChecker(), sourceFile, start); if (!info) return undefined; - if (info.fixKind === FixKind.MissingReturnStatement) { + if (info.kind === FixKind.MissingReturnStatement) { return [ getActionForfixAddReturnStatement(context, info.declaration), getActionForfixRemoveBlockBodyBrace(context, info.declaration), @@ -55,7 +62,7 @@ namespace ts.codefix { removeBlockBodyBrace(changes, diag.file, info.declaration, /* withParen */ true); break; case fixIdWrapTheBlockWithParen: - wrapBlockWithParen(changes, diag.file, info.declaration); + wrapBlockWithParen(changes, diag.file, info.declaration); break; default: Debug.fail(JSON.stringify(context.fixId)); @@ -82,61 +89,43 @@ namespace ts.codefix { } } - function getFixInfo(checker: TypeChecker, declaration: FunctionLikeDeclaration, expectType: Type): Info | undefined { + function getFixInfo(checker: TypeChecker, declaration: FunctionLikeDeclaration, expectType: Type, isFunctionType: boolean): Info | undefined { if (!declaration.body || !isBlock(declaration.body) || length(declaration.body.statements) !== 1) return undefined; const firstStatement = first(declaration.body.statements); - if (isExpressionStatement(firstStatement)) { - const maybeFunction = updateFunctionLikeBody(declaration, createBlock([createReturn(firstStatement.expression)])); - const maybeType = checker.getTypeAtLocation(maybeFunction); - if (checker.isTypeAssignableTo(maybeType, expectType)) { + if (isExpressionStatement(firstStatement) && checkFixedAssignableTo(checker, declaration, firstStatement.expression, expectType, isFunctionType)) { + return { + declaration, + kind: FixKind.MissingReturnStatement + }; + } + else if (isArrowFunction(declaration) && isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { + const node = createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]); + if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) { return { declaration, - fixKind: FixKind.MissingReturnStatement + kind: FixKind.MissingParentheses }; } } - else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { - const maybeFunction = updateFunctionLikeBody(declaration, createBlock([createReturn( - createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]) - )])); - const maybeType = checker.getTypeAtLocation(maybeFunction); - if (checker.isTypeAssignableTo(maybeType, expectType)) { - return { - declaration, - fixKind: FixKind.MissingParentheses - }; + else if (isBlock(firstStatement) && length(firstStatement.statements) === 1) { + const firstBlockStatement = first(firstStatement.statements); + if (isLabeledStatement(firstBlockStatement) && isExpressionStatement(firstBlockStatement.statement)) { + const node = createObjectLiteral([createPropertyAssignment(firstBlockStatement.label, firstBlockStatement.statement.expression)]); + if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) { + return { + declaration, + kind: FixKind.MissingReturnStatement + }; + } } } return undefined; } - function getFixInfoFromTypeAnnotation(checker: TypeChecker, declaration: FunctionLikeDeclaration, expectType: Type): Info | undefined { - if (!declaration.body || !isBlock(declaration.body) || length(declaration.body.statements) !== 1) return undefined; - - const firstStatement = first(declaration.body.statements); - if (isExpressionStatement(firstStatement)) { - const actualType = checker.getTypeAtLocation(firstStatement.expression); - if (checker.isTypeAssignableTo(actualType, expectType)) { - return { - declaration, - fixKind: FixKind.MissingReturnStatement - }; - } - } - else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { - const node = createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]); - const type = checker.getTypeAtLocation(node); - if (checker.isTypeAssignableTo(type, expectType)) { - return { - declaration, - fixKind: FixKind.MissingParentheses - }; - } - } - - return undefined; + function checkFixedAssignableTo (checker: TypeChecker, declaration: FunctionLikeDeclaration, expr: Expression, type: Type, isFunctionType: boolean) { + return checker.isTypeAssignableTo(checker.getTypeAtLocation(isFunctionType ? updateFunctionLikeBody(declaration, createBlock([createReturn(expr)])) : expr), type); } function getInfo(checker: TypeChecker, sourceFile: SourceFile, position: number): Info | undefined { @@ -146,41 +135,54 @@ namespace ts.codefix { const declaration = findAncestor(node.parent, isFunctionLikeDeclaration); if (declaration) { if (declaration.body && declaration.type && rangeContainsRange(declaration.type, node)) { - // check by type annotation - return getFixInfoFromTypeAnnotation(checker, declaration, checker.getTypeFromTypeNode(declaration.type)); + return getFixInfo(checker, declaration, checker.getTypeFromTypeNode(declaration.type), /* isFunctionType */ false); } else if (isCallExpression(declaration.parent) && declaration.body) { const pos = declaration.parent.arguments.indexOf(declaration); const type = checker.getContextualTypeForArgumentAtIndex(declaration.parent, pos); if (!type) return undefined; - return getFixInfo(checker, declaration, type); + return getFixInfo(checker, declaration, type, /* isFunctionType */ true); } } else if (isDeclarationName(node) && isVariableDeclaration(node.parent) && node.parent.initializer && node.parent.type) { const initializer = skipParentheses(node.parent.initializer); if (!isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; - return getFixInfo(checker, initializer, checker.getTypeFromTypeNode(node.parent.type)); + return getFixInfo(checker, initializer, checker.getTypeFromTypeNode(node.parent.type), /* isFunctionType */ true); + } + return Debug.fail("unknow pattern"); + } + + function getReturnExpression (stmt: Statement) { + if (isExpressionStatement(stmt)) { + return stmt.expression; } - Debug.fail("unknow pattern"); + else if (isBlock(stmt) && length(stmt.statements) === 1) { + const block = first(stmt.statements); + if (isLabeledStatement(block) && isExpressionStatement(block.statement)) { + return block.statement.expression; + } + } + + return Debug.fail("unknow statement"); } function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration) { const body = cast(declaration.body, isBlock); - const firstStatement = cast(first(body.statements), isExpressionStatement); - changes.replaceNode(sourceFile, firstStatement, createReturn(firstStatement.expression)); + const firstStatement = first(body.statements); + changes.replaceNode(sourceFile, firstStatement, createReturn(getReturnExpression(firstStatement))); } function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration, withParen: boolean) { const body = cast(declaration.body, isBlock); - const expression = cast(first(body.statements), isExpressionStatement).expression; + const expression = getReturnExpression(first(body.statements)); changes.replaceNode(sourceFile, body, withParen ? createParen(expression) : expression); } - function wrapBlockWithParen(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration) { + function wrapBlockWithParen(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction) { const body = cast(declaration.body, isBlock); const labeledStatement = cast(first(body.statements), isLabeledStatement); const expression = cast(labeledStatement.statement, isExpressionStatement).expression; - changes.replaceNode(sourceFile, body, createParen(createObjectLiteral([createPropertyAssignment(labeledStatement.label, expression)]))); + changes.replaceNode(sourceFile, declaration.body, createParen(createObjectLiteral([createPropertyAssignment(labeledStatement.label, expression)]))); } function getActionForfixAddReturnStatement(context: CodeFixContext, declaration: FunctionLikeDeclaration) { @@ -198,7 +200,7 @@ namespace ts.codefix { return createCodeFixAction(fixId, changes, Diagnostics.Replace_braces_with_parentheses, fixIdReplaceBraceWithParen, Diagnostics.Surmise_all_return_value); } - function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: FunctionLikeDeclaration) { + function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction) { const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration)); return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_block_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Surmise_all_return_value); } diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts new file mode 100644 index 0000000000000..2e06f9958ad22 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts @@ -0,0 +1,15 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// function Foo (): A { +//// { bar: '123' } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts new file mode 100644 index 0000000000000..8672eb7882fd0 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts @@ -0,0 +1,16 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// function Foo (a: () => A) { a() } +//// Foo(() => { +//// { bar: '123' } +//// }) + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts new file mode 100644 index 0000000000000..d1f2f275b41bf --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts @@ -0,0 +1,15 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// const a: () => A = () => { +//// { bar: '1' } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts new file mode 100644 index 0000000000000..59091741c13d8 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts @@ -0,0 +1,13 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// const a: () => A = () => { +//// bar: '1' +//// } + +verify.codeFixAvailable([ + { description: 'Wrap this block with parentheses' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts new file mode 100644 index 0000000000000..8e2970fea2c0e --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts @@ -0,0 +1,12 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// function Foo (a: () => A) { a() } +//// Foo(() => { bar: '1' }) + +verify.codeFixAvailable([ + { description: 'Wrap this block with parentheses' }, + { description: 'Remove unused label' }, +]); From 1f9b9c09f4ab269f09afe28bf8c9031fab3d6f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Thu, 16 Aug 2018 18:22:39 +0800 Subject: [PATCH 04/20] add more test case --- src/services/codefixes/returnValueSurmise.ts | 102 +++++++--------- .../addOrRemoveBracesToArrowFunction.ts | 4 - src/services/utilities.ts | 5 + .../fourslash/codeFixSurmiseReturnValue1.ts | 2 - .../fourslash/codeFixSurmiseReturnValue11.ts | 2 - .../fourslash/codeFixSurmiseReturnValue13.ts | 4 + .../fourslash/codeFixSurmiseReturnValue16.ts | 13 ++ .../fourslash/codeFixSurmiseReturnValue17.ts | 14 +++ .../fourslash/codeFixSurmiseReturnValue2.ts | 2 - .../fourslash/codeFixSurmiseReturnValue3.ts | 2 - .../fourslash/codeFixSurmiseReturnValue6.ts | 2 - .../codeFixSurmiseReturnValue_all1.ts | 115 ++++++++++++++++++ .../codeFixSurmiseReturnValue_all2.ts | 95 +++++++++++++++ .../codeFixSurmiseReturnValue_all3.ts | 95 +++++++++++++++ .../codeFixSurmiseReturnValue_all4.ts | 95 +++++++++++++++ 15 files changed, 483 insertions(+), 69 deletions(-) create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue16.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue17.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts diff --git a/src/services/codefixes/returnValueSurmise.ts b/src/services/codefixes/returnValueSurmise.ts index 75d9f82c982a0..791e9c8ff6f92 100644 --- a/src/services/codefixes/returnValueSurmise.ts +++ b/src/services/codefixes/returnValueSurmise.ts @@ -19,14 +19,16 @@ namespace ts.codefix { interface MissingReturnInfo { kind: FixKind.MissingReturnStatement; declaration: FunctionLikeDeclaration; + expression: Expression; } - interface MissingParenthesesInfo { + interface MissingParenInfo { kind: FixKind.MissingParentheses; declaration: ArrowFunction; + expression: Expression; } - type Info = MissingReturnInfo | MissingParenthesesInfo; + type Info = MissingReturnInfo | MissingParenInfo; registerCodeFix({ errorCodes, @@ -37,14 +39,15 @@ namespace ts.codefix { if (!info) return undefined; if (info.kind === FixKind.MissingReturnStatement) { - return [ - getActionForfixAddReturnStatement(context, info.declaration), - getActionForfixRemoveBlockBodyBrace(context, info.declaration), - getActionForfixReplaceBraceWithParen(context, info.declaration) - ]; + return concatenate( + [getActionForfixAddReturnStatement(context, info.declaration, info.expression)], + isArrowFunction(info.declaration) ? [ + getActionForfixRemoveBlockBodyBrace(context, info.declaration, info.expression), + getActionForfixReplaceBraceWithParen(context, info.declaration, info.expression) + ] : undefined); } else { - return [getActionForfixWrapTheBlockWithParen(context, info.declaration)]; + return [getActionForfixWrapTheBlockWithParen(context, info.declaration, info.expression)]; } }, getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { @@ -53,16 +56,19 @@ namespace ts.codefix { switch (context.fixId) { case fixIdAddReturnStatement: - addReturnStatement(changes, diag.file, info.declaration); + addReturnStatement(changes, diag.file, info.declaration, info.expression); break; case fixIdRemoveBlockBodyBrace: - removeBlockBodyBrace(changes, diag.file, info.declaration, /* withParen */ false); + if (!isArrowFunction(info.declaration)) return undefined; + removeBlockBodyBrace(changes, diag.file, info.declaration, info.expression, /* withParen */ false); break; case fixIdReplaceBraceWithParen: - removeBlockBodyBrace(changes, diag.file, info.declaration, /* withParen */ true); + if (!isArrowFunction(info.declaration)) return undefined; + removeBlockBodyBrace(changes, diag.file, info.declaration, info.expression, /* withParen */ true); break; case fixIdWrapTheBlockWithParen: - wrapBlockWithParen(changes, diag.file, info.declaration); + if (!isArrowFunction(info.declaration)) return undefined; + wrapBlockWithParen(changes, diag.file, info.declaration, info.expression); break; default: Debug.fail(JSON.stringify(context.fixId)); @@ -96,16 +102,22 @@ namespace ts.codefix { if (isExpressionStatement(firstStatement) && checkFixedAssignableTo(checker, declaration, firstStatement.expression, expectType, isFunctionType)) { return { declaration, - kind: FixKind.MissingReturnStatement + kind: FixKind.MissingReturnStatement, + expression: firstStatement.expression }; } - else if (isArrowFunction(declaration) && isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { + else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { const node = createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]); if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) { - return { + return isArrowFunction(declaration) ? { declaration, - kind: FixKind.MissingParentheses - }; + kind: FixKind.MissingParentheses, + expression: node + } : { + declaration, + kind: FixKind.MissingReturnStatement, + expression: node + }; } } else if (isBlock(firstStatement) && length(firstStatement.statements) === 1) { @@ -115,7 +127,8 @@ namespace ts.codefix { if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) { return { declaration, - kind: FixKind.MissingReturnStatement + kind: FixKind.MissingReturnStatement, + expression: node }; } } @@ -124,7 +137,7 @@ namespace ts.codefix { return undefined; } - function checkFixedAssignableTo (checker: TypeChecker, declaration: FunctionLikeDeclaration, expr: Expression, type: Type, isFunctionType: boolean) { + function checkFixedAssignableTo(checker: TypeChecker, declaration: FunctionLikeDeclaration, expr: Expression, type: Type, isFunctionType: boolean) { return checker.isTypeAssignableTo(checker.getTypeAtLocation(isFunctionType ? updateFunctionLikeBody(declaration, createBlock([createReturn(expr)])) : expr), type); } @@ -149,59 +162,38 @@ namespace ts.codefix { if (!isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; return getFixInfo(checker, initializer, checker.getTypeFromTypeNode(node.parent.type), /* isFunctionType */ true); } - return Debug.fail("unknow pattern"); - } - - function getReturnExpression (stmt: Statement) { - if (isExpressionStatement(stmt)) { - return stmt.expression; - } - else if (isBlock(stmt) && length(stmt.statements) === 1) { - const block = first(stmt.statements); - if (isLabeledStatement(block) && isExpressionStatement(block.statement)) { - return block.statement.expression; - } - } - - return Debug.fail("unknow statement"); + return undefined; } - function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration) { - const body = cast(declaration.body, isBlock); - const firstStatement = first(body.statements); - changes.replaceNode(sourceFile, firstStatement, createReturn(getReturnExpression(firstStatement))); + function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration, expression: Expression) { + changes.replaceNode(sourceFile, declaration.body!, createBlock([createReturn(expression)])); } - function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration, withParen: boolean) { - const body = cast(declaration.body, isBlock); - const expression = getReturnExpression(first(body.statements)); - changes.replaceNode(sourceFile, body, withParen ? createParen(expression) : expression); + function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression, withParen: boolean) { + changes.replaceNode(sourceFile, declaration.body, (withParen || needsParentheses(expression)) ? createParen(expression) : expression); } - function wrapBlockWithParen(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction) { - const body = cast(declaration.body, isBlock); - const labeledStatement = cast(first(body.statements), isLabeledStatement); - const expression = cast(labeledStatement.statement, isExpressionStatement).expression; - changes.replaceNode(sourceFile, declaration.body, createParen(createObjectLiteral([createPropertyAssignment(labeledStatement.label, expression)]))); + function wrapBlockWithParen(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression) { + changes.replaceNode(sourceFile, declaration.body, createParen(expression)); } - function getActionForfixAddReturnStatement(context: CodeFixContext, declaration: FunctionLikeDeclaration) { - const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, declaration)); + function getActionForfixAddReturnStatement(context: CodeFixContext, declaration: FunctionLikeDeclaration, expression: Expression) { + const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, declaration, expression)); return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Surmise_all_return_value); } - function getActionForfixRemoveBlockBodyBrace(context: CodeFixContext, declaration: FunctionLikeDeclaration) { - const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, /* withParen */ false)); + function getActionForfixRemoveBlockBodyBrace(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { + const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, /* withParen */ false)); return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Surmise_all_return_value); } - function getActionForfixReplaceBraceWithParen(context: CodeFixContext, declaration: FunctionLikeDeclaration) { - const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, /* withParen */ true)); + function getActionForfixReplaceBraceWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { + const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, /* withParen */ true)); return createCodeFixAction(fixId, changes, Diagnostics.Replace_braces_with_parentheses, fixIdReplaceBraceWithParen, Diagnostics.Surmise_all_return_value); } - function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction) { - const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration)); + function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { + const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration, expression)); return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_block_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Surmise_all_return_value); } } diff --git a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index 57e7dbd97a76c..789dab59dd396 100644 --- a/src/services/refactors/addOrRemoveBracesToArrowFunction.ts +++ b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts @@ -64,10 +64,6 @@ namespace ts.refactor.addOrRemoveBracesToArrowFunction { return { renameFilename: undefined, renameLocation: undefined, edits }; } - function needsParentheses(expression: Expression) { - return isBinaryExpression(expression) && expression.operatorToken.kind === SyntaxKind.CommaToken || isObjectLiteralExpression(expression); - } - function getConvertibleArrowFunctionAtPosition(file: SourceFile, startPosition: number): Info | undefined { const node = getTokenAtPosition(file, startPosition); const func = getContainingFunction(node); diff --git a/src/services/utilities.ts b/src/services/utilities.ts index e8d9c8a904793..9e9621c2f6fa8 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1783,4 +1783,9 @@ namespace ts { if (idx === -1) idx = change.indexOf('"' + name); return idx === -1 ? -1 : idx + 1; } + + /* @internal */ + export function needsParentheses(expression: Expression) { + return isBinaryExpression(expression) && expression.operatorToken.kind === SyntaxKind.CommaToken || isObjectLiteralExpression(expression); + } } diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts index e80ba8fd2ab3e..4dc5bdfc68386 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts @@ -6,6 +6,4 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts index 2e06f9958ad22..a57c05134959c 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts @@ -9,7 +9,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts index d1f2f275b41bf..37360499d9e5e 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts @@ -13,3 +13,7 @@ verify.codeFixAvailable([ { description: 'Replace braces with parentheses' }, { description: 'Remove unused label' }, ]); + +interface A { + bar: string +} diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts new file mode 100644 index 0000000000000..5292a10f776e7 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts @@ -0,0 +1,13 @@ +/// + +//// interface A { +//// bar: string +//// } +//// +//// function Foo (a: () => A) { a() } +//// Foo(() => { bar: '1' }) + +verify.codeFixAvailable([ + { description: 'Wrap this block with parentheses' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue17.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue17.ts new file mode 100644 index 0000000000000..2572b478e1756 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue17.ts @@ -0,0 +1,14 @@ +/// + +//// interface A { +//// bar: string +//// } +//// +//// function Foo (): A { +//// bar: '123' +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts index b7e1086b08846..9d361067c4ca5 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts @@ -10,6 +10,4 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts index 0e3a845c5ec64..364da21f42dfc 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts @@ -10,6 +10,4 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts index deb5170c5f803..693145f8c61b8 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts @@ -6,6 +6,4 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts new file mode 100644 index 0000000000000..404015144937f --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -0,0 +1,115 @@ +/// + +//// interface A { +//// bar: string +//// } +//// +//// function foo1 (_a: () => number ) { } +//// foo1(() => { +//// 1 +//// }) +//// function foo2 (_a: () => A) { } +//// foo2(() => { +//// { bar: '1' } +//// }) +//// foo2(() => { +//// bar: '1' +//// }) +//// function foo3 (_a: () => A | number) { } +//// foo3(() => { +//// 1 +//// }) +//// foo3(() => { +//// bar: '1' +//// }) +//// +//// function bar1 (): number { +//// 1 +//// } +//// function bar2 (): A { +//// { bar: '1' } +//// } +//// function bar3 (): A { +//// bar: '1' +//// } +//// function bar4 (): A | number { +//// 1 +//// } +//// function bar5(): A | number { +//// bar: '1' +//// } +// +//// const baz1: () => number = () => { +//// 1 +//// } +//// const baz2: () => A = () => { +//// { bar: '1' } +//// } +//// const baz3: () => A = () => { +//// bar: '1' +//// } +//// const baz4: ((() => number) | (() => A)) = () => { +//// 1 +//// } +//// const baz5: ((() => number) | (() => A)) = () => { +//// bar: '1' +//// } + +verify.codeFixAll({ + fixId: "fixAddReturnStatement", + fixAllDescription: "Surmise all return value", + newFileContent: +`interface A { + bar: string +} + +function foo1 (_a: () => number ) { } +foo1(() => { + return 1; +}) +function foo2 (_a: () => A) { } +foo2(() => { + return { bar: '1' }; +}) +foo2(() => { + return { bar: '1' }; +}) +function foo3 (_a: () => A | number) { } +foo3(() => { + return 1; +}) +foo3(() => { + return { bar: '1' }; +}) + +function bar1 (): number { + return 1; +} +function bar2 (): A { + return { bar: '1' }; +} +function bar3 (): A { + return { bar: '1' }; +} +function bar4 (): A | number { + return 1; +} +function bar5(): A | number { + return { bar: '1' }; +} +const baz1: () => number = () => { + return 1; +} +const baz2: () => A = () => { + return { bar: '1' }; +} +const baz3: () => A = () => { + return { bar: '1' }; +} +const baz4: ((() => number) | (() => A)) = () => { + return 1; +} +const baz5: ((() => number) | (() => A)) = () => { + return { bar: '1' }; +}`, +}); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts new file mode 100644 index 0000000000000..5061018f1feb0 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts @@ -0,0 +1,95 @@ +/// + +//// interface A { +//// bar: string +//// } +//// +//// function foo1 (_a: () => number ) { } +//// foo1(() => { +//// 1 +//// }) +//// function foo2 (_a: () => A) { } +//// foo2(() => { +//// { bar: '1' } +//// }) +//// foo2(() => { +//// bar: '1' +//// }) +//// function foo3 (_a: () => A | number) { } +//// foo3(() => { +//// 1 +//// }) +//// foo3(() => { +//// bar: '1' +//// }) +//// +//// function bar1 (): number { +//// 1 +//// } +//// function bar2 (): A { +//// { bar: '1' } +//// } +//// function bar3 (): A { +//// bar: '1' +//// } +//// function bar4 (): A | number { +//// 1 +//// } +//// function bar5(): A | number { +//// bar: '1' +//// } +// +//// const baz1: () => number = () => { +//// 1 +//// } +//// const baz2: () => A = () => { +//// { bar: '1' } +//// } +//// const baz3: () => A = () => { +//// bar: '1' +//// } +//// const baz4: ((() => number) | (() => A)) = () => { +//// 1 +//// } +//// const baz5: ((() => number) | (() => A)) = () => { +//// bar: '1' +//// } + +verify.codeFixAll({ + fixId: "fixRemoveBlockBodyBrace", + fixAllDescription: "Surmise all return value", + newFileContent: +`interface A { + bar: string +} + +function foo1 (_a: () => number ) { } +foo1(() => 1) +function foo2 (_a: () => A) { } +foo2(() => ({ bar: '1' })) +foo2(() => ({ bar: '1' })) +function foo3 (_a: () => A | number) { } +foo3(() => 1) +foo3(() => ({ bar: '1' })) + +function bar1 (): number { + 1 +} +function bar2 (): A { + { bar: '1' } +} +function bar3 (): A { + bar: '1' +} +function bar4 (): A | number { + 1 +} +function bar5(): A | number { + bar: '1' +} +const baz1: () => number = () => 1 +const baz2: () => A = () => ({ bar: '1' }) +const baz3: () => A = () => ({ bar: '1' }) +const baz4: ((() => number) | (() => A)) = () => 1 +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +}); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts new file mode 100644 index 0000000000000..31d71cda5439c --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts @@ -0,0 +1,95 @@ +/// + +//// interface A { +//// bar: string +//// } +//// +//// function foo1 (_a: () => number ) { } +//// foo1(() => { +//// 1 +//// }) +//// function foo2 (_a: () => A) { } +//// foo2(() => { +//// { bar: '1' } +//// }) +//// foo2(() => { +//// bar: '1' +//// }) +//// function foo3 (_a: () => A | number) { } +//// foo3(() => { +//// 1 +//// }) +//// foo3(() => { +//// bar: '1' +//// }) +//// +//// function bar1 (): number { +//// 1 +//// } +//// function bar2 (): A { +//// { bar: '1' } +//// } +//// function bar3 (): A { +//// bar: '1' +//// } +//// function bar4 (): A | number { +//// 1 +//// } +//// function bar5(): A | number { +//// bar: '1' +//// } +// +//// const baz1: () => number = () => { +//// 1 +//// } +//// const baz2: () => A = () => { +//// { bar: '1' } +//// } +//// const baz3: () => A = () => { +//// bar: '1' +//// } +//// const baz4: ((() => number) | (() => A)) = () => { +//// 1 +//// } +//// const baz5: ((() => number) | (() => A)) = () => { +//// bar: '1' +//// } + +verify.codeFixAll({ + fixId: "fixReplaceBraceWithParen", + fixAllDescription: "Surmise all return value", + newFileContent: +`interface A { + bar: string +} + +function foo1 (_a: () => number ) { } +foo1(() => (1)) +function foo2 (_a: () => A) { } +foo2(() => ({ bar: '1' })) +foo2(() => ({ bar: '1' })) +function foo3 (_a: () => A | number) { } +foo3(() => (1)) +foo3(() => ({ bar: '1' })) + +function bar1 (): number { + 1 +} +function bar2 (): A { + { bar: '1' } +} +function bar3 (): A { + bar: '1' +} +function bar4 (): A | number { + 1 +} +function bar5(): A | number { + bar: '1' +} +const baz1: () => number = () => (1) +const baz2: () => A = () => ({ bar: '1' }) +const baz3: () => A = () => ({ bar: '1' }) +const baz4: ((() => number) | (() => A)) = () => (1) +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +}); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts new file mode 100644 index 0000000000000..760e422580338 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts @@ -0,0 +1,95 @@ +/// + +//// interface A { +//// bar: string +//// } +//// +//// function foo1 (_a: () => number ) { } +//// foo1(() => { +//// 1 +//// }) +//// function foo2 (_a: () => A) { } +//// foo2(() => { +//// { bar: '1' } +//// }) +//// foo2(() => { +//// bar: '1' +//// }) +//// function foo3 (_a: () => A | number) { } +//// foo3(() => { +//// 1 +//// }) +//// foo3(() => { +//// bar: '1' +//// }) +//// +//// function bar1 (): number { +//// 1 +//// } +//// function bar2 (): A { +//// { bar: '1' } +//// } +//// function bar3 (): A { +//// bar: '1' +//// } +//// function bar4 (): A | number { +//// 1 +//// } +//// function bar5(): A | number { +//// bar: '1' +//// } +// +//// const baz1: () => number = () => { +//// 1 +//// } +//// const baz2: () => A = () => { +//// { bar: '1' } +//// } +//// const baz3: () => A = () => { +//// bar: '1' +//// } +//// const baz4: ((() => number) | (() => A)) = () => { +//// 1 +//// } +//// const baz5: ((() => number) | (() => A)) = () => { +//// bar: '1' +//// } + +verify.codeFixAll({ + fixId: "fixWrapTheBlockWithParen", + fixAllDescription: "Surmise all return value", + newFileContent: +`interface A { + bar: string +} + +function foo1 (_a: () => number ) { } +foo1(() => (1)) +function foo2 (_a: () => A) { } +foo2(() => ({ bar: '1' })) +foo2(() => ({ bar: '1' })) +function foo3 (_a: () => A | number) { } +foo3(() => (1)) +foo3(() => ({ bar: '1' })) + +function bar1 (): number { + 1 +} +function bar2 (): A { + { bar: '1' } +} +function bar3 (): A { + bar: '1' +} +function bar4 (): A | number { + 1 +} +function bar5(): A | number { + bar: '1' +} +const baz1: () => number = () => (1) +const baz2: () => A = () => ({ bar: '1' }) +const baz3: () => A = () => ({ bar: '1' }) +const baz4: ((() => number) | (() => A)) = () => (1) +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +}); From aca172239c9ba451a0de8ab448ce2059d7f549a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Mon, 20 Aug 2018 10:22:26 +0800 Subject: [PATCH 05/20] add more testcase and fix all test --- src/services/codefixes/returnValueSurmise.ts | 53 ++++++++++++++----- .../fourslash/codeFixSurmiseReturnValue18.ts | 17 ++++++ .../fourslash/codeFixSurmiseReturnValue19.ts | 19 +++++++ .../fourslash/codeFixSurmiseReturnValue20.ts | 23 ++++++++ .../fourslash/codeFixSurmiseReturnValue21.ts | 15 ++++++ .../fourslash/codeFixSurmiseReturnValue22.ts | 17 ++++++ .../fourslash/codeFixSurmiseReturnValue23.ts | 11 ++++ .../codeFixSurmiseReturnValue_all1.ts | 8 ++- .../codeFixSurmiseReturnValue_all2.ts | 6 ++- .../codeFixSurmiseReturnValue_all3.ts | 6 ++- .../codeFixSurmiseReturnValue_all4.ts | 6 ++- 11 files changed, 164 insertions(+), 17 deletions(-) create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue18.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue19.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue20.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue21.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue22.ts create mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue23.ts diff --git a/src/services/codefixes/returnValueSurmise.ts b/src/services/codefixes/returnValueSurmise.ts index 791e9c8ff6f92..0ad39875acc67 100644 --- a/src/services/codefixes/returnValueSurmise.ts +++ b/src/services/codefixes/returnValueSurmise.ts @@ -34,8 +34,8 @@ namespace ts.codefix { errorCodes, fixIds: [fixIdAddReturnStatement, fixIdRemoveBlockBodyBrace, fixIdReplaceBraceWithParen, fixIdWrapTheBlockWithParen], getCodeActions: context => { - const { program, sourceFile, span: { start } } = context; - const info = getInfo(program.getTypeChecker(), sourceFile, start); + const { program, sourceFile, span: { start }, errorCode } = context; + const info = getInfo(program.getTypeChecker(), sourceFile, start, errorCode); if (!info) return undefined; if (info.kind === FixKind.MissingReturnStatement) { @@ -51,7 +51,7 @@ namespace ts.codefix { } }, getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { - const info = getInfo(context.program.getTypeChecker(), diag.file, diag.start); + const info = getInfo(context.program.getTypeChecker(), diag.file, diag.start, diag.code); if (!info) return undefined; switch (context.fixId) { @@ -141,30 +141,57 @@ namespace ts.codefix { return checker.isTypeAssignableTo(checker.getTypeAtLocation(isFunctionType ? updateFunctionLikeBody(declaration, createBlock([createReturn(expr)])) : expr), type); } - function getInfo(checker: TypeChecker, sourceFile: SourceFile, position: number): Info | undefined { + function getInfo(checker: TypeChecker, sourceFile: SourceFile, position: number, errorCode: number): Info | undefined { const node = getTokenAtPosition(sourceFile, position); if (!node.parent) return undefined; const declaration = findAncestor(node.parent, isFunctionLikeDeclaration); - if (declaration) { - if (declaration.body && declaration.type && rangeContainsRange(declaration.type, node)) { + switch (errorCode) { + case Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value.code: + if (!declaration || !declaration.body || !declaration.type || !rangeContainsRange(declaration.type, node)) return undefined; return getFixInfo(checker, declaration, checker.getTypeFromTypeNode(declaration.type), /* isFunctionType */ false); - } - else if (isCallExpression(declaration.parent) && declaration.body) { + case Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code: + if (!declaration || !isCallExpression(declaration.parent) || !declaration.body) return undefined; const pos = declaration.parent.arguments.indexOf(declaration); const type = checker.getContextualTypeForArgumentAtIndex(declaration.parent, pos); if (!type) return undefined; return getFixInfo(checker, declaration, type, /* isFunctionType */ true); - } + case Diagnostics.Type_0_is_not_assignable_to_type_1.code: + if (!isDeclarationName(node) || !isVariableLike(node.parent) && !isJsxAttribute(node.parent)) return undefined; + const initializer = getVariableLikeInitializer(node.parent); + if (!initializer || !isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; + return getFixInfo(checker, initializer, checker.getTypeAtLocation(node.parent), /* isFunctionType */ true); } - else if (isDeclarationName(node) && isVariableDeclaration(node.parent) && node.parent.initializer && node.parent.type) { - const initializer = skipParentheses(node.parent.initializer); - if (!isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; - return getFixInfo(checker, initializer, checker.getTypeFromTypeNode(node.parent.type), /* isFunctionType */ true); + + if (isDeclarationName(node) && (isVariableLike(node.parent) || isJsxAttribute(node.parent))) { + const initializer = getVariableLikeInitializer(node.parent); + if (!initializer || !isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; + return getFixInfo(checker, initializer, checker.getTypeAtLocation(node.parent), /* isFunctionType */ true); } return undefined; } + function getVariableLikeInitializer(declaration: VariableLikeDeclaration): Expression | undefined { + switch (declaration.kind) { + case SyntaxKind.VariableDeclaration: + case SyntaxKind.Parameter: + case SyntaxKind.BindingElement: + case SyntaxKind.PropertyDeclaration: + case SyntaxKind.PropertyAssignment: + return declaration.initializer; + case SyntaxKind.JsxAttribute: + return declaration.initializer && (isJsxExpression(declaration.initializer) ? declaration.initializer.expression : undefined); + case SyntaxKind.ShorthandPropertyAssignment: + case SyntaxKind.PropertySignature: + case SyntaxKind.EnumMember: + case SyntaxKind.JSDocPropertyTag: + case SyntaxKind.JSDocParameterTag: + return undefined; + default: + Debug.fail("unexpected token"); + } + } + function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration, expression: Expression) { changes.replaceNode(sourceFile, declaration.body!, createBlock([createReturn(expression)])); } diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts new file mode 100644 index 0000000000000..c731ff1486aad --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts @@ -0,0 +1,17 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// } +//// interface ElementAttributesProperty { props; } +//// } +//// class Comp { props: { t: () => number } } +//// var x = { 1 }} />; + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts new file mode 100644 index 0000000000000..ae694d561e770 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts @@ -0,0 +1,19 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// } +//// interface ElementAttributesProperty { props; } +//// } +//// interface A { +//// bar: string +//// } +//// class Comp { props: { t: () => number } } +//// var x = { bar: '1' }} />; + +verify.codeFixAvailable([ + { description: 'Wrap this block with parentheses' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts new file mode 100644 index 0000000000000..fb7a6f00e657e --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts @@ -0,0 +1,23 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// } +//// interface ElementAttributesProperty { props; } +//// } +//// interface A { +//// bar: string +//// } +//// class Comp { props: { t: () => number } } +//// var x = { +//// { bar: '1' } +//// }} />; + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts new file mode 100644 index 0000000000000..dd82c68326437 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts @@ -0,0 +1,15 @@ +/// + +//// interface A { +//// a: () => number +//// } +//// +//// let b: A = { +//// a: () => { 1 } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts new file mode 100644 index 0000000000000..daa3ef279781c --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts @@ -0,0 +1,17 @@ +/// + +//// interface A { +//// a: () => number +//// } +//// +//// function Foo (): A { +//// return { +//// a: () => { 1 } +//// } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts new file mode 100644 index 0000000000000..a783ea19348d6 --- /dev/null +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts @@ -0,0 +1,11 @@ +/// + +//// class Foo { +//// bar: () => number = () => { 1 } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Replace braces with parentheses' }, +]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts index 404015144937f..f964064439728 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixAddReturnStatement", @@ -111,5 +113,9 @@ const baz4: ((() => number) | (() => A)) = () => { } const baz5: ((() => number) | (() => A)) = () => { return { bar: '1' }; -}`, +} + +const test: { a: A } = { a: () => { + return { bar: '1' }; +} }`, }); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts index 5061018f1feb0..b6edba3815fff 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixRemoveBlockBodyBrace", @@ -91,5 +93,7 @@ const baz1: () => number = () => 1 const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => 1 -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) + +const test: { a: A } = { a: () => ({ bar: '1' }) }`, }); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts index 31d71cda5439c..5fad951c250cb 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixReplaceBraceWithParen", @@ -91,5 +93,7 @@ const baz1: () => number = () => (1) const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => (1) -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) + +const test: { a: A } = { a: () => ({ bar: '1' }) }`, }); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts index 760e422580338..30c3323c2a201 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixWrapTheBlockWithParen", @@ -91,5 +93,7 @@ const baz1: () => number = () => (1) const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => (1) -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) + +const test: { a: A } = { a: () => ({ bar: '1' }) }`, }); From cbe59bb06344958113b3e2a37f33edca95f41653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Tue, 30 Apr 2019 15:17:24 +0800 Subject: [PATCH 06/20] fix changed diagnosis --- src/services/codefixes/returnValueSurmise.ts | 8 -------- tests/cases/fourslash/codeFixSurmiseReturnValue18.ts | 1 + tests/cases/fourslash/codeFixSurmiseReturnValue19.ts | 1 + tests/cases/fourslash/codeFixSurmiseReturnValue20.ts | 1 + .../cases/fourslash/codeFixSurmiseReturnValue_all1.ts | 10 ++-------- .../cases/fourslash/codeFixSurmiseReturnValue_all2.ts | 6 +----- .../cases/fourslash/codeFixSurmiseReturnValue_all3.ts | 6 +----- .../cases/fourslash/codeFixSurmiseReturnValue_all4.ts | 6 +----- 8 files changed, 8 insertions(+), 31 deletions(-) diff --git a/src/services/codefixes/returnValueSurmise.ts b/src/services/codefixes/returnValueSurmise.ts index 0ad39875acc67..80864db326498 100644 --- a/src/services/codefixes/returnValueSurmise.ts +++ b/src/services/codefixes/returnValueSurmise.ts @@ -162,12 +162,6 @@ namespace ts.codefix { if (!initializer || !isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; return getFixInfo(checker, initializer, checker.getTypeAtLocation(node.parent), /* isFunctionType */ true); } - - if (isDeclarationName(node) && (isVariableLike(node.parent) || isJsxAttribute(node.parent))) { - const initializer = getVariableLikeInitializer(node.parent); - if (!initializer || !isFunctionLikeDeclaration(initializer) || !initializer.body) return undefined; - return getFixInfo(checker, initializer, checker.getTypeAtLocation(node.parent), /* isFunctionType */ true); - } return undefined; } @@ -187,8 +181,6 @@ namespace ts.codefix { case SyntaxKind.JSDocPropertyTag: case SyntaxKind.JSDocParameterTag: return undefined; - default: - Debug.fail("unexpected token"); } } diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts index c731ff1486aad..4f5180f43b267 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts @@ -14,4 +14,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, { description: 'Replace braces with parentheses' }, + { description: `Infer type of 'props' from usage` } ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts index ae694d561e770..88e1556ce9838 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts @@ -15,5 +15,6 @@ verify.codeFixAvailable([ { description: 'Wrap this block with parentheses' }, + { description: `Infer type of 'props' from usage` }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts index fb7a6f00e657e..fa0d4245e0523 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts @@ -19,5 +19,6 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, { description: 'Replace braces with parentheses' }, + { description: `Infer type of 'props' from usage` }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts index f964064439728..34efa0a3b2050 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -54,8 +54,6 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } -//// -//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixAddReturnStatement", @@ -113,9 +111,5 @@ const baz4: ((() => number) | (() => A)) = () => { } const baz5: ((() => number) | (() => A)) = () => { return { bar: '1' }; -} - -const test: { a: A } = { a: () => { - return { bar: '1' }; -} }`, -}); +}`, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts index b6edba3815fff..5061018f1feb0 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts @@ -54,8 +54,6 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } -//// -//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixRemoveBlockBodyBrace", @@ -93,7 +91,5 @@ const baz1: () => number = () => 1 const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => 1 -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) - -const test: { a: A } = { a: () => ({ bar: '1' }) }`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, }); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts index 5fad951c250cb..31d71cda5439c 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts @@ -54,8 +54,6 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } -//// -//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixReplaceBraceWithParen", @@ -93,7 +91,5 @@ const baz1: () => number = () => (1) const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => (1) -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) - -const test: { a: A } = { a: () => ({ bar: '1' }) }`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, }); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts index 30c3323c2a201..760e422580338 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts @@ -54,8 +54,6 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } -//// -//// const test: { a: A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixWrapTheBlockWithParen", @@ -93,7 +91,5 @@ const baz1: () => number = () => (1) const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => (1) -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) - -const test: { a: A } = { a: () => ({ bar: '1' }) }`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, }); From 98d7ebaca496d2ff6d5aabded69b88c49ec6a327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Wed, 8 May 2019 17:08:16 +0800 Subject: [PATCH 07/20] fix broken test case --- tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts | 3 ++- tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts | 3 ++- tests/cases/fourslash/codeFixAwaitInSyncFunction12.ts | 1 + tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts | 1 + tests/cases/fourslash/codeFixAwaitInSyncFunction14.ts | 1 + tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts | 1 + tests/cases/fourslash/codeFixAwaitInSyncFunction8.ts | 1 + tests/cases/fourslash/codeFixAwaitInSyncFunction9.ts | 1 + 8 files changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts index 42e0bc5be780f..24a4f7cbca4bc 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts @@ -5,9 +5,10 @@ ////} verify.codeFix({ + index: 3, description: "Add async modifier to containing function", newFileContent: `const f: () => Promise = async () => { await Promise.resolve('foo'); -}`, +}` }); diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts index bc7b17f8db5b4..c1e80654cf0c0 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts @@ -6,9 +6,10 @@ // should not change type if it's incorrectly set verify.codeFix({ + index: 3, description: "Add async modifier to containing function", newFileContent: `const f: string = async () => { await Promise.resolve('foo'); -}`, +}` }); diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction12.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction12.ts index ee694a80e9f26..4e780d4b57dbc 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction12.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction12.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 1, description: "Add async modifier to containing function", newFileContent: `const f: () => Promise> = async function() { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts index 06f54d29eeb68..201b7c2f37df8 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 3, description: "Add async modifier to containing function", newFileContent: `const f: () => Promise = async () => { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction14.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction14.ts index c798af5f5abd6..7b9ca8c7239a7 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction14.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction14.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 1, description: "Add async modifier to containing function", newFileContent: `const f = async function(): Promise { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts index a2c6f7dcb1ad9..a6ca0b7d4fb1d 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 3, description: "Add async modifier to containing function", newFileContent: `const f = async (): Promise => { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction8.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction8.ts index 7c43add3edd21..726927b77b793 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction8.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction8.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 1, description: "Add async modifier to containing function", newFileContent: `async function f(): Promise { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction9.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction9.ts index f93603e69c457..652a669832e32 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction9.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction9.ts @@ -7,6 +7,7 @@ ////} verify.codeFix({ + index: 1, description: "Add async modifier to containing function", newFileContent: `class Foo { From 537e806b03fbe7d4337c8221dcae3a5e10e2391c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Wed, 8 May 2019 17:34:58 +0800 Subject: [PATCH 08/20] add more case --- tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts | 8 +++++++- tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts | 6 +++++- tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts | 6 +++++- tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts | 6 +++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts index 34efa0a3b2050..8f5b960393f98 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: () => A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixAddReturnStatement", @@ -111,5 +113,9 @@ const baz4: ((() => number) | (() => A)) = () => { } const baz5: ((() => number) | (() => A)) = () => { return { bar: '1' }; -}`, +} + +const test: { a: () => A } = { a: () => { + return { bar: '1' }; +} }`, }); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts index 5061018f1feb0..2078b456e0253 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: () => A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixRemoveBlockBodyBrace", @@ -91,5 +93,7 @@ const baz1: () => number = () => 1 const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => 1 -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) + +const test: { a: () => A } = { a: () => ({ bar: '1' }) }`, }); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts index 31d71cda5439c..4980cdd9c09be 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: () => A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixReplaceBraceWithParen", @@ -91,5 +93,7 @@ const baz1: () => number = () => (1) const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => (1) -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) + +const test: { a: () => A } = { a: () => ({ bar: '1' }) }`, }); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts index 760e422580338..1f5a1b08f2010 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts @@ -54,6 +54,8 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// +//// const test: { a: () => A } = { a: () => { bar: '1' } } verify.codeFixAll({ fixId: "fixWrapTheBlockWithParen", @@ -91,5 +93,7 @@ const baz1: () => number = () => (1) const baz2: () => A = () => ({ bar: '1' }) const baz3: () => A = () => ({ bar: '1' }) const baz4: ((() => number) | (() => A)) = () => (1) -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' })`, +const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) + +const test: { a: () => A } = { a: () => ({ bar: '1' }) }`, }); From 6fecf32dbc18fa1d63dbb47c0b519a4fa8f39cdb Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 9 Jan 2020 19:04:18 +0800 Subject: [PATCH 09/20] rename quickfix --- .../{returnValueSurmise.ts => returnValueCorrect.ts} | 10 +++++----- src/services/tsconfig.json | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) rename src/services/codefixes/{returnValueSurmise.ts => returnValueCorrect.ts} (96%) diff --git a/src/services/codefixes/returnValueSurmise.ts b/src/services/codefixes/returnValueCorrect.ts similarity index 96% rename from src/services/codefixes/returnValueSurmise.ts rename to src/services/codefixes/returnValueCorrect.ts index 80864db326498..bff6a4c58dca3 100644 --- a/src/services/codefixes/returnValueSurmise.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -1,6 +1,6 @@ /* @internal */ namespace ts.codefix { - const fixId = "returnValueSurmise"; + const fixId = "returnValueCorrect"; const fixIdAddReturnStatement = "fixAddReturnStatement"; const fixIdRemoveBlockBodyBrace = "fixRemoveBlockBodyBrace"; const fixIdReplaceBraceWithParen = "fixReplaceBraceWithParen"; @@ -198,21 +198,21 @@ namespace ts.codefix { function getActionForfixAddReturnStatement(context: CodeFixContext, declaration: FunctionLikeDeclaration, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, declaration, expression)); - return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Surmise_all_return_value); + return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Correct_all_return_expressions); } function getActionForfixRemoveBlockBodyBrace(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, /* withParen */ false)); - return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Surmise_all_return_value); + return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Correct_all_return_expressions); } function getActionForfixReplaceBraceWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, /* withParen */ true)); - return createCodeFixAction(fixId, changes, Diagnostics.Replace_braces_with_parentheses, fixIdReplaceBraceWithParen, Diagnostics.Surmise_all_return_value); + return createCodeFixAction(fixId, changes, Diagnostics.Replace_braces_with_parentheses, fixIdReplaceBraceWithParen, Diagnostics.Correct_all_return_expressions); } function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration, expression)); - return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_block_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Surmise_all_return_value); + return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_block_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Correct_all_return_expressions); } } diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 759cd59fa28b8..c6bc06b1a1ee8 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -62,7 +62,7 @@ "codefixes/fixClassIncorrectlyImplementsInterface.ts", "codefixes/importFixes.ts", "codefixes/fixSpelling.ts", - "codefixes/returnValueSurmise.ts", + "codefixes/returnValueCorrect.ts", "codefixes/fixAddMissingMember.ts", "codefixes/fixAddMissingNewOperator.ts", "codefixes/fixCannotFindModule.ts", From 4c0af7279f3127265da19e4e1fe1138d3045c87c Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 9 Jan 2020 19:10:50 +0800 Subject: [PATCH 10/20] fix conflict --- src/compiler/checker.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 791563181e20b..8e8157fc025ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -536,9 +536,7 @@ namespace ts { }, getApparentType, getUnionType, - isTypeAssignableTo: (source, target) => { - return isTypeAssignableTo(source, target); - }, + isTypeAssignableTo, createAnonymousType, createSignature, createSymbol, @@ -564,7 +562,6 @@ namespace ts { isArrayLikeType, isTypeInvalidDueToUnionDiscriminant, getAllPossiblePropertiesOfTypes, - isTypeAssignableTo, getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type), getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning), getSuggestionForNonexistentExport: (node, target) => getSuggestionForNonexistentExport(node, target), From 98377f380f2207879e25105b8a92c81c02ecfc4c Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 9 Jan 2020 23:44:50 +0800 Subject: [PATCH 11/20] fix fix desc --- tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts | 2 +- tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts | 2 +- tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts | 2 +- tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts index 8f5b960393f98..c0055060857bd 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -59,7 +59,7 @@ verify.codeFixAll({ fixId: "fixAddReturnStatement", - fixAllDescription: "Surmise all return value", + fixAllDescription: "Correct all return expressions", newFileContent: `interface A { bar: string diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts index 2078b456e0253..af236faf31b33 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts @@ -59,7 +59,7 @@ verify.codeFixAll({ fixId: "fixRemoveBlockBodyBrace", - fixAllDescription: "Surmise all return value", + fixAllDescription: "Correct all return expressions", newFileContent: `interface A { bar: string diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts index 4980cdd9c09be..a2d536ec61771 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts @@ -59,7 +59,7 @@ verify.codeFixAll({ fixId: "fixReplaceBraceWithParen", - fixAllDescription: "Surmise all return value", + fixAllDescription: "Correct all return expressions", newFileContent: `interface A { bar: string diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts index 1f5a1b08f2010..23909d3672e73 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts @@ -59,7 +59,7 @@ verify.codeFixAll({ fixId: "fixWrapTheBlockWithParen", - fixAllDescription: "Surmise all return value", + fixAllDescription: "Correct all return expressions", newFileContent: `interface A { bar: string From 49d21bf0c6c6fd8bd77f6d08e71a32910a8b3bb6 Mon Sep 17 00:00:00 2001 From: kingwl Date: Fri, 10 Jan 2020 00:24:33 +0800 Subject: [PATCH 12/20] fix semi --- src/compiler/types.ts | 1 - .../codeFixSurmiseReturnValue_all1.ts | 32 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f346b8cb2b413..98f2531b6c6d1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3546,7 +3546,6 @@ namespace ts { * e.g. it specifies `kind: "a"` and obj has `kind: "b"`. */ /* @internal */ isTypeInvalidDueToUnionDiscriminant(contextualType: Type, obj: ObjectLiteralExpression | JsxAttributes): boolean; - /* @internal */ isTypeAssignableTo(type1: Type, type2: Type): boolean; /** * For a union, will include a property if it's defined in *any* of the member types. * So for `{ a } | { b }`, this will include both `a` and `b`. diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts index c0055060857bd..7e8cfb125f0a7 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -67,55 +67,55 @@ verify.codeFixAll({ function foo1 (_a: () => number ) { } foo1(() => { - return 1; + return 1 }) function foo2 (_a: () => A) { } foo2(() => { - return { bar: '1' }; + return { bar: '1' } }) foo2(() => { - return { bar: '1' }; + return { bar: '1' } }) function foo3 (_a: () => A | number) { } foo3(() => { - return 1; + return 1 }) foo3(() => { - return { bar: '1' }; + return { bar: '1' } }) function bar1 (): number { - return 1; + return 1 } function bar2 (): A { - return { bar: '1' }; + return { bar: '1' } } function bar3 (): A { - return { bar: '1' }; + return { bar: '1' } } function bar4 (): A | number { - return 1; + return 1 } function bar5(): A | number { - return { bar: '1' }; + return { bar: '1' } } const baz1: () => number = () => { - return 1; + return 1 } const baz2: () => A = () => { - return { bar: '1' }; + return { bar: '1' } } const baz3: () => A = () => { - return { bar: '1' }; + return { bar: '1' } } const baz4: ((() => number) | (() => A)) = () => { - return 1; + return 1 } const baz5: ((() => number) | (() => A)) = () => { - return { bar: '1' }; + return { bar: '1' } } const test: { a: () => A } = { a: () => { - return { bar: '1' }; + return { bar: '1' } } }`, }); \ No newline at end of file From 944ddf46e9fb18b078068603da00e64c84273b78 Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 18 Mar 2020 09:16:48 +0800 Subject: [PATCH 13/20] Avoid replace brace with paren --- src/compiler/diagnosticMessages.json | 8 +- src/services/codefixes/returnValueCorrect.ts | 19 +--- .../fourslash/codeFixSurmiseReturnValue10.ts | 3 +- .../fourslash/codeFixSurmiseReturnValue12.ts | 1 - .../fourslash/codeFixSurmiseReturnValue13.ts | 1 - .../fourslash/codeFixSurmiseReturnValue18.ts | 1 - .../fourslash/codeFixSurmiseReturnValue20.ts | 1 - .../fourslash/codeFixSurmiseReturnValue21.ts | 1 - .../fourslash/codeFixSurmiseReturnValue22.ts | 1 - .../fourslash/codeFixSurmiseReturnValue23.ts | 3 +- .../fourslash/codeFixSurmiseReturnValue7.ts | 3 +- .../fourslash/codeFixSurmiseReturnValue8.ts | 3 +- .../fourslash/codeFixSurmiseReturnValue9.ts | 3 +- .../codeFixSurmiseReturnValue_all3.ts | 99 ------------------- 14 files changed, 10 insertions(+), 137 deletions(-) delete mode 100644 tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c65d4b9ac6f3a..cf8fc21f512a1 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5505,17 +5505,13 @@ "category": "Message", "code": 95103 }, - "Replace braces with parentheses": { - "category": "Message", - "code": 95104 - }, "Wrap this block with parentheses": { "category": "Message", - "code": 95105 + "code": 95104 }, "Correct all return expressions": { "category": "Message", - "code": 95106 + "code": 95105 }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { diff --git a/src/services/codefixes/returnValueCorrect.ts b/src/services/codefixes/returnValueCorrect.ts index bff6a4c58dca3..b6de942ce62bf 100644 --- a/src/services/codefixes/returnValueCorrect.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -3,7 +3,6 @@ namespace ts.codefix { const fixId = "returnValueCorrect"; const fixIdAddReturnStatement = "fixAddReturnStatement"; const fixIdRemoveBlockBodyBrace = "fixRemoveBlockBodyBrace"; - const fixIdReplaceBraceWithParen = "fixReplaceBraceWithParen"; const fixIdWrapTheBlockWithParen = "fixWrapTheBlockWithParen"; const errorCodes = [ Diagnostics.A_function_whose_declared_type_is_neither_void_nor_any_must_return_a_value.code, @@ -32,19 +31,16 @@ namespace ts.codefix { registerCodeFix({ errorCodes, - fixIds: [fixIdAddReturnStatement, fixIdRemoveBlockBodyBrace, fixIdReplaceBraceWithParen, fixIdWrapTheBlockWithParen], + fixIds: [fixIdAddReturnStatement, fixIdRemoveBlockBodyBrace, fixIdWrapTheBlockWithParen], getCodeActions: context => { const { program, sourceFile, span: { start }, errorCode } = context; const info = getInfo(program.getTypeChecker(), sourceFile, start, errorCode); if (!info) return undefined; if (info.kind === FixKind.MissingReturnStatement) { - return concatenate( + return append( [getActionForfixAddReturnStatement(context, info.declaration, info.expression)], - isArrowFunction(info.declaration) ? [ - getActionForfixRemoveBlockBodyBrace(context, info.declaration, info.expression), - getActionForfixReplaceBraceWithParen(context, info.declaration, info.expression) - ] : undefined); + isArrowFunction(info.declaration) ? getActionForfixRemoveBlockBodyBrace(context, info.declaration, info.expression): undefined); } else { return [getActionForfixWrapTheBlockWithParen(context, info.declaration, info.expression)]; @@ -62,10 +58,6 @@ namespace ts.codefix { if (!isArrowFunction(info.declaration)) return undefined; removeBlockBodyBrace(changes, diag.file, info.declaration, info.expression, /* withParen */ false); break; - case fixIdReplaceBraceWithParen: - if (!isArrowFunction(info.declaration)) return undefined; - removeBlockBodyBrace(changes, diag.file, info.declaration, info.expression, /* withParen */ true); - break; case fixIdWrapTheBlockWithParen: if (!isArrowFunction(info.declaration)) return undefined; wrapBlockWithParen(changes, diag.file, info.declaration, info.expression); @@ -206,11 +198,6 @@ namespace ts.codefix { return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Correct_all_return_expressions); } - function getActionForfixReplaceBraceWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { - const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, /* withParen */ true)); - return createCodeFixAction(fixId, changes, Diagnostics.Replace_braces_with_parentheses, fixIdReplaceBraceWithParen, Diagnostics.Correct_all_return_expressions); - } - function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration, expression)); return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_block_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Correct_all_return_expressions); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts index c8a94d90a7d6d..cdbf416984d58 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts @@ -4,6 +4,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, + { description: 'Remove block body braces' } ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts index 8672eb7882fd0..d242e4f4cf87e 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts @@ -11,6 +11,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts index 37360499d9e5e..a1e2cc553ccac 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts @@ -10,7 +10,6 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts index 4f5180f43b267..ffa038a48fc5c 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts @@ -13,6 +13,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, { description: `Infer type of 'props' from usage` } ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts index fa0d4245e0523..f552edaee6713 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts @@ -18,7 +18,6 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, { description: `Infer type of 'props' from usage` }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts index dd82c68326437..1a248f77909f2 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts @@ -11,5 +11,4 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts index daa3ef279781c..372513592afb8 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts @@ -13,5 +13,4 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts index a783ea19348d6..14fde02533799 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts @@ -6,6 +6,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, + { description: 'Remove block body braces' } ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts index 5a40e0a7f66dd..bd0bfd3b343b3 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts @@ -5,6 +5,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, + { description: 'Remove block body braces' } ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts index 8ebc2500cabcc..cde3ab2e42ff6 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts @@ -5,6 +5,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, + { description: 'Remove block body braces' } ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts index 8abf79d631f7b..104fd785f00a1 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts @@ -4,6 +4,5 @@ verify.codeFixAvailable([ { description: 'Add a return statement' }, - { description: 'Remove block body braces' }, - { description: 'Replace braces with parentheses' }, + { description: 'Remove block body braces' } ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts deleted file mode 100644 index a2d536ec61771..0000000000000 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts +++ /dev/null @@ -1,99 +0,0 @@ -/// - -//// interface A { -//// bar: string -//// } -//// -//// function foo1 (_a: () => number ) { } -//// foo1(() => { -//// 1 -//// }) -//// function foo2 (_a: () => A) { } -//// foo2(() => { -//// { bar: '1' } -//// }) -//// foo2(() => { -//// bar: '1' -//// }) -//// function foo3 (_a: () => A | number) { } -//// foo3(() => { -//// 1 -//// }) -//// foo3(() => { -//// bar: '1' -//// }) -//// -//// function bar1 (): number { -//// 1 -//// } -//// function bar2 (): A { -//// { bar: '1' } -//// } -//// function bar3 (): A { -//// bar: '1' -//// } -//// function bar4 (): A | number { -//// 1 -//// } -//// function bar5(): A | number { -//// bar: '1' -//// } -// -//// const baz1: () => number = () => { -//// 1 -//// } -//// const baz2: () => A = () => { -//// { bar: '1' } -//// } -//// const baz3: () => A = () => { -//// bar: '1' -//// } -//// const baz4: ((() => number) | (() => A)) = () => { -//// 1 -//// } -//// const baz5: ((() => number) | (() => A)) = () => { -//// bar: '1' -//// } -//// -//// const test: { a: () => A } = { a: () => { bar: '1' } } - -verify.codeFixAll({ - fixId: "fixReplaceBraceWithParen", - fixAllDescription: "Correct all return expressions", - newFileContent: -`interface A { - bar: string -} - -function foo1 (_a: () => number ) { } -foo1(() => (1)) -function foo2 (_a: () => A) { } -foo2(() => ({ bar: '1' })) -foo2(() => ({ bar: '1' })) -function foo3 (_a: () => A | number) { } -foo3(() => (1)) -foo3(() => ({ bar: '1' })) - -function bar1 (): number { - 1 -} -function bar2 (): A { - { bar: '1' } -} -function bar3 (): A { - bar: '1' -} -function bar4 (): A | number { - 1 -} -function bar5(): A | number { - bar: '1' -} -const baz1: () => number = () => (1) -const baz2: () => A = () => ({ bar: '1' }) -const baz3: () => A = () => ({ bar: '1' }) -const baz4: ((() => number) | (() => A)) = () => (1) -const baz5: ((() => number) | (() => A)) = () => ({ bar: '1' }) - -const test: { a: () => A } = { a: () => ({ bar: '1' }) }`, -}); From 6c88a01a286b1c3d23b8040586c372774d9c6f0b Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 18 Mar 2020 09:26:24 +0800 Subject: [PATCH 14/20] Split fix all action --- src/compiler/diagnosticMessages.json | 12 ++++++++++-- src/services/codefixes/returnValueCorrect.ts | 6 +++--- tests/cases/fourslash/codeFixSurmiseReturnValue14.ts | 2 +- tests/cases/fourslash/codeFixSurmiseReturnValue15.ts | 2 +- tests/cases/fourslash/codeFixSurmiseReturnValue16.ts | 2 +- tests/cases/fourslash/codeFixSurmiseReturnValue19.ts | 2 +- .../fourslash/codeFixSurmiseReturnValue_all1.ts | 2 +- .../fourslash/codeFixSurmiseReturnValue_all2.ts | 2 +- ...lue_all4.ts => codeFixSurmiseReturnValue_all3.ts} | 2 +- 9 files changed, 20 insertions(+), 12 deletions(-) rename tests/cases/fourslash/{codeFixSurmiseReturnValue_all4.ts => codeFixSurmiseReturnValue_all3.ts} (96%) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index cf8fc21f512a1..3c38c5f07e3cb 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5505,14 +5505,22 @@ "category": "Message", "code": 95103 }, - "Wrap this block with parentheses": { + "Wrap this object literal with parentheses": { "category": "Message", "code": 95104 }, - "Correct all return expressions": { + "Add all missing return statement": { "category": "Message", "code": 95105 }, + "Remove all incorrect body block braces": { + "category": "Message", + "code": 95106 + }, + "Wrap all object literal with parentheses": { + "category": "Message", + "code": 95107 + }, "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", diff --git a/src/services/codefixes/returnValueCorrect.ts b/src/services/codefixes/returnValueCorrect.ts index b6de942ce62bf..a0623b64700cb 100644 --- a/src/services/codefixes/returnValueCorrect.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -190,16 +190,16 @@ namespace ts.codefix { function getActionForfixAddReturnStatement(context: CodeFixContext, declaration: FunctionLikeDeclaration, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, declaration, expression)); - return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Correct_all_return_expressions); + return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Add_all_missing_return_statement); } function getActionForfixRemoveBlockBodyBrace(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, /* withParen */ false)); - return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Correct_all_return_expressions); + return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Remove_all_incorrect_body_block_braces); } function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration, expression)); - return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_block_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Correct_all_return_expressions); + return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_object_literal_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Wrap_all_object_literal_with_parentheses); } } diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts index 59091741c13d8..8951f82993d0b 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts @@ -8,6 +8,6 @@ //// } verify.codeFixAvailable([ - { description: 'Wrap this block with parentheses' }, + { description: 'Wrap this object literal with parentheses' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts index 8e2970fea2c0e..b6ba1824293b3 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts @@ -7,6 +7,6 @@ //// Foo(() => { bar: '1' }) verify.codeFixAvailable([ - { description: 'Wrap this block with parentheses' }, + { description: 'Wrap this object literal with parentheses' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts index 5292a10f776e7..1a8e685ab6f30 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts @@ -8,6 +8,6 @@ //// Foo(() => { bar: '1' }) verify.codeFixAvailable([ - { description: 'Wrap this block with parentheses' }, + { description: 'Wrap this object literal with parentheses' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts index 88e1556ce9838..00ef70a3a1840 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts @@ -14,7 +14,7 @@ //// var x = { bar: '1' }} />; verify.codeFixAvailable([ - { description: 'Wrap this block with parentheses' }, + { description: 'Wrap this object literal with parentheses' }, { description: `Infer type of 'props' from usage` }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts index 7e8cfb125f0a7..bda9f16663f3f 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -59,7 +59,7 @@ verify.codeFixAll({ fixId: "fixAddReturnStatement", - fixAllDescription: "Correct all return expressions", + fixAllDescription: "Add all missing return statement", newFileContent: `interface A { bar: string diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts index af236faf31b33..e3b0d1c0b1d39 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts @@ -59,7 +59,7 @@ verify.codeFixAll({ fixId: "fixRemoveBlockBodyBrace", - fixAllDescription: "Correct all return expressions", + fixAllDescription: "Remove all incorrect body block braces", newFileContent: `interface A { bar: string diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts similarity index 96% rename from tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts rename to tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts index 23909d3672e73..0d4806b19c18c 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all4.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts @@ -59,7 +59,7 @@ verify.codeFixAll({ fixId: "fixWrapTheBlockWithParen", - fixAllDescription: "Correct all return expressions", + fixAllDescription: "Wrap all object literal with parentheses", newFileContent: `interface A { bar: string From 94f845af510946ba473348792b94912292db04e4 Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 18 Mar 2020 09:51:44 +0800 Subject: [PATCH 15/20] Add return work in same line --- src/services/codefixes/returnValueCorrect.ts | 26 ++++++++++++------- .../codeFixSurmiseReturnValue_all1.ts | 6 ++--- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/services/codefixes/returnValueCorrect.ts b/src/services/codefixes/returnValueCorrect.ts index a0623b64700cb..ad440e561430c 100644 --- a/src/services/codefixes/returnValueCorrect.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -19,12 +19,14 @@ namespace ts.codefix { kind: FixKind.MissingReturnStatement; declaration: FunctionLikeDeclaration; expression: Expression; + statement: Statement; } interface MissingParenInfo { kind: FixKind.MissingParentheses; declaration: ArrowFunction; expression: Expression; + statement: Statement; } type Info = MissingReturnInfo | MissingParenInfo; @@ -39,7 +41,7 @@ namespace ts.codefix { if (info.kind === FixKind.MissingReturnStatement) { return append( - [getActionForfixAddReturnStatement(context, info.declaration, info.expression)], + [getActionForfixAddReturnStatement(context, info.expression, info.statement)], isArrowFunction(info.declaration) ? getActionForfixRemoveBlockBodyBrace(context, info.declaration, info.expression): undefined); } else { @@ -52,7 +54,7 @@ namespace ts.codefix { switch (context.fixId) { case fixIdAddReturnStatement: - addReturnStatement(changes, diag.file, info.declaration, info.expression); + addReturnStatement(changes, diag.file, info.expression, info.statement); break; case fixIdRemoveBlockBodyBrace: if (!isArrowFunction(info.declaration)) return undefined; @@ -95,7 +97,8 @@ namespace ts.codefix { return { declaration, kind: FixKind.MissingReturnStatement, - expression: firstStatement.expression + expression: firstStatement.expression, + statement: firstStatement }; } else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { @@ -104,11 +107,13 @@ namespace ts.codefix { return isArrowFunction(declaration) ? { declaration, kind: FixKind.MissingParentheses, - expression: node + expression: node, + statement: firstStatement } : { declaration, kind: FixKind.MissingReturnStatement, - expression: node + expression: node, + statement: firstStatement }; } } @@ -120,7 +125,8 @@ namespace ts.codefix { return { declaration, kind: FixKind.MissingReturnStatement, - expression: node + expression: node, + statement: firstStatement }; } } @@ -176,8 +182,8 @@ namespace ts.codefix { } } - function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: FunctionLikeDeclaration, expression: Expression) { - changes.replaceNode(sourceFile, declaration.body!, createBlock([createReturn(expression)])); + function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, expression: Expression, statement: Statement) { + changes.replaceNode(sourceFile, statement, createReturn(expression)); } function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression, withParen: boolean) { @@ -188,8 +194,8 @@ namespace ts.codefix { changes.replaceNode(sourceFile, declaration.body, createParen(expression)); } - function getActionForfixAddReturnStatement(context: CodeFixContext, declaration: FunctionLikeDeclaration, expression: Expression) { - const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, declaration, expression)); + function getActionForfixAddReturnStatement(context: CodeFixContext, expression: Expression, statement: Statement) { + const changes = textChanges.ChangeTracker.with(context, t => addReturnStatement(t, context.sourceFile, expression, statement)); return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Add_all_missing_return_statement); } diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts index bda9f16663f3f..e14f31e5e8b9c 100644 --- a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts +++ b/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts @@ -54,6 +54,7 @@ //// const baz5: ((() => number) | (() => A)) = () => { //// bar: '1' //// } +//// const baz6: () => number = () => { 1 } //// //// const test: { a: () => A } = { a: () => { bar: '1' } } @@ -114,8 +115,7 @@ const baz4: ((() => number) | (() => A)) = () => { const baz5: ((() => number) | (() => A)) = () => { return { bar: '1' } } +const baz6: () => number = () => { return 1 } -const test: { a: () => A } = { a: () => { - return { bar: '1' } -} }`, +const test: { a: () => A } = { a: () => { return { bar: '1' } } }`, }); \ No newline at end of file From 7fe9a82928d77d548894b46b9a88e579d6595097 Mon Sep 17 00:00:00 2001 From: kingwl Date: Wed, 18 Mar 2020 10:31:05 +0800 Subject: [PATCH 16/20] fix test cases --- tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts | 2 +- tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts | 2 +- tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts | 2 +- tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts index 24a4f7cbca4bc..3518b017050ee 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts @@ -5,7 +5,7 @@ ////} verify.codeFix({ - index: 3, + index: 2, description: "Add async modifier to containing function", newFileContent: `const f: () => Promise = async () => { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts index c1e80654cf0c0..1cfce8eda2e39 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction11.ts @@ -6,7 +6,7 @@ // should not change type if it's incorrectly set verify.codeFix({ - index: 3, + index: 2, description: "Add async modifier to containing function", newFileContent: `const f: string = async () => { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts index 201b7c2f37df8..57fb74a4fe091 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts @@ -5,7 +5,7 @@ ////} verify.codeFix({ - index: 3, + index: 2, description: "Add async modifier to containing function", newFileContent: `const f: () => Promise = async () => { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts index a6ca0b7d4fb1d..47f474ecb0c61 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts @@ -5,7 +5,7 @@ ////} verify.codeFix({ - index: 3, + index: 2, description: "Add async modifier to containing function", newFileContent: `const f = async (): Promise => { From af9552edb26d708794a2c0680b2384d0f8e0929c Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 26 Mar 2020 21:53:47 +0800 Subject: [PATCH 17/20] rename baseline --- ...odeFixSurmiseReturnValue1.ts => codeFixCorrectReturnValue1.ts} | 0 ...eFixSurmiseReturnValue10.ts => codeFixCorrectReturnValue10.ts} | 0 ...eFixSurmiseReturnValue11.ts => codeFixCorrectReturnValue11.ts} | 0 ...eFixSurmiseReturnValue12.ts => codeFixCorrectReturnValue12.ts} | 0 ...eFixSurmiseReturnValue13.ts => codeFixCorrectReturnValue13.ts} | 0 ...eFixSurmiseReturnValue14.ts => codeFixCorrectReturnValue14.ts} | 0 ...eFixSurmiseReturnValue15.ts => codeFixCorrectReturnValue15.ts} | 0 ...eFixSurmiseReturnValue16.ts => codeFixCorrectReturnValue16.ts} | 0 ...eFixSurmiseReturnValue17.ts => codeFixCorrectReturnValue17.ts} | 0 ...eFixSurmiseReturnValue18.ts => codeFixCorrectReturnValue18.ts} | 0 ...eFixSurmiseReturnValue19.ts => codeFixCorrectReturnValue19.ts} | 0 ...odeFixSurmiseReturnValue2.ts => codeFixCorrectReturnValue2.ts} | 0 ...eFixSurmiseReturnValue20.ts => codeFixCorrectReturnValue20.ts} | 0 ...eFixSurmiseReturnValue21.ts => codeFixCorrectReturnValue21.ts} | 0 ...eFixSurmiseReturnValue22.ts => codeFixCorrectReturnValue22.ts} | 0 ...eFixSurmiseReturnValue23.ts => codeFixCorrectReturnValue23.ts} | 0 ...odeFixSurmiseReturnValue3.ts => codeFixCorrectReturnValue3.ts} | 0 ...odeFixSurmiseReturnValue4.ts => codeFixCorrectReturnValue4.ts} | 0 ...odeFixSurmiseReturnValue5.ts => codeFixCorrectReturnValue5.ts} | 0 ...odeFixSurmiseReturnValue6.ts => codeFixCorrectReturnValue6.ts} | 0 ...odeFixSurmiseReturnValue7.ts => codeFixCorrectReturnValue7.ts} | 0 ...odeFixSurmiseReturnValue8.ts => codeFixCorrectReturnValue8.ts} | 0 ...odeFixSurmiseReturnValue9.ts => codeFixCorrectReturnValue9.ts} | 0 ...rmiseReturnValue_all1.ts => codeFixCorrectReturnValue_all1.ts} | 0 ...rmiseReturnValue_all2.ts => codeFixCorrectReturnValue_all2.ts} | 0 ...rmiseReturnValue_all3.ts => codeFixCorrectReturnValue_all3.ts} | 0 26 files changed, 0 insertions(+), 0 deletions(-) rename tests/cases/fourslash/{codeFixSurmiseReturnValue1.ts => codeFixCorrectReturnValue1.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue10.ts => codeFixCorrectReturnValue10.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue11.ts => codeFixCorrectReturnValue11.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue12.ts => codeFixCorrectReturnValue12.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue13.ts => codeFixCorrectReturnValue13.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue14.ts => codeFixCorrectReturnValue14.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue15.ts => codeFixCorrectReturnValue15.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue16.ts => codeFixCorrectReturnValue16.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue17.ts => codeFixCorrectReturnValue17.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue18.ts => codeFixCorrectReturnValue18.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue19.ts => codeFixCorrectReturnValue19.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue2.ts => codeFixCorrectReturnValue2.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue20.ts => codeFixCorrectReturnValue20.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue21.ts => codeFixCorrectReturnValue21.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue22.ts => codeFixCorrectReturnValue22.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue23.ts => codeFixCorrectReturnValue23.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue3.ts => codeFixCorrectReturnValue3.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue4.ts => codeFixCorrectReturnValue4.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue5.ts => codeFixCorrectReturnValue5.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue6.ts => codeFixCorrectReturnValue6.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue7.ts => codeFixCorrectReturnValue7.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue8.ts => codeFixCorrectReturnValue8.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue9.ts => codeFixCorrectReturnValue9.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue_all1.ts => codeFixCorrectReturnValue_all1.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue_all2.ts => codeFixCorrectReturnValue_all2.ts} (100%) rename tests/cases/fourslash/{codeFixSurmiseReturnValue_all3.ts => codeFixCorrectReturnValue_all3.ts} (100%) diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue1.ts b/tests/cases/fourslash/codeFixCorrectReturnValue1.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue1.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue1.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue10.ts b/tests/cases/fourslash/codeFixCorrectReturnValue10.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue10.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue10.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue11.ts b/tests/cases/fourslash/codeFixCorrectReturnValue11.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue11.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue11.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue12.ts b/tests/cases/fourslash/codeFixCorrectReturnValue12.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue12.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue12.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue13.ts b/tests/cases/fourslash/codeFixCorrectReturnValue13.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue13.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue13.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue14.ts b/tests/cases/fourslash/codeFixCorrectReturnValue14.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue14.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue14.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue15.ts b/tests/cases/fourslash/codeFixCorrectReturnValue15.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue15.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue15.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue16.ts b/tests/cases/fourslash/codeFixCorrectReturnValue16.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue16.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue16.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue17.ts b/tests/cases/fourslash/codeFixCorrectReturnValue17.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue17.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue17.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue18.ts b/tests/cases/fourslash/codeFixCorrectReturnValue18.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue18.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue18.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue19.ts b/tests/cases/fourslash/codeFixCorrectReturnValue19.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue19.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue19.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue2.ts b/tests/cases/fourslash/codeFixCorrectReturnValue2.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue2.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue2.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue20.ts b/tests/cases/fourslash/codeFixCorrectReturnValue20.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue20.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue20.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue21.ts b/tests/cases/fourslash/codeFixCorrectReturnValue21.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue21.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue21.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue22.ts b/tests/cases/fourslash/codeFixCorrectReturnValue22.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue22.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue22.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue23.ts b/tests/cases/fourslash/codeFixCorrectReturnValue23.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue23.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue23.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue3.ts b/tests/cases/fourslash/codeFixCorrectReturnValue3.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue3.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue3.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue4.ts b/tests/cases/fourslash/codeFixCorrectReturnValue4.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue4.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue4.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue5.ts b/tests/cases/fourslash/codeFixCorrectReturnValue5.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue5.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue5.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue6.ts b/tests/cases/fourslash/codeFixCorrectReturnValue6.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue6.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue6.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue7.ts b/tests/cases/fourslash/codeFixCorrectReturnValue7.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue7.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue7.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue8.ts b/tests/cases/fourslash/codeFixCorrectReturnValue8.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue8.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue8.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue9.ts b/tests/cases/fourslash/codeFixCorrectReturnValue9.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue9.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue9.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts b/tests/cases/fourslash/codeFixCorrectReturnValue_all1.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue_all1.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue_all1.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts b/tests/cases/fourslash/codeFixCorrectReturnValue_all2.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue_all2.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue_all2.ts diff --git a/tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts b/tests/cases/fourslash/codeFixCorrectReturnValue_all3.ts similarity index 100% rename from tests/cases/fourslash/codeFixSurmiseReturnValue_all3.ts rename to tests/cases/fourslash/codeFixCorrectReturnValue_all3.ts From 601fc5ea615b3f51c7a1632fe10b253c2104ed50 Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 26 Mar 2020 22:26:47 +0800 Subject: [PATCH 18/20] refactor and handle comment --- src/compiler/diagnosticMessages.json | 2 +- src/compiler/factoryPublic.ts | 19 ++++++ src/services/codefixes/returnValueCorrect.ts | 68 ++++++++----------- .../convertParamsToDestructuredObject.ts | 21 ------ src/services/utilities.ts | 21 ++++++ .../fourslash/codeFixCorrectReturnValue14.ts | 2 +- .../fourslash/codeFixCorrectReturnValue15.ts | 2 +- .../fourslash/codeFixCorrectReturnValue16.ts | 2 +- .../fourslash/codeFixCorrectReturnValue19.ts | 2 +- .../fourslash/codeFixCorrectReturnValue24.ts | 12 ++++ .../fourslash/codeFixCorrectReturnValue25.ts | 12 ++++ 11 files changed, 99 insertions(+), 64 deletions(-) create mode 100644 tests/cases/fourslash/codeFixCorrectReturnValue24.ts create mode 100644 tests/cases/fourslash/codeFixCorrectReturnValue25.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index dc36b872bfb0e..8438eb7ec9834 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5561,7 +5561,7 @@ "category": "Message", "code": 95109 }, - "Wrap this object literal with parentheses": { + "Wrap the following body with parentheses which should be an object literal": { "category": "Message", "code": 95110 }, diff --git a/src/compiler/factoryPublic.ts b/src/compiler/factoryPublic.ts index 8ffc3ee83918e..0360e61745dba 100644 --- a/src/compiler/factoryPublic.ts +++ b/src/compiler/factoryPublic.ts @@ -2042,6 +2042,25 @@ namespace ts { : node; } + export function updateFunctionLikeBody(declaration: FunctionLikeDeclaration, body: Block): FunctionLikeDeclaration { + switch (declaration.kind) { + case SyntaxKind.FunctionDeclaration: + return createFunctionDeclaration(declaration.decorators, declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.typeParameters, declaration.parameters, declaration.type, body); + case SyntaxKind.MethodDeclaration: + return createMethod(declaration.decorators, declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.questionToken, declaration.typeParameters, declaration.parameters, declaration.type, body); + case SyntaxKind.GetAccessor: + return createGetAccessor(declaration.decorators, declaration.modifiers, declaration.name, declaration.parameters, declaration.type, body); + case SyntaxKind.SetAccessor: + return createSetAccessor(declaration.decorators, declaration.modifiers, declaration.name, declaration.parameters, body); + case SyntaxKind.Constructor: + return createConstructor(declaration.decorators, declaration.modifiers, declaration.parameters, body); + case SyntaxKind.FunctionExpression: + return createFunctionExpression(declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.typeParameters, declaration.parameters, declaration.type, body); + case SyntaxKind.ArrowFunction: + return createArrowFunction(declaration.modifiers, declaration.typeParameters, declaration.parameters, declaration.type, declaration.equalsGreaterThanToken, body); + } + } + export function createClassDeclaration( decorators: readonly Decorator[] | undefined, modifiers: readonly Modifier[] | undefined, diff --git a/src/services/codefixes/returnValueCorrect.ts b/src/services/codefixes/returnValueCorrect.ts index ad440e561430c..4804bb807b837 100644 --- a/src/services/codefixes/returnValueCorrect.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -10,23 +10,25 @@ namespace ts.codefix { Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1.code ]; - enum FixKind { + enum ProblemKind { MissingReturnStatement, MissingParentheses } interface MissingReturnInfo { - kind: FixKind.MissingReturnStatement; + kind: ProblemKind.MissingReturnStatement; declaration: FunctionLikeDeclaration; expression: Expression; statement: Statement; + commentSource: Node; } interface MissingParenInfo { - kind: FixKind.MissingParentheses; + kind: ProblemKind.MissingParentheses; declaration: ArrowFunction; expression: Expression; statement: Statement; + commentSource: Node; } type Info = MissingReturnInfo | MissingParenInfo; @@ -39,10 +41,10 @@ namespace ts.codefix { const info = getInfo(program.getTypeChecker(), sourceFile, start, errorCode); if (!info) return undefined; - if (info.kind === FixKind.MissingReturnStatement) { + if (info.kind === ProblemKind.MissingReturnStatement) { return append( [getActionForfixAddReturnStatement(context, info.expression, info.statement)], - isArrowFunction(info.declaration) ? getActionForfixRemoveBlockBodyBrace(context, info.declaration, info.expression): undefined); + isArrowFunction(info.declaration) ? getActionForfixRemoveBlockBodyBrace(context, info.declaration, info.expression, info.commentSource): undefined); } else { return [getActionForfixWrapTheBlockWithParen(context, info.declaration, info.expression)]; @@ -58,7 +60,7 @@ namespace ts.codefix { break; case fixIdRemoveBlockBodyBrace: if (!isArrowFunction(info.declaration)) return undefined; - removeBlockBodyBrace(changes, diag.file, info.declaration, info.expression, /* withParen */ false); + removeBlockBodyBrace(changes, diag.file, info.declaration, info.expression, info.commentSource, /* withParen */ false); break; case fixIdWrapTheBlockWithParen: if (!isArrowFunction(info.declaration)) return undefined; @@ -70,25 +72,6 @@ namespace ts.codefix { }), }); - function updateFunctionLikeBody(declaration: FunctionLikeDeclaration, body: Block): FunctionLikeDeclaration { - switch (declaration.kind) { - case SyntaxKind.FunctionDeclaration: - return createFunctionDeclaration(declaration.decorators, declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.typeParameters, declaration.parameters, declaration.type, body); - case SyntaxKind.MethodDeclaration: - return createMethod(declaration.decorators, declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.questionToken, declaration.typeParameters, declaration.parameters, declaration.type, body); - case SyntaxKind.GetAccessor: - return createGetAccessor(declaration.decorators, declaration.modifiers, declaration.name, declaration.parameters, declaration.type, body); - case SyntaxKind.SetAccessor: - return createSetAccessor(declaration.decorators, declaration.modifiers, declaration.name, declaration.parameters, body); - case SyntaxKind.Constructor: - return createConstructor(declaration.decorators, declaration.modifiers, declaration.parameters, body); - case SyntaxKind.FunctionExpression: - return createFunctionExpression(declaration.modifiers, declaration.asteriskToken, declaration.name, declaration.typeParameters, declaration.parameters, declaration.type, body); - case SyntaxKind.ArrowFunction: - return createArrowFunction(declaration.modifiers, declaration.typeParameters, declaration.parameters, declaration.type, declaration.equalsGreaterThanToken, body); - } - } - function getFixInfo(checker: TypeChecker, declaration: FunctionLikeDeclaration, expectType: Type, isFunctionType: boolean): Info | undefined { if (!declaration.body || !isBlock(declaration.body) || length(declaration.body.statements) !== 1) return undefined; @@ -96,9 +79,10 @@ namespace ts.codefix { if (isExpressionStatement(firstStatement) && checkFixedAssignableTo(checker, declaration, firstStatement.expression, expectType, isFunctionType)) { return { declaration, - kind: FixKind.MissingReturnStatement, + kind: ProblemKind.MissingReturnStatement, expression: firstStatement.expression, - statement: firstStatement + statement: firstStatement, + commentSource: firstStatement.expression }; } else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { @@ -106,14 +90,16 @@ namespace ts.codefix { if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) { return isArrowFunction(declaration) ? { declaration, - kind: FixKind.MissingParentheses, + kind: ProblemKind.MissingParentheses, expression: node, - statement: firstStatement + statement: firstStatement, + commentSource: firstStatement.statement.expression } : { declaration, - kind: FixKind.MissingReturnStatement, + kind: ProblemKind.MissingReturnStatement, expression: node, - statement: firstStatement + statement: firstStatement, + commentSource: firstStatement.statement.expression }; } } @@ -124,9 +110,10 @@ namespace ts.codefix { if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) { return { declaration, - kind: FixKind.MissingReturnStatement, + kind: ProblemKind.MissingReturnStatement, expression: node, - statement: firstStatement + statement: firstStatement, + commentSource: firstBlockStatement }; } } @@ -183,11 +170,16 @@ namespace ts.codefix { } function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, expression: Expression, statement: Statement) { + suppressLeadingAndTrailingTrivia(expression); changes.replaceNode(sourceFile, statement, createReturn(expression)); } - function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression, withParen: boolean) { - changes.replaceNode(sourceFile, declaration.body, (withParen || needsParentheses(expression)) ? createParen(expression) : expression); + function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression, commentSource: Node, withParen: boolean) { + const newBody = (withParen || needsParentheses(expression)) ? createParen(expression) : expression; + suppressLeadingAndTrailingTrivia(commentSource); + copyComments(commentSource, newBody); + + changes.replaceNode(sourceFile, declaration.body, newBody); } function wrapBlockWithParen(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression) { @@ -199,13 +191,13 @@ namespace ts.codefix { return createCodeFixAction(fixId, changes, Diagnostics.Add_a_return_statement, fixIdAddReturnStatement, Diagnostics.Add_all_missing_return_statement); } - function getActionForfixRemoveBlockBodyBrace(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { - const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, /* withParen */ false)); + function getActionForfixRemoveBlockBodyBrace(context: CodeFixContext, declaration: ArrowFunction, expression: Expression, commentSource: Node) { + const changes = textChanges.ChangeTracker.with(context, t => removeBlockBodyBrace(t, context.sourceFile, declaration, expression, commentSource, /* withParen */ false)); return createCodeFixAction(fixId, changes, Diagnostics.Remove_block_body_braces, fixIdRemoveBlockBodyBrace, Diagnostics.Remove_all_incorrect_body_block_braces); } function getActionForfixWrapTheBlockWithParen(context: CodeFixContext, declaration: ArrowFunction, expression: Expression) { const changes = textChanges.ChangeTracker.with(context, t => wrapBlockWithParen(t, context.sourceFile, declaration, expression)); - return createCodeFixAction(fixId, changes, Diagnostics.Wrap_this_object_literal_with_parentheses, fixIdWrapTheBlockWithParen, Diagnostics.Wrap_all_object_literal_with_parentheses); + return createCodeFixAction(fixId, changes, Diagnostics.Wrap_the_following_body_with_parentheses_which_should_be_an_object_literal, fixIdWrapTheBlockWithParen, Diagnostics.Wrap_all_object_literal_with_parentheses); } } diff --git a/src/services/refactors/convertParamsToDestructuredObject.ts b/src/services/refactors/convertParamsToDestructuredObject.ts index 803a767761090..dcb0d520f9f87 100644 --- a/src/services/refactors/convertParamsToDestructuredObject.ts +++ b/src/services/refactors/convertParamsToDestructuredObject.ts @@ -491,27 +491,6 @@ namespace ts.refactor.convertParamsToDestructuredObject { } } - function copyComments(sourceNode: Node, targetNode: Node) { - const sourceFile = sourceNode.getSourceFile(); - const text = sourceFile.text; - if (hasLeadingLineBreak(sourceNode, text)) { - copyLeadingComments(sourceNode, targetNode, sourceFile); - } - else { - copyTrailingAsLeadingComments(sourceNode, targetNode, sourceFile); - } - copyTrailingComments(sourceNode, targetNode, sourceFile); - } - - function hasLeadingLineBreak(node: Node, text: string) { - const start = node.getFullStart(); - const end = node.getStart(); - for (let i = start; i < end; i++) { - if (text.charCodeAt(i) === CharacterCodes.lineFeed) return true; - } - return false; - } - function getParameterName(paramDeclaration: ValidParameterDeclaration) { return getTextOfIdentifierOrLiteral(paramDeclaration.name); } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 090c350496144..91a6a856fedab 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2250,6 +2250,27 @@ namespace ts { addEmitFlagsRecursively(node, EmitFlags.NoTrailingComments, getLastChild); } + export function copyComments(sourceNode: Node, targetNode: Node) { + const sourceFile = sourceNode.getSourceFile(); + const text = sourceFile.text; + if (hasLeadingLineBreak(sourceNode, text)) { + copyLeadingComments(sourceNode, targetNode, sourceFile); + } + else { + copyTrailingAsLeadingComments(sourceNode, targetNode, sourceFile); + } + copyTrailingComments(sourceNode, targetNode, sourceFile); + } + + function hasLeadingLineBreak(node: Node, text: string) { + const start = node.getFullStart(); + const end = node.getStart(); + for (let i = start; i < end; i++) { + if (text.charCodeAt(i) === CharacterCodes.lineFeed) return true; + } + return false; + } + function addEmitFlagsRecursively(node: Node, flag: EmitFlags, getChild: (n: Node) => Node | undefined) { addEmitFlags(node, flag); const child = getChild(node); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue14.ts b/tests/cases/fourslash/codeFixCorrectReturnValue14.ts index 8951f82993d0b..0f602eb1a139d 100644 --- a/tests/cases/fourslash/codeFixCorrectReturnValue14.ts +++ b/tests/cases/fourslash/codeFixCorrectReturnValue14.ts @@ -8,6 +8,6 @@ //// } verify.codeFixAvailable([ - { description: 'Wrap this object literal with parentheses' }, + { description: 'Wrap the following body with parentheses which should be an object literal' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue15.ts b/tests/cases/fourslash/codeFixCorrectReturnValue15.ts index b6ba1824293b3..6b6280cec4a0d 100644 --- a/tests/cases/fourslash/codeFixCorrectReturnValue15.ts +++ b/tests/cases/fourslash/codeFixCorrectReturnValue15.ts @@ -7,6 +7,6 @@ //// Foo(() => { bar: '1' }) verify.codeFixAvailable([ - { description: 'Wrap this object literal with parentheses' }, + { description: 'Wrap the following body with parentheses which should be an object literal' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue16.ts b/tests/cases/fourslash/codeFixCorrectReturnValue16.ts index 1a8e685ab6f30..bacb9a373b2e0 100644 --- a/tests/cases/fourslash/codeFixCorrectReturnValue16.ts +++ b/tests/cases/fourslash/codeFixCorrectReturnValue16.ts @@ -8,6 +8,6 @@ //// Foo(() => { bar: '1' }) verify.codeFixAvailable([ - { description: 'Wrap this object literal with parentheses' }, + { description: 'Wrap the following body with parentheses which should be an object literal' }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue19.ts b/tests/cases/fourslash/codeFixCorrectReturnValue19.ts index 00ef70a3a1840..a8726a5ed6f17 100644 --- a/tests/cases/fourslash/codeFixCorrectReturnValue19.ts +++ b/tests/cases/fourslash/codeFixCorrectReturnValue19.ts @@ -14,7 +14,7 @@ //// var x = { bar: '1' }} />; verify.codeFixAvailable([ - { description: 'Wrap this object literal with parentheses' }, + { description: 'Wrap the following body with parentheses which should be an object literal' }, { description: `Infer type of 'props' from usage` }, { description: 'Remove unused label' }, ]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue24.ts b/tests/cases/fourslash/codeFixCorrectReturnValue24.ts new file mode 100644 index 0000000000000..89f6c780b79b4 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue24.ts @@ -0,0 +1,12 @@ +/// + +//// function Foo (a: () => number) { a() } +//// Foo(() => { /* leading */ 1 /* trailing */ }) + +verify.codeFix({ + description: "Add a return statement", + index: 0, + newFileContent: + `function Foo (a: () => number) { a() } +Foo(() => { /* leading */ return 1 /* trailing */ })` +}) \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue25.ts b/tests/cases/fourslash/codeFixCorrectReturnValue25.ts new file mode 100644 index 0000000000000..205863efd4b19 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue25.ts @@ -0,0 +1,12 @@ +/// + +//// function Foo (a: () => number) { a() } +//// Foo(() => { /* leading */ 1 /* trailing */ }) + +verify.codeFix({ + description: "Remove block body braces", + index: 1, + newFileContent: + `function Foo (a: () => number) { a() } +Foo(() => /* leading */ 1 /* trailing */)` +}) \ No newline at end of file From 175cf4ea9f1f1b24a8cc312c67d3e41095a4e882 Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 26 Mar 2020 23:02:00 +0800 Subject: [PATCH 19/20] Support semi --- src/services/codefixes/returnValueCorrect.ts | 7 +++++- .../fourslash/codeFixCorrectReturnValue24.ts | 2 +- .../fourslash/codeFixCorrectReturnValue25.ts | 2 +- .../fourslash/codeFixCorrectReturnValue26.ts | 23 +++++++++++++++++++ 4 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/codeFixCorrectReturnValue26.ts diff --git a/src/services/codefixes/returnValueCorrect.ts b/src/services/codefixes/returnValueCorrect.ts index 4804bb807b837..947b9933e302e 100644 --- a/src/services/codefixes/returnValueCorrect.ts +++ b/src/services/codefixes/returnValueCorrect.ts @@ -171,7 +171,12 @@ namespace ts.codefix { function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, expression: Expression, statement: Statement) { suppressLeadingAndTrailingTrivia(expression); - changes.replaceNode(sourceFile, statement, createReturn(expression)); + const probablyNeedSemi = probablyUsesSemicolons(sourceFile); + changes.replaceNode(sourceFile, statement, createReturn(expression), { + leadingTriviaOption: textChanges.LeadingTriviaOption.Exclude, + trailingTriviaOption: textChanges.TrailingTriviaOption.Exclude, + suffix: probablyNeedSemi ? ";" : undefined + }); } function removeBlockBodyBrace(changes: textChanges.ChangeTracker, sourceFile: SourceFile, declaration: ArrowFunction, expression: Expression, commentSource: Node, withParen: boolean) { diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue24.ts b/tests/cases/fourslash/codeFixCorrectReturnValue24.ts index 89f6c780b79b4..6cc9e52e4d0d9 100644 --- a/tests/cases/fourslash/codeFixCorrectReturnValue24.ts +++ b/tests/cases/fourslash/codeFixCorrectReturnValue24.ts @@ -7,6 +7,6 @@ verify.codeFix({ description: "Add a return statement", index: 0, newFileContent: - `function Foo (a: () => number) { a() } +`function Foo (a: () => number) { a() } Foo(() => { /* leading */ return 1 /* trailing */ })` }) \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue25.ts b/tests/cases/fourslash/codeFixCorrectReturnValue25.ts index 205863efd4b19..3d7f750367305 100644 --- a/tests/cases/fourslash/codeFixCorrectReturnValue25.ts +++ b/tests/cases/fourslash/codeFixCorrectReturnValue25.ts @@ -7,6 +7,6 @@ verify.codeFix({ description: "Remove block body braces", index: 1, newFileContent: - `function Foo (a: () => number) { a() } +`function Foo (a: () => number) { a() } Foo(() => /* leading */ 1 /* trailing */)` }) \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue26.ts b/tests/cases/fourslash/codeFixCorrectReturnValue26.ts new file mode 100644 index 0000000000000..36f9ff9fa3b13 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue26.ts @@ -0,0 +1,23 @@ +/// +//// interface A { +//// bar: string; +//// } +//// +//// function Foo (a: () => A) { a(); } +//// Foo(() => { +//// { bar: '123'; } +//// }) + +verify.codeFix({ + description: "Add a return statement", + index: 0, + newFileContent: +`interface A { + bar: string; +} + +function Foo (a: () => A) { a(); } +Foo(() => { + return { bar: '123' }; +})` +}) From 5d8355c6b1e3fca0d62ab41c7f5924816f2a717b Mon Sep 17 00:00:00 2001 From: kingwl Date: Thu, 26 Mar 2020 23:48:17 +0800 Subject: [PATCH 20/20] make helper internal --- src/compiler/factoryPublic.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/factoryPublic.ts b/src/compiler/factoryPublic.ts index a952041cbed40..f0aae0daf938b 100644 --- a/src/compiler/factoryPublic.ts +++ b/src/compiler/factoryPublic.ts @@ -2057,6 +2057,7 @@ namespace ts { : node; } + /* @internal */ export function updateFunctionLikeBody(declaration: FunctionLikeDeclaration, body: Block): FunctionLikeDeclaration { switch (declaration.kind) { case SyntaxKind.FunctionDeclaration: