From e7acef125d8a4453542e9728be9ceaa724db0f21 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Fri, 17 Jun 2016 13:21:47 -0700 Subject: [PATCH 1/4] Allow to find all references of the 'this 'keyword --- src/harness/fourslash.ts | 10 +-- src/services/services.ts | 61 +++++++++++++++---- .../cases/fourslash/findAllRefsThisKeyword.ts | 33 ++++++++++ .../findAllRefsThisKeywordMultipleFiles.ts | 17 +++--- tests/cases/fourslash/fourslash.ts | 2 +- tests/cases/fourslash/renameThis.ts | 22 +++++++ 6 files changed, 116 insertions(+), 29 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsThisKeyword.ts create mode 100644 tests/cases/fourslash/renameThis.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 695e19b4667be..60d3bb8361972 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -760,7 +760,7 @@ namespace FourSlash { // Find the unaccounted-for reference. for (const actual of actualReferences) { if (!ts.forEach(expectedReferences, r => r.start === actual.textSpan.start)) { - this.raiseError(`A reference ${actual} is unaccounted for.`); + this.raiseError(`A reference ${stringify(actual)} is unaccounted for.`); } } // Probably will never reach here. @@ -907,13 +907,13 @@ namespace FourSlash { assert.equal(getDisplayPartsJson(actualQuickInfo.documentation), getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation")); } - public verifyRenameLocations(findInStrings: boolean, findInComments: boolean) { + public verifyRenameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]) { const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition); if (renameInfo.canRename) { let references = this.languageService.findRenameLocations( this.activeFile.fileName, this.currentCaretPosition, findInStrings, findInComments); - let ranges = this.getRanges(); + ranges = ranges || this.getRanges(); if (!references) { if (ranges.length !== 0) { @@ -3128,8 +3128,8 @@ namespace FourSlashInterface { this.state.verifyRenameInfoFailed(message); } - public renameLocations(findInStrings: boolean, findInComments: boolean) { - this.state.verifyRenameLocations(findInStrings, findInComments); + public renameLocations(findInStrings: boolean, findInComments: boolean, ranges?: FourSlash.Range[]) { + this.state.verifyRenameLocations(findInStrings, findInComments, ranges); } public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; }, diff --git a/src/services/services.ts b/src/services/services.ts index 983ddfbf250f6..853817b05e841 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -5811,17 +5811,32 @@ namespace ts { return undefined; } - if (node.kind !== SyntaxKind.Identifier && - // TODO (drosen): This should be enabled in a later release - currently breaks rename. - // node.kind !== SyntaxKind.ThisKeyword && - // node.kind !== SyntaxKind.SuperKeyword && - node.kind !== SyntaxKind.StringLiteral && - !isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) { - return undefined; + switch (node.kind) { + case SyntaxKind.NumericLiteral: + if (!isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) { + break; + } + // Fallthrough + case SyntaxKind.Identifier: + case SyntaxKind.ThisKeyword: + // case SyntaxKind.SuperKeyword: TODO:GH#9268 + case SyntaxKind.StringLiteral: + return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments); } + return undefined; + } - Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral); - return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments); + function isThis(node: Node): boolean { + switch (node.kind) { + case SyntaxKind.ThisKeyword: + // case SyntaxKind.ThisType: TODO: GH#9267 + return true; + case SyntaxKind.Identifier: + // 'this' as a parameter + return (node as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword && node.parent.kind === SyntaxKind.Parameter; + default: + return false; + } } function getReferencedSymbolsForNode(node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] { @@ -5841,7 +5856,7 @@ namespace ts { } } - if (node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.ThisType) { + if (isThis(node)) { return getReferencesForThisKeyword(node, sourceFiles); } @@ -6376,7 +6391,7 @@ namespace ts { cancellationToken.throwIfCancellationRequested(); const node = getTouchingWord(sourceFile, position); - if (!node || (node.kind !== SyntaxKind.ThisKeyword && node.kind !== SyntaxKind.ThisType)) { + if (!node || !isThis(node)) { return; } @@ -8003,11 +8018,11 @@ namespace ts { const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true); - // Can only rename an identifier. if (node) { if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.StringLiteral || - isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) { + isLiteralNameOfPropertyDeclarationOrIndexAccess(node) || + isThis(node)) { const symbol = typeChecker.getSymbolAtLocation(node); // Only allow a symbol to be renamed if it actually has at least one declaration. @@ -8054,6 +8069,26 @@ namespace ts { } } } + else if (node.kind === SyntaxKind.ThisKeyword) { + const container = ts.getThisContainer(node, /*includeArrowFunctions*/ false); + // Only allow rename to change a function with a 'this' type to one with a regular parameter, + // e.g. `function(this: number) { return this; }` to `function(x: number) { return x; }` + if (isFunctionLike(container)) { + const sig = typeChecker.getSignatureFromDeclaration(container); + if (sig.thisType) { + return { + canRename: true, + kind: ScriptElementKind.parameterElement, + displayName: "this", + localizedErrorMessage: undefined, + fullDisplayName: "this", + kindModifiers: "", + triggerSpan: createTriggerSpanForNode(node, sourceFile) + }; + } + } + // fallthrough to error + } } } diff --git a/tests/cases/fourslash/findAllRefsThisKeyword.ts b/tests/cases/fourslash/findAllRefsThisKeyword.ts new file mode 100644 index 0000000000000..f08a70ccf6553 --- /dev/null +++ b/tests/cases/fourslash/findAllRefsThisKeyword.ts @@ -0,0 +1,33 @@ +/// +// @noLib: true + +////[|this|]; +////function f([|this|]) { +//// return [|this|]; +//// function g([|this|]) { return [|this|]; } +////} +////class C { +//// static x() { +//// [|this|]; +//// } +//// static y() { +//// () => [|this|]; +//// } +//// constructor() { +//// [|this|]; +//// } +//// method() { +//// () => [|this|]; +//// } +////} +////// These are *not* real uses of the 'this' keyword, they are identifiers. +////const x = { [|this|]: 0 } +////x.[|this|]; + +const [global, f0, f1, g0, g1, x, y, constructor, method, propDef, propUse] = test.ranges(); +verify.referencesOf(global, [global]); +verify.rangesReferenceEachOther([f0, f1]); +verify.rangesReferenceEachOther([g0, g1]); +verify.rangesReferenceEachOther([x, y]); +verify.rangesReferenceEachOther([constructor, method]); +verify.rangesReferenceEachOther([propDef, propUse]); diff --git a/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts b/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts index 4b9f7a450ec46..a4bab7f876c89 100644 --- a/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts +++ b/tests/cases/fourslash/findAllRefsThisKeywordMultipleFiles.ts @@ -1,18 +1,15 @@ /// // @Filename: file1.ts -////this; this; +////[|this|]; [|this|]; // @Filename: file2.ts -////this; -////this; +////[|this|]; +////[|this|]; // @Filename: file3.ts -//// ((x = this, y) => t/**/his)(this, this); +//// ((x = [|this|], y) => [|this|])([|this|], [|this|]); +//// // different 'this' +//// function f(this) { return this; } -goTo.file("file1.ts"); -goTo.marker(); - -// TODO (drosen): The CURRENT behavior is that findAllRefs doesn't work on 'this' or 'super' keywords. -// This should change down the line. -verify.referencesCountIs(0); \ No newline at end of file +verify.rangesReferenceEachOther(); diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index dd8a61ac45814..9a6e19150c305 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -216,7 +216,7 @@ declare namespace FourSlashInterface { }[]): void; renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string): void; renameInfoFailed(message?: string): void; - renameLocations(findInStrings: boolean, findInComments: boolean): void; + renameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]): void; verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; diff --git a/tests/cases/fourslash/renameThis.ts b/tests/cases/fourslash/renameThis.ts new file mode 100644 index 0000000000000..011110ef72e45 --- /dev/null +++ b/tests/cases/fourslash/renameThis.ts @@ -0,0 +1,22 @@ +/// + +////function f([|this|]) { +//// return [|this|]; +////} +////this/**/; +////const _ = { [|this|]: 0 }.[|this|]; + +let [r0, r1, r2, r3] = test.ranges() +for (let range of [r0, r1]) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [r0, r1]); +} + +// Trying to rename a legitimate 'this' should fail +goTo.marker(); +verify.renameInfoFailed("You cannot rename this element."); + +for (let range of [r2, r3]) { + goTo.position(range.start); + verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [r2, r3]); +} From 07437a6e7c5db627d4e73f622e4a1d9126a39bfc Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 20 Jun 2016 10:55:17 -0700 Subject: [PATCH 2/4] Make goto-definition work for `this` parameter --- src/compiler/checker.ts | 37 +++++++++++++++---- src/compiler/types.ts | 2 + src/services/services.ts | 21 +---------- ...ParameterWithMethodCallInitializer.symbols | 4 +- .../reference/thisTypeInAccessors.symbols | 20 +++++----- .../reference/thisTypeInFunctions.symbols | 36 +++++++++--------- tests/cases/fourslash/goToDefinitionThis.ts | 20 ++++++++++ tests/cases/fourslash/quickInfoOnThis.ts | 2 +- tests/cases/fourslash/quickInfoOnThis3.ts | 2 +- tests/cases/fourslash/renameThis.ts | 2 +- 10 files changed, 86 insertions(+), 60 deletions(-) create mode 100644 tests/cases/fourslash/goToDefinitionThis.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ac8a47a991379..0fb345cf61004 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -134,8 +134,8 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); - const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const anySignature = createSignature(undefined, undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const unknownSignature = createSignature(undefined, undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); @@ -3198,6 +3198,11 @@ namespace ts { return undefined; } + function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol { + const parameter = getAccessorThisParameter(accessor); + return parameter && parameter.symbol; + } + function getAnnotatedAccessorThisType(accessor: AccessorDeclaration): Type { if (accessor) { const parameter = getAccessorThisParameter(accessor); @@ -3888,12 +3893,13 @@ namespace ts { resolveObjectTypeMembers(type, source, typeParameters, typeArguments); } - function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisType: Type, parameters: Symbol[], + function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, thisType: Type | undefined, parameters: Symbol[], resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { const sig = new Signature(checker); sig.declaration = declaration; sig.typeParameters = typeParameters; sig.parameters = parameters; + sig.thisParameter = thisParameter; sig.thisType = thisType; sig.resolvedReturnType = resolvedReturnType; sig.typePredicate = typePredicate; @@ -3904,7 +3910,7 @@ namespace ts { } function cloneSignature(sig: Signature): Signature { - return createSignature(sig.declaration, sig.typeParameters, sig.thisType, sig.parameters, sig.resolvedReturnType, + return createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.thisType, sig.parameters, sig.resolvedReturnType, sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals); } @@ -3912,7 +3918,7 @@ namespace ts { const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); if (baseSignatures.length === 0) { - return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; + return [createSignature(undefined, classType.localTypeParameters, undefined, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; } const baseTypeNode = getBaseTypeNodeOfClass(classType); const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode); @@ -4454,6 +4460,7 @@ namespace ts { const parameters: Symbol[] = []; let hasStringLiterals = false; let minArgumentCount = -1; + let thisParameter: Symbol = undefined; let thisType: Type = undefined; let hasThisParameter: boolean; const isJSConstructSignature = isJSDocConstructSignature(declaration); @@ -4472,6 +4479,7 @@ namespace ts { } if (i === 0 && paramSymbol.name === "this") { hasThisParameter = true; + thisParameter = param.symbol; thisType = param.type ? getTypeFromTypeNode(param.type) : unknownType; } else { @@ -4498,8 +4506,11 @@ namespace ts { !hasDynamicName(declaration) && (!hasThisParameter || thisType === unknownType)) { const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; - const setter = getDeclarationOfKind(declaration.symbol, otherKind); - thisType = getAnnotatedAccessorThisType(setter); + const other = getDeclarationOfKind(declaration.symbol, otherKind); + if (other) { + thisParameter = getAnnotatedAccessorThisParameter(other); + thisType = getAnnotatedAccessorThisType(other); + } } if (minArgumentCount < 0) { @@ -4520,7 +4531,7 @@ namespace ts { createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : undefined; - links.resolvedSignature = createSignature(declaration, typeParameters, thisType, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); + links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, thisType, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); } return links.resolvedSignature; } @@ -5453,6 +5464,7 @@ namespace ts { freshTypePredicate = cloneTypePredicate(signature.typePredicate, mapper); } const result = createSignature(signature.declaration, freshTypeParameters, + signature.thisParameter && instantiateSymbol(signature.thisParameter, mapper), signature.thisType && instantiateType(signature.thisType, mapper), instantiateList(signature.parameters, mapper, instantiateSymbol), instantiateType(signature.resolvedReturnType, mapper), @@ -17156,6 +17168,15 @@ namespace ts { return getSymbolOfEntityNameOrPropertyAccessExpression(node); case SyntaxKind.ThisKeyword: + const container = getThisContainer(node, /*includeArrowFunctions*/ false); + if (isFunctionLike(container)) { + const sig = getSignatureFromDeclaration(container); + if (sig.thisParameter) { + return sig.thisParameter; + } + } + // fallthrough + case SyntaxKind.SuperKeyword: const type = isExpression(node) ? checkExpression(node) : getTypeFromTypeNode(node); return type.symbol; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 22861f04c5e02..fbda9df5e0346 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2388,6 +2388,8 @@ namespace ts { parameters: Symbol[]; // Parameters thisType?: Type; // type of this-type /* @internal */ + thisParameter?: Symbol; // symbol of this-type parameter + /* @internal */ resolvedReturnType: Type; // Resolved return type /* @internal */ minArgumentCount: number; // Number of non-optional parameters diff --git a/src/services/services.ts b/src/services/services.ts index 853817b05e841..a2b2bc4409799 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -779,6 +779,7 @@ namespace ts { declaration: SignatureDeclaration; typeParameters: TypeParameter[]; parameters: Symbol[]; + thisParameter: Symbol; thisType: Type; resolvedReturnType: Type; minArgumentCount: number; @@ -8069,26 +8070,6 @@ namespace ts { } } } - else if (node.kind === SyntaxKind.ThisKeyword) { - const container = ts.getThisContainer(node, /*includeArrowFunctions*/ false); - // Only allow rename to change a function with a 'this' type to one with a regular parameter, - // e.g. `function(this: number) { return this; }` to `function(x: number) { return x; }` - if (isFunctionLike(container)) { - const sig = typeChecker.getSignatureFromDeclaration(container); - if (sig.thisType) { - return { - canRename: true, - kind: ScriptElementKind.parameterElement, - displayName: "this", - localizedErrorMessage: undefined, - fullDisplayName: "this", - kindModifiers: "", - triggerSpan: createTriggerSpanForNode(node, sourceFile) - }; - } - } - // fallthrough to error - } } } diff --git a/tests/baselines/reference/inferParameterWithMethodCallInitializer.symbols b/tests/baselines/reference/inferParameterWithMethodCallInitializer.symbols index e0734a4b39b19..ce098f6e24345 100644 --- a/tests/baselines/reference/inferParameterWithMethodCallInitializer.symbols +++ b/tests/baselines/reference/inferParameterWithMethodCallInitializer.symbols @@ -30,7 +30,7 @@ function weird(this: Example, a = this.getNumber()) { >Example : Symbol(Example, Decl(inferParameterWithMethodCallInitializer.ts, 2, 1)) >a : Symbol(a, Decl(inferParameterWithMethodCallInitializer.ts, 11, 29)) >this.getNumber : Symbol(Example.getNumber, Decl(inferParameterWithMethodCallInitializer.ts, 3, 15)) ->this : Symbol(Example, Decl(inferParameterWithMethodCallInitializer.ts, 2, 1)) +>this : Symbol(this, Decl(inferParameterWithMethodCallInitializer.ts, 11, 15)) >getNumber : Symbol(Example.getNumber, Decl(inferParameterWithMethodCallInitializer.ts, 3, 15)) return a; @@ -45,7 +45,7 @@ class Weird { >Example : Symbol(Example, Decl(inferParameterWithMethodCallInitializer.ts, 2, 1)) >a : Symbol(a, Decl(inferParameterWithMethodCallInitializer.ts, 15, 30)) >this.getNumber : Symbol(Example.getNumber, Decl(inferParameterWithMethodCallInitializer.ts, 3, 15)) ->this : Symbol(Example, Decl(inferParameterWithMethodCallInitializer.ts, 2, 1)) +>this : Symbol(this, Decl(inferParameterWithMethodCallInitializer.ts, 15, 16)) >getNumber : Symbol(Example.getNumber, Decl(inferParameterWithMethodCallInitializer.ts, 3, 15)) return a; diff --git a/tests/baselines/reference/thisTypeInAccessors.symbols b/tests/baselines/reference/thisTypeInAccessors.symbols index ef2201d3808d9..43f3f2fb0a1b0 100644 --- a/tests/baselines/reference/thisTypeInAccessors.symbols +++ b/tests/baselines/reference/thisTypeInAccessors.symbols @@ -20,7 +20,7 @@ const explicit = { >this : Symbol(this, Decl(thisTypeInAccessors.ts, 7, 10)) >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 7, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) set x(this: Foo, n: number) { this.n = n; } @@ -29,7 +29,7 @@ const explicit = { >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 8, 20)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 8, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 8, 20)) } @@ -44,14 +44,14 @@ const copiedFromGetter = { >this : Symbol(this, Decl(thisTypeInAccessors.ts, 12, 10)) >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 12, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) set x(n) { this.n = n; } >x : Symbol(x, Decl(thisTypeInAccessors.ts, 11, 10), Decl(thisTypeInAccessors.ts, 12, 48)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 13, 10)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 12, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 13, 10)) } @@ -64,7 +64,7 @@ const copiedFromSetter = { get x() { return this.n }, >x : Symbol(x, Decl(thisTypeInAccessors.ts, 16, 10), Decl(thisTypeInAccessors.ts, 17, 30)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 18, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) set x(this: Foo, n: number) { this.n = n; } @@ -73,7 +73,7 @@ const copiedFromSetter = { >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 18, 20)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 18, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 18, 20)) } @@ -88,7 +88,7 @@ const copiedFromGetterUnannotated = { >this : Symbol(this, Decl(thisTypeInAccessors.ts, 22, 10)) >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 22, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) set x(this, n) { this.n = n; } @@ -96,7 +96,7 @@ const copiedFromGetterUnannotated = { >this : Symbol(this, Decl(thisTypeInAccessors.ts, 23, 10)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 23, 15)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 22, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 23, 15)) } @@ -112,7 +112,7 @@ class Explicit { >this : Symbol(this, Decl(thisTypeInAccessors.ts, 28, 10)) >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 28, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) set x(this: Foo, n: number) { this.n = n; } @@ -121,7 +121,7 @@ class Explicit { >Foo : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 29, 20)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(Foo, Decl(thisTypeInAccessors.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 29, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 29, 20)) } diff --git a/tests/baselines/reference/thisTypeInFunctions.symbols b/tests/baselines/reference/thisTypeInFunctions.symbols index 3d4c3f6ecc70f..8a0be7427ed4c 100644 --- a/tests/baselines/reference/thisTypeInFunctions.symbols +++ b/tests/baselines/reference/thisTypeInFunctions.symbols @@ -19,7 +19,7 @@ class C { return this.n + m; >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 6, 17)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 6, 28)) } @@ -31,7 +31,7 @@ class C { return this.n + m; >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 9, 14)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 9, 22)) } @@ -43,7 +43,7 @@ class C { return this.n + m; >this.n : Symbol(n, Decl(thisTypeInFunctions.ts, 12, 28)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 12, 26)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 12, 21)) >n : Symbol(n, Decl(thisTypeInFunctions.ts, 12, 28)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 12, 39)) } @@ -97,7 +97,7 @@ function explicitStructural(this: { y: number }, x: number): number { return x + this.y; >x : Symbol(x, Decl(thisTypeInFunctions.ts, 28, 48)) >this.y : Symbol(y, Decl(thisTypeInFunctions.ts, 28, 35)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 28, 33)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 28, 28)) >y : Symbol(y, Decl(thisTypeInFunctions.ts, 28, 35)) } function justThis(this: { y: number }): number { @@ -107,7 +107,7 @@ function justThis(this: { y: number }): number { return this.y; >this.y : Symbol(y, Decl(thisTypeInFunctions.ts, 31, 25)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 31, 23)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 31, 18)) >y : Symbol(y, Decl(thisTypeInFunctions.ts, 31, 25)) } function implicitThis(n: number): number { @@ -435,7 +435,7 @@ c.explicitC = function(this: C, m: number) { return this.n + m }; >C : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 105, 31)) >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 105, 23)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 105, 31)) @@ -453,7 +453,7 @@ c.explicitProperty = function(this: {n: number}, m: number) { return this.n + m >n : Symbol(n, Decl(thisTypeInFunctions.ts, 107, 37)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 107, 48)) >this.n : Symbol(n, Decl(thisTypeInFunctions.ts, 107, 37)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 107, 35)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 107, 30)) >n : Symbol(n, Decl(thisTypeInFunctions.ts, 107, 37)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 107, 48)) @@ -525,7 +525,7 @@ c.explicitThis = function(this: C, m: number) { return this.n + m }; >C : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 123, 34)) >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 123, 26)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 123, 34)) @@ -568,7 +568,7 @@ c.explicitThis = function(this, m) { return this.n + m }; >this : Symbol(this, Decl(thisTypeInFunctions.ts, 131, 26)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 131, 31)) >this.n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) ->this : Symbol(C, Decl(thisTypeInFunctions.ts, 3, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 131, 26)) >n : Symbol(C.n, Decl(thisTypeInFunctions.ts, 4, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 131, 31)) @@ -581,7 +581,7 @@ c.explicitC = function(this: B, m: number) { return this.n + m }; >B : Symbol(B, Decl(thisTypeInFunctions.ts, 0, 0)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 134, 31)) >this.n : Symbol(B.n, Decl(thisTypeInFunctions.ts, 1, 9)) ->this : Symbol(B, Decl(thisTypeInFunctions.ts, 0, 0)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 134, 23)) >n : Symbol(B.n, Decl(thisTypeInFunctions.ts, 1, 9)) >m : Symbol(m, Decl(thisTypeInFunctions.ts, 134, 31)) @@ -604,7 +604,7 @@ class Base1 { >polymorphic : Symbol(Base1.polymorphic, Decl(thisTypeInFunctions.ts, 141, 14)) >this : Symbol(this, Decl(thisTypeInFunctions.ts, 142, 23)) >this.x : Symbol(Base1.x, Decl(thisTypeInFunctions.ts, 140, 13)) ->this : Symbol(Base1, Decl(thisTypeInFunctions.ts, 137, 24)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 142, 23)) >x : Symbol(Base1.x, Decl(thisTypeInFunctions.ts, 140, 13)) explicit(this: Base1): number { return this.x; } @@ -612,7 +612,7 @@ class Base1 { >this : Symbol(this, Decl(thisTypeInFunctions.ts, 143, 13)) >Base1 : Symbol(Base1, Decl(thisTypeInFunctions.ts, 137, 24)) >this.x : Symbol(Base1.x, Decl(thisTypeInFunctions.ts, 140, 13)) ->this : Symbol(Base1, Decl(thisTypeInFunctions.ts, 137, 24)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 143, 13)) >x : Symbol(Base1.x, Decl(thisTypeInFunctions.ts, 140, 13)) static explicitStatic(this: typeof Base1): number { return this.y; } @@ -620,7 +620,7 @@ class Base1 { >this : Symbol(this, Decl(thisTypeInFunctions.ts, 144, 26)) >Base1 : Symbol(Base1, Decl(thisTypeInFunctions.ts, 137, 24)) >this.y : Symbol(Base1.y, Decl(thisTypeInFunctions.ts, 144, 72)) ->this : Symbol(Base1, Decl(thisTypeInFunctions.ts, 137, 24)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 144, 26)) >y : Symbol(Base1.y, Decl(thisTypeInFunctions.ts, 144, 72)) static y: number; @@ -643,7 +643,7 @@ class Base2 { >polymorphic : Symbol(Base2.polymorphic, Decl(thisTypeInFunctions.ts, 151, 13)) >this : Symbol(this, Decl(thisTypeInFunctions.ts, 152, 16)) >this.y : Symbol(Base2.y, Decl(thisTypeInFunctions.ts, 150, 13)) ->this : Symbol(Base2, Decl(thisTypeInFunctions.ts, 149, 1)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 152, 16)) >y : Symbol(Base2.y, Decl(thisTypeInFunctions.ts, 150, 13)) explicit(this: Base1): number { return this.x; } @@ -651,7 +651,7 @@ class Base2 { >this : Symbol(this, Decl(thisTypeInFunctions.ts, 153, 13)) >Base1 : Symbol(Base1, Decl(thisTypeInFunctions.ts, 137, 24)) >this.x : Symbol(Base1.x, Decl(thisTypeInFunctions.ts, 140, 13)) ->this : Symbol(Base1, Decl(thisTypeInFunctions.ts, 137, 24)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 153, 13)) >x : Symbol(Base1.x, Decl(thisTypeInFunctions.ts, 140, 13)) } class Derived2 extends Base2 { @@ -734,7 +734,7 @@ function InterfaceThis(this: I) { this.a = 12; >this.a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) ->this : Symbol(I, Decl(thisTypeInFunctions.ts, 19, 21)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 172, 23)) >a : Symbol(I.a, Decl(thisTypeInFunctions.ts, 20, 13)) } function LiteralTypeThis(this: {x: string}) { @@ -744,7 +744,7 @@ function LiteralTypeThis(this: {x: string}) { this.x = "ok"; >this.x : Symbol(x, Decl(thisTypeInFunctions.ts, 175, 32)) ->this : Symbol(, Decl(thisTypeInFunctions.ts, 175, 30)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 175, 25)) >x : Symbol(x, Decl(thisTypeInFunctions.ts, 175, 32)) } function AnyThis(this: any) { @@ -752,6 +752,7 @@ function AnyThis(this: any) { >this : Symbol(this, Decl(thisTypeInFunctions.ts, 178, 17)) this.x = "ok"; +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 178, 17)) } let interfaceThis = new InterfaceThis(); >interfaceThis : Symbol(interfaceThis, Decl(thisTypeInFunctions.ts, 181, 3)) @@ -793,5 +794,6 @@ function missingTypeIsImplicitAny(this, a: number) { return this.anything + a; } >missingTypeIsImplicitAny : Symbol(missingTypeIsImplicitAny, Decl(thisTypeInFunctions.ts, 190, 27)) >this : Symbol(this, Decl(thisTypeInFunctions.ts, 192, 34)) >a : Symbol(a, Decl(thisTypeInFunctions.ts, 192, 39)) +>this : Symbol(this, Decl(thisTypeInFunctions.ts, 192, 34)) >a : Symbol(a, Decl(thisTypeInFunctions.ts, 192, 39)) diff --git a/tests/cases/fourslash/goToDefinitionThis.ts b/tests/cases/fourslash/goToDefinitionThis.ts new file mode 100644 index 0000000000000..300e3423d81cf --- /dev/null +++ b/tests/cases/fourslash/goToDefinitionThis.ts @@ -0,0 +1,20 @@ +/// + +// @noLib: true +////function f(/*fnDecl*/this: number) { +//// return /*fnUse*/this; +////} +/////*cls*/class C { +//// constructor() { return /*clsUse*/this; } +//// get self(/*getterDecl*/this: number) { return /*getterUse*/this; } +////} + +function verifyDefinition(a, b) { + goTo.marker(a); + goTo.definition(); + verify.caretAtMarker(b); +} + +verifyDefinition("fnUse", "fnDecl"); +verifyDefinition("clsUse", "cls"); +verifyDefinition("getterUse", "getterDecl"); diff --git a/tests/cases/fourslash/quickInfoOnThis.ts b/tests/cases/fourslash/quickInfoOnThis.ts index 4191c0846ec93..14737486b87f8 100644 --- a/tests/cases/fourslash/quickInfoOnThis.ts +++ b/tests/cases/fourslash/quickInfoOnThis.ts @@ -25,7 +25,7 @@ goTo.marker('0'); verify.quickInfoIs('this: this'); goTo.marker('1'); -verify.quickInfoIs('void'); +verify.quickInfoIs('this: void'); goTo.marker('2'); verify.quickInfoIs('this: this'); goTo.marker('3'); diff --git a/tests/cases/fourslash/quickInfoOnThis3.ts b/tests/cases/fourslash/quickInfoOnThis3.ts index 6988ac14860bf..239e49d5dc0fc 100644 --- a/tests/cases/fourslash/quickInfoOnThis3.ts +++ b/tests/cases/fourslash/quickInfoOnThis3.ts @@ -20,7 +20,7 @@ verify.quickInfoIs('any'); goTo.marker('2'); verify.quickInfoIs('(parameter) this: void'); goTo.marker('3'); -verify.quickInfoIs('void'); +verify.quickInfoIs('this: void'); goTo.marker('4'); verify.quickInfoIs('(parameter) this: Restricted'); goTo.marker('5'); diff --git a/tests/cases/fourslash/renameThis.ts b/tests/cases/fourslash/renameThis.ts index 011110ef72e45..f567cace70dd0 100644 --- a/tests/cases/fourslash/renameThis.ts +++ b/tests/cases/fourslash/renameThis.ts @@ -12,7 +12,7 @@ for (let range of [r0, r1]) { verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [r0, r1]); } -// Trying to rename a legitimate 'this' should fail +// Trying to rename a non-parameter 'this' should fail goTo.marker(); verify.renameInfoFailed("You cannot rename this element."); From 62607722d41119ab47017a06464a8383e9fa83e8 Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Tue, 21 Jun 2016 12:58:07 -0700 Subject: [PATCH 3/4] Remove Signature#thisType and use Signature#thisParameter everywhere --- src/compiler/checker.ts | 128 ++++++++++-------- src/compiler/types.ts | 3 +- src/services/services.ts | 1 - src/services/signatureHelp.ts | 10 +- .../reference/thisTypeInAccessors.symbols | 2 +- .../reference/thisTypeInFunctions.types | 4 +- 6 files changed, 78 insertions(+), 70 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0fb345cf61004..36a26abe393ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -134,8 +134,8 @@ namespace ts { const noConstraintType = createAnonymousType(undefined, emptySymbols, emptyArray, emptyArray, undefined, undefined); - const anySignature = createSignature(undefined, undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); - const unknownSignature = createSignature(undefined, undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); + const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false); const enumNumberIndexInfo = createIndexInfo(stringType, /*isReadonly*/ true); @@ -2473,16 +2473,13 @@ namespace ts { } } - function buildDisplayForParametersAndDelimiters(thisType: Type, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { + function buildDisplayForParametersAndDelimiters(thisParameter: Symbol | undefined, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { writePunctuation(writer, SyntaxKind.OpenParenToken); - if (thisType) { - writeKeyword(writer, SyntaxKind.ThisKeyword); - writePunctuation(writer, SyntaxKind.ColonToken); - writeSpace(writer); - buildTypeDisplay(thisType, writer, enclosingDeclaration, flags, symbolStack); + if (thisParameter) { + buildParameterDisplay(thisParameter, writer, enclosingDeclaration, flags, symbolStack); } for (let i = 0; i < parameters.length; i++) { - if (i > 0 || thisType) { + if (i > 0 || thisParameter) { writePunctuation(writer, SyntaxKind.CommaToken); writeSpace(writer); } @@ -2538,7 +2535,7 @@ namespace ts { buildDisplayForTypeParametersAndDelimiters(signature.typeParameters, writer, enclosingDeclaration, flags, symbolStack); } - buildDisplayForParametersAndDelimiters(signature.thisType, signature.parameters, writer, enclosingDeclaration, flags, symbolStack); + buildDisplayForParametersAndDelimiters(signature.thisParameter, signature.parameters, writer, enclosingDeclaration, flags, symbolStack); buildReturnTypeDisplay(signature, writer, enclosingDeclaration, flags, symbolStack); } @@ -2977,12 +2974,14 @@ namespace ts { if (func.kind === SyntaxKind.SetAccessor && !hasDynamicName(func)) { const getter = getDeclarationOfKind(declaration.parent.symbol, SyntaxKind.GetAccessor); if (getter) { - const signature = getSignatureFromDeclaration(getter); + const getterSignature = getSignatureFromDeclaration(getter); const thisParameter = getAccessorThisParameter(func as AccessorDeclaration); if (thisParameter && declaration === thisParameter) { - return signature.thisType; + // Use the type from the *getter* + Debug.assert(!thisParameter.type); + return getTypeOfSymbol(getterSignature.thisParameter); } - return getReturnTypeOfSignature(signature); + return getReturnTypeOfSignature(getterSignature); } } // Use contextual parameter type if one is available @@ -3198,19 +3197,13 @@ namespace ts { return undefined; } - function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol { + function getAnnotatedAccessorThisParameter(accessor: AccessorDeclaration): Symbol | undefined { const parameter = getAccessorThisParameter(accessor); return parameter && parameter.symbol; } - function getAnnotatedAccessorThisType(accessor: AccessorDeclaration): Type { - if (accessor) { - const parameter = getAccessorThisParameter(accessor); - if (parameter && parameter.type) { - return getTypeFromTypeNode(accessor.parameters[0].type); - } - } - return undefined; + function getAnnotatedAccessorThisType(accessor: AccessorDeclaration): Type | undefined { + return getThisTypeOfSignature(getSignatureFromDeclaration(accessor)); } function getTypeOfAccessors(symbol: Symbol): Type { @@ -3893,14 +3886,13 @@ namespace ts { resolveObjectTypeMembers(type, source, typeParameters, typeArguments); } - function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, thisType: Type | undefined, parameters: Symbol[], + function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { const sig = new Signature(checker); sig.declaration = declaration; sig.typeParameters = typeParameters; sig.parameters = parameters; sig.thisParameter = thisParameter; - sig.thisType = thisType; sig.resolvedReturnType = resolvedReturnType; sig.typePredicate = typePredicate; sig.minArgumentCount = minArgumentCount; @@ -3910,7 +3902,7 @@ namespace ts { } function cloneSignature(sig: Signature): Signature { - return createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.thisType, sig.parameters, sig.resolvedReturnType, + return createSignature(sig.declaration, sig.typeParameters, sig.thisParameter, sig.parameters, sig.resolvedReturnType, sig.typePredicate, sig.minArgumentCount, sig.hasRestParameter, sig.hasStringLiterals); } @@ -3918,7 +3910,7 @@ namespace ts { const baseConstructorType = getBaseConstructorTypeOfClass(classType); const baseSignatures = getSignaturesOfType(baseConstructorType, SignatureKind.Construct); if (baseSignatures.length === 0) { - return [createSignature(undefined, classType.localTypeParameters, undefined, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; + return [createSignature(undefined, classType.localTypeParameters, undefined, emptyArray, classType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasStringLiterals*/ false)]; } const baseTypeNode = getBaseTypeNodeOfClass(classType); const typeArguments = map(baseTypeNode.typeArguments, getTypeFromTypeNode); @@ -4008,8 +4000,9 @@ namespace ts { // Union the result types when more than one signature matches if (unionSignatures.length > 1) { s = cloneSignature(signature); - if (forEach(unionSignatures, sig => sig.thisType)) { - s.thisType = getUnionType(map(unionSignatures, sig => sig.thisType || anyType)); + if (forEach(unionSignatures, sig => sig.thisParameter)) { + const thisType = getUnionType(map(unionSignatures, sig => getTypeOfSymbol(sig.thisParameter) || anyType)); + s.thisParameter = createTransientSymbol(signature.thisParameter, thisType); } // Clear resolved return type we possibly got from cloneSignature s.resolvedReturnType = undefined; @@ -4461,7 +4454,6 @@ namespace ts { let hasStringLiterals = false; let minArgumentCount = -1; let thisParameter: Symbol = undefined; - let thisType: Type = undefined; let hasThisParameter: boolean; const isJSConstructSignature = isJSDocConstructSignature(declaration); @@ -4480,7 +4472,6 @@ namespace ts { if (i === 0 && paramSymbol.name === "this") { hasThisParameter = true; thisParameter = param.symbol; - thisType = param.type ? getTypeFromTypeNode(param.type) : unknownType; } else { parameters.push(paramSymbol); @@ -4504,12 +4495,11 @@ namespace ts { // If only one accessor includes a this-type annotation, the other behaves as if it had the same type annotation if ((declaration.kind === SyntaxKind.GetAccessor || declaration.kind === SyntaxKind.SetAccessor) && !hasDynamicName(declaration) && - (!hasThisParameter || thisType === unknownType)) { + (!hasThisParameter || !thisParameter)) { const otherKind = declaration.kind === SyntaxKind.GetAccessor ? SyntaxKind.SetAccessor : SyntaxKind.GetAccessor; const other = getDeclarationOfKind(declaration.symbol, otherKind); if (other) { thisParameter = getAnnotatedAccessorThisParameter(other); - thisType = getAnnotatedAccessorThisType(other); } } @@ -4531,7 +4521,7 @@ namespace ts { createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : undefined; - links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, thisType, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); + links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, typePredicate, minArgumentCount, hasRestParameter(declaration), hasStringLiterals); } return links.resolvedSignature; } @@ -4613,6 +4603,12 @@ namespace ts { return anyType; } + function getThisTypeOfSignature(signature: Signature): Type | undefined { + if (signature.thisParameter) { + return getTypeOfSymbol(signature.thisParameter); + } + } + function getReturnTypeOfSignature(signature: Signature): Type { if (!signature.resolvedReturnType) { if (!pushTypeResolution(signature, TypeSystemPropertyName.ResolvedReturnType)) { @@ -5465,7 +5461,6 @@ namespace ts { } const result = createSignature(signature.declaration, freshTypeParameters, signature.thisParameter && instantiateSymbol(signature.thisParameter, mapper), - signature.thisType && instantiateType(signature.thisType, mapper), instantiateList(signature.parameters, mapper, instantiateSymbol), instantiateType(signature.resolvedReturnType, mapper), freshTypePredicate, @@ -5737,17 +5732,22 @@ namespace ts { target = getErasedSignature(target); let result = Ternary.True; - if (source.thisType && target.thisType && source.thisType !== voidType) { - // void sources are assignable to anything. - const related = compareTypes(source.thisType, target.thisType, /*reportErrors*/ false) - || compareTypes(target.thisType, source.thisType, reportErrors); - if (!related) { - if (reportErrors) { - errorReporter(Diagnostics.The_this_types_of_each_signature_are_incompatible); + + const sourceThisType = getThisTypeOfSignature(source); + if (sourceThisType && sourceThisType !== voidType) { + const targetThisType = getThisTypeOfSignature(target); + if (targetThisType) { + // void sources are assignable to anything. + const related = compareTypes(sourceThisType, targetThisType, /*reportErrors*/ false) + || compareTypes(targetThisType, sourceThisType, reportErrors); + if (!related) { + if (reportErrors) { + errorReporter(Diagnostics.The_this_types_of_each_signature_are_incompatible); + } + return Ternary.False; } - return Ternary.False; + result &= related; } - result &= related; } const sourceMax = getNumNonRestParameters(source); @@ -6762,13 +6762,21 @@ namespace ts { source = getErasedSignature(source); target = getErasedSignature(target); let result = Ternary.True; - if (!ignoreThisTypes && source.thisType && target.thisType) { - const related = compareTypes(source.thisType, target.thisType); - if (!related) { - return Ternary.False; + + if (!ignoreThisTypes) { + const sourceThisType = getThisTypeOfSignature(source); + if (sourceThisType) { + const targetThisType = getThisTypeOfSignature(target); + if (targetThisType) { + const related = compareTypes(sourceThisType, targetThisType); + if (!related) { + return Ternary.False; + } + result &= related; + } } - result &= related; } + const targetLen = target.parameters.length; for (let i = 0; i < targetLen; i++) { const s = isRestParameterIndex(source, i) ? getRestTypeOfSignature(source) : getTypeOfParameter(source.parameters[i]); @@ -8594,9 +8602,10 @@ namespace ts { if (type) { return type; } - const signature = getSignatureFromDeclaration(container); - if (signature.thisType) { - return signature.thisType; + + const thisType = getThisTypeOfSignature(getSignatureFromDeclaration(container)); + if (thisType) { + return thisType; } } if (isClassLike(container.parent)) { @@ -8837,7 +8846,7 @@ namespace ts { if (isContextSensitiveFunctionOrObjectLiteralMethod(func) && func.kind !== SyntaxKind.ArrowFunction) { const contextualSignature = getContextualSignature(func); if (contextualSignature) { - return contextualSignature.thisType; + return getThisTypeOfSignature(contextualSignature); } } @@ -10637,10 +10646,11 @@ namespace ts { context.failedTypeParameterIndex = undefined; } - if (signature.thisType) { + const thisType = getThisTypeOfSignature(signature); + if (thisType) { const thisArgumentNode = getThisArgumentOfCall(node); const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType; - inferTypes(context, thisArgumentType, signature.thisType); + inferTypes(context, thisArgumentType, thisType); } // We perform two passes over the arguments. In the first pass we infer from all arguments, but use @@ -10716,8 +10726,8 @@ namespace ts { } function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { - - if (signature.thisType && signature.thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { + const thisType = getThisTypeOfSignature(signature); + if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) { // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType // If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible. // If the expression is a new expression, then the check is skipped. @@ -10725,7 +10735,7 @@ namespace ts { const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType; const errorNode = reportErrors ? (thisArgumentNode || node) : undefined; const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1; - if (!checkTypeRelatedTo(thisArgumentType, signature.thisType, relation, errorNode, headMessage)) { + if (!checkTypeRelatedTo(thisArgumentType, getThisTypeOfSignature(signature), relation, errorNode, headMessage)) { return false; } } @@ -11444,7 +11454,7 @@ namespace ts { if (getReturnTypeOfSignature(signature) !== voidType) { error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); } - if (signature.thisType === voidType) { + if (getThisTypeOfSignature(signature) === voidType) { error(node, Diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void); } return signature; @@ -18706,7 +18716,7 @@ namespace ts { return getAccessorThisParameter(accessor) || accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 0 : 1); } - function getAccessorThisParameter(accessor: AccessorDeclaration) { + function getAccessorThisParameter(accessor: AccessorDeclaration): ParameterDeclaration { if (accessor.parameters.length === (accessor.kind === SyntaxKind.GetAccessor ? 1 : 2) && accessor.parameters[0].name.kind === SyntaxKind.Identifier && (accessor.parameters[0].name).originalKeywordKind === SyntaxKind.ThisKeyword) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index fbda9df5e0346..e70bfd4bae666 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1866,7 +1866,7 @@ namespace ts { buildTypeParameterDisplay(tp: TypeParameter, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildTypePredicateDisplay(predicate: TypePredicate, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildTypeParameterDisplayFromSymbol(symbol: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; - buildDisplayForParametersAndDelimiters(thisType: Type, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; + buildDisplayForParametersAndDelimiters(thisParameter: Symbol, parameters: Symbol[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildDisplayForTypeParametersAndDelimiters(typeParameters: TypeParameter[], writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; buildReturnTypeDisplay(signature: Signature, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags): void; } @@ -2386,7 +2386,6 @@ namespace ts { declaration: SignatureDeclaration; // Originating declaration typeParameters: TypeParameter[]; // Type parameters (undefined if non-generic) parameters: Symbol[]; // Parameters - thisType?: Type; // type of this-type /* @internal */ thisParameter?: Symbol; // symbol of this-type parameter /* @internal */ diff --git a/src/services/services.ts b/src/services/services.ts index a2b2bc4409799..04aa635e91622 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -780,7 +780,6 @@ namespace ts { typeParameters: TypeParameter[]; parameters: Symbol[]; thisParameter: Symbol; - thisType: Type; resolvedReturnType: Type; minArgumentCount: number; hasRestParameter: boolean; diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index b5df2b5af206d..50378aa64b1a1 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -357,8 +357,8 @@ namespace ts.SignatureHelp { } function getArgumentIndex(argumentsList: Node, node: Node) { - // The list we got back can include commas. In the presence of errors it may - // also just have nodes without commas. For example "Foo(a b c)" will have 3 + // The list we got back can include commas. In the presence of errors it may + // also just have nodes without commas. For example "Foo(a b c)" will have 3 // args without commas. We want to find what index we're at. So we count // forward until we hit ourselves, only incrementing the index if it isn't a // comma. @@ -390,8 +390,8 @@ namespace ts.SignatureHelp { // 'a' ''. So, in the case where the last child is a comma, we increase the // arg count by one to compensate. // - // Note: this subtlety only applies to the last comma. If you had "Foo(a,," then - // we'll have: 'a' '' '' + // Note: this subtlety only applies to the last comma. If you had "Foo(a,," then + // we'll have: 'a' '' '' // That will give us 2 non-commas. We then add one for the last comma, givin us an // arg count of 3. const listChildren = argumentsList.getChildren(); @@ -563,7 +563,7 @@ namespace ts.SignatureHelp { signatureHelpParameters = typeParameters && typeParameters.length > 0 ? map(typeParameters, createSignatureHelpParameterForTypeParameter) : emptyArray; suffixDisplayParts.push(punctuationPart(SyntaxKind.GreaterThanToken)); const parameterParts = mapToDisplayParts(writer => - typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisType, candidateSignature.parameters, writer, invocation)); + typeChecker.getSymbolDisplayBuilder().buildDisplayForParametersAndDelimiters(candidateSignature.thisParameter, candidateSignature.parameters, writer, invocation)); addRange(suffixDisplayParts, parameterParts); } else { diff --git a/tests/baselines/reference/thisTypeInAccessors.symbols b/tests/baselines/reference/thisTypeInAccessors.symbols index 43f3f2fb0a1b0..a68341dfc14ee 100644 --- a/tests/baselines/reference/thisTypeInAccessors.symbols +++ b/tests/baselines/reference/thisTypeInAccessors.symbols @@ -96,7 +96,7 @@ const copiedFromGetterUnannotated = { >this : Symbol(this, Decl(thisTypeInAccessors.ts, 23, 10)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 23, 15)) >this.n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) ->this : Symbol(this, Decl(thisTypeInAccessors.ts, 22, 10)) +>this : Symbol(this, Decl(thisTypeInAccessors.ts, 23, 10)) >n : Symbol(Foo.n, Decl(thisTypeInAccessors.ts, 0, 15)) >n : Symbol(n, Decl(thisTypeInAccessors.ts, 23, 15)) } diff --git a/tests/baselines/reference/thisTypeInFunctions.types b/tests/baselines/reference/thisTypeInFunctions.types index ddef8544a540c..24c4fb87bafb1 100644 --- a/tests/baselines/reference/thisTypeInFunctions.types +++ b/tests/baselines/reference/thisTypeInFunctions.types @@ -692,11 +692,11 @@ c.explicitThis = function(m) { return this.n + m }; // this: contextual typing c.explicitThis = function(this, m) { return this.n + m }; ->c.explicitThis = function(this, m) { return this.n + m } : (this: any, m: number) => number +>c.explicitThis = function(this, m) { return this.n + m } : (this: C, m: number) => number >c.explicitThis : (this: C, m: number) => number >c : C >explicitThis : (this: C, m: number) => number ->function(this, m) { return this.n + m } : (this: any, m: number) => number +>function(this, m) { return this.n + m } : (this: C, m: number) => number >this : C >m : number >this.n + m : number From 89c992a8a1ff23575306ab4a96653fd165535eee Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 23 Jun 2016 12:08:18 -0700 Subject: [PATCH 4/4] Respond to PR comments --- src/compiler/checker.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 36a26abe393ec..eb4545c6810fb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3202,8 +3202,8 @@ namespace ts { return parameter && parameter.symbol; } - function getAnnotatedAccessorThisType(accessor: AccessorDeclaration): Type | undefined { - return getThisTypeOfSignature(getSignatureFromDeclaration(accessor)); + function getThisTypeOfDeclaration(declaration: SignatureDeclaration): Type | undefined { + return getThisTypeOfSignature(getSignatureFromDeclaration(declaration)); } function getTypeOfAccessors(symbol: Symbol): Type { @@ -8603,7 +8603,7 @@ namespace ts { return type; } - const thisType = getThisTypeOfSignature(getSignatureFromDeclaration(container)); + const thisType = getThisTypeOfDeclaration(container); if (thisType) { return thisType; } @@ -13500,7 +13500,7 @@ namespace ts { // TypeScript 1.0 spec (April 2014): 4.5 // If both accessors include type annotations, the specified types must be identical. checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorType, Diagnostics.get_and_set_accessor_must_have_the_same_type); - checkAccessorDeclarationTypesIdentical(node, otherAccessor, getAnnotatedAccessorThisType, Diagnostics.get_and_set_accessor_must_have_the_same_this_type); + checkAccessorDeclarationTypesIdentical(node, otherAccessor, getThisTypeOfDeclaration, Diagnostics.get_and_set_accessor_must_have_the_same_this_type); } } getTypeOfAccessors(getSymbolOfNode(node));