Quick fix for functions lacking return expressions (#26434)

* stash

* add surmise for return type

* add support for more case

* add more test case

* add more testcase and fix all test

* fix changed diagnosis

* fix broken test case

* add more case

* rename quickfix

* fix conflict

* fix fix desc

* fix semi

* Avoid replace brace with paren

* Split fix all action

* Add return work in same line

* fix test cases

* rename baseline

* refactor and handle comment

* Support semi

* make helper internal
This commit is contained in:
Wenlu Wang
2020-04-02 10:06:14 -07:00
committed by GitHub
parent 54b0e4acc5
commit afc41f095d
45 changed files with 947 additions and 30 deletions
+1 -3
View File
@@ -574,9 +574,7 @@ namespace ts {
},
getApparentType,
getUnionType,
isTypeAssignableTo: (source, target) => {
return isTypeAssignableTo(source, target);
},
isTypeAssignableTo,
createAnonymousType,
createSignature,
createSymbol,
+25
View File
@@ -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
+20
View File
@@ -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,
@@ -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(<Expression>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);
}
}
@@ -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);
@@ -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);
}
+1
View File
@@ -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",
+26
View File
@@ -2273,6 +2273,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);
@@ -2367,6 +2388,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) {
@@ -5,9 +5,10 @@
////}
verify.codeFix({
index: 2,
description: "Add async modifier to containing function",
newFileContent:
`const f: () => Promise<number | string> = async () => {
await Promise.resolve('foo');
}`,
}`
});
@@ -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');
}`,
}`
});
@@ -5,6 +5,7 @@
////}
verify.codeFix({
index: 1,
description: "Add async modifier to containing function",
newFileContent:
`const f: () => Promise<Array<number | string>> = async function() {
@@ -5,6 +5,7 @@
////}
verify.codeFix({
index: 2,
description: "Add async modifier to containing function",
newFileContent:
`const f: () => Promise<number | string> = async () => {
@@ -5,6 +5,7 @@
////}
verify.codeFix({
index: 1,
description: "Add async modifier to containing function",
newFileContent:
`const f = async function(): Promise<number> {
@@ -5,6 +5,7 @@
////}
verify.codeFix({
index: 2,
description: "Add async modifier to containing function",
newFileContent:
`const f = async (): Promise<number[]> => {
@@ -5,6 +5,7 @@
////}
verify.codeFix({
index: 1,
description: "Add async modifier to containing function",
newFileContent:
`async function f(): Promise<number | string> {
@@ -7,6 +7,7 @@
////}
verify.codeFix({
index: 1,
description: "Add async modifier to containing function",
newFileContent:
`class Foo {
@@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
//// function Foo (): number {
//// 1
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
]);
@@ -0,0 +1,8 @@
/// <reference path='fourslash.ts' />
//// const a: ((() => number) | (() => undefined)) = () => { 1 }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' }
]);
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// interface A {
//// bar: string
//// }
////
//// function Foo (): A {
//// { bar: '123' }
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove unused label' },
]);
@@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// 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' },
]);
@@ -0,0 +1,18 @@
/// <reference path='fourslash.ts' />
//// 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
}
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// 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' },
]);
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
//// 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' },
]);
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// 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' },
]);
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// interface A {
//// bar: string
//// }
////
//// function Foo (): A {
//// bar: '123'
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove unused label' },
]);
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// }
//// interface ElementAttributesProperty { props; }
//// }
//// class Comp { props: { t: () => number } }
//// var x = <Comp t={() => { 1 }} />;
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' },
{ description: `Infer type of 'props' from usage` }
]);
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// }
//// interface ElementAttributesProperty { props; }
//// }
//// interface A {
//// bar: string
//// }
//// class Comp { props: { t: () => number } }
//// var x = <Comp t={() => { 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' },
]);
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// interface A {
//// foo: number
//// }
//// function Foo (): A {
//// ({ foo: 1 })
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
]);
@@ -0,0 +1,23 @@
/// <reference path='fourslash.ts' />
//@Filename: file.tsx
//// declare module JSX {
//// interface Element { }
//// interface IntrinsicElements {
//// }
//// interface ElementAttributesProperty { props; }
//// }
//// interface A {
//// bar: string
//// }
//// class Comp { props: { t: () => number } }
//// var x = <Comp t={() => {
//// { 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' },
]);
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// interface A {
//// a: () => number
//// }
////
//// let b: A = {
//// a: () => { 1 }
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' },
]);
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />
//// interface A {
//// a: () => number
//// }
////
//// function Foo (): A {
//// return {
//// a: () => { 1 }
//// }
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' },
]);
@@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
//// class Foo {
//// bar: () => number = () => { 1 }
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' }
]);
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
//// 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 */ })`
})
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />
//// 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 */)`
})
@@ -0,0 +1,23 @@
/// <reference path='fourslash.ts' />
//// 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' };
})`
})
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// interface A {
//// foo: number
//// }
//// function Foo (): A | number {
//// 1
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
]);
@@ -0,0 +1,7 @@
/// <reference path='fourslash.ts' />
//// function Foo (): any {
//// 1
//// }
verify.not.codeFixAvailable();
@@ -0,0 +1,7 @@
/// <reference path='fourslash.ts' />
//// function Foo (): void {
//// undefined
//// }
verify.not.codeFixAvailable();
@@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
//// function Foo (): undefined {
//// undefined
//// }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
]);
@@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
//// function Foo (a: () => number) { a() }
//// Foo(() => { 1 })
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' }
]);
@@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />
//// function Foo (a: (() => number) | (() => undefined) ) { a() }
//// Foo(() => { 1 })
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' }
]);
@@ -0,0 +1,8 @@
/// <reference path='fourslash.ts' />
//// const a: () => number = () => { 1 }
verify.codeFixAvailable([
{ description: 'Add a return statement' },
{ description: 'Remove block body braces' }
]);
@@ -0,0 +1,121 @@
/// <reference path='fourslash.ts' />
//// 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' } } }`,
});
@@ -0,0 +1,99 @@
/// <reference path='fourslash.ts' />
//// 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' }) }`,
});
@@ -0,0 +1,99 @@
/// <reference path='fourslash.ts' />
//// 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' }) }`,
});