mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-12 11:08:26 -05:00
Merge pull request #16045 from aozgaa/codeFixAddMissingMethod
Code fix add missing method
This commit is contained in:
@@ -2291,7 +2291,7 @@ namespace ts {
|
||||
|
||||
function typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string {
|
||||
const typeNode = nodeBuilder.typeToTypeNode(type, enclosingDeclaration, toNodeBuilderFlags(flags) | NodeBuilderFlags.IgnoreErrors | NodeBuilderFlags.WriteTypeParametersInQualifiedName);
|
||||
Debug.assert(typeNode !== undefined, "should always get typenode?");
|
||||
Debug.assert(typeNode !== undefined, "should always get typenode");
|
||||
const options = { removeComments: true };
|
||||
const writer = createTextWriter("");
|
||||
const printer = createPrinter(options);
|
||||
|
||||
@@ -3559,11 +3559,11 @@
|
||||
"category": "Message",
|
||||
"code": 90015
|
||||
},
|
||||
"Add declaration for missing property '{0}'.": {
|
||||
"Declare property '{0}'.": {
|
||||
"category": "Message",
|
||||
"code": 90016
|
||||
},
|
||||
"Add index signature for missing property '{0}'.": {
|
||||
"Add index signature for property '{0}'.": {
|
||||
"category": "Message",
|
||||
"code": 90017
|
||||
},
|
||||
@@ -3587,7 +3587,15 @@
|
||||
"category": "Message",
|
||||
"code": 90022
|
||||
},
|
||||
|
||||
"Declare method '{0}'.": {
|
||||
"category": "Message",
|
||||
"code": 90023
|
||||
},
|
||||
"Declare static method '{0}'.": {
|
||||
"category": "Message",
|
||||
"code": 90024
|
||||
},
|
||||
|
||||
"Convert function to an ES2015 class": {
|
||||
"category": "Message",
|
||||
"code": 95001
|
||||
|
||||
@@ -2535,7 +2535,6 @@ namespace ts {
|
||||
getNonNullableType(type: Type): Type;
|
||||
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
|
||||
typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode;
|
||||
/** Note that the resulting nodes cannot be checked. */
|
||||
signatureToSignatureDeclaration(signature: Signature, kind: SyntaxKind, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): SignatureDeclaration;
|
||||
|
||||
@@ -2263,23 +2263,22 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares expected text to the text that would be in the sole range
|
||||
* (ie: [|...|]) in the file after applying the codefix sole codefix
|
||||
* in the source file.
|
||||
*
|
||||
* Because codefixes are only applied on the working file, it is unsafe
|
||||
* to apply this more than once (consider a refactoring across files).
|
||||
* Finds and applies a code action corresponding to the supplied parameters.
|
||||
* If index is undefined, applies the unique code action available.
|
||||
* @param errorCode The error code that generated the code action.
|
||||
* @param index The nth (0-index-based) codeaction available generated by errorCode.
|
||||
*/
|
||||
public verifyRangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number) {
|
||||
public getAndApplyCodeActions(errorCode?: number, index?: number) {
|
||||
const fileName = this.activeFile.fileName;
|
||||
this.applyCodeActions(this.getCodeFixActions(fileName, errorCode), index);
|
||||
}
|
||||
|
||||
public verifyRangeIs(expectedText: string, includeWhiteSpace?: boolean) {
|
||||
const ranges = this.getRanges();
|
||||
if (ranges.length !== 1) {
|
||||
this.raiseError("Exactly one range should be specified in the testfile.");
|
||||
}
|
||||
|
||||
const fileName = this.activeFile.fileName;
|
||||
|
||||
this.applyCodeAction(fileName, this.getCodeFixActions(fileName, errorCode), index);
|
||||
|
||||
const actualText = this.rangeText(ranges[0]);
|
||||
|
||||
const result = includeWhiteSpace
|
||||
@@ -2291,6 +2290,16 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares expected text to the text that would be in the sole range
|
||||
* (ie: [|...|]) in the file after applying the codefix sole codefix
|
||||
* in the source file.
|
||||
*/
|
||||
public verifyRangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number) {
|
||||
this.getAndApplyCodeActions(errorCode, index);
|
||||
this.verifyRangeIs(expectedText, includeWhiteSpace);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies fixes for the errors in fileName and compares the results to
|
||||
* expectedContents after all fixes have been applied.
|
||||
@@ -2303,7 +2312,7 @@ namespace FourSlash {
|
||||
public verifyFileAfterCodeFix(expectedContents: string, fileName?: string) {
|
||||
fileName = fileName ? fileName : this.activeFile.fileName;
|
||||
|
||||
this.applyCodeAction(fileName, this.getCodeFixActions(fileName));
|
||||
this.applyCodeActions(this.getCodeFixActions(fileName));
|
||||
|
||||
const actualContents: string = this.getFileContent(fileName);
|
||||
if (this.removeWhitespace(actualContents) !== this.removeWhitespace(expectedContents)) {
|
||||
@@ -2341,11 +2350,10 @@ namespace FourSlash {
|
||||
return actions;
|
||||
}
|
||||
|
||||
private applyCodeAction(fileName: string, actions: ts.CodeAction[], index?: number): void {
|
||||
private applyCodeActions(actions: ts.CodeAction[], index?: number): void {
|
||||
if (index === undefined) {
|
||||
if (!(actions && actions.length === 1)) {
|
||||
const actionText = (actions && actions.length) ? JSON.stringify(actions) : "none";
|
||||
this.raiseError(`Should find exactly one codefix, but found ${actionText}`);
|
||||
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found.`);
|
||||
}
|
||||
index = 0;
|
||||
}
|
||||
@@ -2355,12 +2363,11 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
const fileChanges = ts.find(actions[index].changes, change => change.fileName === fileName);
|
||||
if (!fileChanges) {
|
||||
this.raiseError("The CodeFix found doesn't provide any changes in this file.");
|
||||
}
|
||||
const changes = actions[index].changes;
|
||||
|
||||
this.applyEdits(fileChanges.fileName, fileChanges.textChanges, /*isFormattingEdit*/ false);
|
||||
for (const change of changes) {
|
||||
this.applyEdits(change.fileName, change.textChanges, /*isFormattingEdit*/ false);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) {
|
||||
@@ -2748,7 +2755,7 @@ namespace FourSlash {
|
||||
|
||||
const codeActions = this.languageService.getRefactorCodeActions(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply);
|
||||
|
||||
this.applyCodeAction(this.activeFile.fileName, codeActions);
|
||||
this.applyCodeActions(codeActions);
|
||||
const actualContent = this.getFileContent(this.activeFile.fileName);
|
||||
|
||||
if (this.normalizeNewlines(actualContent) !== this.normalizeNewlines(expectedContent)) {
|
||||
@@ -3795,6 +3802,14 @@ namespace FourSlashInterface {
|
||||
this.state.verifyFileAfterApplyingRefactorAtMarker(markerName, expectedContent, refactorNameToApply, formattingOptions);
|
||||
}
|
||||
|
||||
public rangeIs(expectedText: string, includeWhiteSpace?: boolean): void {
|
||||
this.state.verifyRangeIs(expectedText, includeWhiteSpace);
|
||||
}
|
||||
|
||||
public getAndApplyCodeFix(errorCode?: number, index?: number): void {
|
||||
this.state.getAndApplyCodeActions(errorCode, index);
|
||||
}
|
||||
|
||||
public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void {
|
||||
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode);
|
||||
}
|
||||
|
||||
@@ -8,107 +8,161 @@ namespace ts.codefix {
|
||||
|
||||
function getActionsForAddMissingMember(context: CodeFixContext): CodeAction[] | undefined {
|
||||
|
||||
const sourceFile = context.sourceFile;
|
||||
const tokenSourceFile = context.sourceFile;
|
||||
const start = context.span.start;
|
||||
// This is the identifier of the missing property. eg:
|
||||
// The identifier of the missing property. eg:
|
||||
// this.missing = 1;
|
||||
// ^^^^^^^
|
||||
const token = getTokenAtPosition(sourceFile, start, /*includeJsDocComment*/ false);
|
||||
const token = getTokenAtPosition(tokenSourceFile, start, /*includeJsDocComment*/ false);
|
||||
|
||||
if (token.kind !== SyntaxKind.Identifier) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!isPropertyAccessExpression(token.parent) || token.parent.expression.kind !== SyntaxKind.ThisKeyword) {
|
||||
if (!isPropertyAccessExpression(token.parent)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const classMemberDeclaration = getThisContainer(token, /*includeArrowFunctions*/ false);
|
||||
if (!isClassElement(classMemberDeclaration)) {
|
||||
return undefined;
|
||||
const tokenName = token.getText(tokenSourceFile);
|
||||
|
||||
let makeStatic = false;
|
||||
let classDeclaration: ClassLikeDeclaration;
|
||||
|
||||
if (token.parent.expression.kind === SyntaxKind.ThisKeyword) {
|
||||
const containingClassMemberDeclaration = getThisContainer(token, /*includeArrowFunctions*/ false);
|
||||
if (!isClassElement(containingClassMemberDeclaration)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
classDeclaration = <ClassLikeDeclaration>containingClassMemberDeclaration.parent;
|
||||
|
||||
// Property accesses on `this` in a static method are accesses of a static member.
|
||||
makeStatic = classDeclaration && hasModifier(containingClassMemberDeclaration, ModifierFlags.Static);
|
||||
}
|
||||
else {
|
||||
|
||||
const checker = context.program.getTypeChecker();
|
||||
const leftExpression = token.parent.expression;
|
||||
const leftExpressionType = checker.getTypeAtLocation(leftExpression);
|
||||
|
||||
if (leftExpressionType.flags & TypeFlags.Object) {
|
||||
const symbol = leftExpressionType.symbol;
|
||||
if (symbol.flags & SymbolFlags.Class) {
|
||||
classDeclaration = symbol.declarations && <ClassLikeDeclaration>symbol.declarations[0];
|
||||
if (leftExpressionType !== checker.getDeclaredTypeOfSymbol(symbol)) {
|
||||
// The expression is a class symbol but the type is not the instance-side.
|
||||
makeStatic = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const classDeclaration = <ClassLikeDeclaration>classMemberDeclaration.parent;
|
||||
if (!classDeclaration || !isClassLike(classDeclaration)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const isStatic = hasModifier(classMemberDeclaration, ModifierFlags.Static);
|
||||
const classDeclarationSourceFile = getSourceFileOfNode(classDeclaration);
|
||||
const classOpenBrace = getOpenBraceOfClassLike(classDeclaration, classDeclarationSourceFile);
|
||||
|
||||
return isInJavaScriptFile(sourceFile) ? getActionsForAddMissingMemberInJavaScriptFile() : getActionsForAddMissingMemberInTypeScriptFile();
|
||||
return isInJavaScriptFile(classDeclarationSourceFile) ?
|
||||
getActionsForAddMissingMemberInJavaScriptFile(classDeclaration, makeStatic) :
|
||||
getActionsForAddMissingMemberInTypeScriptFile(classDeclaration, makeStatic);
|
||||
|
||||
function getActionsForAddMissingMemberInJavaScriptFile(): CodeAction[] | undefined {
|
||||
const memberName = token.getText();
|
||||
function getActionsForAddMissingMemberInJavaScriptFile(classDeclaration: ClassLikeDeclaration, makeStatic: boolean): CodeAction[] | undefined {
|
||||
let actions: CodeAction[];
|
||||
|
||||
if (isStatic) {
|
||||
const methodCodeAction = getActionForMethodDeclaration(/*includeTypeScriptSyntax*/ false);
|
||||
if (methodCodeAction) {
|
||||
actions = [methodCodeAction];
|
||||
}
|
||||
|
||||
if (makeStatic) {
|
||||
if (classDeclaration.kind === SyntaxKind.ClassExpression) {
|
||||
return undefined;
|
||||
return actions;
|
||||
}
|
||||
|
||||
const className = classDeclaration.name.getText();
|
||||
|
||||
return [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_static_property_0), [memberName]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: classDeclaration.getEnd(), length: 0 },
|
||||
newText: `${context.newLineCharacter}${className}.${memberName} = undefined;${context.newLineCharacter}`
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
const staticInitialization = createStatement(createAssignment(
|
||||
createPropertyAccess(createIdentifier(className), tokenName),
|
||||
createIdentifier("undefined")));
|
||||
|
||||
const staticInitializationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
staticInitializationChangeTracker.insertNodeAfter(
|
||||
classDeclarationSourceFile,
|
||||
classDeclaration,
|
||||
staticInitialization,
|
||||
{ suffix: context.newLineCharacter });
|
||||
const initializeStaticAction = {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_static_property_0), [tokenName]),
|
||||
changes: staticInitializationChangeTracker.getChanges()
|
||||
};
|
||||
|
||||
(actions || (actions = [])).push(initializeStaticAction);
|
||||
return actions;
|
||||
}
|
||||
else {
|
||||
const classConstructor = getFirstConstructorWithBody(classDeclaration);
|
||||
if (!classConstructor) {
|
||||
return undefined;
|
||||
return actions;
|
||||
}
|
||||
|
||||
return [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [memberName]),
|
||||
changes: [{
|
||||
fileName: sourceFile.fileName,
|
||||
textChanges: [{
|
||||
span: { start: classConstructor.body.getEnd() - 1, length: 0 },
|
||||
newText: `this.${memberName} = undefined;${context.newLineCharacter}`
|
||||
}]
|
||||
}]
|
||||
}];
|
||||
const propertyInitialization = createStatement(createAssignment(
|
||||
createPropertyAccess(createThis(), tokenName),
|
||||
createIdentifier("undefined")));
|
||||
|
||||
const propertyInitializationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
propertyInitializationChangeTracker.insertNodeAt(
|
||||
classDeclarationSourceFile,
|
||||
classConstructor.body.getEnd() - 1,
|
||||
propertyInitialization,
|
||||
{ prefix: context.newLineCharacter, suffix: context.newLineCharacter });
|
||||
|
||||
const initializeAction = {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]),
|
||||
changes: propertyInitializationChangeTracker.getChanges()
|
||||
};
|
||||
|
||||
(actions || (actions = [])).push(initializeAction);
|
||||
return actions;
|
||||
}
|
||||
}
|
||||
|
||||
function getActionsForAddMissingMemberInTypeScriptFile(): CodeAction[] | undefined {
|
||||
let typeNode: TypeNode;
|
||||
function getActionsForAddMissingMemberInTypeScriptFile(classDeclaration: ClassLikeDeclaration, makeStatic: boolean): CodeAction[] | undefined {
|
||||
let actions: CodeAction[];
|
||||
|
||||
if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const binaryExpression = token.parent.parent as BinaryExpression;
|
||||
|
||||
const checker = context.program.getTypeChecker();
|
||||
const widenedType = checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(binaryExpression.right)));
|
||||
typeNode = checker.typeToTypeNode(widenedType, classDeclaration);
|
||||
const methodCodeAction = getActionForMethodDeclaration(/*includeTypeScriptSyntax*/ true);
|
||||
if (methodCodeAction) {
|
||||
actions = [methodCodeAction];
|
||||
}
|
||||
|
||||
let typeNode: TypeNode;
|
||||
if (token.parent.parent.kind === SyntaxKind.BinaryExpression) {
|
||||
const binaryExpression = token.parent.parent as BinaryExpression;
|
||||
const otherExpression = token.parent === binaryExpression.left ? binaryExpression.right : binaryExpression.left;
|
||||
const checker = context.program.getTypeChecker();
|
||||
const widenedType = checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(otherExpression)));
|
||||
typeNode = checker.typeToTypeNode(widenedType, classDeclaration);
|
||||
}
|
||||
typeNode = typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword);
|
||||
|
||||
const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile);
|
||||
|
||||
const property = createProperty(
|
||||
/*decorators*/undefined,
|
||||
/*modifiers*/ isStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined,
|
||||
token.getText(sourceFile),
|
||||
/*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined,
|
||||
tokenName,
|
||||
/*questionToken*/ undefined,
|
||||
typeNode,
|
||||
/*initializer*/ undefined);
|
||||
const propertyChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
propertyChangeTracker.insertNodeAfter(sourceFile, openBrace, property, { suffix: context.newLineCharacter });
|
||||
propertyChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, property, { suffix: context.newLineCharacter });
|
||||
|
||||
const actions = [{
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]),
|
||||
(actions || (actions = [])).push({
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Declare_property_0), [tokenName]),
|
||||
changes: propertyChangeTracker.getChanges()
|
||||
}];
|
||||
});
|
||||
|
||||
if (!isStatic) {
|
||||
if (!makeStatic) {
|
||||
// Index signatures cannot have the static modifier.
|
||||
const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword);
|
||||
const indexingParameter = createParameter(
|
||||
/*decorators*/ undefined,
|
||||
@@ -125,15 +179,32 @@ namespace ts.codefix {
|
||||
typeNode);
|
||||
|
||||
const indexSignatureChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
indexSignatureChangeTracker.insertNodeAfter(sourceFile, openBrace, indexSignature, { suffix: context.newLineCharacter });
|
||||
indexSignatureChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, indexSignature, { suffix: context.newLineCharacter });
|
||||
|
||||
actions.push({
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_missing_property_0), [token.getText()]),
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]),
|
||||
changes: indexSignatureChangeTracker.getChanges()
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
function getActionForMethodDeclaration(includeTypeScriptSyntax: boolean): CodeAction | undefined {
|
||||
if (token.parent.parent.kind === SyntaxKind.CallExpression) {
|
||||
const callExpression = <CallExpression>token.parent.parent;
|
||||
const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName, includeTypeScriptSyntax, makeStatic);
|
||||
|
||||
const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
|
||||
methodDeclarationChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, methodDeclaration, { suffix: context.newLineCharacter });
|
||||
return {
|
||||
description: formatStringFromArgs(getLocaleSpecificMessage(makeStatic ?
|
||||
Diagnostics.Declare_method_0 :
|
||||
Diagnostics.Declare_static_method_0),
|
||||
[tokenName]),
|
||||
changes: methodDeclarationChangeTracker.getChanges()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,6 +142,50 @@ namespace ts.codefix {
|
||||
}
|
||||
}
|
||||
|
||||
export function createMethodFromCallExpression(callExpression: CallExpression, methodName: string, includeTypeScriptSyntax: boolean, makeStatic: boolean): MethodDeclaration {
|
||||
const parameters = createDummyParameters(callExpression.arguments.length, /*names*/ undefined, /*minArgumentCount*/ undefined, includeTypeScriptSyntax);
|
||||
|
||||
let typeParameters: TypeParameterDeclaration[];
|
||||
if (includeTypeScriptSyntax) {
|
||||
const typeArgCount = length(callExpression.typeArguments);
|
||||
for (let i = 0; i < typeArgCount; i++) {
|
||||
const name = typeArgCount < 8 ? String.fromCharCode(CharacterCodes.T + i) : `T${i}`;
|
||||
const typeParameter = createTypeParameterDeclaration(name, /*constraint*/ undefined, /*defaultType*/ undefined);
|
||||
(typeParameters ? typeParameters : typeParameters = []).push(typeParameter);
|
||||
}
|
||||
}
|
||||
|
||||
const newMethod = createMethod(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined,
|
||||
/*asteriskToken*/ undefined,
|
||||
methodName,
|
||||
/*questionToken*/ undefined,
|
||||
typeParameters,
|
||||
parameters,
|
||||
/*type*/ includeTypeScriptSyntax ? createKeywordTypeNode(SyntaxKind.AnyKeyword) : undefined,
|
||||
createStubbedMethodBody()
|
||||
);
|
||||
return newMethod;
|
||||
}
|
||||
|
||||
function createDummyParameters(argCount: number, names: string[] | undefined, minArgumentCount: number | undefined, addAnyType: boolean) {
|
||||
const parameters: ParameterDeclaration[] = [];
|
||||
for (let i = 0; i < argCount; i++) {
|
||||
const newParameter = createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
/*name*/ names && names[i] || `arg${i}`,
|
||||
/*questionToken*/ minArgumentCount !== undefined && i >= minArgumentCount ? createToken(SyntaxKind.QuestionToken) : undefined,
|
||||
/*type*/ addAnyType ? createKeywordTypeNode(SyntaxKind.AnyKeyword) : undefined,
|
||||
/*initializer*/ undefined);
|
||||
parameters.push(newParameter);
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
function createMethodImplementingSignatures(signatures: Signature[], name: PropertyName, optional: boolean, modifiers: Modifier[] | undefined): MethodDeclaration {
|
||||
/** This is *a* signature with the maximal number of arguments,
|
||||
* such that if there is a "maximal" signature without rest arguments,
|
||||
@@ -163,19 +207,7 @@ namespace ts.codefix {
|
||||
const maxNonRestArgs = maxArgsSignature.parameters.length - (maxArgsSignature.hasRestParameter ? 1 : 0);
|
||||
const maxArgsParameterSymbolNames = maxArgsSignature.parameters.map(symbol => symbol.getName());
|
||||
|
||||
const parameters: ParameterDeclaration[] = [];
|
||||
for (let i = 0; i < maxNonRestArgs; i++) {
|
||||
const anyType = createKeywordTypeNode(SyntaxKind.AnyKeyword);
|
||||
const newParameter = createParameter(
|
||||
/*decorators*/ undefined,
|
||||
/*modifiers*/ undefined,
|
||||
/*dotDotDotToken*/ undefined,
|
||||
maxArgsParameterSymbolNames[i],
|
||||
/*questionToken*/ i >= minArgumentCount ? createToken(SyntaxKind.QuestionToken) : undefined,
|
||||
anyType,
|
||||
/*initializer*/ undefined);
|
||||
parameters.push(newParameter);
|
||||
}
|
||||
const parameters = createDummyParameters(maxNonRestArgs, maxArgsParameterSymbolNames, minArgumentCount, /*addAnyType*/ true);
|
||||
|
||||
if (someSigHasRestParameter) {
|
||||
const anyArrayType = createArrayTypeNode(createKeywordTypeNode(SyntaxKind.AnyKeyword));
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
////}
|
||||
////|]
|
||||
|
||||
verify.rangeAfterCodeFix(`class C {
|
||||
verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 0);
|
||||
verify.currentFileContentIs(`class C {
|
||||
static method() {
|
||||
()=>{ this.foo === 10 };
|
||||
}
|
||||
}
|
||||
C.foo = undefined;`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0);
|
||||
C.foo = undefined;
|
||||
`);
|
||||
@@ -9,7 +9,9 @@
|
||||
////}
|
||||
////|]
|
||||
|
||||
verify.rangeAfterCodeFix(`class C {
|
||||
verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 2)
|
||||
verify.currentFileContentIs(`class C {
|
||||
static p = ()=>{ this.foo === 10 };
|
||||
}
|
||||
C.foo = undefined;`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 2);
|
||||
C.foo = undefined;
|
||||
`);
|
||||
|
||||
34
tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts
Normal file
34
tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
|
||||
// @Filename: f2.js
|
||||
//// import * as X from "./f1";
|
||||
//// X.C.m0(1, "", []);
|
||||
//// X.C.x;
|
||||
//// let c = new X.C;
|
||||
//// c.m1();
|
||||
//// c.y = {};
|
||||
|
||||
// @Filename: f1.ts
|
||||
//// export class C {[|
|
||||
//// |]x: number;
|
||||
//// static y: string;
|
||||
//// }
|
||||
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
|
||||
verify.rangeIs(`
|
||||
y: { [x: string]: any; };
|
||||
m1(): any {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
static x: any;
|
||||
static m0(arg0: any, arg1: any, arg2: any): any {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
39
tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts
Normal file
39
tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
|
||||
// @Filename: f2.ts
|
||||
//// import * as X from "./f1";
|
||||
//// X.C.m0(1, "", []);
|
||||
//// X.C.x;
|
||||
//// let c = new X.C;
|
||||
//// c.m1();
|
||||
//// c.y = {};
|
||||
|
||||
// @Filename: f1.js
|
||||
//// [|export class C {
|
||||
//// constructor() { }
|
||||
//// }
|
||||
////
|
||||
//// |]
|
||||
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
|
||||
verify.rangeIs(`
|
||||
export class C {
|
||||
m1() {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
static m0(arg0, arg1, arg2) {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
constructor() {
|
||||
this.y = undefined;
|
||||
}
|
||||
}
|
||||
C.x = undefined;
|
||||
`);
|
||||
26
tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
Normal file
26
tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// class A {[|
|
||||
//// |]static foo0() {
|
||||
//// this.m1(1,2,3);
|
||||
//// A.m2(1,2);
|
||||
//// this.prop1 = 10;
|
||||
//// A.prop2 = "asdf";
|
||||
//// }
|
||||
//// }
|
||||
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
|
||||
verify.rangeIs(`
|
||||
static prop2: string;
|
||||
static prop1: number;
|
||||
static m2(arg0: any, arg1: any): any {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
static m1(arg0: any, arg1: any, arg2: any): any {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
27
tests/cases/fourslash/codeFixUndeclaredMethod.ts
Normal file
27
tests/cases/fourslash/codeFixUndeclaredMethod.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// class A {[|
|
||||
//// |]constructor() {
|
||||
//// this.foo1(1,2,3);
|
||||
//// // 7 type args
|
||||
//// this.foo2<1,2,3,4,5,6,7>();
|
||||
//// // 8 type args
|
||||
//// this.foo3<1,2,3,4,5,6,7,8>();
|
||||
//// }
|
||||
//// }
|
||||
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
verify.getAndApplyCodeFix(/*errorCode*/undefined, 0);
|
||||
|
||||
verify.rangeIs(`
|
||||
foo3<T0, T1, T2, T3, T4, T5, T6, T7>(): any {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
foo2<T, U, V, W, X, Y, Z>(): any {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
foo1(arg0: any, arg1: any, arg2: any): any {
|
||||
throw new Error("Method not implemented.");
|
||||
}
|
||||
`);
|
||||
17
tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts
Normal file
17
tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
//// interface I { x: number; }
|
||||
//// let i: I;
|
||||
//// i.y;
|
||||
//// i.foo();
|
||||
//// enum E { a,b }
|
||||
//// let e: typeof E;
|
||||
//// e.a;
|
||||
//// e.c;
|
||||
//// let obj = { a: 1, b: "asdf"};
|
||||
//// obj.c;
|
||||
//// type T<U> = I | U;
|
||||
//// let t: T<number>;
|
||||
//// t.x;
|
||||
|
||||
verify.not.codeFixAvailable();
|
||||
@@ -236,7 +236,9 @@ declare namespace FourSlashInterface {
|
||||
noMatchingBracePositionInCurrentFile(bracePosition: number): void;
|
||||
DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void;
|
||||
noDocCommentTemplate(): void;
|
||||
rangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number): void;
|
||||
rangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number): void
|
||||
getAndApplyCodeFix(errorCode?: number, index?: number): void;
|
||||
rangeIs(expectedText: string, includeWhiteSpace?: boolean): void;
|
||||
fileAfterApplyingRefactorAtMarker(markerName: string, expectedContent: string, refactorNameToApply: string, formattingOptions?: FormatCodeOptions): void;
|
||||
importFixAtPosition(expectedTextArray: string[], errorCode?: number): void;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user