From 1663d01844ee79b9d87ad07008e5c6d7cb2cc28b Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Wed, 9 Aug 2017 14:38:26 -0700 Subject: [PATCH 1/9] Fix outlining of objects in array --- src/services/outliningElementsCollector.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 5099f8b66bd..0958892c1e6 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -16,6 +16,18 @@ namespace ts.OutliningElementsCollector { } } + function addOutliningForObjectLiteralsInArray(startElement: Node, endElement: Node, autoCollapse: boolean) { + if (startElement && endElement) { + const span: OutliningSpan = { + textSpan: createTextSpanFromBounds(startElement.getStart(), endElement.end), + hintSpan: createTextSpanFromBounds(startElement.getStart(), endElement.end), + bannerText: collapseText, + autoCollapse + }; + elements.push(span); + } + } + function addOutliningSpanComments(commentSpan: CommentRange, autoCollapse: boolean) { if (commentSpan) { const span: OutliningSpan = { @@ -161,6 +173,10 @@ namespace ts.OutliningElementsCollector { case SyntaxKind.CaseBlock: { const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); + if (n.kind === SyntaxKind.ObjectLiteralExpression && n.parent.kind === SyntaxKind.ArrayLiteralExpression) { + addOutliningForObjectLiteralsInArray(openBrace, closeBrace, autoCollapse(n)); + break; + } addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n)); break; } From df5e1a0f6971a06916be4f6046267c0b72ddedde Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Wed, 9 Aug 2017 15:55:02 -0700 Subject: [PATCH 2/9] add fourslash test --- .../getOutliningForObjectsInArray.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/cases/fourslash/getOutliningForObjectsInArray.ts diff --git a/tests/cases/fourslash/getOutliningForObjectsInArray.ts b/tests/cases/fourslash/getOutliningForObjectsInArray.ts new file mode 100644 index 00000000000..87d2bc96e2e --- /dev/null +++ b/tests/cases/fourslash/getOutliningForObjectsInArray.ts @@ -0,0 +1,23 @@ +/// + +////// objects in x should generate outlining spans that do not render in VS +//// const x =[| [ +//// [|{ a: 0 }|], +//// [|{ b: 1 }|], +//// [|{ c: 2 }|] +//// ]|]; +//// +////// objects in y should generate outlining spans that render as expected +//// const y =[| [ +//// [|{ +//// a: 0 +//// }|], +//// [|{ +//// b: 1 +//// }|], +//// [|{ +//// c: 2 +//// }|] +//// ]|]; + +verify.outliningSpansInCurrentFile(test.ranges()); \ No newline at end of file From c7d691dc15550b69cfec59bcb10c8e261e7dd7db Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Thu, 10 Aug 2017 13:27:24 -0700 Subject: [PATCH 3/9] Generalize to nested arrays and refactor --- src/services/outliningElementsCollector.ts | 40 +++++++------------ .../getOutliningForObjectsInArray.ts | 19 +++++++++ 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 0958892c1e6..edebf98a24b 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -4,25 +4,13 @@ namespace ts.OutliningElementsCollector { const elements: OutliningSpan[] = []; const collapseText = "..."; - function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean) { + function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean, fullStart: boolean) { if (hintSpanNode && startElement && endElement) { const span: OutliningSpan = { - textSpan: createTextSpanFromBounds(startElement.pos, endElement.end), - hintSpan: createTextSpanFromNode(hintSpanNode, sourceFile), - bannerText: collapseText, - autoCollapse, - }; - elements.push(span); - } - } - - function addOutliningForObjectLiteralsInArray(startElement: Node, endElement: Node, autoCollapse: boolean) { - if (startElement && endElement) { - const span: OutliningSpan = { - textSpan: createTextSpanFromBounds(startElement.getStart(), endElement.end), + textSpan: createTextSpanFromBounds(fullStart ? startElement.pos : startElement.getStart(), endElement.end), hintSpan: createTextSpanFromBounds(startElement.getStart(), endElement.end), bannerText: collapseText, - autoCollapse + autoCollapse, }; elements.push(span); } @@ -125,7 +113,7 @@ namespace ts.OutliningElementsCollector { parent.kind === SyntaxKind.WithStatement || parent.kind === SyntaxKind.CatchClause) { - addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n)); + addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); break; } @@ -133,13 +121,13 @@ namespace ts.OutliningElementsCollector { // Could be the try-block, or the finally-block. const tryStatement = parent; if (tryStatement.tryBlock === n) { - addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n)); + addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); break; } else if (tryStatement.finallyBlock === n) { const finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile); if (finallyKeyword) { - addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n)); + addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); break; } } @@ -163,27 +151,27 @@ namespace ts.OutliningElementsCollector { case SyntaxKind.ModuleBlock: { const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); - addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n)); + addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); break; } case SyntaxKind.ClassDeclaration: case SyntaxKind.InterfaceDeclaration: case SyntaxKind.EnumDeclaration: - case SyntaxKind.ObjectLiteralExpression: case SyntaxKind.CaseBlock: { const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); - if (n.kind === SyntaxKind.ObjectLiteralExpression && n.parent.kind === SyntaxKind.ArrayLiteralExpression) { - addOutliningForObjectLiteralsInArray(openBrace, closeBrace, autoCollapse(n)); - break; - } - addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n)); + addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); break; } + case SyntaxKind.ObjectLiteralExpression: + const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); + const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); + addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n), /* fullStart */ n.parent.kind !== SyntaxKind.ArrayLiteralExpression); + break; case SyntaxKind.ArrayLiteralExpression: const openBracket = findChildOfKind(n, SyntaxKind.OpenBracketToken, sourceFile); const closeBracket = findChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile); - addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n)); + addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n), /* fullStart */ n.parent.kind !== SyntaxKind.ArrayLiteralExpression); break; } depth++; diff --git a/tests/cases/fourslash/getOutliningForObjectsInArray.ts b/tests/cases/fourslash/getOutliningForObjectsInArray.ts index 87d2bc96e2e..207da1997b4 100644 --- a/tests/cases/fourslash/getOutliningForObjectsInArray.ts +++ b/tests/cases/fourslash/getOutliningForObjectsInArray.ts @@ -19,5 +19,24 @@ //// c: 2 //// }|] //// ]|]; +//// +////// same behavior for nested arrays +//// const w =[| [ +//// [|[ a: 0 ]|], +//// [|[ b: 1 ]|], +//// [|[ c: 2 ]|] +//// ]|]; +//// +//// const z =[| [ +//// [|[ +//// a: 0 +//// ]|], +//// [|[ +//// b: 1 +//// ]|], +//// [|[ +//// c: 2 +//// ]|] +//// ]|]; verify.outliningSpansInCurrentFile(test.ranges()); \ No newline at end of file From d6ccee67661987373949af9c97bf3c636a22d496 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Fri, 11 Aug 2017 13:42:14 -0700 Subject: [PATCH 4/9] Cleans up and adds nested case to test --- .../getOutliningForObjectsInArray.ts | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/cases/fourslash/getOutliningForObjectsInArray.ts b/tests/cases/fourslash/getOutliningForObjectsInArray.ts index 207da1997b4..2d57d3335bd 100644 --- a/tests/cases/fourslash/getOutliningForObjectsInArray.ts +++ b/tests/cases/fourslash/getOutliningForObjectsInArray.ts @@ -22,20 +22,34 @@ //// ////// same behavior for nested arrays //// const w =[| [ -//// [|[ a: 0 ]|], -//// [|[ b: 1 ]|], -//// [|[ c: 2 ]|] +//// [|[ 0 ]|], +//// [|[ 1 ]|], +//// [|[ 2 ]|] //// ]|]; //// //// const z =[| [ //// [|[ -//// a: 0 +//// 0 //// ]|], //// [|[ -//// b: 1 +//// 1 //// ]|], //// [|[ -//// c: 2 +//// 2 +//// ]|] +//// ]|]; +//// +////// multiple levels of nesting work as expected +//// const z =[| [ +//// [|[ +//// [|{ hello: 0 }|] +//// ]|], +//// [|[ +//// [|{ hello: 3 }|] +//// ]|], +//// [|[ +//// [|{ hello: 5 }|], +//// [|{ hello: 7 }|] //// ]|] //// ]|]; From 760812f7143ccc311746e0c3eac191d2905722c9 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Mon, 14 Aug 2017 09:27:45 -0700 Subject: [PATCH 5/9] Add explanatory comments, consolidate main body --- src/services/outliningElementsCollector.ts | 18 +++++++++++------- .../fourslash/getOutliningForObjectsInArray.ts | 8 ++++---- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index edebf98a24b..cdeba21b32b 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -3,11 +3,17 @@ namespace ts.OutliningElementsCollector { export function collectElements(sourceFile: SourceFile, cancellationToken: CancellationToken): OutliningSpan[] { const elements: OutliningSpan[] = []; const collapseText = "..."; + let depth = 0; + const maxDepth = 20; - function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean, fullStart: boolean) { + walk(sourceFile); + return elements; + + // If useFullStart is true, then the collapsing span includes leading whitespace, including linebreaks. + function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean, useFullStart: boolean) { if (hintSpanNode && startElement && endElement) { const span: OutliningSpan = { - textSpan: createTextSpanFromBounds(fullStart ? startElement.pos : startElement.getStart(), endElement.end), + textSpan: createTextSpanFromBounds(useFullStart ? startElement.pos : startElement.getStart(), endElement.end), hintSpan: createTextSpanFromBounds(startElement.getStart(), endElement.end), bannerText: collapseText, autoCollapse, @@ -82,8 +88,6 @@ namespace ts.OutliningElementsCollector { return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction; } - let depth = 0; - const maxDepth = 20; function walk(n: Node): void { cancellationToken.throwIfCancellationRequested(); if (depth > maxDepth) { @@ -163,6 +167,9 @@ namespace ts.OutliningElementsCollector { addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); break; } + // If the block has no leading keywords and is a member of an array literal, + // we again want to only collapse the span of the block. + // Otherwise, the collapsed section will include the end of the previous line. case SyntaxKind.ObjectLiteralExpression: const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); @@ -178,8 +185,5 @@ namespace ts.OutliningElementsCollector { forEachChild(n, walk); depth--; } - - walk(sourceFile); - return elements; } } \ No newline at end of file diff --git a/tests/cases/fourslash/getOutliningForObjectsInArray.ts b/tests/cases/fourslash/getOutliningForObjectsInArray.ts index 2d57d3335bd..89634224832 100644 --- a/tests/cases/fourslash/getOutliningForObjectsInArray.ts +++ b/tests/cases/fourslash/getOutliningForObjectsInArray.ts @@ -1,13 +1,13 @@ /// -////// objects in x should generate outlining spans that do not render in VS +// objects in x should generate outlining spans that do not render in VS //// const x =[| [ //// [|{ a: 0 }|], //// [|{ b: 1 }|], //// [|{ c: 2 }|] //// ]|]; //// -////// objects in y should generate outlining spans that render as expected +// objects in y should generate outlining spans that render as expected //// const y =[| [ //// [|{ //// a: 0 @@ -20,7 +20,7 @@ //// }|] //// ]|]; //// -////// same behavior for nested arrays +// same behavior for nested arrays //// const w =[| [ //// [|[ 0 ]|], //// [|[ 1 ]|], @@ -39,7 +39,7 @@ //// ]|] //// ]|]; //// -////// multiple levels of nesting work as expected +// multiple levels of nesting work as expected //// const z =[| [ //// [|[ //// [|{ hello: 0 }|] From a14aaf47721bd1728897c1b527a1326a3ca7fbb0 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Fri, 18 Aug 2017 15:58:21 -0700 Subject: [PATCH 6/9] Add release-2.5 to covered branches --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 4a99aaf2237..27b14739d8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,6 +16,7 @@ matrix: branches: only: - master + - release-2.5 install: - npm uninstall typescript --no-save From e6c1afb4a0b8f04f6202a7dd1f39d7cdffeaf096 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Fri, 18 Aug 2017 15:59:22 -0700 Subject: [PATCH 7/9] Style changes and cleanup --- src/services/outliningElementsCollector.ts | 29 +++++++++++----------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index cdeba21b32b..e0d99d1d271 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -1,20 +1,21 @@ /* @internal */ namespace ts.OutliningElementsCollector { + const collapseText = "..."; + const maxDepth = 20; + export function collectElements(sourceFile: SourceFile, cancellationToken: CancellationToken): OutliningSpan[] { const elements: OutliningSpan[] = []; - const collapseText = "..."; let depth = 0; - const maxDepth = 20; walk(sourceFile); return elements; - // If useFullStart is true, then the collapsing span includes leading whitespace, including linebreaks. + /** If useFullStart is true, then the collapsing span includes leading whitespace, including linebreaks. */ function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean, useFullStart: boolean) { if (hintSpanNode && startElement && endElement) { const span: OutliningSpan = { - textSpan: createTextSpanFromBounds(useFullStart ? startElement.pos : startElement.getStart(), endElement.end), - hintSpan: createTextSpanFromBounds(startElement.getStart(), endElement.end), + textSpan: createTextSpanFromBounds(useFullStart ? startElement.getFullStart() : startElement.getStart(), endElement.getEnd()), + hintSpan: createTextSpanFromNode(hintSpanNode, sourceFile), bannerText: collapseText, autoCollapse, }; @@ -117,7 +118,7 @@ namespace ts.OutliningElementsCollector { parent.kind === SyntaxKind.WithStatement || parent.kind === SyntaxKind.CatchClause) { - addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); + addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n), /*useFullStart*/ true); break; } @@ -125,13 +126,13 @@ namespace ts.OutliningElementsCollector { // Could be the try-block, or the finally-block. const tryStatement = parent; if (tryStatement.tryBlock === n) { - addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); + addOutliningSpan(parent, openBrace, closeBrace, autoCollapse(n), /*useFullStart*/ true); break; } else if (tryStatement.finallyBlock === n) { const finallyKeyword = findChildOfKind(tryStatement, SyntaxKind.FinallyKeyword, sourceFile); if (finallyKeyword) { - addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); + addOutliningSpan(finallyKeyword, openBrace, closeBrace, autoCollapse(n), /*useFullStart*/ true); break; } } @@ -155,7 +156,7 @@ namespace ts.OutliningElementsCollector { case SyntaxKind.ModuleBlock: { const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); - addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); + addOutliningSpan(n.parent, openBrace, closeBrace, autoCollapse(n), /*useFullStart*/ true); break; } case SyntaxKind.ClassDeclaration: @@ -164,21 +165,21 @@ namespace ts.OutliningElementsCollector { case SyntaxKind.CaseBlock: { const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); - addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n), /* fullStart */ true); + addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n), /*useFullStart*/ true); break; } - // If the block has no leading keywords and is a member of an array literal, - // we again want to only collapse the span of the block. + // If the block has no leading keywords and is inside an array literal, + // we only want to collapse the span of the block. // Otherwise, the collapsed section will include the end of the previous line. case SyntaxKind.ObjectLiteralExpression: const openBrace = findChildOfKind(n, SyntaxKind.OpenBraceToken, sourceFile); const closeBrace = findChildOfKind(n, SyntaxKind.CloseBraceToken, sourceFile); - addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n), /* fullStart */ n.parent.kind !== SyntaxKind.ArrayLiteralExpression); + addOutliningSpan(n, openBrace, closeBrace, autoCollapse(n), /*useFullStart*/ !isArrayLiteralExpression(n.parent)); break; case SyntaxKind.ArrayLiteralExpression: const openBracket = findChildOfKind(n, SyntaxKind.OpenBracketToken, sourceFile); const closeBracket = findChildOfKind(n, SyntaxKind.CloseBracketToken, sourceFile); - addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n), /* fullStart */ n.parent.kind !== SyntaxKind.ArrayLiteralExpression); + addOutliningSpan(n, openBracket, closeBracket, autoCollapse(n), /*useFullStart*/ !isArrayLiteralExpression(n.parent)); break; } depth++; From a136f554a708f8caf2fe4b9060207e7d9869a7d2 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 18 Aug 2017 17:20:57 -0700 Subject: [PATCH 8/9] Fix stack overflow when resolving default construct signatures (#17878) * Fix stack overflow when resolving default construct signatures * No need for || emptyArray --- src/compiler/checker.ts | 16 ++++--- .../reference/cloduleGenericOnSelfMember.js | 42 +++++++++++++++++++ .../cloduleGenericOnSelfMember.symbols | 30 +++++++++++++ .../cloduleGenericOnSelfMember.types | 33 +++++++++++++++ .../compiler/cloduleGenericOnSelfMember.ts | 11 +++++ 5 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/cloduleGenericOnSelfMember.js create mode 100644 tests/baselines/reference/cloduleGenericOnSelfMember.symbols create mode 100644 tests/baselines/reference/cloduleGenericOnSelfMember.types create mode 100644 tests/cases/compiler/cloduleGenericOnSelfMember.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6df6b1f9ce1..6febaf0052d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5657,17 +5657,12 @@ namespace ts { else { // Combinations of function, class, enum and module let members = emptySymbols; - let constructSignatures: Signature[] = emptyArray; let stringIndexInfo: IndexInfo = undefined; if (symbol.exports) { members = getExportsOfSymbol(symbol); } if (symbol.flags & SymbolFlags.Class) { const classType = getDeclaredTypeOfClassOrInterface(symbol); - constructSignatures = getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.Constructor)); - if (!constructSignatures.length) { - constructSignatures = getDefaultConstructSignatures(classType); - } const baseConstructorType = getBaseConstructorTypeOfClass(classType); if (baseConstructorType.flags & (TypeFlags.Object | TypeFlags.Intersection | TypeFlags.TypeVariable)) { members = createSymbolTable(getNamedMembers(members)); @@ -5678,7 +5673,7 @@ namespace ts { } } const numberIndexInfo = symbol.flags & SymbolFlags.Enum ? enumNumberIndexInfo : undefined; - setStructuredTypeMembers(type, members, emptyArray, constructSignatures, stringIndexInfo, numberIndexInfo); + setStructuredTypeMembers(type, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); // We resolve the members before computing the signatures because a signature may use // typeof with a qualified name expression that circularly references the type we are // in the process of resolving (see issue #6072). The temporarily empty signature list @@ -5686,6 +5681,15 @@ namespace ts { if (symbol.flags & (SymbolFlags.Function | SymbolFlags.Method)) { (type).callSignatures = getSignaturesOfSymbol(symbol); } + // And likewise for construct signatures for classes + if (symbol.flags & SymbolFlags.Class) { + const classType = getDeclaredTypeOfClassOrInterface(symbol); + let constructSignatures = getSignaturesOfSymbol(symbol.members.get(InternalSymbolName.Constructor)); + if (!constructSignatures.length) { + constructSignatures = getDefaultConstructSignatures(classType); + } + (type).constructSignatures = constructSignatures; + } } } diff --git a/tests/baselines/reference/cloduleGenericOnSelfMember.js b/tests/baselines/reference/cloduleGenericOnSelfMember.js new file mode 100644 index 00000000000..972651f1045 --- /dev/null +++ b/tests/baselines/reference/cloduleGenericOnSelfMember.js @@ -0,0 +1,42 @@ +//// [cloduleGenericOnSelfMember.ts] +class ServiceBase { + field: T; +} +class Service extends ServiceBase { +} +namespace Service { + export const Base = { + name: "1", + value: 5 + }; +} + +//// [cloduleGenericOnSelfMember.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var ServiceBase = /** @class */ (function () { + function ServiceBase() { + } + return ServiceBase; +}()); +var Service = /** @class */ (function (_super) { + __extends(Service, _super); + function Service() { + return _super !== null && _super.apply(this, arguments) || this; + } + return Service; +}(ServiceBase)); +(function (Service) { + Service.Base = { + name: "1", + value: 5 + }; +})(Service || (Service = {})); diff --git a/tests/baselines/reference/cloduleGenericOnSelfMember.symbols b/tests/baselines/reference/cloduleGenericOnSelfMember.symbols new file mode 100644 index 00000000000..c7e0126fa7a --- /dev/null +++ b/tests/baselines/reference/cloduleGenericOnSelfMember.symbols @@ -0,0 +1,30 @@ +=== tests/cases/compiler/cloduleGenericOnSelfMember.ts === +class ServiceBase { +>ServiceBase : Symbol(ServiceBase, Decl(cloduleGenericOnSelfMember.ts, 0, 0)) +>T : Symbol(T, Decl(cloduleGenericOnSelfMember.ts, 0, 18)) + + field: T; +>field : Symbol(ServiceBase.field, Decl(cloduleGenericOnSelfMember.ts, 0, 22)) +>T : Symbol(T, Decl(cloduleGenericOnSelfMember.ts, 0, 18)) +} +class Service extends ServiceBase { +>Service : Symbol(Service, Decl(cloduleGenericOnSelfMember.ts, 2, 1), Decl(cloduleGenericOnSelfMember.ts, 4, 1)) +>ServiceBase : Symbol(ServiceBase, Decl(cloduleGenericOnSelfMember.ts, 0, 0)) +>Service.Base : Symbol(Service.Base, Decl(cloduleGenericOnSelfMember.ts, 6, 16)) +>Service : Symbol(Service, Decl(cloduleGenericOnSelfMember.ts, 2, 1), Decl(cloduleGenericOnSelfMember.ts, 4, 1)) +>Base : Symbol(Service.Base, Decl(cloduleGenericOnSelfMember.ts, 6, 16)) +} +namespace Service { +>Service : Symbol(Service, Decl(cloduleGenericOnSelfMember.ts, 2, 1), Decl(cloduleGenericOnSelfMember.ts, 4, 1)) + + export const Base = { +>Base : Symbol(Base, Decl(cloduleGenericOnSelfMember.ts, 6, 16)) + + name: "1", +>name : Symbol(name, Decl(cloduleGenericOnSelfMember.ts, 6, 25)) + + value: 5 +>value : Symbol(value, Decl(cloduleGenericOnSelfMember.ts, 7, 18)) + + }; +} diff --git a/tests/baselines/reference/cloduleGenericOnSelfMember.types b/tests/baselines/reference/cloduleGenericOnSelfMember.types new file mode 100644 index 00000000000..87590745c6f --- /dev/null +++ b/tests/baselines/reference/cloduleGenericOnSelfMember.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/cloduleGenericOnSelfMember.ts === +class ServiceBase { +>ServiceBase : ServiceBase +>T : T + + field: T; +>field : T +>T : T +} +class Service extends ServiceBase { +>Service : Service +>ServiceBase : ServiceBase<{ name: string; value: number; }> +>Service.Base : { name: string; value: number; } +>Service : typeof Service +>Base : { name: string; value: number; } +} +namespace Service { +>Service : typeof Service + + export const Base = { +>Base : { name: string; value: number; } +>{ name: "1", value: 5 } : { name: string; value: number; } + + name: "1", +>name : string +>"1" : "1" + + value: 5 +>value : number +>5 : 5 + + }; +} diff --git a/tests/cases/compiler/cloduleGenericOnSelfMember.ts b/tests/cases/compiler/cloduleGenericOnSelfMember.ts new file mode 100644 index 00000000000..23f0e6af1b6 --- /dev/null +++ b/tests/cases/compiler/cloduleGenericOnSelfMember.ts @@ -0,0 +1,11 @@ +class ServiceBase { + field: T; +} +class Service extends ServiceBase { +} +namespace Service { + export const Base = { + name: "1", + value: 5 + }; +} \ No newline at end of file From 5e8e735db55e82ff0cf4bddaf4f0fe6d8a4d39f7 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 18 Aug 2017 17:21:25 -0700 Subject: [PATCH 9/9] quickInfo: Don't check for `type === undefined`, check for `any` (#17815) * quickInfo: Don't check for `type === undefined`, check for `any` * Fixes: * We still want to do some work even if type is `any` * Second test for `if (type)` is necessary because it might not have been assigned. --- src/services/symbolDisplay.ts | 215 ++++++++++---------- tests/cases/fourslash/quickInfoTypeError.ts | 10 + 2 files changed, 118 insertions(+), 107 deletions(-) create mode 100644 tests/cases/fourslash/quickInfoTypeError.ts diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index 303bb8395ee..05c451cc3f3 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -111,124 +111,123 @@ namespace ts.SymbolDisplay { let signature: Signature; type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol.exportSymbol || symbol, location); - if (type) { - if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { - const right = (location.parent).name; - // Either the location is on the right of a property access, or on the left and the right is missing - if (right === location || (right && right.getFullWidth() === 0)) { - location = location.parent; + + if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) { + const right = (location.parent).name; + // Either the location is on the right of a property access, or on the left and the right is missing + if (right === location || (right && right.getFullWidth() === 0)) { + location = location.parent; + } + } + + // try get the call/construct signature from the type if it matches + let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement; + if (isCallOrNewExpression(location)) { + callExpressionLike = location; + } + else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { + callExpressionLike = location.parent; + } + else if (location.parent && isJsxOpeningLikeElement(location.parent) && isFunctionLike(symbol.valueDeclaration)) { + callExpressionLike = location.parent; + } + + if (callExpressionLike) { + const candidateSignatures: Signature[] = []; + signature = typeChecker.getResolvedSignature(callExpressionLike, candidateSignatures); + if (!signature && candidateSignatures.length) { + // Use the first candidate: + signature = candidateSignatures[0]; + } + + const useConstructSignatures = callExpressionLike.kind === SyntaxKind.NewExpression || (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword); + + const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); + + if (!contains(allSignatures, signature.target) && !contains(allSignatures, signature)) { + // Get the first signature if there is one -- allSignatures may contain + // either the original signature or its target, so check for either + signature = allSignatures.length ? allSignatures[0] : undefined; + } + + if (signature) { + if (useConstructSignatures && (symbolFlags & SymbolFlags.Class)) { + // Constructor + symbolKind = ScriptElementKind.constructorImplementationElement; + addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); } - } - - // try get the call/construct signature from the type if it matches - let callExpressionLike: CallExpression | NewExpression | JsxOpeningLikeElement; - if (isCallOrNewExpression(location)) { - callExpressionLike = location; - } - else if (isCallExpressionTarget(location) || isNewExpressionTarget(location)) { - callExpressionLike = location.parent; - } - else if (location.parent && isJsxOpeningLikeElement(location.parent) && isFunctionLike(symbol.valueDeclaration)) { - callExpressionLike = location.parent; - } - - if (callExpressionLike) { - const candidateSignatures: Signature[] = []; - signature = typeChecker.getResolvedSignature(callExpressionLike, candidateSignatures); - if (!signature && candidateSignatures.length) { - // Use the first candidate: - signature = candidateSignatures[0]; - } - - const useConstructSignatures = callExpressionLike.kind === SyntaxKind.NewExpression || (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword); - - const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); - - if (!contains(allSignatures, signature.target) && !contains(allSignatures, signature)) { - // Get the first signature if there is one -- allSignatures may contain - // either the original signature or its target, so check for either - signature = allSignatures.length ? allSignatures[0] : undefined; - } - - if (signature) { - if (useConstructSignatures && (symbolFlags & SymbolFlags.Class)) { - // Constructor - symbolKind = ScriptElementKind.constructorImplementationElement; - addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); + else if (symbolFlags & SymbolFlags.Alias) { + symbolKind = ScriptElementKind.alias; + pushTypePart(symbolKind); + displayParts.push(spacePart()); + if (useConstructSignatures) { + displayParts.push(keywordPart(SyntaxKind.NewKeyword)); + displayParts.push(spacePart()); } - else if (symbolFlags & SymbolFlags.Alias) { - symbolKind = ScriptElementKind.alias; - pushTypePart(symbolKind); + addFullSymbolName(symbol); + } + else { + addPrefixForAnyFunctionOrVar(symbol, symbolKind); + } + + switch (symbolKind) { + case ScriptElementKind.jsxAttribute: + case ScriptElementKind.memberVariableElement: + case ScriptElementKind.variableElement: + case ScriptElementKind.constElement: + case ScriptElementKind.letElement: + case ScriptElementKind.parameterElement: + case ScriptElementKind.localVariableElement: + // If it is call or construct signature of lambda's write type name + displayParts.push(punctuationPart(SyntaxKind.ColonToken)); displayParts.push(spacePart()); if (useConstructSignatures) { displayParts.push(keywordPart(SyntaxKind.NewKeyword)); displayParts.push(spacePart()); } - addFullSymbolName(symbol); - } - else { - addPrefixForAnyFunctionOrVar(symbol, symbolKind); - } + if (!(type.flags & TypeFlags.Object && (type).objectFlags & ObjectFlags.Anonymous) && type.symbol) { + addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)); + } + addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature); + break; - switch (symbolKind) { - case ScriptElementKind.jsxAttribute: - case ScriptElementKind.memberVariableElement: - case ScriptElementKind.variableElement: - case ScriptElementKind.constElement: - case ScriptElementKind.letElement: - case ScriptElementKind.parameterElement: - case ScriptElementKind.localVariableElement: - // If it is call or construct signature of lambda's write type name - displayParts.push(punctuationPart(SyntaxKind.ColonToken)); - displayParts.push(spacePart()); - if (useConstructSignatures) { - displayParts.push(keywordPart(SyntaxKind.NewKeyword)); - displayParts.push(spacePart()); - } - if (!(type.flags & TypeFlags.Object && (type).objectFlags & ObjectFlags.Anonymous) && type.symbol) { - addRange(displayParts, symbolToDisplayParts(typeChecker, type.symbol, enclosingDeclaration, /*meaning*/ undefined, SymbolFormatFlags.WriteTypeParametersOrArguments)); - } - addSignatureDisplayParts(signature, allSignatures, TypeFormatFlags.WriteArrowStyleSignature); - break; - - default: - // Just signature - addSignatureDisplayParts(signature, allSignatures); - } - hasAddedSymbolInfo = true; + default: + // Just signature + addSignatureDisplayParts(signature, allSignatures); } + hasAddedSymbolInfo = true; } - else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration - (location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration - // get the signature from the declaration and write it - const functionDeclaration = location.parent; - // Use function declaration to write the signatures only if the symbol corresponding to this declaration - const locationIsSymbolDeclaration = find(symbol.declarations, declaration => - declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration)); + } + else if ((isNameOfFunctionDeclaration(location) && !(symbolFlags & SymbolFlags.Accessor)) || // name of function declaration + (location.kind === SyntaxKind.ConstructorKeyword && location.parent.kind === SyntaxKind.Constructor)) { // At constructor keyword of constructor declaration + // get the signature from the declaration and write it + const functionDeclaration = location.parent; + // Use function declaration to write the signatures only if the symbol corresponding to this declaration + const locationIsSymbolDeclaration = find(symbol.declarations, declaration => + declaration === (location.kind === SyntaxKind.ConstructorKeyword ? functionDeclaration.parent : functionDeclaration)); - if (locationIsSymbolDeclaration) { - const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures(); - if (!typeChecker.isImplementationOfOverload(functionDeclaration)) { - signature = typeChecker.getSignatureFromDeclaration(functionDeclaration); - } - else { - signature = allSignatures[0]; - } - - if (functionDeclaration.kind === SyntaxKind.Constructor) { - // show (constructor) Type(...) signature - symbolKind = ScriptElementKind.constructorImplementationElement; - addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); - } - else { - // (function/method) symbol(..signature) - addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature && - !(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind); - } - - addSignatureDisplayParts(signature, allSignatures); - hasAddedSymbolInfo = true; + if (locationIsSymbolDeclaration) { + const allSignatures = functionDeclaration.kind === SyntaxKind.Constructor ? type.getNonNullableType().getConstructSignatures() : type.getNonNullableType().getCallSignatures(); + if (!typeChecker.isImplementationOfOverload(functionDeclaration)) { + signature = typeChecker.getSignatureFromDeclaration(functionDeclaration); } + else { + signature = allSignatures[0]; + } + + if (functionDeclaration.kind === SyntaxKind.Constructor) { + // show (constructor) Type(...) signature + symbolKind = ScriptElementKind.constructorImplementationElement; + addPrefixForAnyFunctionOrVar(type.symbol, symbolKind); + } + else { + // (function/method) symbol(..signature) + addPrefixForAnyFunctionOrVar(functionDeclaration.kind === SyntaxKind.CallSignature && + !(type.symbol.flags & SymbolFlags.TypeLiteral || type.symbol.flags & SymbolFlags.ObjectLiteral) ? type.symbol : symbol, symbolKind); + } + + addSignatureDisplayParts(signature, allSignatures); + hasAddedSymbolInfo = true; } } } @@ -417,7 +416,9 @@ namespace ts.SymbolDisplay { symbolFlags & SymbolFlags.Accessor || symbolKind === ScriptElementKind.memberFunctionElement) { const allSignatures = type.getNonNullableType().getCallSignatures(); - addSignatureDisplayParts(allSignatures[0], allSignatures); + if (allSignatures.length) { + addSignatureDisplayParts(allSignatures[0], allSignatures); + } } } } diff --git a/tests/cases/fourslash/quickInfoTypeError.ts b/tests/cases/fourslash/quickInfoTypeError.ts new file mode 100644 index 00000000000..7e0c9b20303 --- /dev/null +++ b/tests/cases/fourslash/quickInfoTypeError.ts @@ -0,0 +1,10 @@ +/// + +////foo({ +//// /**/f: function() {}, +//// f() {} +////}); + +// The symbol indicates that this is a funciton, but the type is `any`. +// Regression test that we don't crash (by trying to get signatures from `any`). +verify.quickInfoAt("", "(method) f");