From 420279b99d436d4652c44c33c0d7706fe66c047c Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 23 May 2017 11:34:37 -0700 Subject: [PATCH 01/14] add missing method --- src/compiler/diagnosticMessages.json | 6 ++- src/services/codefixes/fixAddMissingMember.ts | 27 +++++++++---- src/services/codefixes/helpers.ts | 40 +++++++++++++++++++ 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a7b6db70e5e..3803a9fbc23 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3583,7 +3583,11 @@ "category": "Message", "code": 90022 }, - + "Add declaration for missing method '{0}'.": { + "category": "Message", + "code": 90023 + }, + "Convert function to an ES2015 class": { "category": "Message", "code": 95001 diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 64e7e560094..64f1618a675 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -2,7 +2,7 @@ namespace ts.codefix { registerCodeFix({ errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code, - Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code], + Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code], getCodeActions: getActionsForAddMissingMember }); @@ -79,20 +79,31 @@ namespace ts.codefix { } function getActionsForAddMissingMemberInTypeScriptFile(): CodeAction[] | undefined { - let typeNode: TypeNode; + const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); + const tokenName = token.getText(sourceFile); + let actions: CodeAction[]; + if (token.parent.parent.kind === SyntaxKind.CallExpression) { + const callExpression = token.parent.parent; + const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName); + + const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); + methodDeclarationChangeTracker.insertNodeAfter(sourceFile, openBrace, methodDeclaration, { suffix: context.newLineCharacter }); + actions = [{ + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_method_0), [tokenName]), + changes: methodDeclarationChangeTracker.getChanges() + }]; + } + + let typeNode: TypeNode; 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); } - typeNode = typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword); - const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); - const property = createProperty( /*decorators*/undefined, /*modifiers*/ isStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, @@ -103,10 +114,10 @@ namespace ts.codefix { const propertyChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); propertyChangeTracker.insertNodeAfter(sourceFile, openBrace, property, { suffix: context.newLineCharacter }); - const actions = [{ + (actions || (actions = [])).push({ description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]), changes: propertyChangeTracker.getChanges() - }]; + }); if (!isStatic) { const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword); diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 39d8e13dde9..dc51129736d 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -142,6 +142,46 @@ namespace ts.codefix { } } + export function createMethodFromCallExpression(callExpression: CallExpression, methodName: string): MethodDeclaration { + const argCount = callExpression.arguments.length; + const parameters: ParameterDeclaration[] = []; + for (let i = 0; i < argCount; i++) { + const typeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword); + const newParameter = createParameter( + /*decorators*/ undefined, + /*modifiers*/ undefined, + /*dotDotDotToken*/ undefined, + `arg${i}`, + /*questionToken*/ undefined, + typeNode, + /*initializer*/ undefined); + parameters.push(newParameter); + } + + const typeArgCount = callExpression.typeArguments ? callExpression.typeArguments.length : 0; + let typeParameters: TypeParameterDeclaration[]; + 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*/ undefined, + /*asteriskToken*/ undefined, + methodName, + /*questionToken*/ undefined, + typeParameters, + parameters, + /*type*/ undefined, + createStubbedMethodBody() + ) + + return newMethod; + } + 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, From 15c029189c36f6cc835403c5307e274e14d49277 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 23 May 2017 13:32:41 -0700 Subject: [PATCH 02/14] testing --- src/compiler/types.ts | 1 - src/harness/fourslash.ts | 65 ++++++++++++------- src/services/codefixes/helpers.ts | 2 +- .../fourslash/codeFixUndeclaredMethod.ts | 27 ++++++++ tests/cases/fourslash/fourslash.ts | 4 +- 5 files changed, 73 insertions(+), 26 deletions(-) create mode 100644 tests/cases/fourslash/codeFixUndeclaredMethod.ts diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a8da09a11c5..7aa8890d73d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2522,7 +2522,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; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 8f19b83c7ad..a021a4d5660 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2275,6 +2275,34 @@ namespace FourSlash { }); } + /** + * 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 getAndApplyCodeActions(errorCode?: number, index?: number) { + const fileName = this.activeFile.fileName; + this.applyCodeActions(fileName, 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 actualText = this.normalizeNewlines(this.rangeText(ranges[0])); + + const result = includeWhiteSpace + ? actualText === this.normalizeNewlines(expectedText) + : this.removeWhitespace(actualText) === this.removeWhitespace(expectedText); + + if (!result) { + this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`); + } + } + /** * Compares expected text to the text that would be in the sole range * (ie: [|...|]) in the file after applying the codefix sole codefix @@ -2284,24 +2312,8 @@ namespace FourSlash { * to apply this more than once (consider a refactoring across files). */ public verifyRangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number) { - 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 - ? normalizeNewLines(actualText) === normalizeNewLines(expectedText) - : this.removeWhitespace(actualText) === this.removeWhitespace(expectedText); - - if (!result) { - this.raiseError(`Actual text doesn't match expected text. Actual:\n'${actualText}'\nExpected:\n'${expectedText}'`); - } + this.getAndApplyCodeActions(errorCode, index); + this.verifyRangeIs(expectedText, includeWhiteSpace); } /** @@ -2316,7 +2328,7 @@ namespace FourSlash { public verifyFileAfterCodeFix(expectedContents: string, fileName?: string) { fileName = fileName ? fileName : this.activeFile.fileName; - this.applyCodeAction(fileName, this.getCodeFixActions(fileName)); + this.applyCodeActions(fileName, this.getCodeFixActions(fileName)); const actualContents: string = this.getFileContent(fileName); if (this.removeWhitespace(actualContents) !== this.removeWhitespace(expectedContents)) { @@ -2354,11 +2366,10 @@ namespace FourSlash { return actions; } - private applyCodeAction(fileName: string, actions: ts.CodeAction[], index?: number): void { + private applyCodeActions(fileName: string, 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; } @@ -2758,7 +2769,7 @@ namespace FourSlash { const codeActions = this.languageService.getRefactorCodeActions(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply); - this.applyCodeAction(this.activeFile.fileName, codeActions); + this.applyCodeActions(this.activeFile.fileName, codeActions); const actualContent = this.getFileContent(this.activeFile.fileName); if (this.normalizeNewlines(actualContent) !== this.normalizeNewlines(expectedContent)) { @@ -3805,6 +3816,14 @@ namespace FourSlashInterface { this.state.verifyFileAfterApplyingRefactorAtMarker(markerName, expectedContent, refactorNameToApply, formattingOptions); } + public rangeIs(expectedText: string, includeWhiteSpace?: boolean): void { + this.state.verifyRangeIs(expectedText, includeWhiteSpace); + } + + public applyCodeFix(errorCode?: number, index?: number): void { + this.state.getAndApplyCodeActions(errorCode, index); + } + public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void { this.state.verifyImportFixAtPosition(expectedTextArray, errorCode); } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index dc51129736d..5b81f71b6c9 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -177,7 +177,7 @@ namespace ts.codefix { parameters, /*type*/ undefined, createStubbedMethodBody() - ) + ); return newMethod; } diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts new file mode 100644 index 00000000000..b595a09c6a7 --- /dev/null +++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts @@ -0,0 +1,27 @@ +/// + +//// 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.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); + +verify.rangeIs(` + foo3() { + throw new Error("Method not implemented."); + } + foo2() { + throw new Error("Method not implemented."); + } + foo1(arg0: any, arg1: any, arg2: any) { + throw new Error("Method not implemented."); + } +`); \ No newline at end of file diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 3fada673f35..b0f731dcdea 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -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 + applyCodeFix(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; From 1939c65fc694421e4e2f33958cc5d9d1cad2385f Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 23 May 2017 13:37:53 -0700 Subject: [PATCH 03/14] rename helper --- src/harness/fourslash.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index a021a4d5660..ee7329102b2 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2283,7 +2283,7 @@ namespace FourSlash { */ public getAndApplyCodeActions(errorCode?: number, index?: number) { const fileName = this.activeFile.fileName; - this.applyCodeActions(fileName, this.getCodeFixActions(fileName, errorCode), index); + this.applyCodeAction(fileName, this.getCodeFixActions(fileName, errorCode), index); } public verifyRangeIs(expectedText: string, includeWhiteSpace?: boolean) { @@ -2328,7 +2328,7 @@ namespace FourSlash { public verifyFileAfterCodeFix(expectedContents: string, fileName?: string) { fileName = fileName ? fileName : this.activeFile.fileName; - this.applyCodeActions(fileName, this.getCodeFixActions(fileName)); + this.applyCodeAction(fileName, this.getCodeFixActions(fileName)); const actualContents: string = this.getFileContent(fileName); if (this.removeWhitespace(actualContents) !== this.removeWhitespace(expectedContents)) { @@ -2366,7 +2366,7 @@ namespace FourSlash { return actions; } - private applyCodeActions(fileName: string, actions: ts.CodeAction[], index?: number): void { + private applyCodeAction(fileName: string, actions: ts.CodeAction[], index?: number): void { if (index === undefined) { if (!(actions && actions.length === 1)) { this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found.`); @@ -2769,7 +2769,7 @@ namespace FourSlash { const codeActions = this.languageService.getRefactorCodeActions(this.activeFile.fileName, formattingOptions, markerPos, refactorNameToApply); - this.applyCodeActions(this.activeFile.fileName, codeActions); + this.applyCodeAction(this.activeFile.fileName, codeActions); const actualContent = this.getFileContent(this.activeFile.fileName); if (this.normalizeNewlines(actualContent) !== this.normalizeNewlines(expectedContent)) { From f56be99b114a643f8c4ca1ec1623f9717fb95448 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Tue, 23 May 2017 13:47:53 -0700 Subject: [PATCH 04/14] cleanup --- src/harness/fourslash.ts | 4 ++-- src/services/codefixes/fixAddMissingMember.ts | 20 +++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index ee7329102b2..92886cb7603 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2292,10 +2292,10 @@ namespace FourSlash { this.raiseError("Exactly one range should be specified in the testfile."); } - const actualText = this.normalizeNewlines(this.rangeText(ranges[0])); + const actualText = this.rangeText(ranges[0]); const result = includeWhiteSpace - ? actualText === this.normalizeNewlines(expectedText) + ? normalizeNewLines(actualText) === normalizeNewLines(expectedText) : this.removeWhitespace(actualText) === this.removeWhitespace(expectedText); if (!result) { diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 64f1618a675..231e320c93b 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -2,7 +2,7 @@ namespace ts.codefix { registerCodeFix({ errorCodes: [Diagnostics.Property_0_does_not_exist_on_type_1.code, - Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code], + Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2.code], getCodeActions: getActionsForAddMissingMember }); @@ -33,13 +33,12 @@ namespace ts.codefix { return undefined; } + const tokenName = token.getText(sourceFile); const isStatic = hasModifier(classMemberDeclaration, ModifierFlags.Static); return isInJavaScriptFile(sourceFile) ? getActionsForAddMissingMemberInJavaScriptFile() : getActionsForAddMissingMemberInTypeScriptFile(); function getActionsForAddMissingMemberInJavaScriptFile(): CodeAction[] | undefined { - const memberName = token.getText(); - if (isStatic) { if (classDeclaration.kind === SyntaxKind.ClassExpression) { return undefined; @@ -48,12 +47,12 @@ namespace ts.codefix { const className = classDeclaration.name.getText(); return [{ - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_static_property_0), [memberName]), + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_static_property_0), [tokenName]), changes: [{ fileName: sourceFile.fileName, textChanges: [{ span: { start: classDeclaration.getEnd(), length: 0 }, - newText: `${context.newLineCharacter}${className}.${memberName} = undefined;${context.newLineCharacter}` + newText: `${context.newLineCharacter}${className}.${tokenName} = undefined;${context.newLineCharacter}` }] }] }]; @@ -66,12 +65,12 @@ namespace ts.codefix { } return [{ - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [memberName]), + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]), changes: [{ fileName: sourceFile.fileName, textChanges: [{ span: { start: classConstructor.body.getEnd() - 1, length: 0 }, - newText: `this.${memberName} = undefined;${context.newLineCharacter}` + newText: `this.${tokenName} = undefined;${context.newLineCharacter}` }] }] }]; @@ -80,7 +79,6 @@ namespace ts.codefix { function getActionsForAddMissingMemberInTypeScriptFile(): CodeAction[] | undefined { const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); - const tokenName = token.getText(sourceFile); let actions: CodeAction[]; if (token.parent.parent.kind === SyntaxKind.CallExpression) { @@ -107,7 +105,7 @@ namespace ts.codefix { const property = createProperty( /*decorators*/undefined, /*modifiers*/ isStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, - token.getText(sourceFile), + tokenName, /*questionToken*/ undefined, typeNode, /*initializer*/ undefined); @@ -115,7 +113,7 @@ namespace ts.codefix { propertyChangeTracker.insertNodeAfter(sourceFile, openBrace, property, { suffix: context.newLineCharacter }); (actions || (actions = [])).push({ - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [token.getText()]), + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [tokenName]), changes: propertyChangeTracker.getChanges() }); @@ -139,7 +137,7 @@ namespace ts.codefix { indexSignatureChangeTracker.insertNodeAfter(sourceFile, openBrace, 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_missing_property_0), [tokenName]), changes: indexSignatureChangeTracker.getChanges() }); } From f19c3121c2ebd95f316a76be6b4571cfe6df235e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 31 May 2017 17:17:58 -0700 Subject: [PATCH 05/14] Apply codefixes across files --- src/harness/fourslash.ts | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 92886cb7603..0cbf98cebf7 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2283,7 +2283,7 @@ namespace FourSlash { */ public getAndApplyCodeActions(errorCode?: number, index?: number) { const fileName = this.activeFile.fileName; - this.applyCodeAction(fileName, this.getCodeFixActions(fileName, errorCode), index); + this.applyCodeActions(this.getCodeFixActions(fileName, errorCode), index); } public verifyRangeIs(expectedText: string, includeWhiteSpace?: boolean) { @@ -2307,9 +2307,6 @@ 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). */ public verifyRangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number) { this.getAndApplyCodeActions(errorCode, index); @@ -2328,7 +2325,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)) { @@ -2366,7 +2363,7 @@ 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)) { this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found.`); @@ -2379,12 +2376,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) { @@ -2769,7 +2765,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)) { From c125f080a43a60706cff8dec6a6b548014480d43 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 31 May 2017 17:18:36 -0700 Subject: [PATCH 06/14] add tests --- .../codeFixUndeclaredAcrossFiles1.ts | 34 ++++++++++++++ .../codeFixUndeclaredAcrossFiles2.ts | 40 +++++++++++++++++ .../codeFixUndeclaredInStaticMethod.ts | 44 +++++++++++++++++++ .../fourslash/codeFixUndeclaredMethod.ts | 6 +-- .../codeFixUndeclaredPropertyAccesses.ts | 17 +++++++ 5 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts create mode 100644 tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts create mode 100644 tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts create mode 100644 tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts new file mode 100644 index 00000000000..830259e3ca7 --- /dev/null +++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts @@ -0,0 +1,34 @@ +/// + +// @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.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*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."); + } +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts new file mode 100644 index 00000000000..cc01f8d50ee --- /dev/null +++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts @@ -0,0 +1,40 @@ +/// + +// @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 { +//// x: number; +//// static y: string; +//// constructor() { } +//// }|] + +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*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."); + } + x: number; + static y: string; + constructor() { + this.y = undefined; + } +} +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts new file mode 100644 index 00000000000..94ad3f77615 --- /dev/null +++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts @@ -0,0 +1,44 @@ +/// + +//// class A {[| +//// |]static foo0() { +//// this.m1(1,2,3); +//// A.m2(1,2); +//// this.prop1 = 10; +//// A.prop2 = "asdf"; +//// } +//// } + +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.applyCodeFix(/*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."); + } +`); + +// class A { +// 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."); +// } +// static foo0() { +// this.m1(1,2,3); +// A.m2(1,2); +// this.prop1 = 10; +// A.prop2 = "asdf"; +// } +// } + diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts index b595a09c6a7..1f842a5bcf1 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts @@ -15,13 +15,13 @@ verify.applyCodeFix(/*errorCode*/undefined, 0); verify.applyCodeFix(/*errorCode*/undefined, 0); verify.rangeIs(` - foo3() { + foo3(): any { throw new Error("Method not implemented."); } - foo2() { + foo2(): any { throw new Error("Method not implemented."); } - foo1(arg0: any, arg1: any, arg2: any) { + foo1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } `); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts b/tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts new file mode 100644 index 00000000000..ee05d2c0a9c --- /dev/null +++ b/tests/cases/fourslash/codeFixUndeclaredPropertyAccesses.ts @@ -0,0 +1,17 @@ +/// + +//// 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 = I | U; +//// let t: T; +//// t.x; + +verify.not.codeFixAvailable(); \ No newline at end of file From 21fc30f69bdb10aa3ef2a67ac528a68103983c27 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Wed, 31 May 2017 17:41:35 -0700 Subject: [PATCH 07/14] Add support across files and static, js methods --- src/compiler/checker.ts | 7 +- src/compiler/diagnosticMessages.json | 10 +- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 4 + src/services/codefixes/fixAddMissingMember.ts | 132 ++++++++++++------ src/services/codefixes/helpers.ts | 66 ++++----- .../codeFixUndeclaredAcrossFiles2.ts | 4 +- 7 files changed, 137 insertions(+), 87 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9ff1c4b41b9..e9b197f2863 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -110,6 +110,7 @@ namespace ts { getParameterType: getTypeAtPosition, getReturnTypeOfSignature, getNonNullableType, + getBaseTypeVariableOfClass, typeToTypeNode: nodeBuilder.typeToTypeNode, indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration, signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration, @@ -681,10 +682,6 @@ namespace ts { return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 }); } - function getObjectFlags(type: Type): ObjectFlags { - return type.flags & TypeFlags.Object ? (type).objectFlags : 0; - } - function isGlobalSourceFile(node: Node) { return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node); } @@ -2282,7 +2279,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, writer); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 3803a9fbc23..f0b33ac9fdf 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3555,11 +3555,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 }, @@ -3583,10 +3583,14 @@ "category": "Message", "code": 90022 }, - "Add declaration for missing method '{0}'.": { + "Declare method '{0}'.": { "category": "Message", "code": 90023 }, + "Declare static method '{0}'.": { + "category": "Message", + "code": 90024 + }, "Convert function to an ES2015 class": { "category": "Message", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 561d84b5d71..ee02d37addb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2530,6 +2530,7 @@ namespace ts { */ /* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type; getNonNullableType(type: Type): Type; + /* @internal */ getBaseTypeVariableOfClass(symbol: Symbol): Type | undefined; /** Note that the resulting nodes cannot be checked. */ typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 506d2728ffd..187d90559f1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -733,6 +733,10 @@ namespace ts { return false; } + export function getObjectFlags(type: Type): ObjectFlags { + return type.flags & TypeFlags.Object ? (type).objectFlags : 0; + } + export function isChildOfNodeWithKind(node: Node, kind: SyntaxKind): boolean { while (node) { if (node.kind === kind) { diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 231e320c93b..358c2255789 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -8,116 +8,151 @@ 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 = 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 && symbol.declarations[0]; + if (getObjectFlags(leftExpressionType) & ObjectFlags.Anonymous && symbol.flags & SymbolFlags.Class && !checker.getBaseTypeVariableOfClass(symbol)) { + makeStatic = true; + } + } + } } - const classDeclaration = classMemberDeclaration.parent; if (!classDeclaration || !isClassLike(classDeclaration)) { return undefined; } - const tokenName = token.getText(sourceFile); - 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 { - if (isStatic) { + function getActionsForAddMissingMemberInJavaScriptFile(classDeclaration: ClassLikeDeclaration, makeStatic: boolean): CodeAction[] | undefined { + let actions: CodeAction[]; + + const methodCodeAction = getActionForMethodDeclaration(); + if (methodCodeAction) { + actions = [methodCodeAction]; + } + + if (makeStatic) { if (classDeclaration.kind === SyntaxKind.ClassExpression) { - return undefined; + return actions; } const className = classDeclaration.name.getText(); - return [{ + const initializeStaticAction = { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_static_property_0), [tokenName]), changes: [{ - fileName: sourceFile.fileName, + fileName: classDeclarationSourceFile.fileName, textChanges: [{ span: { start: classDeclaration.getEnd(), length: 0 }, newText: `${context.newLineCharacter}${className}.${tokenName} = undefined;${context.newLineCharacter}` }] }] - }]; + }; + (actions || (actions = [])).push(initializeStaticAction); + return actions; } else { const classConstructor = getFirstConstructorWithBody(classDeclaration); if (!classConstructor) { - return undefined; + return actions; } - return [{ + const initializeAction = { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]), changes: [{ - fileName: sourceFile.fileName, + fileName: classDeclarationSourceFile.fileName, textChanges: [{ span: { start: classConstructor.body.getEnd() - 1, length: 0 }, newText: `this.${tokenName} = undefined;${context.newLineCharacter}` }] }] - }]; + }; + + (actions || (actions = [])).push(initializeAction); + return actions; } } - function getActionsForAddMissingMemberInTypeScriptFile(): CodeAction[] | undefined { - const openBrace = getOpenBraceOfClassLike(classDeclaration, sourceFile); + function getActionsForAddMissingMemberInTypeScriptFile(classDeclaration: ClassLikeDeclaration, makeStatic: boolean): CodeAction[] | undefined { let actions: CodeAction[]; - if (token.parent.parent.kind === SyntaxKind.CallExpression) { - const callExpression = token.parent.parent; - const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName); - - const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); - methodDeclarationChangeTracker.insertNodeAfter(sourceFile, openBrace, methodDeclaration, { suffix: context.newLineCharacter }); - actions = [{ - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_method_0), [tokenName]), - changes: methodDeclarationChangeTracker.getChanges() - }]; + const methodCodeAction = getActionForMethodDeclaration(); + 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(binaryExpression.right))); + const widenedType = checker.getWidenedType(checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(otherExpression))); typeNode = checker.typeToTypeNode(widenedType, classDeclaration); } typeNode = typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword); const property = createProperty( /*decorators*/undefined, - /*modifiers*/ isStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, + /*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 }); (actions || (actions = [])).push({ - description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_declaration_for_missing_property_0), [tokenName]), + 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, @@ -134,15 +169,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), [tokenName]), + description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes: indexSignatureChangeTracker.getChanges() }); } return actions; } + + function getActionForMethodDeclaration(): CodeAction | undefined { + if (token.parent.parent.kind === SyntaxKind.CallExpression) { + const callExpression = token.parent.parent; + const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName, /*includeTypeScriptSyntax*/ true, 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() + }; + } + } } } diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index 5b81f71b6c9..c2c51d5c788 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -142,46 +142,50 @@ namespace ts.codefix { } } - export function createMethodFromCallExpression(callExpression: CallExpression, methodName: string): MethodDeclaration { - const argCount = callExpression.arguments.length; - const parameters: ParameterDeclaration[] = []; - for (let i = 0; i < argCount; i++) { - const typeNode = createKeywordTypeNode(SyntaxKind.AnyKeyword); - const newParameter = createParameter( - /*decorators*/ undefined, - /*modifiers*/ undefined, - /*dotDotDotToken*/ undefined, - `arg${i}`, - /*questionToken*/ undefined, - typeNode, - /*initializer*/ undefined); - parameters.push(newParameter); - } + export function createMethodFromCallExpression(callExpression: CallExpression, methodName: string, includeTypeScriptSyntax: boolean, makeStatic: boolean): MethodDeclaration { + const parameters = createDummyParameters(callExpression.arguments.length, /*names*/ undefined, /*minArgumentCount*/ undefined, includeTypeScriptSyntax); - const typeArgCount = callExpression.typeArguments ? callExpression.typeArguments.length : 0; let typeParameters: TypeParameterDeclaration[]; - 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); + if (includeTypeScriptSyntax) { + const typeArgCount = callExpression.typeArguments ? callExpression.typeArguments.length : 0; + 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*/ undefined, + /*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, /*asteriskToken*/ undefined, methodName, /*questionToken*/ undefined, typeParameters, parameters, - /*type*/ undefined, + /*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, @@ -203,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)); diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts index cc01f8d50ee..6064ee5ae30 100644 --- a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts +++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts @@ -25,10 +25,10 @@ verify.applyCodeFix(/*errorCode*/undefined, 0); verify.rangeIs(` export class C { - m1() { + m1(): any { throw new Error("Method not implemented."); } - static m0(arg0, arg1, arg2) { + static m0(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); } x: number; From 5030d0f468f7dcfd4bbc2f0031e9787e933ef5b6 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 1 Jun 2017 10:49:08 -0700 Subject: [PATCH 08/14] Use factory for all fixes --- src/services/codefixes/fixAddMissingMember.ts | 37 ++++++++++++------- .../fourslash/codeFixAddMissingMember5.ts | 6 ++- .../fourslash/codeFixAddMissingMember7.ts | 6 ++- 3 files changed, 31 insertions(+), 18 deletions(-) diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 358c2255789..0859500e2d2 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -82,15 +82,19 @@ namespace ts.codefix { const className = classDeclaration.name.getText(); + 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: [{ - fileName: classDeclarationSourceFile.fileName, - textChanges: [{ - span: { start: classDeclaration.getEnd(), length: 0 }, - newText: `${context.newLineCharacter}${className}.${tokenName} = undefined;${context.newLineCharacter}` - }] - }] + changes: staticInitializationChangeTracker.getChanges() }; (actions || (actions = [])).push(initializeStaticAction); @@ -102,15 +106,20 @@ namespace ts.codefix { return actions; } + 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: [{ - fileName: classDeclarationSourceFile.fileName, - textChanges: [{ - span: { start: classConstructor.body.getEnd() - 1, length: 0 }, - newText: `this.${tokenName} = undefined;${context.newLineCharacter}` - }] - }] + changes: propertyInitializationChangeTracker.getChanges() }; (actions || (actions = [])).push(initializeAction); diff --git a/tests/cases/fourslash/codeFixAddMissingMember5.ts b/tests/cases/fourslash/codeFixAddMissingMember5.ts index cbabc0ba6c5..53d47e9a8d3 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember5.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts @@ -11,9 +11,11 @@ ////} ////|] -verify.rangeAfterCodeFix(`class C { +verify.applyCodeFix(/*errorCode*/ undefined, /*index*/ 0); +verify.currentFileContentIs(`class C { static method() { ()=>{ this.foo === 10 }; } } -C.foo = undefined;`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 0); \ No newline at end of file +C.foo = undefined; +`); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixAddMissingMember7.ts b/tests/cases/fourslash/codeFixAddMissingMember7.ts index 0e937a3ab6e..04c3efc86b8 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember7.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts @@ -9,7 +9,9 @@ ////} ////|] -verify.rangeAfterCodeFix(`class C { +verify.applyCodeFix(/*errorCode*/ undefined, /*index*/ 2) +verify.currentFileContentIs(`class C { static p = ()=>{ this.foo === 10 }; } -C.foo = undefined;`, /*includeWhiteSpace*/false, /*errorCode*/ undefined, /*index*/ 2); \ No newline at end of file +C.foo = undefined; +`); From f9123aa5cc7a748299877db134da383949d0dd1e Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 1 Jun 2017 11:01:00 -0700 Subject: [PATCH 09/14] rename fourslash method --- src/harness/fourslash.ts | 2 +- tests/cases/fourslash/codeFixAddMissingMember5.ts | 2 +- tests/cases/fourslash/codeFixAddMissingMember7.ts | 2 +- tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts | 8 ++++---- tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts | 8 ++++---- tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts | 8 ++++---- tests/cases/fourslash/codeFixUndeclaredMethod.ts | 6 +++--- tests/cases/fourslash/fourslash.ts | 2 +- 8 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 0cbf98cebf7..f1146919e49 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -3816,7 +3816,7 @@ namespace FourSlashInterface { this.state.verifyRangeIs(expectedText, includeWhiteSpace); } - public applyCodeFix(errorCode?: number, index?: number): void { + public getAndApplyCodeFix(errorCode?: number, index?: number): void { this.state.getAndApplyCodeActions(errorCode, index); } diff --git a/tests/cases/fourslash/codeFixAddMissingMember5.ts b/tests/cases/fourslash/codeFixAddMissingMember5.ts index 53d47e9a8d3..db893cb61d3 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember5.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts @@ -11,7 +11,7 @@ ////} ////|] -verify.applyCodeFix(/*errorCode*/ undefined, /*index*/ 0); +verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 0); verify.currentFileContentIs(`class C { static method() { ()=>{ this.foo === 10 }; diff --git a/tests/cases/fourslash/codeFixAddMissingMember7.ts b/tests/cases/fourslash/codeFixAddMissingMember7.ts index 04c3efc86b8..8ac7f2b5aff 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember7.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts @@ -9,7 +9,7 @@ ////} ////|] -verify.applyCodeFix(/*errorCode*/ undefined, /*index*/ 2) +verify.getAndApplyCodeFix(/*errorCode*/ undefined, /*index*/ 2) verify.currentFileContentIs(`class C { static p = ()=>{ this.foo === 10 }; } diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts index 830259e3ca7..435b78ba3fa 100644 --- a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts +++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles1.ts @@ -17,10 +17,10 @@ //// static y: string; //// } -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); +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; }; diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts index 6064ee5ae30..9f40c967658 100644 --- a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts +++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts @@ -18,10 +18,10 @@ //// constructor() { } //// }|] -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); +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 { diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts index 94ad3f77615..0669a9ee462 100644 --- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts @@ -9,10 +9,10 @@ //// } //// } -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); +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; diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts index 1f842a5bcf1..6a61f8421aa 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts @@ -10,9 +10,9 @@ //// } //// } -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); -verify.applyCodeFix(/*errorCode*/undefined, 0); +verify.getAndApplyCodeFix(/*errorCode*/undefined, 0); +verify.getAndApplyCodeFix(/*errorCode*/undefined, 0); +verify.getAndApplyCodeFix(/*errorCode*/undefined, 0); verify.rangeIs(` foo3(): any { diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index b0f731dcdea..3e80b005625 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -237,7 +237,7 @@ declare namespace FourSlashInterface { DocCommentTemplate(expectedText: string, expectedOffset: number, empty?: boolean): void; noDocCommentTemplate(): void; rangeAfterCodeFix(expectedText: string, includeWhiteSpace?: boolean, errorCode?: number, index?: number): void - applyCodeFix(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; From f2163fecbd2d8b988f1f1897f8646e28af2742ba Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 1 Jun 2017 11:09:08 -0700 Subject: [PATCH 10/14] use length helper --- src/services/codefixes/helpers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/codefixes/helpers.ts b/src/services/codefixes/helpers.ts index c2c51d5c788..81aae4c0e5e 100644 --- a/src/services/codefixes/helpers.ts +++ b/src/services/codefixes/helpers.ts @@ -147,7 +147,7 @@ namespace ts.codefix { let typeParameters: TypeParameterDeclaration[]; if (includeTypeScriptSyntax) { - const typeArgCount = callExpression.typeArguments ? callExpression.typeArguments.length : 0; + 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); From 6742f9dcd8ddabdfbbdb9df98f6ef65a45ba042d Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 1 Jun 2017 11:17:24 -0700 Subject: [PATCH 11/14] remove comment --- .../codeFixUndeclaredInStaticMethod.ts | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts index 0669a9ee462..75be5420e43 100644 --- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts @@ -24,21 +24,3 @@ verify.rangeIs(` throw new Error("Method not implemented."); } `); - -// class A { -// 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."); -// } -// static foo0() { -// this.m1(1,2,3); -// A.m2(1,2); -// this.prop1 = 10; -// A.prop2 = "asdf"; -// } -// } - From c1d466a716eef632ecbc44a2fb53b6bdc92694bc Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 1 Jun 2017 14:12:25 -0700 Subject: [PATCH 12/14] suppress type annotations in js file --- src/services/codefixes/fixAddMissingMember.ts | 8 ++++---- .../fourslash/codeFixUndeclaredAcrossFiles2.ts | 13 ++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 0859500e2d2..80dd16c5f58 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -70,7 +70,7 @@ namespace ts.codefix { function getActionsForAddMissingMemberInJavaScriptFile(classDeclaration: ClassLikeDeclaration, makeStatic: boolean): CodeAction[] | undefined { let actions: CodeAction[]; - const methodCodeAction = getActionForMethodDeclaration(); + const methodCodeAction = getActionForMethodDeclaration(/*includeTypeScriptSyntax*/ false); if (methodCodeAction) { actions = [methodCodeAction]; } @@ -130,7 +130,7 @@ namespace ts.codefix { function getActionsForAddMissingMemberInTypeScriptFile(classDeclaration: ClassLikeDeclaration, makeStatic: boolean): CodeAction[] | undefined { let actions: CodeAction[]; - const methodCodeAction = getActionForMethodDeclaration(); + const methodCodeAction = getActionForMethodDeclaration(/*includeTypeScriptSyntax*/ true); if (methodCodeAction) { actions = [methodCodeAction]; } @@ -189,10 +189,10 @@ namespace ts.codefix { return actions; } - function getActionForMethodDeclaration(): CodeAction | undefined { + function getActionForMethodDeclaration(includeTypeScriptSyntax: boolean): CodeAction | undefined { if (token.parent.parent.kind === SyntaxKind.CallExpression) { const callExpression = token.parent.parent; - const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName, /*includeTypeScriptSyntax*/ true, makeStatic); + const methodDeclaration = createMethodFromCallExpression(callExpression, tokenName, includeTypeScriptSyntax, makeStatic); const methodDeclarationChangeTracker = textChanges.ChangeTracker.fromCodeFixContext(context); methodDeclarationChangeTracker.insertNodeAfter(classDeclarationSourceFile, classOpenBrace, methodDeclaration, { suffix: context.newLineCharacter }); diff --git a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts index 9f40c967658..04e2afc667c 100644 --- a/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts +++ b/tests/cases/fourslash/codeFixUndeclaredAcrossFiles2.ts @@ -13,10 +13,10 @@ // @Filename: f1.js //// [|export class C { -//// x: number; -//// static y: string; //// constructor() { } -//// }|] +//// } +//// +//// |] verify.getAndApplyCodeFix(/*errorCode*/undefined, 0); verify.getAndApplyCodeFix(/*errorCode*/undefined, 0); @@ -25,16 +25,15 @@ verify.getAndApplyCodeFix(/*errorCode*/undefined, 0); verify.rangeIs(` export class C { - m1(): any { + m1() { throw new Error("Method not implemented."); } - static m0(arg0: any, arg1: any, arg2: any): any { + static m0(arg0, arg1, arg2) { throw new Error("Method not implemented."); } - x: number; - static y: string; constructor() { this.y = undefined; } } +C.x = undefined; `); \ No newline at end of file From 2fb9ab798c2c3615bb51dba22b6855fe41eea4bc Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 1 Jun 2017 16:03:11 -0700 Subject: [PATCH 13/14] repond to feedback --- src/compiler/checker.ts | 1 - src/compiler/types.ts | 1 - src/services/codefixes/fixAddMissingMember.ts | 3 ++- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e9b197f2863..f7450bb6da6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -110,7 +110,6 @@ namespace ts { getParameterType: getTypeAtPosition, getReturnTypeOfSignature, getNonNullableType, - getBaseTypeVariableOfClass, typeToTypeNode: nodeBuilder.typeToTypeNode, indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration, signatureToSignatureDeclaration: nodeBuilder.signatureToSignatureDeclaration, diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ee02d37addb..561d84b5d71 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2530,7 +2530,6 @@ namespace ts { */ /* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type; getNonNullableType(type: Type): Type; - /* @internal */ getBaseTypeVariableOfClass(symbol: Symbol): Type | undefined; /** Note that the resulting nodes cannot be checked. */ typeToTypeNode(type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags): TypeNode; diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 80dd16c5f58..1587b47c01b 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -49,7 +49,8 @@ namespace ts.codefix { const symbol = leftExpressionType.symbol; if (symbol.flags & SymbolFlags.Class) { classDeclaration = symbol.declarations && symbol.declarations[0]; - if (getObjectFlags(leftExpressionType) & ObjectFlags.Anonymous && symbol.flags & SymbolFlags.Class && !checker.getBaseTypeVariableOfClass(symbol)) { + if (leftExpressionType !== checker.getDeclaredTypeOfSymbol(symbol)) { + // The expression is a class symbol but the type is not the instance-side. makeStatic = true; } } From 40f22ec6e29323890c834594ead2b63ff30a39b7 Mon Sep 17 00:00:00 2001 From: Arthur Ozga Date: Thu, 1 Jun 2017 16:41:46 -0700 Subject: [PATCH 14/14] respond to comment --- src/compiler/checker.ts | 4 ++++ src/compiler/utilities.ts | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f7450bb6da6..cb9c44afdbb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -681,6 +681,10 @@ namespace ts { return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 }); } + function getObjectFlags(type: Type): ObjectFlags { + return type.flags & TypeFlags.Object ? (type).objectFlags : 0; + } + function isGlobalSourceFile(node: Node) { return node.kind === SyntaxKind.SourceFile && !isExternalOrCommonJsModule(node); } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 187d90559f1..506d2728ffd 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -733,10 +733,6 @@ namespace ts { return false; } - export function getObjectFlags(type: Type): ObjectFlags { - return type.flags & TypeFlags.Object ? (type).objectFlags : 0; - } - export function isChildOfNodeWithKind(node: Node, kind: SyntaxKind): boolean { while (node) { if (node.kind === kind) {