diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4ddec362457b4..da214d08e9808 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -574,9 +574,7 @@ namespace ts { }, getApparentType, getUnionType, - isTypeAssignableTo: (source, target) => { - return isTypeAssignableTo(source, target); - }, + isTypeAssignableTo, createAnonymousType, createSignature, createSymbol, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d1e13c777529d..f53ef4cb1d35c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5621,6 +5621,31 @@ "category": "Message", "code": 95110 }, + "Add a return statement": { + "category": "Message", + "code": 95111 + }, + "Remove block body braces": { + "category": "Message", + "code": 95112 + }, + "Wrap the following body with parentheses which should be an object literal": { + "category": "Message", + "code": 95113 + }, + "Add all missing return statement": { + "category": "Message", + "code": 95114 + }, + "Remove all incorrect body block braces": { + "category": "Message", + "code": 95115 + }, + "Wrap all object literal with parentheses": { + "category": "Message", + "code": 95116 + }, + "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", "code": 18004 diff --git a/src/compiler/factoryPublic.ts b/src/compiler/factoryPublic.ts index 49f7f6255c7f0..f0aae0daf938b 100644 --- a/src/compiler/factoryPublic.ts +++ b/src/compiler/factoryPublic.ts @@ -2057,6 +2057,26 @@ namespace ts { : node; } + /* @internal */ + 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 new file mode 100644 index 0000000000000..947b9933e302e --- /dev/null +++ b/src/services/codefixes/returnValueCorrect.ts @@ -0,0 +1,208 @@ +/* @internal */ +namespace ts.codefix { + const fixId = "returnValueCorrect"; + const fixIdAddReturnStatement = "fixAddReturnStatement"; + const fixIdRemoveBlockBodyBrace = "fixRemoveBlockBodyBrace"; + 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 ProblemKind { + MissingReturnStatement, + MissingParentheses + } + + interface MissingReturnInfo { + kind: ProblemKind.MissingReturnStatement; + declaration: FunctionLikeDeclaration; + expression: Expression; + statement: Statement; + commentSource: Node; + } + + interface MissingParenInfo { + kind: ProblemKind.MissingParentheses; + declaration: ArrowFunction; + expression: Expression; + statement: Statement; + commentSource: Node; + } + + type Info = MissingReturnInfo | MissingParenInfo; + + registerCodeFix({ + errorCodes, + 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 === ProblemKind.MissingReturnStatement) { + return append( + [getActionForfixAddReturnStatement(context, info.expression, info.statement)], + isArrowFunction(info.declaration) ? getActionForfixRemoveBlockBodyBrace(context, info.declaration, info.expression, info.commentSource): undefined); + } + else { + return [getActionForfixWrapTheBlockWithParen(context, info.declaration, info.expression)]; + } + }, + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => { + const info = getInfo(context.program.getTypeChecker(), diag.file, diag.start, diag.code); + if (!info) return undefined; + + switch (context.fixId) { + case fixIdAddReturnStatement: + addReturnStatement(changes, diag.file, info.expression, info.statement); + break; + case fixIdRemoveBlockBodyBrace: + if (!isArrowFunction(info.declaration)) return undefined; + removeBlockBodyBrace(changes, diag.file, info.declaration, info.expression, info.commentSource, /* withParen */ false); + break; + case fixIdWrapTheBlockWithParen: + if (!isArrowFunction(info.declaration)) return undefined; + wrapBlockWithParen(changes, diag.file, info.declaration, info.expression); + break; + default: + Debug.fail(JSON.stringify(context.fixId)); + } + }), + }); + + 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) && checkFixedAssignableTo(checker, declaration, firstStatement.expression, expectType, isFunctionType)) { + return { + declaration, + kind: ProblemKind.MissingReturnStatement, + expression: firstStatement.expression, + statement: firstStatement, + commentSource: firstStatement.expression + }; + } + else if (isLabeledStatement(firstStatement) && isExpressionStatement(firstStatement.statement)) { + const node = createObjectLiteral([createPropertyAssignment(firstStatement.label, firstStatement.statement.expression)]); + if (checkFixedAssignableTo(checker, declaration, node, expectType, isFunctionType)) { + return isArrowFunction(declaration) ? { + declaration, + kind: ProblemKind.MissingParentheses, + expression: node, + statement: firstStatement, + commentSource: firstStatement.statement.expression + } : { + declaration, + kind: ProblemKind.MissingReturnStatement, + expression: node, + statement: firstStatement, + commentSource: firstStatement.statement.expression + }; + } + } + 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: ProblemKind.MissingReturnStatement, + expression: node, + statement: firstStatement, + commentSource: firstBlockStatement + }; + } + } + } + + 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, errorCode: number): Info | undefined { + const node = getTokenAtPosition(sourceFile, position); + if (!node.parent) return undefined; + + const declaration = findAncestor(node.parent, isFunctionLikeDeclaration); + 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); + 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); + } + 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; + } + } + + function addReturnStatement(changes: textChanges.ChangeTracker, sourceFile: SourceFile, expression: Expression, statement: Statement) { + suppressLeadingAndTrailingTrivia(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) { + 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) { + changes.replaceNode(sourceFile, declaration.body, createParen(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); + } + + 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_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/addOrRemoveBracesToArrowFunction.ts b/src/services/refactors/addOrRemoveBracesToArrowFunction.ts index 89a03c3c23989..7f423f4df0555 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/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/tsconfig.json b/src/services/tsconfig.json index 9d1b8b44052fd..117461bc1900c 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -65,6 +65,7 @@ "codefixes/importFixes.ts", "codefixes/fixImplicitThis.ts", "codefixes/fixSpelling.ts", + "codefixes/returnValueCorrect.ts", "codefixes/fixAddMissingMember.ts", "codefixes/fixAddMissingNewOperator.ts", "codefixes/fixCannotFindModule.ts", diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 677e79235d7e5..3d958a5bad6a9 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2269,6 +2269,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); @@ -2363,6 +2384,11 @@ namespace ts { return idx === -1 ? -1 : idx + 1; } + /* @internal */ + export function needsParentheses(expression: Expression) { + return isBinaryExpression(expression) && expression.operatorToken.kind === SyntaxKind.CommaToken || isObjectLiteralExpression(expression); + } + export function getContextualTypeFromParent(node: Expression, checker: TypeChecker): Type | undefined { const { parent } = node; switch (parent.kind) { diff --git a/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts b/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts index 42e0bc5be780f..3518b017050ee 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction10.ts @@ -5,9 +5,10 @@ ////} verify.codeFix({ + index: 2, 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..1cfce8eda2e39 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: 2, 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..57fb74a4fe091 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction13.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 2, 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..47f474ecb0c61 100644 --- a/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts +++ b/tests/cases/fourslash/codeFixAwaitInSyncFunction15.ts @@ -5,6 +5,7 @@ ////} verify.codeFix({ + index: 2, 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 { diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue1.ts b/tests/cases/fourslash/codeFixCorrectReturnValue1.ts new file mode 100644 index 0000000000000..4dc5bdfc68386 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue1.ts @@ -0,0 +1,9 @@ +/// + +//// function Foo (): number { +//// 1 +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue10.ts b/tests/cases/fourslash/codeFixCorrectReturnValue10.ts new file mode 100644 index 0000000000000..cdbf416984d58 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue10.ts @@ -0,0 +1,8 @@ +/// + +//// const a: ((() => number) | (() => undefined)) = () => { 1 } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' } +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue11.ts b/tests/cases/fourslash/codeFixCorrectReturnValue11.ts new file mode 100644 index 0000000000000..a57c05134959c --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue11.ts @@ -0,0 +1,13 @@ +/// +//// 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/codeFixCorrectReturnValue12.ts b/tests/cases/fourslash/codeFixCorrectReturnValue12.ts new file mode 100644 index 0000000000000..d242e4f4cf87e --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue12.ts @@ -0,0 +1,15 @@ +/// +//// 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: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue13.ts b/tests/cases/fourslash/codeFixCorrectReturnValue13.ts new file mode 100644 index 0000000000000..a1e2cc553ccac --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue13.ts @@ -0,0 +1,18 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// const a: () => A = () => { +//// { bar: '1' } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, + { description: 'Remove unused label' }, +]); + +interface A { + bar: string +} diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue14.ts b/tests/cases/fourslash/codeFixCorrectReturnValue14.ts new file mode 100644 index 0000000000000..0f602eb1a139d --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue14.ts @@ -0,0 +1,13 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// const a: () => A = () => { +//// bar: '1' +//// } + +verify.codeFixAvailable([ + { 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 new file mode 100644 index 0000000000000..6b6280cec4a0d --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue15.ts @@ -0,0 +1,12 @@ +/// +//// interface A { +//// bar: string +//// } +//// +//// function Foo (a: () => A) { a() } +//// Foo(() => { bar: '1' }) + +verify.codeFixAvailable([ + { 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 new file mode 100644 index 0000000000000..bacb9a373b2e0 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue16.ts @@ -0,0 +1,13 @@ +/// + +//// interface A { +//// bar: string +//// } +//// +//// function Foo (a: () => A) { a() } +//// Foo(() => { bar: '1' }) + +verify.codeFixAvailable([ + { description: 'Wrap the following body with parentheses which should be an object literal' }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue17.ts b/tests/cases/fourslash/codeFixCorrectReturnValue17.ts new file mode 100644 index 0000000000000..2572b478e1756 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue17.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/codeFixCorrectReturnValue18.ts b/tests/cases/fourslash/codeFixCorrectReturnValue18.ts new file mode 100644 index 0000000000000..ffa038a48fc5c --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue18.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: `Infer type of 'props' from usage` } +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue19.ts b/tests/cases/fourslash/codeFixCorrectReturnValue19.ts new file mode 100644 index 0000000000000..a8726a5ed6f17 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue19.ts @@ -0,0 +1,20 @@ +/// + +//@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 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/codeFixCorrectReturnValue2.ts b/tests/cases/fourslash/codeFixCorrectReturnValue2.ts new file mode 100644 index 0000000000000..9d361067c4ca5 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue2.ts @@ -0,0 +1,13 @@ +/// + +//// interface A { +//// foo: number +//// } + +//// function Foo (): A { +//// ({ foo: 1 }) +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue20.ts b/tests/cases/fourslash/codeFixCorrectReturnValue20.ts new file mode 100644 index 0000000000000..f552edaee6713 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue20.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: `Infer type of 'props' from usage` }, + { description: 'Remove unused label' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue21.ts b/tests/cases/fourslash/codeFixCorrectReturnValue21.ts new file mode 100644 index 0000000000000..1a248f77909f2 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue21.ts @@ -0,0 +1,14 @@ +/// + +//// interface A { +//// a: () => number +//// } +//// +//// let b: A = { +//// a: () => { 1 } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue22.ts b/tests/cases/fourslash/codeFixCorrectReturnValue22.ts new file mode 100644 index 0000000000000..372513592afb8 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue22.ts @@ -0,0 +1,16 @@ +/// + +//// interface A { +//// a: () => number +//// } +//// +//// function Foo (): A { +//// return { +//// a: () => { 1 } +//// } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue23.ts b/tests/cases/fourslash/codeFixCorrectReturnValue23.ts new file mode 100644 index 0000000000000..14fde02533799 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue23.ts @@ -0,0 +1,10 @@ +/// + +//// class Foo { +//// bar: () => number = () => { 1 } +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' } +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue24.ts b/tests/cases/fourslash/codeFixCorrectReturnValue24.ts new file mode 100644 index 0000000000000..6cc9e52e4d0d9 --- /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..3d7f750367305 --- /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 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' }; +})` +}) diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue3.ts b/tests/cases/fourslash/codeFixCorrectReturnValue3.ts new file mode 100644 index 0000000000000..364da21f42dfc --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue3.ts @@ -0,0 +1,13 @@ +/// + +//// interface A { +//// foo: number +//// } + +//// function Foo (): A | number { +//// 1 +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue4.ts b/tests/cases/fourslash/codeFixCorrectReturnValue4.ts new file mode 100644 index 0000000000000..e1270bee3f361 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue4.ts @@ -0,0 +1,7 @@ +/// + +//// function Foo (): any { +//// 1 +//// } + +verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue5.ts b/tests/cases/fourslash/codeFixCorrectReturnValue5.ts new file mode 100644 index 0000000000000..de457636368eb --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue5.ts @@ -0,0 +1,7 @@ +/// + +//// function Foo (): void { +//// undefined +//// } + +verify.not.codeFixAvailable(); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue6.ts b/tests/cases/fourslash/codeFixCorrectReturnValue6.ts new file mode 100644 index 0000000000000..693145f8c61b8 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue6.ts @@ -0,0 +1,9 @@ +/// + +//// function Foo (): undefined { +//// undefined +//// } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue7.ts b/tests/cases/fourslash/codeFixCorrectReturnValue7.ts new file mode 100644 index 0000000000000..bd0bfd3b343b3 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue7.ts @@ -0,0 +1,9 @@ +/// + +//// function Foo (a: () => number) { a() } +//// Foo(() => { 1 }) + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' } +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue8.ts b/tests/cases/fourslash/codeFixCorrectReturnValue8.ts new file mode 100644 index 0000000000000..cde3ab2e42ff6 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue8.ts @@ -0,0 +1,9 @@ +/// + +//// function Foo (a: (() => number) | (() => undefined) ) { a() } +//// Foo(() => { 1 }) + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' } +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue9.ts b/tests/cases/fourslash/codeFixCorrectReturnValue9.ts new file mode 100644 index 0000000000000..104fd785f00a1 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue9.ts @@ -0,0 +1,8 @@ +/// + +//// const a: () => number = () => { 1 } + +verify.codeFixAvailable([ + { description: 'Add a return statement' }, + { description: 'Remove block body braces' } +]); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue_all1.ts b/tests/cases/fourslash/codeFixCorrectReturnValue_all1.ts new file mode 100644 index 0000000000000..e14f31e5e8b9c --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue_all1.ts @@ -0,0 +1,121 @@ +/// + +//// 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 baz6: () => number = () => { 1 } +//// +//// const test: { a: () => A } = { a: () => { bar: '1' } } + +verify.codeFixAll({ + fixId: "fixAddReturnStatement", + fixAllDescription: "Add all missing return statement", + 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' } +} +const baz6: () => number = () => { return 1 } + +const test: { a: () => A } = { a: () => { return { bar: '1' } } }`, +}); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue_all2.ts b/tests/cases/fourslash/codeFixCorrectReturnValue_all2.ts new file mode 100644 index 0000000000000..e3b0d1c0b1d39 --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue_all2.ts @@ -0,0 +1,99 @@ +/// + +//// 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: "fixRemoveBlockBodyBrace", + fixAllDescription: "Remove all incorrect body block braces", + 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' }) }`, +}); diff --git a/tests/cases/fourslash/codeFixCorrectReturnValue_all3.ts b/tests/cases/fourslash/codeFixCorrectReturnValue_all3.ts new file mode 100644 index 0000000000000..0d4806b19c18c --- /dev/null +++ b/tests/cases/fourslash/codeFixCorrectReturnValue_all3.ts @@ -0,0 +1,99 @@ +/// + +//// 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: "fixWrapTheBlockWithParen", + fixAllDescription: "Wrap all object literal with parentheses", + 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' }) }`, +}); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy