Merge pull request #16045 from aozgaa/codeFixAddMissingMethod

Code fix add missing method
This commit is contained in:
Arthur Ozga
2017-06-05 16:50:46 -07:00
committed by GitHub
14 changed files with 372 additions and 98 deletions

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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()
};
}
}
}
}

View File

@@ -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));

View File

@@ -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;
`);

View File

@@ -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;
`);

View 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.");
}
`);

View 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;
`);

View 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.");
}
`);

View 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.");
}
`);

View 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();

View File

@@ -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;