From f9f91571264a352fc483dbb36b568438b2aa685c Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 10:16:36 -0700
Subject: [PATCH 01/49] Add initial tests
---
tests/cases/conformance/jsdoc/callbackTag1.ts | 12 +++++++
.../fourslash/server/jsdocCallbackTag.ts | 31 +++++++++++++++++
.../server/jsdocCallbackTagNavigateTo.ts | 33 +++++++++++++++++++
.../server/jsdocCallbackTagRename01.ts | 15 +++++++++
4 files changed, 91 insertions(+)
create mode 100644 tests/cases/conformance/jsdoc/callbackTag1.ts
create mode 100644 tests/cases/fourslash/server/jsdocCallbackTag.ts
create mode 100644 tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts
create mode 100644 tests/cases/fourslash/server/jsdocCallbackTagRename01.ts
diff --git a/tests/cases/conformance/jsdoc/callbackTag1.ts b/tests/cases/conformance/jsdoc/callbackTag1.ts
new file mode 100644
index 0000000000000..e97cd23921bcd
--- /dev/null
+++ b/tests/cases/conformance/jsdoc/callbackTag1.ts
@@ -0,0 +1,12 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: cb.js
+
+/** @callback Sid
+ * @param {string} s
+ * @returns {string} What were you expecting
+ */
+
+/** @type {Sid} smallId */
+var sid = s => s + "!";
diff --git a/tests/cases/fourslash/server/jsdocCallbackTag.ts b/tests/cases/fourslash/server/jsdocCallbackTag.ts
new file mode 100644
index 0000000000000..d0d944cc2778d
--- /dev/null
+++ b/tests/cases/fourslash/server/jsdocCallbackTag.ts
@@ -0,0 +1,31 @@
+///
+
+// @allowNonTsExtensions: true
+// @Filename: jsdocCallbackTag.js
+
+//// /**
+//// * @callback FooHandler
+//// * @param {string} eventName - So many words
+//// * @param eventName2 {number | string} - Silence is golden
+//// * @param eventName3 - Osterreich mos def
+//// */
+//// /**
+//// * @type {FooHandler} callback
+//// */
+//// var t/*1*/;
+////
+//// /**
+//// * @callback FooHandler2 - What, another one?
+//// * @param {string=} eventName - it keeps happening
+//// * @param {string} [eventName2] - i WARNED you dog
+//// */
+//// /**
+//// * @type {FooHandler2} callback
+//// */
+//// var t2/*2*/;
+
+
+goTo.marker("1");
+verify.quickInfoIs("var t: (eventName: string, eventName2: number | string, eventName3: any) => void");
+goTo.marker("2");
+verify.quickInfoIs("var t2: (eventName?: string, eventName2?: string) => void");
diff --git a/tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts b/tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts
new file mode 100644
index 0000000000000..e17192e65b7e6
--- /dev/null
+++ b/tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts
@@ -0,0 +1,33 @@
+///
+
+// @allowNonTsExtensions: true
+// @Filename: jsDocCallback.js
+
+//// /**
+//// * @callback FooCallback
+//// * @param {string} eventName - What even is the navigation bar?
+//// */
+//// /** @type {FooCallback} */
+//// var t;
+
+verify.navigationBar([
+ {
+ "text": "",
+ "kind": "module",
+ "childItems": [
+ {
+ "text": "FooCallback",
+ "kind": "type"
+ },
+ {
+ "text": "t",
+ "kind": "var"
+ }
+ ]
+ },
+ {
+ "text": "FooCallback",
+ "kind": "type",
+ "indent": 1
+ }
+])
diff --git a/tests/cases/fourslash/server/jsdocCallbackTagRename01.ts b/tests/cases/fourslash/server/jsdocCallbackTagRename01.ts
new file mode 100644
index 0000000000000..bb7dd32cfc28d
--- /dev/null
+++ b/tests/cases/fourslash/server/jsdocCallbackTagRename01.ts
@@ -0,0 +1,15 @@
+///
+
+// @allowNonTsExtensions: true
+// @Filename: jsDocCallback.js
+////
+//// /**
+//// * @callback [|FooCallback|]
+//// * @param {string} eventName - Rename should work
+//// */
+////
+//// /** @type {/*1*/[|FooCallback|]} */
+//// var t;
+
+const ranges = test.ranges();
+verify.renameLocations(ranges[0], { findInStrings: false, findInComments: true, ranges });
From 5fab72fae42209efb9e491537a11561447c1e0cd Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 10:16:47 -0700
Subject: [PATCH 02/49] Add types
---
src/compiler/types.ts | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 3608b6b5c3232..d71aa8404e262 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -411,9 +411,11 @@ namespace ts {
JSDocVariadicType,
JSDocComment,
JSDocTypeLiteral,
+ JSDocSignature,
JSDocTag,
JSDocAugmentsTag,
JSDocClassTag,
+ JSDocCallbackTag,
JSDocParameterTag,
JSDocReturnTag,
JSDocTypeTag,
@@ -822,6 +824,7 @@ namespace ts {
| FunctionTypeNode
| ConstructorTypeNode
| JSDocFunctionType
+ | JSDocSignature // TODO: Different from JSDocFunctionType??????
| FunctionDeclaration
| MethodDeclaration
| ConstructorDeclaration
@@ -2385,6 +2388,19 @@ namespace ts {
typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
}
+ export interface JSDocCallbackTag extends JSDocTag, NamedDeclaration {
+ parent: JSDoc;
+ kind: SyntaxKind.JSDocCallbackTag;
+ fullName?: JSDocNamespaceDeclaration | Identifier;
+ name?: Identifier; // TODO: Not sure whether this rigamarole is needed for callback...but probably!
+ signature: JSDocSignature;
+ }
+
+ // TODO: name it JSDocSignatureDeclaration? Could just try to reuse JSDocTypeLiteral
+ export interface JSDocSignature extends JSDocType, SignatureDeclarationBase {
+ kind: SyntaxKind.JSDocSignature;
+ }
+
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
From 37076b3536dedd71d067f74ac46d5744a3db57c0 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 10:30:21 -0700
Subject: [PATCH 03/49] Half of parsing (builds but does not pass tests)
---
src/compiler/checker.ts | 1 +
src/compiler/parser.ts | 50 +++++++++++++++++++++++++++------------
src/compiler/utilities.ts | 1 +
3 files changed, 37 insertions(+), 15 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index d825b0d971758..c034f12b2b712 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -22340,6 +22340,7 @@ namespace ts {
break;
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
+ case SyntaxKind.JSDocSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 32e7ca19dd313..9f60d2ee85ef7 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -489,6 +489,15 @@ namespace ts {
return visitNode(cbNode, (node).fullName) ||
visitNode(cbNode, (node).typeExpression);
}
+ case SyntaxKind.JSDocCallbackTag:
+ return visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
+ visitNode(cbNode, (node as JSDocCallbackTag).signature);
+ case SyntaxKind.JSDocSignature:
+ return visitNodes(cbNode, cbNodes, node.decorators) ||
+ visitNodes(cbNode, cbNodes, node.modifiers) ||
+ visitNodes(cbNode, cbNodes, (node).typeParameters) ||
+ visitNodes(cbNode, cbNodes, (node).parameters) ||
+ visitNode(cbNode, (node).type);
case SyntaxKind.JSDocTypeLiteral:
if ((node as JSDocTypeLiteral).jsDocPropertyTags) {
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
@@ -6457,6 +6466,9 @@ namespace ts {
case "typedef":
tag = parseTypedefTag(atToken, tagName);
break;
+ case "callback":
+ tag = parseCallbackTag(atToken, tagName);
+ break;
default:
tag = parseUnknownTag(atToken, tagName);
break;
@@ -6710,6 +6722,14 @@ namespace ts {
return finishNode(tag);
}
+ function parseCallbackTag(atToken: AtToken, tagName: Identifier): JSDocCallbackTag {
+ const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, atToken.pos) as JSDocCallbackTag;
+ callbackTag.atToken = atToken;
+ callbackTag.tagName = tagName;
+ callbackTag.fullName = parseJSDocTypeNameWithNamespace(/*flags*/ 0);
+ return callbackTag;
+ }
+
function parseTypedefTag(atToken: AtToken, tagName: Identifier): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
@@ -6765,24 +6785,24 @@ namespace ts {
}
return finishNode(typedefTag);
+ }
- function parseJSDocTypeNameWithNamespace(flags: NodeFlags) {
- const pos = scanner.getTokenPos();
- const typeNameOrNamespaceName = parseJSDocIdentifierName();
-
- if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) {
- const jsDocNamespaceNode = createNode(SyntaxKind.ModuleDeclaration, pos);
- jsDocNamespaceNode.flags |= flags;
- jsDocNamespaceNode.name = typeNameOrNamespaceName;
- jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(NodeFlags.NestedNamespace);
- return finishNode(jsDocNamespaceNode);
- }
+ function parseJSDocTypeNameWithNamespace(flags: NodeFlags) {
+ const pos = scanner.getTokenPos();
+ const typeNameOrNamespaceName = parseJSDocIdentifierName();
+
+ if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) {
+ const jsDocNamespaceNode = createNode(SyntaxKind.ModuleDeclaration, pos);
+ jsDocNamespaceNode.flags |= flags;
+ jsDocNamespaceNode.name = typeNameOrNamespaceName;
+ jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(NodeFlags.NestedNamespace);
+ return finishNode(jsDocNamespaceNode);
+ }
- if (typeNameOrNamespaceName && flags & NodeFlags.NestedNamespace) {
- typeNameOrNamespaceName.isInJSDocNamespace = true;
- }
- return typeNameOrNamespaceName;
+ if (typeNameOrNamespaceName && flags & NodeFlags.NestedNamespace) {
+ typeNameOrNamespaceName.isInJSDocNamespace = true;
}
+ return typeNameOrNamespaceName;
}
function escapedTextsEqual(a: EntityName, b: EntityName): boolean {
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 04db855d4f8c8..b98582e982ac3 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -509,6 +509,7 @@ namespace ts {
export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters {
switch (node.kind) {
case SyntaxKind.CallSignature:
+ case SyntaxKind.JSDocSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.MethodSignature:
case SyntaxKind.IndexSignature:
From 248cd06ad3bef71f87722ee3d40e2b9f5d034188 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 11:34:15 -0700
Subject: [PATCH 04/49] Parsing done; types are uglier; doesn't crash but
doesn't pass
---
src/compiler/checker.ts | 1 -
src/compiler/parser.ts | 52 +++++++++++++++++++++++++--------------
src/compiler/types.ts | 8 +++---
src/compiler/utilities.ts | 1 -
4 files changed, 38 insertions(+), 24 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index c034f12b2b712..d825b0d971758 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -22340,7 +22340,6 @@ namespace ts {
break;
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
- case SyntaxKind.JSDocSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType:
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 9f60d2ee85ef7..dd197f7ab265e 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6726,8 +6726,31 @@ namespace ts {
const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, atToken.pos) as JSDocCallbackTag;
callbackTag.atToken = atToken;
callbackTag.tagName = tagName;
- callbackTag.fullName = parseJSDocTypeNameWithNamespace(/*flags*/ 0);
- return callbackTag;
+ callbackTag.fullName = parseJSDocTypeNameWithNamespace();
+ callbackTag.name = getJSDocTypeAliasName(callbackTag.fullName);
+ skipWhitespace();
+
+ let child: JSDocParameterTag | false;
+ const start = scanner.getStartPos();
+ const jsdocSignature = createNode(SyntaxKind.JSDocSignature, start) as JSDocSignature;
+ while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter) as JSDocParameterTag)) {
+ // Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
+ jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
+ }
+ callbackTag.signature = finishNode(jsdocSignature);
+ return finishNode(callbackTag);
+ }
+
+ function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) {
+ if (fullName) {
+ let rightNode = fullName;
+ while (true) {
+ if (ts.isIdentifier(rightNode) || !rightNode.body) {
+ return ts.isIdentifier(rightNode) ? rightNode : rightNode.name;
+ }
+ rightNode = rightNode.body;
+ }
+ }
}
function parseTypedefTag(atToken: AtToken, tagName: Identifier): JSDocTypedefTag {
@@ -6737,19 +6760,8 @@ namespace ts {
const typedefTag = createNode(SyntaxKind.JSDocTypedefTag, atToken.pos);
typedefTag.atToken = atToken;
typedefTag.tagName = tagName;
- typedefTag.fullName = parseJSDocTypeNameWithNamespace(/*flags*/ 0);
- if (typedefTag.fullName) {
- let rightNode = typedefTag.fullName;
- while (true) {
- if (rightNode.kind === SyntaxKind.Identifier || !rightNode.body) {
- // if node is identifier - use it as name
- // otherwise use name of the rightmost part that we were able to parse
- typedefTag.name = rightNode.kind === SyntaxKind.Identifier ? rightNode : rightNode.name;
- break;
- }
- rightNode = rightNode.body;
- }
- }
+ typedefTag.fullName = parseJSDocTypeNameWithNamespace();
+ typedefTag.name = getJSDocTypeAliasName(typedefTag.fullName);
skipWhitespace();
typedefTag.typeExpression = typeExpression;
@@ -6787,19 +6799,21 @@ namespace ts {
return finishNode(typedefTag);
}
- function parseJSDocTypeNameWithNamespace(flags: NodeFlags) {
+ function parseJSDocTypeNameWithNamespace(nested?: boolean) {
const pos = scanner.getTokenPos();
const typeNameOrNamespaceName = parseJSDocIdentifierName();
if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) {
const jsDocNamespaceNode = createNode(SyntaxKind.ModuleDeclaration, pos);
- jsDocNamespaceNode.flags |= flags;
+ if (nested) {
+ jsDocNamespaceNode.flags |= NodeFlags.NestedNamespace;
+ }
jsDocNamespaceNode.name = typeNameOrNamespaceName;
- jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(NodeFlags.NestedNamespace);
+ jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(/*nested*/ true);
return finishNode(jsDocNamespaceNode);
}
- if (typeNameOrNamespaceName && flags & NodeFlags.NestedNamespace) {
+ if (typeNameOrNamespaceName && nested) {
typeNameOrNamespaceName.isInJSDocNamespace = true;
}
return typeNameOrNamespaceName;
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index d71aa8404e262..0458dc3642077 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -824,7 +824,6 @@ namespace ts {
| FunctionTypeNode
| ConstructorTypeNode
| JSDocFunctionType
- | JSDocSignature // TODO: Different from JSDocFunctionType??????
| FunctionDeclaration
| MethodDeclaration
| ConstructorDeclaration
@@ -2396,9 +2395,12 @@ namespace ts {
signature: JSDocSignature;
}
- // TODO: name it JSDocSignatureDeclaration? Could just try to reuse JSDocTypeLiteral
- export interface JSDocSignature extends JSDocType, SignatureDeclarationBase {
+ // TODO: Could just try to reuse JSDocTypeLiteral
+ export interface JSDocSignature extends JSDocType, Declaration {
kind: SyntaxKind.JSDocSignature;
+ typeParameters?: ReadonlyArray;
+ parameters: ReadonlyArray;
+ type: JSDocReturnTag | undefined;
}
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index b98582e982ac3..04db855d4f8c8 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -509,7 +509,6 @@ namespace ts {
export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters {
switch (node.kind) {
case SyntaxKind.CallSignature:
- case SyntaxKind.JSDocSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.MethodSignature:
case SyntaxKind.IndexSignature:
From 2ce53ba4f0873cbfc550d3f3eaecfba2e891d2f5 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 13:27:31 -0700
Subject: [PATCH 05/49] Bind callback tag
Builds but tests still don't pass
---
src/compiler/binder.ts | 22 ++++++++++++++--------
1 file changed, 14 insertions(+), 8 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index d470a9d778509..297e0d50e9eb7 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -118,7 +118,7 @@ namespace ts {
let thisParentContainer: Node; // Container one level up
let blockScopeContainer: Node;
let lastContainer: Node;
- let delayedTypedefs: { typedef: JSDocTypedefTag, container: Node, lastContainer: Node, blockScopeContainer: Node, parent: Node }[];
+ let delayedTypedefs: { typeAlias: JSDocTypedefTag | JSDocCallbackTag, container: Node, lastContainer: Node, blockScopeContainer: Node, parent: Node }[];
let seenThisKeyword: boolean;
// state used by control flow analysis
@@ -273,6 +273,7 @@ namespace ts {
return InternalSymbolName.Constructor;
case SyntaxKind.FunctionType:
case SyntaxKind.CallSignature:
+ case SyntaxKind.JSDocSignature:
return InternalSymbolName.Call;
case SyntaxKind.ConstructorType:
case SyntaxKind.ConstructSignature:
@@ -1452,6 +1453,7 @@ namespace ts {
case SyntaxKind.GetAccessor:
case SyntaxKind.SetAccessor:
case SyntaxKind.CallSignature:
+ case SyntaxKind.JSDocSignature:
case SyntaxKind.JSDocFunctionType:
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructSignature:
@@ -1541,6 +1543,7 @@ namespace ts {
case SyntaxKind.ConstructorType:
case SyntaxKind.CallSignature:
case SyntaxKind.ConstructSignature:
+ case SyntaxKind.JSDocSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.MethodDeclaration:
case SyntaxKind.MethodSignature:
@@ -1646,7 +1649,7 @@ namespace ts {
return state;
}
- function bindFunctionOrConstructorType(node: SignatureDeclaration): void {
+ function bindFunctionOrConstructorType(node: SignatureDeclaration | JSDocSignature): void {
// For a given function symbol "<...>(...) => T" we want to generate a symbol identical
// to the one we would get for: { <...>(...): T }
//
@@ -1757,7 +1760,7 @@ namespace ts {
const saveParent = parent;
for (const delay of delayedTypedefs) {
({ container, lastContainer, blockScopeContainer, parent } = delay);
- bindBlockScopedDeclaration(delay.typedef, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
+ bindBlockScopedDeclaration(delay.typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
}
container = saveContainer;
lastContainer = saveLastContainer;
@@ -2100,6 +2103,7 @@ namespace ts {
case SyntaxKind.TypeParameter:
return bindTypeParameter(node as TypeParameterDeclaration);
case SyntaxKind.Parameter:
+ case SyntaxKind.JSDocParameterTag:
return bindParameter(node);
case SyntaxKind.VariableDeclaration:
return bindVariableDeclarationOrBindingElement(node);
@@ -2137,8 +2141,9 @@ namespace ts {
return bindPropertyOrMethodOrAccessor(node, SymbolFlags.SetAccessor, SymbolFlags.SetAccessorExcludes);
case SyntaxKind.FunctionType:
case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.JSDocSignature:
case SyntaxKind.ConstructorType:
- return bindFunctionOrConstructorType(node);
+ return bindFunctionOrConstructorType(node);
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.MappedType:
@@ -2211,10 +2216,11 @@ namespace ts {
SymbolFlags.Property | SymbolFlags.Optional :
SymbolFlags.Property;
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
- case SyntaxKind.JSDocTypedefTag: {
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag: {
const { fullName } = node as JSDocTypedefTag;
if (!fullName || fullName.kind === SyntaxKind.Identifier) {
- (delayedTypedefs || (delayedTypedefs = [])).push({ typedef: node as JSDocTypedefTag, container, lastContainer, blockScopeContainer, parent });
+ (delayedTypedefs || (delayedTypedefs = [])).push({ typeAlias: node as JSDocTypedefTag | JSDocCallbackTag, container, lastContainer, blockScopeContainer, parent });
}
break;
}
@@ -2611,7 +2617,7 @@ namespace ts {
}
}
- function bindParameter(node: ParameterDeclaration) {
+ function bindParameter(node: ParameterDeclaration | JSDocParameterTag) {
if (inStrictMode && !(node.flags & NodeFlags.Ambient)) {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
@@ -2619,7 +2625,7 @@ namespace ts {
}
if (isBindingPattern(node.name)) {
- bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + node.parent.parameters.indexOf(node) as __String);
+ bindAnonymousDeclaration(node, SymbolFlags.FunctionScopedVariable, "__" + (node as ParameterDeclaration).parent.parameters.indexOf(node as ParameterDeclaration) as __String);
}
else {
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes);
From 8991502766fa1162989af2dec3e9bfc1cc9461d4 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 15:03:04 -0700
Subject: [PATCH 06/49] Only bind param tags inside callback tags
---
src/compiler/binder.ts | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 297e0d50e9eb7..338aaeca7b95d 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2618,6 +2618,9 @@ namespace ts {
}
function bindParameter(node: ParameterDeclaration | JSDocParameterTag) {
+ if (node.kind === SyntaxKind.JSDocParameterTag && container.kind !== SyntaxKind.JSDocSignature) {
+ return;
+ }
if (inStrictMode && !(node.flags & NodeFlags.Ambient)) {
// It is a SyntaxError if the identifier eval or arguments appears within a FormalParameterList of a
// strict mode FunctionLikeDeclaration or FunctionExpression(13.1)
From 7737c7c590cfe8fae51f58178b1bdccb5c34879f Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 15:30:00 -0700
Subject: [PATCH 07/49] Fix binding switch to only handle param tags once
---
src/compiler/binder.ts | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 338aaeca7b95d..d189c69e2bb10 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2103,7 +2103,6 @@ namespace ts {
case SyntaxKind.TypeParameter:
return bindTypeParameter(node as TypeParameterDeclaration);
case SyntaxKind.Parameter:
- case SyntaxKind.JSDocParameterTag:
return bindParameter(node);
case SyntaxKind.VariableDeclaration:
return bindVariableDeclarationOrBindingElement(node);
@@ -2206,6 +2205,9 @@ namespace ts {
return updateStrictModeStatementList((node).statements);
case SyntaxKind.JSDocParameterTag:
+ if (node.parent.kind === SyntaxKind.JSDocCallbackTag) {
+ return bindParameter(node as JSDocParameterTag);
+ }
if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) {
break;
}
From ec7ddf88a4e4bb432d27c9b33c9ee25e5a80c47c Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 15:34:26 -0700
Subject: [PATCH 08/49] Checking is 1/3 done or so.
Now I'm going to go rename some members to be more uniform. I hate
unnnecessary conditionals.
---
src/compiler/checker.ts | 21 +++++++++++----------
src/compiler/utilities.ts | 12 ++++++++++++
2 files changed, 23 insertions(+), 10 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index d825b0d971758..2ca479492b5e6 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -5355,9 +5355,9 @@ namespace ts {
return unknownType;
}
- const declaration = find(symbol.declarations, d =>
- d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration);
- const typeNode = declaration.kind === SyntaxKind.JSDocTypedefTag ? declaration.typeExpression : declaration.type;
+ const declaration = find(symbol.declarations, d =>
+ isJSDocTypeAlias(d) || d.kind === SyntaxKind.TypeAliasDeclaration);
+ const typeNode = isJSDocTypedefTag(declaration) ? declaration.typeExpression : isJSDocCallbackTag(declaration) ? declaration.signature : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
let type = typeNode ? getTypeFromTypeNode(typeNode) : unknownType;
@@ -7575,24 +7575,25 @@ namespace ts {
*/
function getTypeFromTypeAliasReference(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[]): Type {
const type = getDeclaredTypeOfSymbol(symbol);
+ // TODO: call getEffectiveTypeParameterDeclarations here and upgrade it to understand in-comment template tags as type parameters
const typeParameters = getSymbolLinks(symbol).typeParameters;
if (typeParameters) {
const numTypeArguments = length(node.typeArguments);
const minTypeArgumentCount = getMinTypeArgumentCount(typeParameters);
if (numTypeArguments < minTypeArgumentCount || numTypeArguments > typeParameters.length) {
error(node,
- minTypeArgumentCount === typeParameters.length
- ? Diagnostics.Generic_type_0_requires_1_type_argument_s
- : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments,
- symbolToString(symbol),
- minTypeArgumentCount,
- typeParameters.length);
+ minTypeArgumentCount === typeParameters.length
+ ? Diagnostics.Generic_type_0_requires_1_type_argument_s
+ : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments,
+ symbolToString(symbol),
+ minTypeArgumentCount,
+ typeParameters.length);
return unknownType;
}
return getTypeAliasInstantiation(symbol, typeArguments);
}
return checkNoTypeArguments(node, symbol) ? type : unknownType;
- }
+ }
function getTypeReferenceName(node: TypeReferenceType): EntityNameOrEntityNameExpression | undefined {
switch (node.kind) {
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 04db855d4f8c8..e41502c9e6db7 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1772,6 +1772,10 @@ namespace ts {
((node as JSDocFunctionType).parameters[0].name as Identifier).escapedText === "new";
}
+ export function isJSDocTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag {
+ return node.kind === SyntaxKind.JSDocTypedefTag || node.kind === SyntaxKind.JSDocCallbackTag;
+ }
+
function getSourceOfAssignment(node: Node): Node {
return isExpressionStatement(node) &&
node.expression && isBinaryExpression(node.expression) &&
@@ -5435,6 +5439,14 @@ namespace ts {
export function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral {
return node.kind === SyntaxKind.JSDocTypeLiteral;
}
+
+ export function isJSDocCallbackTag(node: Node): node is JSDocCallbackTag {
+ return node.kind === SyntaxKind.JSDocCallbackTag;
+ }
+
+ export function isJSDocSignature(node: Node): node is JSDocSignature {
+ return node.kind === SyntaxKind.JSDocSignature;
+ }
}
// Node tests
From 7d2233a00e5c6d794c1de32c03802e8ccce1914c Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 15:44:10 -0700
Subject: [PATCH 09/49] Rename typeExpression to type (for some jsdoc)
(maybe I'll rename more later)
---
src/compiler/checker.ts | 7 +++----
src/compiler/parser.ts | 16 ++++++++--------
src/compiler/types.ts | 4 ++--
3 files changed, 13 insertions(+), 14 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 2ca479492b5e6..2b2e3e00cd014 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -5357,9 +5357,8 @@ namespace ts {
const declaration = find(symbol.declarations, d =>
isJSDocTypeAlias(d) || d.kind === SyntaxKind.TypeAliasDeclaration);
- const typeNode = isJSDocTypedefTag(declaration) ? declaration.typeExpression : isJSDocCallbackTag(declaration) ? declaration.signature : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
- let type = typeNode ? getTypeFromTypeNode(typeNode) : unknownType;
+ let type = declaration.type ? getTypeFromTypeNode(declaration.type) : unknownType;
if (popTypeResolution()) {
const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
@@ -22147,7 +22146,7 @@ namespace ts {
}
function checkJSDocTypedefTag(node: JSDocTypedefTag) {
- if (!node.typeExpression) {
+ if (!node.type) {
// If the node had `@property` tags, `typeExpression` would have been set to the first property tag.
error(node.name, Diagnostics.JSDoc_typedef_tag_should_either_have_a_type_annotation_or_be_followed_by_property_or_member_tags);
}
@@ -22155,7 +22154,7 @@ namespace ts {
if (node.name) {
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
}
- checkSourceElement(node.typeExpression);
+ checkSourceElement(node.type);
}
function checkJSDocParameterTag(node: JSDocParameterTag) {
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index dd197f7ab265e..d516c3898bad3 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -480,18 +480,18 @@ namespace ts {
case SyntaxKind.JSDocTemplateTag:
return visitNodes(cbNode, cbNodes, (node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
- if ((node as JSDocTypedefTag).typeExpression &&
- (node as JSDocTypedefTag).typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
- return visitNode(cbNode, (node).typeExpression) ||
+ if ((node as JSDocTypedefTag).type &&
+ (node as JSDocTypedefTag).type.kind === SyntaxKind.JSDocTypeExpression) {
+ return visitNode(cbNode, (node).type) ||
visitNode(cbNode, (node).fullName);
}
else {
return visitNode(cbNode, (node).fullName) ||
- visitNode(cbNode, (node).typeExpression);
+ visitNode(cbNode, (node).type);
}
case SyntaxKind.JSDocCallbackTag:
return visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
- visitNode(cbNode, (node as JSDocCallbackTag).signature);
+ visitNode(cbNode, (node as JSDocCallbackTag).type);
case SyntaxKind.JSDocSignature:
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
@@ -6737,7 +6737,7 @@ namespace ts {
// Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
}
- callbackTag.signature = finishNode(jsdocSignature);
+ callbackTag.type = finishNode(jsdocSignature);
return finishNode(callbackTag);
}
@@ -6764,7 +6764,7 @@ namespace ts {
typedefTag.name = getJSDocTypeAliasName(typedefTag.fullName);
skipWhitespace();
- typedefTag.typeExpression = typeExpression;
+ typedefTag.type = typeExpression;
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
let child: JSDocTypeTag | JSDocPropertyTag | false;
let jsdocTypeLiteral: JSDocTypeLiteral;
@@ -6790,7 +6790,7 @@ namespace ts {
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
jsdocTypeLiteral.isArrayType = true;
}
- typedefTag.typeExpression = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
+ typedefTag.type = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
childTypeTag.typeExpression :
finishNode(jsdocTypeLiteral);
}
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 0458dc3642077..b0939668d91af 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2384,7 +2384,7 @@ namespace ts {
kind: SyntaxKind.JSDocTypedefTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
name?: Identifier;
- typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
+ type?: JSDocTypeExpression | JSDocTypeLiteral;
}
export interface JSDocCallbackTag extends JSDocTag, NamedDeclaration {
@@ -2392,7 +2392,7 @@ namespace ts {
kind: SyntaxKind.JSDocCallbackTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
name?: Identifier; // TODO: Not sure whether this rigamarole is needed for callback...but probably!
- signature: JSDocSignature;
+ type: JSDocSignature;
}
// TODO: Could just try to reuse JSDocTypeLiteral
From f41a96b24d44a6b696d39eee9e91ef7f606bea52 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 15:59:12 -0700
Subject: [PATCH 10/49] Rename the rest of typeExpressions
Turns out there is a constraint in services such that they all need to
be named the same.
---
src/compiler/binder.ts | 2 +-
src/compiler/checker.ts | 22 +++++++++++-----------
src/compiler/parser.ts | 18 +++++++++---------
src/compiler/types.ts | 6 +++---
src/compiler/utilities.ts | 8 ++++----
src/services/classifier.ts | 12 ++++++------
src/services/completions.ts | 8 ++++----
src/services/jsDoc.ts | 2 +-
8 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index d189c69e2bb10..15925377cd956 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2214,7 +2214,7 @@ namespace ts {
// falls through
case SyntaxKind.JSDocPropertyTag:
const propTag = node as JSDocPropertyLikeTag;
- const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
+ const flags = propTag.isBracketed || propTag.type && propTag.type.type.kind === SyntaxKind.JSDocOptionalType ?
SymbolFlags.Property | SymbolFlags.Optional :
SymbolFlags.Property;
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 2b2e3e00cd014..8936f5cc1b78a 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4708,8 +4708,8 @@ namespace ts {
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((declaration).expression);
}
- if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.typeExpression) {
- return links.type = getTypeFromTypeNode(declaration.typeExpression.type);
+ if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.type) {
+ return links.type = getTypeFromTypeNode(declaration.type.type);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
@@ -6911,8 +6911,8 @@ namespace ts {
return isInJavaScriptFile(node) && (
// node.type should only be a JSDocOptionalType when node is a parameter of a JSDocFunctionType
node.type && node.type.kind === SyntaxKind.JSDocOptionalType
- || getJSDocParameterTags(node).some(({ isBracketed, typeExpression }) =>
- isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType));
+ || getJSDocParameterTags(node).some(({ isBracketed, type }) =>
+ isBracketed || !!type && type.type.kind === SyntaxKind.JSDocOptionalType));
}
function tryFindAmbientModule(moduleName: string, withAugmentations: boolean) {
@@ -7105,7 +7105,7 @@ namespace ts {
const lastParam = lastOrUndefined(declaration.parameters);
const lastParamTags = lastParam ? getJSDocParameterTags(lastParam) : getJSDocTags(declaration).filter(isJSDocParameterTag);
const lastParamVariadicType = firstDefined(lastParamTags, p =>
- p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined);
+ p.type && isJSDocVariadicType(p.type.type) ? p.type.type : undefined);
const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String);
syntheticArgsSymbol.type = lastParamVariadicType ? createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)) : anyArrayType;
@@ -15166,7 +15166,7 @@ namespace ts {
case SyntaxKind.ParenthesizedExpression: {
// Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast.
const tag = isInJavaScriptFile(parent) ? getJSDocTypeTag(parent) : undefined;
- return tag ? getTypeFromTypeNode(tag.typeExpression.type) : getContextualType(parent);
+ return tag ? getTypeFromTypeNode(tag.type.type) : getContextualType(parent);
}
case SyntaxKind.JsxExpression:
return getContextualTypeForJsxExpression(parent);
@@ -20465,7 +20465,7 @@ namespace ts {
function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type {
const tag = isInJavaScriptFile(node) ? getJSDocTypeTag(node) : undefined;
if (tag) {
- return checkAssertionWorker(tag, tag.typeExpression.type, node.expression, checkMode);
+ return checkAssertionWorker(tag, tag.type.type, node.expression, checkMode);
}
return checkExpression(node.expression, checkMode);
}
@@ -22147,7 +22147,7 @@ namespace ts {
function checkJSDocTypedefTag(node: JSDocTypedefTag) {
if (!node.type) {
- // If the node had `@property` tags, `typeExpression` would have been set to the first property tag.
+ // If the node had `@property` tags, `type` would have been set to the first property tag.
error(node.name, Diagnostics.JSDoc_typedef_tag_should_either_have_a_type_annotation_or_be_followed_by_property_or_member_tags);
}
@@ -22158,7 +22158,7 @@ namespace ts {
}
function checkJSDocParameterTag(node: JSDocParameterTag) {
- checkSourceElement(node.typeExpression);
+ checkSourceElement(node.type);
if (!getParameterSymbolFromJSDoc(node)) {
const decl = getHostSignatureFromJSDoc(node);
// don't issue an error for invalid hosts -- just functions --
@@ -22175,8 +22175,8 @@ namespace ts {
idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
}
else if (findLast(getJSDocTags(decl), isJSDocParameterTag) === node &&
- node.typeExpression && node.typeExpression.type &&
- !isArrayType(getTypeFromTypeNode(node.typeExpression.type))) {
+ node.type && node.type.type &&
+ !isArrayType(getTypeFromTypeNode(node.type.type))) {
error(node.name,
Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type,
idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index d516c3898bad3..e47908efa32bc 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -465,16 +465,16 @@ namespace ts {
case SyntaxKind.JSDocPropertyTag:
if ((node as JSDocPropertyLikeTag).isNameFirst) {
return visitNode(cbNode, (node).name) ||
- visitNode(cbNode, (node).typeExpression);
+ visitNode(cbNode, (node).type);
}
else {
- return visitNode(cbNode, (node).typeExpression) ||
+ return visitNode(cbNode, (node).type) ||
visitNode(cbNode, (node).name);
}
case SyntaxKind.JSDocReturnTag:
- return visitNode(cbNode, (node).typeExpression);
+ return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocTypeTag:
- return visitNode(cbNode, (node).typeExpression);
+ return visitNode(cbNode, (node).type);
case SyntaxKind.JSDocAugmentsTag:
return visitNode(cbNode, (node).class);
case SyntaxKind.JSDocTemplateTag:
@@ -6628,7 +6628,7 @@ namespace ts {
}
result.atToken = atToken;
result.tagName = tagName;
- result.typeExpression = typeExpression;
+ result.type = typeExpression;
result.name = name;
result.isNameFirst = isNameFirst;
result.isBracketed = isBracketed;
@@ -6668,7 +6668,7 @@ namespace ts {
const result = createNode(SyntaxKind.JSDocReturnTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
- result.typeExpression = tryParseTypeExpression();
+ result.type = tryParseTypeExpression();
return finishNode(result);
}
@@ -6680,7 +6680,7 @@ namespace ts {
const result = createNode(SyntaxKind.JSDocTypeTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
- result.typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
+ result.type = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
return finishNode(result);
}
@@ -6790,8 +6790,8 @@ namespace ts {
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
jsdocTypeLiteral.isArrayType = true;
}
- typedefTag.type = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
- childTypeTag.typeExpression :
+ typedefTag.type = childTypeTag && childTypeTag.type && !isObjectOrObjectArrayTypeReference(childTypeTag.type.type) ?
+ childTypeTag.type :
finishNode(jsdocTypeLiteral);
}
}
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index b0939668d91af..35e2254c2dca6 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2371,12 +2371,12 @@ namespace ts {
export interface JSDocReturnTag extends JSDocTag {
kind: SyntaxKind.JSDocReturnTag;
- typeExpression: JSDocTypeExpression;
+ type: JSDocTypeExpression;
}
export interface JSDocTypeTag extends JSDocTag {
kind: SyntaxKind.JSDocTypeTag;
- typeExpression: JSDocTypeExpression;
+ type: JSDocTypeExpression;
}
export interface JSDocTypedefTag extends JSDocTag, NamedDeclaration {
@@ -2406,7 +2406,7 @@ namespace ts {
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
- typeExpression?: JSDocTypeExpression;
+ type?: JSDocTypeExpression;
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
isNameFirst: boolean;
isBracketed: boolean;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index e41502c9e6db7..9c6e82ff0b05f 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -4702,7 +4702,7 @@ namespace ts {
export function getJSDocTypeTag(node: Node): JSDocTypeTag | undefined {
// We should have already issued an error if there were multiple type jsdocs, so just use the first one.
const tag = getFirstJSDocTag(node, isJSDocTypeTag);
- if (tag && tag.typeExpression && tag.typeExpression.type) {
+ if (tag && tag.type && tag.type.type) {
return tag;
}
return undefined;
@@ -4722,10 +4722,10 @@ namespace ts {
export function getJSDocType(node: Node): TypeNode | undefined {
let tag: JSDocTypeTag | JSDocParameterTag | undefined = getFirstJSDocTag(node, isJSDocTypeTag);
if (!tag && isParameter(node)) {
- tag = find(getJSDocParameterTags(node), tag => !!tag.typeExpression);
+ tag = find(getJSDocParameterTags(node), tag => !!tag.type);
}
- return tag && tag.typeExpression && tag.typeExpression.type;
+ return tag && tag.type && tag.type.type;
}
/**
@@ -4736,7 +4736,7 @@ namespace ts {
*/
export function getJSDocReturnType(node: Node): TypeNode | undefined {
const returnTag = getJSDocReturnTag(node);
- return returnTag && returnTag.typeExpression && returnTag.typeExpression.type;
+ return returnTag && returnTag.type && returnTag.type.type;
}
/** Get all JSDoc tags related to a node, including those on parent nodes. */
diff --git a/src/services/classifier.ts b/src/services/classifier.ts
index 89c96a45021b9..9b39d3a7c5d34 100644
--- a/src/services/classifier.ts
+++ b/src/services/classifier.ts
@@ -708,10 +708,10 @@ namespace ts {
processJSDocTemplateTag(tag);
break;
case SyntaxKind.JSDocTypeTag:
- processElement((tag).typeExpression);
+ processElement((tag).type);
break;
case SyntaxKind.JSDocReturnTag:
- processElement((tag).typeExpression);
+ processElement((tag).type);
break;
}
@@ -732,10 +732,10 @@ namespace ts {
pos = tag.name.end;
}
- if (tag.typeExpression) {
- pushCommentRange(pos, tag.typeExpression.pos - pos);
- processElement(tag.typeExpression);
- pos = tag.typeExpression.end;
+ if (tag.type) {
+ pushCommentRange(pos, tag.type.pos - pos);
+ processElement(tag.type);
+ pos = tag.type.end;
}
if (!tag.isNameFirst) {
diff --git a/src/services/completions.ts b/src/services/completions.ts
index f5aba7f99e8f7..0fba04868e78a 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -817,14 +817,14 @@ namespace ts.Completions {
if (tag.tagName.pos <= position && position <= tag.tagName.end) {
return { kind: CompletionDataKind.JsDocTagName };
}
- if (isTagWithTypeExpression(tag) && tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
+ if (isTagWithType(tag) && tag.type && tag.type.kind === SyntaxKind.JSDocTypeExpression) {
currentToken = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ true);
if (!currentToken ||
(!isDeclarationName(currentToken) &&
(currentToken.parent.kind !== SyntaxKind.JSDocPropertyTag ||
(currentToken.parent).name !== currentToken))) {
// Use as type location if inside tag's type expression
- insideJsDocTagTypeExpression = isCurrentlyEditingNode(tag.typeExpression);
+ insideJsDocTagTypeExpression = isCurrentlyEditingNode(tag.type);
}
}
if (isJSDocParameterTag(tag) && (nodeIsMissing(tag.name) || tag.name.pos <= position && position <= tag.name.end)) {
@@ -1002,9 +1002,9 @@ namespace ts.Completions {
const recommendedCompletion = previousToken && getRecommendedCompletion(previousToken, position, sourceFile, typeChecker);
return { kind: CompletionDataKind.Data, symbols, completionKind, isInSnippetScope, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer };
- type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
+ type JSDocTagWithType = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
- function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression {
+ function isTagWithType(tag: JSDocTag): tag is JSDocTagWithType {
switch (tag.kind) {
case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocPropertyTag:
diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts
index ca0dafbc74cb4..cdf42bf8b0ca7 100644
--- a/src/services/jsDoc.ts
+++ b/src/services/jsDoc.ts
@@ -96,7 +96,7 @@ namespace ts.JsDoc {
case SyntaxKind.JSDocTemplateTag:
return withList((tag as JSDocTemplateTag).typeParameters);
case SyntaxKind.JSDocTypeTag:
- return withNode((tag as JSDocTypeTag).typeExpression);
+ return withNode((tag as JSDocTypeTag).type);
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.JSDocPropertyTag:
case SyntaxKind.JSDocParameterTag:
From 64631e580d2f56b5ade44774bd61d4232150cb8c Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 16:08:53 -0700
Subject: [PATCH 11/49] Few more checker changes
---
src/compiler/checker.ts | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 8936f5cc1b78a..da4406b7a8cb7 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -21616,8 +21616,9 @@ namespace ts {
switch (d.kind) {
case SyntaxKind.InterfaceDeclaration:
case SyntaxKind.TypeAliasDeclaration:
- // A jsdoc typedef is, by definition, a type alias
+ // A jsdoc typedef and callback are, by definition, type aliases
case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
return DeclarationSpaces.ExportType;
case SyntaxKind.ModuleDeclaration:
return isAmbientModule(d as ModuleDeclaration) || getModuleInstanceState(d as ModuleDeclaration) !== ModuleInstanceState.NonInstantiated
@@ -22145,7 +22146,7 @@ namespace ts {
}
}
- function checkJSDocTypedefTag(node: JSDocTypedefTag) {
+ function checkJSDocTypeAliasTag(node: JSDocTypedefTag | JSDocCallbackTag) {
if (!node.type) {
// If the node had `@property` tags, `type` would have been set to the first property tag.
error(node.name, Diagnostics.JSDoc_typedef_tag_should_either_have_a_type_annotation_or_be_followed_by_property_or_member_tags);
@@ -25051,7 +25052,8 @@ namespace ts {
case SyntaxKind.JSDocAugmentsTag:
return checkJSDocAugmentsTag(node as JSDocAugmentsTag);
case SyntaxKind.JSDocTypedefTag:
- return checkJSDocTypedefTag(node as JSDocTypedefTag);
+ case SyntaxKind.JSDocCallbackTag:
+ return checkJSDocTypeAliasTag(node as JSDocTypedefTag);
case SyntaxKind.JSDocParameterTag:
return checkJSDocParameterTag(node as JSDocParameterTag);
case SyntaxKind.JSDocFunctionType:
From 009c411e80d34cee1eddc83bfbd81e8ec6c908c3 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 16:09:03 -0700
Subject: [PATCH 12/49] Revert "Rename the rest of typeExpressions"
This reverts commit f41a96b24d44a6b696d39eee9e91ef7f606bea52.
---
src/compiler/binder.ts | 2 +-
src/compiler/checker.ts | 22 +++++++++++-----------
src/compiler/parser.ts | 18 +++++++++---------
src/compiler/types.ts | 6 +++---
src/compiler/utilities.ts | 8 ++++----
src/services/classifier.ts | 12 ++++++------
src/services/completions.ts | 8 ++++----
src/services/jsDoc.ts | 2 +-
8 files changed, 39 insertions(+), 39 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 15925377cd956..d189c69e2bb10 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2214,7 +2214,7 @@ namespace ts {
// falls through
case SyntaxKind.JSDocPropertyTag:
const propTag = node as JSDocPropertyLikeTag;
- const flags = propTag.isBracketed || propTag.type && propTag.type.type.kind === SyntaxKind.JSDocOptionalType ?
+ const flags = propTag.isBracketed || propTag.typeExpression && propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
SymbolFlags.Property | SymbolFlags.Optional :
SymbolFlags.Property;
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index da4406b7a8cb7..ec36d0606f97b 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4708,8 +4708,8 @@ namespace ts {
if (declaration.kind === SyntaxKind.ExportAssignment) {
return links.type = checkExpression((declaration).expression);
}
- if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.type) {
- return links.type = getTypeFromTypeNode(declaration.type.type);
+ if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.typeExpression) {
+ return links.type = getTypeFromTypeNode(declaration.typeExpression.type);
}
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
@@ -6911,8 +6911,8 @@ namespace ts {
return isInJavaScriptFile(node) && (
// node.type should only be a JSDocOptionalType when node is a parameter of a JSDocFunctionType
node.type && node.type.kind === SyntaxKind.JSDocOptionalType
- || getJSDocParameterTags(node).some(({ isBracketed, type }) =>
- isBracketed || !!type && type.type.kind === SyntaxKind.JSDocOptionalType));
+ || getJSDocParameterTags(node).some(({ isBracketed, typeExpression }) =>
+ isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType));
}
function tryFindAmbientModule(moduleName: string, withAugmentations: boolean) {
@@ -7105,7 +7105,7 @@ namespace ts {
const lastParam = lastOrUndefined(declaration.parameters);
const lastParamTags = lastParam ? getJSDocParameterTags(lastParam) : getJSDocTags(declaration).filter(isJSDocParameterTag);
const lastParamVariadicType = firstDefined(lastParamTags, p =>
- p.type && isJSDocVariadicType(p.type.type) ? p.type.type : undefined);
+ p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined);
const syntheticArgsSymbol = createSymbol(SymbolFlags.Variable, "args" as __String);
syntheticArgsSymbol.type = lastParamVariadicType ? createArrayType(getTypeFromTypeNode(lastParamVariadicType.type)) : anyArrayType;
@@ -15166,7 +15166,7 @@ namespace ts {
case SyntaxKind.ParenthesizedExpression: {
// Like in `checkParenthesizedExpression`, an `/** @type {xyz} */` comment before a parenthesized expression acts as a type cast.
const tag = isInJavaScriptFile(parent) ? getJSDocTypeTag(parent) : undefined;
- return tag ? getTypeFromTypeNode(tag.type.type) : getContextualType(parent);
+ return tag ? getTypeFromTypeNode(tag.typeExpression.type) : getContextualType(parent);
}
case SyntaxKind.JsxExpression:
return getContextualTypeForJsxExpression(parent);
@@ -20465,7 +20465,7 @@ namespace ts {
function checkParenthesizedExpression(node: ParenthesizedExpression, checkMode?: CheckMode): Type {
const tag = isInJavaScriptFile(node) ? getJSDocTypeTag(node) : undefined;
if (tag) {
- return checkAssertionWorker(tag, tag.type.type, node.expression, checkMode);
+ return checkAssertionWorker(tag, tag.typeExpression.type, node.expression, checkMode);
}
return checkExpression(node.expression, checkMode);
}
@@ -22148,7 +22148,7 @@ namespace ts {
function checkJSDocTypeAliasTag(node: JSDocTypedefTag | JSDocCallbackTag) {
if (!node.type) {
- // If the node had `@property` tags, `type` would have been set to the first property tag.
+ // If the node had `@property` tags, `typeExpression` would have been set to the first property tag.
error(node.name, Diagnostics.JSDoc_typedef_tag_should_either_have_a_type_annotation_or_be_followed_by_property_or_member_tags);
}
@@ -22159,7 +22159,7 @@ namespace ts {
}
function checkJSDocParameterTag(node: JSDocParameterTag) {
- checkSourceElement(node.type);
+ checkSourceElement(node.typeExpression);
if (!getParameterSymbolFromJSDoc(node)) {
const decl = getHostSignatureFromJSDoc(node);
// don't issue an error for invalid hosts -- just functions --
@@ -22176,8 +22176,8 @@ namespace ts {
idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
}
else if (findLast(getJSDocTags(decl), isJSDocParameterTag) === node &&
- node.type && node.type.type &&
- !isArrayType(getTypeFromTypeNode(node.type.type))) {
+ node.typeExpression && node.typeExpression.type &&
+ !isArrayType(getTypeFromTypeNode(node.typeExpression.type))) {
error(node.name,
Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name_It_would_match_arguments_if_it_had_an_array_type,
idText(node.name.kind === SyntaxKind.QualifiedName ? node.name.right : node.name));
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index e47908efa32bc..d516c3898bad3 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -465,16 +465,16 @@ namespace ts {
case SyntaxKind.JSDocPropertyTag:
if ((node as JSDocPropertyLikeTag).isNameFirst) {
return visitNode(cbNode, (node).name) ||
- visitNode(cbNode, (node).type);
+ visitNode(cbNode, (node).typeExpression);
}
else {
- return visitNode(cbNode, (node).type) ||
+ return visitNode(cbNode, (node).typeExpression) ||
visitNode(cbNode, (node).name);
}
case SyntaxKind.JSDocReturnTag:
- return visitNode(cbNode, (node).type);
+ return visitNode(cbNode, (node).typeExpression);
case SyntaxKind.JSDocTypeTag:
- return visitNode(cbNode, (node).type);
+ return visitNode(cbNode, (node).typeExpression);
case SyntaxKind.JSDocAugmentsTag:
return visitNode(cbNode, (node).class);
case SyntaxKind.JSDocTemplateTag:
@@ -6628,7 +6628,7 @@ namespace ts {
}
result.atToken = atToken;
result.tagName = tagName;
- result.type = typeExpression;
+ result.typeExpression = typeExpression;
result.name = name;
result.isNameFirst = isNameFirst;
result.isBracketed = isBracketed;
@@ -6668,7 +6668,7 @@ namespace ts {
const result = createNode(SyntaxKind.JSDocReturnTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
- result.type = tryParseTypeExpression();
+ result.typeExpression = tryParseTypeExpression();
return finishNode(result);
}
@@ -6680,7 +6680,7 @@ namespace ts {
const result = createNode(SyntaxKind.JSDocTypeTag, atToken.pos);
result.atToken = atToken;
result.tagName = tagName;
- result.type = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
+ result.typeExpression = parseJSDocTypeExpression(/*mayOmitBraces*/ true);
return finishNode(result);
}
@@ -6790,8 +6790,8 @@ namespace ts {
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
jsdocTypeLiteral.isArrayType = true;
}
- typedefTag.type = childTypeTag && childTypeTag.type && !isObjectOrObjectArrayTypeReference(childTypeTag.type.type) ?
- childTypeTag.type :
+ typedefTag.type = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
+ childTypeTag.typeExpression :
finishNode(jsdocTypeLiteral);
}
}
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 35e2254c2dca6..b0939668d91af 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2371,12 +2371,12 @@ namespace ts {
export interface JSDocReturnTag extends JSDocTag {
kind: SyntaxKind.JSDocReturnTag;
- type: JSDocTypeExpression;
+ typeExpression: JSDocTypeExpression;
}
export interface JSDocTypeTag extends JSDocTag {
kind: SyntaxKind.JSDocTypeTag;
- type: JSDocTypeExpression;
+ typeExpression: JSDocTypeExpression;
}
export interface JSDocTypedefTag extends JSDocTag, NamedDeclaration {
@@ -2406,7 +2406,7 @@ namespace ts {
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
- type?: JSDocTypeExpression;
+ typeExpression?: JSDocTypeExpression;
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
isNameFirst: boolean;
isBracketed: boolean;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 9c6e82ff0b05f..e41502c9e6db7 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -4702,7 +4702,7 @@ namespace ts {
export function getJSDocTypeTag(node: Node): JSDocTypeTag | undefined {
// We should have already issued an error if there were multiple type jsdocs, so just use the first one.
const tag = getFirstJSDocTag(node, isJSDocTypeTag);
- if (tag && tag.type && tag.type.type) {
+ if (tag && tag.typeExpression && tag.typeExpression.type) {
return tag;
}
return undefined;
@@ -4722,10 +4722,10 @@ namespace ts {
export function getJSDocType(node: Node): TypeNode | undefined {
let tag: JSDocTypeTag | JSDocParameterTag | undefined = getFirstJSDocTag(node, isJSDocTypeTag);
if (!tag && isParameter(node)) {
- tag = find(getJSDocParameterTags(node), tag => !!tag.type);
+ tag = find(getJSDocParameterTags(node), tag => !!tag.typeExpression);
}
- return tag && tag.type && tag.type.type;
+ return tag && tag.typeExpression && tag.typeExpression.type;
}
/**
@@ -4736,7 +4736,7 @@ namespace ts {
*/
export function getJSDocReturnType(node: Node): TypeNode | undefined {
const returnTag = getJSDocReturnTag(node);
- return returnTag && returnTag.type && returnTag.type.type;
+ return returnTag && returnTag.typeExpression && returnTag.typeExpression.type;
}
/** Get all JSDoc tags related to a node, including those on parent nodes. */
diff --git a/src/services/classifier.ts b/src/services/classifier.ts
index 9b39d3a7c5d34..89c96a45021b9 100644
--- a/src/services/classifier.ts
+++ b/src/services/classifier.ts
@@ -708,10 +708,10 @@ namespace ts {
processJSDocTemplateTag(tag);
break;
case SyntaxKind.JSDocTypeTag:
- processElement((tag).type);
+ processElement((tag).typeExpression);
break;
case SyntaxKind.JSDocReturnTag:
- processElement((tag).type);
+ processElement((tag).typeExpression);
break;
}
@@ -732,10 +732,10 @@ namespace ts {
pos = tag.name.end;
}
- if (tag.type) {
- pushCommentRange(pos, tag.type.pos - pos);
- processElement(tag.type);
- pos = tag.type.end;
+ if (tag.typeExpression) {
+ pushCommentRange(pos, tag.typeExpression.pos - pos);
+ processElement(tag.typeExpression);
+ pos = tag.typeExpression.end;
}
if (!tag.isNameFirst) {
diff --git a/src/services/completions.ts b/src/services/completions.ts
index 0fba04868e78a..f5aba7f99e8f7 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -817,14 +817,14 @@ namespace ts.Completions {
if (tag.tagName.pos <= position && position <= tag.tagName.end) {
return { kind: CompletionDataKind.JsDocTagName };
}
- if (isTagWithType(tag) && tag.type && tag.type.kind === SyntaxKind.JSDocTypeExpression) {
+ if (isTagWithTypeExpression(tag) && tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
currentToken = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ true);
if (!currentToken ||
(!isDeclarationName(currentToken) &&
(currentToken.parent.kind !== SyntaxKind.JSDocPropertyTag ||
(currentToken.parent).name !== currentToken))) {
// Use as type location if inside tag's type expression
- insideJsDocTagTypeExpression = isCurrentlyEditingNode(tag.type);
+ insideJsDocTagTypeExpression = isCurrentlyEditingNode(tag.typeExpression);
}
}
if (isJSDocParameterTag(tag) && (nodeIsMissing(tag.name) || tag.name.pos <= position && position <= tag.name.end)) {
@@ -1002,9 +1002,9 @@ namespace ts.Completions {
const recommendedCompletion = previousToken && getRecommendedCompletion(previousToken, position, sourceFile, typeChecker);
return { kind: CompletionDataKind.Data, symbols, completionKind, isInSnippetScope, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer };
- type JSDocTagWithType = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
+ type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag;
- function isTagWithType(tag: JSDocTag): tag is JSDocTagWithType {
+ function isTagWithTypeExpression(tag: JSDocTag): tag is JSDocTagWithTypeExpression {
switch (tag.kind) {
case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocPropertyTag:
diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts
index cdf42bf8b0ca7..ca0dafbc74cb4 100644
--- a/src/services/jsDoc.ts
+++ b/src/services/jsDoc.ts
@@ -96,7 +96,7 @@ namespace ts.JsDoc {
case SyntaxKind.JSDocTemplateTag:
return withList((tag as JSDocTemplateTag).typeParameters);
case SyntaxKind.JSDocTypeTag:
- return withNode((tag as JSDocTypeTag).type);
+ return withNode((tag as JSDocTypeTag).typeExpression);
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.JSDocPropertyTag:
case SyntaxKind.JSDocParameterTag:
From 0de72c2411c5c562ab40046ca8e45ef8084ec55d Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 16:09:55 -0700
Subject: [PATCH 13/49] Revert "Rename typeExpression to type (for some jsdoc)"
This reverts commit 7d2233a00e5c6d794c1de32c03802e8ccce1914c.
---
src/compiler/checker.ts | 7 ++++---
src/compiler/parser.ts | 16 ++++++++--------
src/compiler/types.ts | 4 ++--
3 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ec36d0606f97b..75d5b4f5d7313 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -5357,8 +5357,9 @@ namespace ts {
const declaration = find(symbol.declarations, d =>
isJSDocTypeAlias(d) || d.kind === SyntaxKind.TypeAliasDeclaration);
+ const typeNode = isJSDocTypedefTag(declaration) ? declaration.typeExpression : isJSDocCallbackTag(declaration) ? declaration.signature : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
- let type = declaration.type ? getTypeFromTypeNode(declaration.type) : unknownType;
+ let type = typeNode ? getTypeFromTypeNode(typeNode) : unknownType;
if (popTypeResolution()) {
const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
@@ -22147,7 +22148,7 @@ namespace ts {
}
function checkJSDocTypeAliasTag(node: JSDocTypedefTag | JSDocCallbackTag) {
- if (!node.type) {
+ if (!node.typeExpression) {
// If the node had `@property` tags, `typeExpression` would have been set to the first property tag.
error(node.name, Diagnostics.JSDoc_typedef_tag_should_either_have_a_type_annotation_or_be_followed_by_property_or_member_tags);
}
@@ -22155,7 +22156,7 @@ namespace ts {
if (node.name) {
checkTypeNameIsReserved(node.name, Diagnostics.Type_alias_name_cannot_be_0);
}
- checkSourceElement(node.type);
+ checkSourceElement(node.typeExpression);
}
function checkJSDocParameterTag(node: JSDocParameterTag) {
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index d516c3898bad3..dd197f7ab265e 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -480,18 +480,18 @@ namespace ts {
case SyntaxKind.JSDocTemplateTag:
return visitNodes(cbNode, cbNodes, (node).typeParameters);
case SyntaxKind.JSDocTypedefTag:
- if ((node as JSDocTypedefTag).type &&
- (node as JSDocTypedefTag).type.kind === SyntaxKind.JSDocTypeExpression) {
- return visitNode(cbNode, (node).type) ||
+ if ((node as JSDocTypedefTag).typeExpression &&
+ (node as JSDocTypedefTag).typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
+ return visitNode(cbNode, (node).typeExpression) ||
visitNode(cbNode, (node).fullName);
}
else {
return visitNode(cbNode, (node).fullName) ||
- visitNode(cbNode, (node).type);
+ visitNode(cbNode, (node).typeExpression);
}
case SyntaxKind.JSDocCallbackTag:
return visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
- visitNode(cbNode, (node as JSDocCallbackTag).type);
+ visitNode(cbNode, (node as JSDocCallbackTag).signature);
case SyntaxKind.JSDocSignature:
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
@@ -6737,7 +6737,7 @@ namespace ts {
// Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
}
- callbackTag.type = finishNode(jsdocSignature);
+ callbackTag.signature = finishNode(jsdocSignature);
return finishNode(callbackTag);
}
@@ -6764,7 +6764,7 @@ namespace ts {
typedefTag.name = getJSDocTypeAliasName(typedefTag.fullName);
skipWhitespace();
- typedefTag.type = typeExpression;
+ typedefTag.typeExpression = typeExpression;
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
let child: JSDocTypeTag | JSDocPropertyTag | false;
let jsdocTypeLiteral: JSDocTypeLiteral;
@@ -6790,7 +6790,7 @@ namespace ts {
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
jsdocTypeLiteral.isArrayType = true;
}
- typedefTag.type = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
+ typedefTag.typeExpression = childTypeTag && childTypeTag.typeExpression && !isObjectOrObjectArrayTypeReference(childTypeTag.typeExpression.type) ?
childTypeTag.typeExpression :
finishNode(jsdocTypeLiteral);
}
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index b0939668d91af..0458dc3642077 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2384,7 +2384,7 @@ namespace ts {
kind: SyntaxKind.JSDocTypedefTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
name?: Identifier;
- type?: JSDocTypeExpression | JSDocTypeLiteral;
+ typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
}
export interface JSDocCallbackTag extends JSDocTag, NamedDeclaration {
@@ -2392,7 +2392,7 @@ namespace ts {
kind: SyntaxKind.JSDocCallbackTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
name?: Identifier; // TODO: Not sure whether this rigamarole is needed for callback...but probably!
- type: JSDocSignature;
+ signature: JSDocSignature;
}
// TODO: Could just try to reuse JSDocTypeLiteral
From 8ae89393b5b7d380828f9f75f9902bb3a6c644a1 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 1 May 2018 16:18:03 -0700
Subject: [PATCH 14/49] Finish undoing typeExpression rename
---
src/compiler/checker.ts | 2 +-
src/compiler/parser.ts | 4 ++--
src/compiler/types.ts | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 75d5b4f5d7313..ea5eca3ef5a09 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -5357,7 +5357,7 @@ namespace ts {
const declaration = find(symbol.declarations, d =>
isJSDocTypeAlias(d) || d.kind === SyntaxKind.TypeAliasDeclaration);
- const typeNode = isJSDocTypedefTag(declaration) ? declaration.typeExpression : isJSDocCallbackTag(declaration) ? declaration.signature : declaration.type;
+ const typeNode = isJSDocTypedefTag(declaration) ? declaration.typeExpression : isJSDocCallbackTag(declaration) ? declaration.typeExpression : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
let type = typeNode ? getTypeFromTypeNode(typeNode) : unknownType;
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index dd197f7ab265e..fa1a6ffc1a1b1 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -491,7 +491,7 @@ namespace ts {
}
case SyntaxKind.JSDocCallbackTag:
return visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
- visitNode(cbNode, (node as JSDocCallbackTag).signature);
+ visitNode(cbNode, (node as JSDocCallbackTag).typeExpression);
case SyntaxKind.JSDocSignature:
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
@@ -6737,7 +6737,7 @@ namespace ts {
// Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
}
- callbackTag.signature = finishNode(jsdocSignature);
+ callbackTag.typeExpression = finishNode(jsdocSignature);
return finishNode(callbackTag);
}
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 0458dc3642077..4cccdc1dabebb 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2392,7 +2392,7 @@ namespace ts {
kind: SyntaxKind.JSDocCallbackTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
name?: Identifier; // TODO: Not sure whether this rigamarole is needed for callback...but probably!
- signature: JSDocSignature;
+ typeExpression: JSDocSignature;
}
// TODO: Could just try to reuse JSDocTypeLiteral
From a96bdfd2f145eaefc198c878f03d9afc21a96793 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 2 May 2018 08:52:28 -0700
Subject: [PATCH 15/49] Rename and improve getTypeParametersForAliasSymbol
Plus some other small fixes
---
src/compiler/checker.ts | 26 +++++++++++--------
src/compiler/utilities.ts | 4 +++
tests/cases/conformance/jsdoc/callbackTag1.ts | 1 +
3 files changed, 20 insertions(+), 11 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index ea5eca3ef5a09..5d06003edffd9 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4695,7 +4695,7 @@ namespace ts {
if (symbol.flags & SymbolFlags.Prototype) {
return links.type = getTypeOfPrototypeProperty(symbol);
}
- // CommonsJS require/module/exports all have type any.
+ // CommonsJS require and module both have type any.
if (symbol === requireSymbol || symbol === moduleSymbol) {
return links.type = anyType;
}
@@ -5057,6 +5057,7 @@ namespace ts {
function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] {
let result: TypeParameter[];
for (const node of symbol.declarations) {
+ // TODO: needs to understand jsdoc typedefs and callbacks
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration ||
node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) {
const declaration = node;
@@ -5357,7 +5358,7 @@ namespace ts {
const declaration = find(symbol.declarations, d =>
isJSDocTypeAlias(d) || d.kind === SyntaxKind.TypeAliasDeclaration);
- const typeNode = isJSDocTypedefTag(declaration) ? declaration.typeExpression : isJSDocCallbackTag(declaration) ? declaration.typeExpression : declaration.type;
+ const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
let type = typeNode ? getTypeFromTypeNode(typeNode) : unknownType;
@@ -8276,8 +8277,9 @@ namespace ts {
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
+ const aliasSymbol = getAliasSymbolForTypeNode(node);
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), UnionReduction.Literal,
- getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
+ aliasSymbol, getTypeParametersForAliasSymbol(aliasSymbol));
}
return links.resolvedType;
}
@@ -8386,8 +8388,9 @@ namespace ts {
function getTypeFromIntersectionTypeNode(node: IntersectionTypeNode): Type {
const links = getNodeLinks(node);
if (!links.resolvedType) {
+ const aliasSymbol = getAliasSymbolForTypeNode(node);
links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode),
- getAliasSymbolForTypeNode(node), getAliasTypeArgumentsForTypeNode(node));
+ aliasSymbol, getTypeParametersForAliasSymbol(aliasSymbol));
}
return links.resolvedType;
}
@@ -8692,7 +8695,7 @@ namespace ts {
const type = createObjectType(ObjectFlags.Mapped, node.symbol);
type.declaration = node;
type.aliasSymbol = getAliasSymbolForTypeNode(node);
- type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
+ type.aliasTypeArguments = getTypeParametersForAliasSymbol(type.aliasSymbol);
links.resolvedType = type;
// Eagerly resolve the constraint type which forces an error if the constraint type circularly
// references itself through one or more type aliases.
@@ -8800,7 +8803,8 @@ namespace ts {
const links = getNodeLinks(node);
if (!links.resolvedType) {
const checkType = getTypeFromTypeNode(node.checkType);
- const aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
+ const aliasSymbol = getAliasSymbolForTypeNode(node);
+ const aliasTypeArguments = getTypeParametersForAliasSymbol(aliasSymbol);
const allOuterTypeParameters = getOuterTypeParameters(node, /*includeThisTypes*/ true);
const outerTypeParameters = aliasTypeArguments ? allOuterTypeParameters : filter(allOuterTypeParameters, tp => isPossiblyReferencedInConditionalType(tp, node));
const root: ConditionalRoot = {
@@ -8813,7 +8817,7 @@ namespace ts {
inferTypeParameters: getInferTypeParameters(node),
outerTypeParameters,
instantiations: undefined,
- aliasSymbol: getAliasSymbolForTypeNode(node),
+ aliasSymbol,
aliasTypeArguments
};
links.resolvedType = getConditionalType(root, /*mapper*/ undefined);
@@ -8917,7 +8921,7 @@ namespace ts {
else {
let type = createObjectType(ObjectFlags.Anonymous, node.symbol);
type.aliasSymbol = aliasSymbol;
- type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
+ type.aliasTypeArguments = getTypeParametersForAliasSymbol(aliasSymbol);
if (isJSDocTypeLiteral(node) && node.isArrayType) {
type = createArrayType(type);
}
@@ -8928,11 +8932,10 @@ namespace ts {
}
function getAliasSymbolForTypeNode(node: TypeNode) {
- return node.parent.kind === SyntaxKind.TypeAliasDeclaration ? getSymbolOfNode(node.parent) : undefined;
+ return isTypeAlias(node.parent) ? getSymbolOfNode(node.parent) : undefined;
}
- function getAliasTypeArgumentsForTypeNode(node: TypeNode) {
- const symbol = getAliasSymbolForTypeNode(node);
+ function getTypeParametersForAliasSymbol(symbol: Symbol) {
return symbol ? getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) : undefined;
}
@@ -9190,6 +9193,7 @@ namespace ts {
case SyntaxKind.TypeLiteral:
case SyntaxKind.JSDocTypeLiteral:
case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.JSDocSignature:
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node);
case SyntaxKind.TypeOperator:
return getTypeFromTypeOperatorNode(node);
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index e41502c9e6db7..d65cccbbdb632 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1776,6 +1776,10 @@ namespace ts {
return node.kind === SyntaxKind.JSDocTypedefTag || node.kind === SyntaxKind.JSDocCallbackTag;
}
+ export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | TypeAliasDeclaration {
+ return isJSDocTypeAlias(node) || isTypeAliasDeclaration(node);
+ }
+
function getSourceOfAssignment(node: Node): Node {
return isExpressionStatement(node) &&
node.expression && isBinaryExpression(node.expression) &&
diff --git a/tests/cases/conformance/jsdoc/callbackTag1.ts b/tests/cases/conformance/jsdoc/callbackTag1.ts
index e97cd23921bcd..a0298954c6efd 100644
--- a/tests/cases/conformance/jsdoc/callbackTag1.ts
+++ b/tests/cases/conformance/jsdoc/callbackTag1.ts
@@ -7,6 +7,7 @@
* @param {string} s
* @returns {string} What were you expecting
*/
+var x = 1
/** @type {Sid} smallId */
var sid = s => s + "!";
From 6a0a5eb659f13f2ab5ec11bdd8f790394e5e9d11 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 2 May 2018 13:58:27 -0700
Subject: [PATCH 16/49] Core checking works, but is flabbergastingly messy
I'm serious.
---
src/compiler/binder.ts | 2 +-
src/compiler/checker.ts | 78 +++++++++++++++----
src/compiler/parser.ts | 23 ++++--
src/compiler/types.ts | 4 +-
src/compiler/utilities.ts | 61 ++++++++++++---
src/services/codefixes/fixUnusedIdentifier.ts | 4 +-
src/services/refactors/extractSymbol.ts | 8 +-
.../reference/api/tsserverlibrary.d.ts | 59 +++++++++-----
tests/baselines/reference/api/typescript.d.ts | 59 +++++++++-----
tests/cases/conformance/jsdoc/callbackTag1.ts | 1 +
.../fourslash/server/jsdocCallbackTag.ts | 2 +-
11 files changed, 217 insertions(+), 84 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index d189c69e2bb10..c892b8dfcfd33 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2205,7 +2205,7 @@ namespace ts {
return updateStrictModeStatementList((node).statements);
case SyntaxKind.JSDocParameterTag:
- if (node.parent.kind === SyntaxKind.JSDocCallbackTag) {
+ if (node.parent.kind === SyntaxKind.JSDocSignature) {
return bindParameter(node as JSDocParameterTag);
}
if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) {
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 5d06003edffd9..cc089a193a632 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -3547,6 +3547,31 @@ namespace ts {
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext, preserveModifierFlags?: boolean): ParameterDeclaration {
const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter);
+ if (!parameterDeclaration && !(isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter)) {
+ const paramTagDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.JSDocParameterTag);
+ // now do a whole bunch of stuff with the parameter tag instead
+ // TODO: Dedupe this!
+ Debug.assert(!!paramTagDeclaration);
+ // TODO: Might need to convert jsdoc type literal to a binding name?
+ Debug.assert(isIdentifier(paramTagDeclaration.name));
+ const dotDotDotToken = isRestParameter(paramTagDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined;
+ const name = paramTagDeclaration.name && isIdentifier(paramTagDeclaration.name) ?
+ // isIdentifier(paramTagDeclaration.name) ?
+ setEmitFlags(getSynthesizedClone(paramTagDeclaration.name), EmitFlags.NoAsciiEscaping) :
+ // cloneQualifiedName(paramTagDeclaration.name) :
+ symbolName(parameterSymbol);
+ const questionToken = isOptionalParameter2(paramTagDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined;
+ const parameterTypeNode = typeToTypeNodeHelper(getTypeOfSymbol(parameterSymbol), context);
+ const parameterNode = createParameter(
+ /*decorators*/ undefined,
+ /*modifiers*/ undefined,
+ dotDotDotToken,
+ name,
+ questionToken,
+ parameterTypeNode,
+ /*initializer*/ undefined);
+ return parameterNode;
+ }
Debug.assert(!!parameterDeclaration || isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter);
let parameterType = getTypeOfSymbol(parameterSymbol);
@@ -3586,6 +3611,15 @@ namespace ts {
return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping);
}
}
+
+ // function cloneQualifiedName(node: QualifiedName): EntityName {
+ // if (isIdentifier(node.left)) {
+ // return setEmitFlags(getSynthesizedClone(node.left), EmitFlags.NoAsciiEscaping);
+ // }
+ // else {
+ // return setEmitFlags(createQualifiedName(cloneQualifiedName(node.left), getSynthesizedClone(node.right)), EmitFlags.NoAsciiEscaping);
+ // }
+ // }
}
function lookupSymbolChain(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags) {
@@ -4711,6 +4745,9 @@ namespace ts {
if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.typeExpression) {
return links.type = getTypeFromTypeNode(declaration.typeExpression.type);
}
+ if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && isJSDocSignature(declaration.parent) && !declaration.typeExpression) {
+ return links.type = unknownType;
+ }
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
@@ -5922,7 +5959,7 @@ namespace ts {
}
function createSignature(
- declaration: SignatureDeclaration,
+ declaration: SignatureDeclaration | JSDocSignature,
typeParameters: TypeParameter[],
thisParameter: Symbol | undefined,
parameters: Symbol[],
@@ -7022,7 +7059,7 @@ namespace ts {
return typeArguments;
}
- function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature {
+ function getSignatureFromDeclaration(declaration: SignatureDeclaration | JSDocSignature): Signature {
const links = getNodeLinks(declaration);
if (!links.resolvedSignature) {
const parameters: Symbol[] = [];
@@ -7045,6 +7082,7 @@ namespace ts {
const param = declaration.parameters[i];
let paramSymbol = param.symbol;
+ const type = isJSDocParameterTag(param) ? (param.typeExpression && param.typeExpression.type) : param.type;
// Include parameter symbol instead of property symbol in the signature
if (paramSymbol && !!(paramSymbol.flags & SymbolFlags.Property) && !isBindingPattern(param.name)) {
const resolvedSymbol = resolveName(param, paramSymbol.escapedName, SymbolFlags.Value, undefined, undefined, /*isUse*/ false);
@@ -7058,16 +7096,14 @@ namespace ts {
parameters.push(paramSymbol);
}
- if (param.type && param.type.kind === SyntaxKind.LiteralType) {
+ if (type && type.kind === SyntaxKind.LiteralType) {
hasLiteralTypes = true;
}
// Record a new minimum argument count if this is not an optional parameter
- const isOptionalParameter = param.initializer || param.questionToken || param.dotDotDotToken ||
- iife && parameters.length > iife.arguments.length && !param.type ||
- isUntypedSignatureInJSFile ||
- isJSDocOptionalParameter(param);
- if (!isOptionalParameter) {
+ if (!(isOptionalParameter2(param) ||
+ iife && parameters.length > iife.arguments.length && !type ||
+ isUntypedSignatureInJSFile)) {
minArgumentCount = parameters.length;
}
}
@@ -7094,14 +7130,26 @@ namespace ts {
return links.resolvedSignature;
}
+ // TODO: Decide between this and the Original and B-b-b-b-best?
+ function isOptionalParameter2(param: ParameterDeclaration | JSDocParameterTag) {
+ if (isParameter(param)) {
+ return param.initializer || param.questionToken || param.dotDotDotToken ||
+ isJSDocOptionalParameter(param);
+ }
+ else {
+ const { isBracketed, typeExpression } = param;
+ return isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType;
+ }
+ }
+
/**
* A JS function gets a synthetic rest parameter if it references `arguments` AND:
* 1. It has no parameters but at least one `@param` with a type that starts with `...`
* OR
* 2. It has at least one parameter, and the last parameter has a matching `@param` with a type that starts with `...`
*/
- function maybeAddJsSyntheticRestParameter(declaration: SignatureDeclaration, parameters: Symbol[]): boolean {
- if (!containsArgumentsReference(declaration)) {
+ function maybeAddJsSyntheticRestParameter(declaration: SignatureDeclaration | JSDocSignature, parameters: Symbol[]): boolean {
+ if (isJSDocSignature(declaration) || !containsArgumentsReference(declaration)) {
return false;
}
const lastParam = lastOrUndefined(declaration.parameters);
@@ -7120,9 +7168,9 @@ namespace ts {
return true;
}
- function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration, isJSConstructSignature: boolean, classType: Type) {
+ function getSignatureReturnTypeFromDeclaration(declaration: SignatureDeclaration | JSDocSignature, isJSConstructSignature: boolean, classType: Type) {
if (isJSConstructSignature) {
- return getTypeFromTypeNode(declaration.parameters[0].type);
+ return getTypeFromTypeNode((declaration.parameters[0] as ParameterDeclaration).type);
}
else if (classType) {
return classType;
@@ -7182,7 +7230,7 @@ namespace ts {
for (let i = 0; i < symbol.declarations.length; i++) {
const decl = symbol.declarations[i];
const node = isPropertyAccessExpression(decl) ? getAssignedJavascriptInitializer(decl) : decl;
- if (!isFunctionLike(node)) continue;
+ if (!isFunctionLike(node) && !(node && isJSDocSignature(node))) continue;
// Don't include signature if node is the implementation of an overloaded function. A node is considered
// an implementation node if it has a body and the previous node is of the same kind and immediately
// precedes the implementation node (i.e. has the same parent and ends where the implementation starts).
@@ -9871,8 +9919,8 @@ namespace ts {
function compareTypePredicateRelatedTo(
source: TypePredicate,
target: TypePredicate,
- sourceDeclaration: SignatureDeclaration,
- targetDeclaration: SignatureDeclaration,
+ sourceDeclaration: SignatureDeclaration | JSDocSignature,
+ targetDeclaration: SignatureDeclaration | JSDocSignature,
reportErrors: boolean,
errorReporter: ErrorReporter,
compareTypes: (s: Type, t: Type, reportErrors?: boolean) => Ternary): Ternary {
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index fa1a6ffc1a1b1..0fddc03652733 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6279,8 +6279,9 @@ namespace ts {
}
const enum PropertyLikeParse {
- Property,
- Parameter,
+ Property = 1 << 0,
+ Parameter = 1 << 1,
+ CallbackParameter = 1 << 2,
}
export function parseJSDocCommentWorker(start: number, length: number): JSDoc {
@@ -6616,11 +6617,12 @@ namespace ts {
typeExpression = tryParseTypeExpression();
}
- const result = target === PropertyLikeParse.Parameter ?
- createNode(SyntaxKind.JSDocParameterTag, atToken.pos) :
- createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
+ const result = target === PropertyLikeParse.Property ?
+ createNode(SyntaxKind.JSDocPropertyTag, atToken.pos) :
+ createNode(SyntaxKind.JSDocParameterTag, atToken.pos);
let comment: string | undefined;
if (indent !== undefined) comment = parseTagComments(indent + scanner.getStartPos() - atToken.pos);
+ // TODO: For nested parsing, CallbackParameter should change to Parameter
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name, target);
if (nestedTypeLiteral) {
typeExpression = nestedTypeLiteral;
@@ -6733,10 +6735,14 @@ namespace ts {
let child: JSDocParameterTag | false;
const start = scanner.getStartPos();
const jsdocSignature = createNode(SyntaxKind.JSDocSignature, start) as JSDocSignature;
- while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter) as JSDocParameterTag)) {
+ while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter) as JSDocParameterTag)) {
// Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
}
+ // const shouldParseReturnTag = true;
+ // if (shouldParseReturnTag) {
+ // jsdocSignature.type = parseReturnTag(x, y);
+ // }
callbackTag.typeExpression = finishNode(jsdocSignature);
return finishNode(callbackTag);
}
@@ -6845,6 +6851,7 @@ namespace ts {
if (canParseTag) {
const child = tryParseChildTag(target);
if (child && child.kind === SyntaxKind.JSDocParameterTag &&
+ target !== PropertyLikeParse.CallbackParameter &&
(ts.isIdentifier(child.name) || !escapedTextsEqual(name, child.name.left))) {
return false;
}
@@ -6893,12 +6900,12 @@ namespace ts {
case "arg":
case "argument":
case "param":
- t = PropertyLikeParse.Parameter;
+ t = PropertyLikeParse.Parameter | PropertyLikeParse.CallbackParameter;
break;
default:
return false;
}
- if (target !== t) {
+ if (!(target & t)) {
return false;
}
const tag = parseParameterOrPropertyTag(atToken, tagName, target, /*indent*/ undefined);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 4cccdc1dabebb..0572106f94649 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2053,7 +2053,7 @@ namespace ts {
export type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
+ export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypeLiteral | JSDocSignature;
export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
@@ -3984,7 +3984,7 @@ namespace ts {
}
export interface Signature {
- declaration?: SignatureDeclaration; // Originating declaration
+ declaration?: SignatureDeclaration | JSDocSignature; // Originating declaration
typeParameters?: TypeParameter[]; // Type parameters (undefined if non-generic)
parameters: Symbol[]; // Parameters
/* @internal */
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index d65cccbbdb632..cfc689a7ffb1d 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -505,6 +505,38 @@ namespace ts {
return false;
}
+ export function getTypeParametersOfDeclaration(node: DeclarationWithTypeParameters): NodeArray | undefined {
+ switch (node.kind) {
+ case SyntaxKind.CallSignature:
+ case SyntaxKind.ConstructSignature:
+ case SyntaxKind.MethodSignature:
+ case SyntaxKind.IndexSignature:
+ case SyntaxKind.FunctionType:
+ case SyntaxKind.ConstructorType:
+ case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.ClassDeclaration:
+ case SyntaxKind.ClassExpression:
+ case SyntaxKind.InterfaceDeclaration:
+ case SyntaxKind.TypeAliasDeclaration:
+ case SyntaxKind.JSDocTemplateTag:
+ case SyntaxKind.FunctionDeclaration:
+ case SyntaxKind.MethodDeclaration:
+ case SyntaxKind.Constructor:
+ case SyntaxKind.GetAccessor:
+ case SyntaxKind.SetAccessor:
+ case SyntaxKind.FunctionExpression:
+ case SyntaxKind.ArrowFunction:
+ return node.typeParameters;
+ case SyntaxKind.JSDocSignature:
+ case SyntaxKind.JSDocTypeLiteral:
+ // TODO: Actually go find them!
+ return undefined;
+ default:
+ assertTypeIsNever(node);
+ return undefined;
+ }
+ }
+
export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters;
export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters {
switch (node.kind) {
@@ -527,6 +559,8 @@ namespace ts {
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
+ case SyntaxKind.JSDocSignature:
+ case SyntaxKind.JSDocTypeLiteral:
return true;
default:
assertTypeIsNever(node);
@@ -1776,7 +1810,7 @@ namespace ts {
return node.kind === SyntaxKind.JSDocTypedefTag || node.kind === SyntaxKind.JSDocCallbackTag;
}
- export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | TypeAliasDeclaration {
+ export function isTypeAlias(node: Node): node is JSDocTypedefTag | JSDocCallbackTag | TypeAliasDeclaration {
return isJSDocTypeAlias(node) || isTypeAliasDeclaration(node);
}
@@ -1915,13 +1949,15 @@ namespace ts {
return find(typeParameters, p => p.name.escapedText === name);
}
- export function hasRestParameter(s: SignatureDeclaration): boolean {
- const last = lastOrUndefined(s.parameters);
+ export function hasRestParameter(s: SignatureDeclaration | JSDocSignature): boolean {
+ // TODO: This type argument wouldn't be needed if I used NodeArray for jsdoc signatures
+ const last = lastOrUndefined(s.parameters);
return last && isRestParameter(last);
}
- export function isRestParameter(node: ParameterDeclaration): boolean {
- return node.dotDotDotToken !== undefined || node.type && node.type.kind === SyntaxKind.JSDocVariadicType;
+ export function isRestParameter(node: ParameterDeclaration | JSDocParameterTag): boolean {
+ const type = isJSDocParameterTag(node) ? (node.typeExpression && node.typeExpression.type) : node.type;
+ return (node as ParameterDeclaration).dotDotDotToken !== undefined || type && type.kind === SyntaxKind.JSDocVariadicType;
}
export const enum AssignmentKind {
@@ -2968,8 +3004,9 @@ namespace ts {
return parameter && parameter.type;
}
- export function getThisParameter(signature: SignatureDeclaration): ParameterDeclaration | undefined {
- if (signature.parameters.length) {
+ export function getThisParameter(signature: SignatureDeclaration | JSDocSignature): ParameterDeclaration | undefined {
+ // TODO: Maybe jsdoc signatures should support this parameters? (parameterIsThisKeyword is not going to work there, I think, since jsdoc doesn't have keywords)
+ if (signature.parameters.length && !isJSDocSignature(signature)) {
const thisParameter = signature.parameters[0];
if (parameterIsThisKeyword(thisParameter)) {
return thisParameter;
@@ -3062,8 +3099,8 @@ namespace ts {
* Gets the effective return type annotation of a signature. If the node was parsed in a
* JavaScript file, gets the return type annotation from JSDoc.
*/
- export function getEffectiveReturnTypeNode(node: SignatureDeclaration): TypeNode | undefined {
- return node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
+ export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined {
+ return !isJSDocSignature(node) && node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
}
/**
@@ -3071,6 +3108,8 @@ namespace ts {
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
*/
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
+ // TODO: Make getJSDocTypeParameterDeclarations do the right thing
+ if (isJSDocTypeLiteral(node) || isJSDocSignature(node)) return undefined;
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined);
}
@@ -4694,6 +4733,9 @@ namespace ts {
/** Gets the JSDoc return tag for the node if present */
export function getJSDocReturnTag(node: Node): JSDocReturnTag | undefined {
+ if (isJSDocSignature(node)) {
+ return node.type;
+ }
return getFirstJSDocTag(node, isJSDocReturnTag);
}
@@ -5612,6 +5654,7 @@ namespace ts {
switch (kind) {
case SyntaxKind.MethodSignature:
case SyntaxKind.CallSignature:
+ case SyntaxKind.JSDocSignature:
case SyntaxKind.ConstructSignature:
case SyntaxKind.IndexSignature:
case SyntaxKind.FunctionType:
diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts
index fd360c73ea1ad..93c1c1681af78 100644
--- a/src/services/codefixes/fixUnusedIdentifier.ts
+++ b/src/services/codefixes/fixUnusedIdentifier.ts
@@ -125,7 +125,7 @@ namespace ts.codefix {
break;
case SyntaxKind.TypeParameter:
- const typeParameters = (parent.parent).typeParameters;
+ const typeParameters = getTypeParametersOfDeclaration(parent.parent);
if (typeParameters.length === 1) {
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1, /*includeJsDocComment*/ false);
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end, /*includeJsDocComment*/ false);
@@ -273,4 +273,4 @@ namespace ts.codefix {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts
index 5805a9861801e..ffd0bdbcdef0d 100644
--- a/src/services/refactors/extractSymbol.ts
+++ b/src/services/refactors/extractSymbol.ts
@@ -1464,8 +1464,8 @@ namespace ts.refactor.extractSymbol {
}
// Note that we add the current node's type parameters *after* updating the corresponding scope.
- if (isDeclarationWithTypeParameters(curr) && curr.typeParameters) {
- for (const typeParameterDecl of curr.typeParameters) {
+ if (isDeclarationWithTypeParameters(curr) && getTypeParametersOfDeclaration(curr)) {
+ for (const typeParameterDecl of getTypeParametersOfDeclaration(curr)) {
const typeParameter = checker.getTypeAtLocation(typeParameterDecl) as TypeParameter;
if (allTypeParameterUsages.has(typeParameter.id.toString())) {
seenTypeParameterUsages.set(typeParameter.id.toString(), typeParameter);
@@ -1536,8 +1536,8 @@ namespace ts.refactor.extractSymbol {
function hasTypeParameters(node: Node) {
return isDeclarationWithTypeParameters(node) &&
- node.typeParameters !== undefined &&
- node.typeParameters.length > 0;
+ getTypeParametersOfDeclaration(node) &&
+ getTypeParametersOfDeclaration(node).length > 0;
}
function isInGenericContext(node: Node) {
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 70f99eaa8d59c..3b07c3f0211d0 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -347,22 +347,24 @@ declare namespace ts {
JSDocVariadicType = 282,
JSDocComment = 283,
JSDocTypeLiteral = 284,
- JSDocTag = 285,
- JSDocAugmentsTag = 286,
- JSDocClassTag = 287,
- JSDocParameterTag = 288,
- JSDocReturnTag = 289,
- JSDocTypeTag = 290,
- JSDocTemplateTag = 291,
- JSDocTypedefTag = 292,
- JSDocPropertyTag = 293,
- SyntaxList = 294,
- NotEmittedStatement = 295,
- PartiallyEmittedExpression = 296,
- CommaListExpression = 297,
- MergeDeclarationMarker = 298,
- EndOfDeclarationMarker = 299,
- Count = 300,
+ JSDocSignature = 285,
+ JSDocTag = 286,
+ JSDocAugmentsTag = 287,
+ JSDocClassTag = 288,
+ JSDocCallbackTag = 289,
+ JSDocParameterTag = 290,
+ JSDocReturnTag = 291,
+ JSDocTypeTag = 292,
+ JSDocTemplateTag = 293,
+ JSDocTypedefTag = 294,
+ JSDocPropertyTag = 295,
+ SyntaxList = 296,
+ NotEmittedStatement = 297,
+ PartiallyEmittedExpression = 298,
+ CommaListExpression = 299,
+ MergeDeclarationMarker = 300,
+ EndOfDeclarationMarker = 301,
+ Count = 302,
FirstAssignment = 58,
LastAssignment = 70,
FirstCompoundAssignment = 59,
@@ -389,9 +391,9 @@ declare namespace ts {
LastBinaryOperator = 70,
FirstNode = 145,
FirstJSDocNode = 275,
- LastJSDocNode = 293,
- FirstJSDocTagNode = 285,
- LastJSDocTagNode = 293
+ LastJSDocNode = 295,
+ FirstJSDocTagNode = 286,
+ LastJSDocTagNode = 295
}
enum NodeFlags {
None = 0,
@@ -1277,7 +1279,7 @@ declare namespace ts {
block: Block;
}
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
+ type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypeLiteral | JSDocSignature;
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
@@ -1534,6 +1536,19 @@ declare namespace ts {
name?: Identifier;
typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
}
+ interface JSDocCallbackTag extends JSDocTag, NamedDeclaration {
+ parent: JSDoc;
+ kind: SyntaxKind.JSDocCallbackTag;
+ fullName?: JSDocNamespaceDeclaration | Identifier;
+ name?: Identifier;
+ typeExpression: JSDocSignature;
+ }
+ interface JSDocSignature extends JSDocType, Declaration {
+ kind: SyntaxKind.JSDocSignature;
+ typeParameters?: ReadonlyArray;
+ parameters: ReadonlyArray;
+ type: JSDocReturnTag | undefined;
+ }
interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
@@ -2251,7 +2266,7 @@ declare namespace ts {
Construct = 1
}
interface Signature {
- declaration?: SignatureDeclaration;
+ declaration?: SignatureDeclaration | JSDocSignature;
typeParameters?: TypeParameter[];
parameters: Symbol[];
}
@@ -3298,6 +3313,8 @@ declare namespace ts {
function isJSDocPropertyTag(node: Node): node is JSDocPropertyTag;
function isJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag;
function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral;
+ function isJSDocCallbackTag(node: Node): node is JSDocCallbackTag;
+ function isJSDocSignature(node: Node): node is JSDocSignature;
}
declare namespace ts {
/**
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index d54eff0acbdc9..f38008ea77afb 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -347,22 +347,24 @@ declare namespace ts {
JSDocVariadicType = 282,
JSDocComment = 283,
JSDocTypeLiteral = 284,
- JSDocTag = 285,
- JSDocAugmentsTag = 286,
- JSDocClassTag = 287,
- JSDocParameterTag = 288,
- JSDocReturnTag = 289,
- JSDocTypeTag = 290,
- JSDocTemplateTag = 291,
- JSDocTypedefTag = 292,
- JSDocPropertyTag = 293,
- SyntaxList = 294,
- NotEmittedStatement = 295,
- PartiallyEmittedExpression = 296,
- CommaListExpression = 297,
- MergeDeclarationMarker = 298,
- EndOfDeclarationMarker = 299,
- Count = 300,
+ JSDocSignature = 285,
+ JSDocTag = 286,
+ JSDocAugmentsTag = 287,
+ JSDocClassTag = 288,
+ JSDocCallbackTag = 289,
+ JSDocParameterTag = 290,
+ JSDocReturnTag = 291,
+ JSDocTypeTag = 292,
+ JSDocTemplateTag = 293,
+ JSDocTypedefTag = 294,
+ JSDocPropertyTag = 295,
+ SyntaxList = 296,
+ NotEmittedStatement = 297,
+ PartiallyEmittedExpression = 298,
+ CommaListExpression = 299,
+ MergeDeclarationMarker = 300,
+ EndOfDeclarationMarker = 301,
+ Count = 302,
FirstAssignment = 58,
LastAssignment = 70,
FirstCompoundAssignment = 59,
@@ -389,9 +391,9 @@ declare namespace ts {
LastBinaryOperator = 70,
FirstNode = 145,
FirstJSDocNode = 275,
- LastJSDocNode = 293,
- FirstJSDocTagNode = 285,
- LastJSDocTagNode = 293
+ LastJSDocNode = 295,
+ FirstJSDocTagNode = 286,
+ LastJSDocTagNode = 295
}
enum NodeFlags {
None = 0,
@@ -1277,7 +1279,7 @@ declare namespace ts {
block: Block;
}
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag;
+ type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypeLiteral | JSDocSignature;
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
@@ -1534,6 +1536,19 @@ declare namespace ts {
name?: Identifier;
typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
}
+ interface JSDocCallbackTag extends JSDocTag, NamedDeclaration {
+ parent: JSDoc;
+ kind: SyntaxKind.JSDocCallbackTag;
+ fullName?: JSDocNamespaceDeclaration | Identifier;
+ name?: Identifier;
+ typeExpression: JSDocSignature;
+ }
+ interface JSDocSignature extends JSDocType, Declaration {
+ kind: SyntaxKind.JSDocSignature;
+ typeParameters?: ReadonlyArray;
+ parameters: ReadonlyArray;
+ type: JSDocReturnTag | undefined;
+ }
interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
@@ -2251,7 +2266,7 @@ declare namespace ts {
Construct = 1
}
interface Signature {
- declaration?: SignatureDeclaration;
+ declaration?: SignatureDeclaration | JSDocSignature;
typeParameters?: TypeParameter[];
parameters: Symbol[];
}
@@ -3298,6 +3313,8 @@ declare namespace ts {
function isJSDocPropertyTag(node: Node): node is JSDocPropertyTag;
function isJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag;
function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral;
+ function isJSDocCallbackTag(node: Node): node is JSDocCallbackTag;
+ function isJSDocSignature(node: Node): node is JSDocSignature;
}
declare namespace ts {
/**
diff --git a/tests/cases/conformance/jsdoc/callbackTag1.ts b/tests/cases/conformance/jsdoc/callbackTag1.ts
index a0298954c6efd..10a20e32d4baf 100644
--- a/tests/cases/conformance/jsdoc/callbackTag1.ts
+++ b/tests/cases/conformance/jsdoc/callbackTag1.ts
@@ -3,6 +3,7 @@
// @checkJs: true
// @Filename: cb.js
+// TODO: Test nested param types using the nested Object style
/** @callback Sid
* @param {string} s
* @returns {string} What were you expecting
diff --git a/tests/cases/fourslash/server/jsdocCallbackTag.ts b/tests/cases/fourslash/server/jsdocCallbackTag.ts
index d0d944cc2778d..a80aa964ce7f3 100644
--- a/tests/cases/fourslash/server/jsdocCallbackTag.ts
+++ b/tests/cases/fourslash/server/jsdocCallbackTag.ts
@@ -26,6 +26,6 @@
goTo.marker("1");
-verify.quickInfoIs("var t: (eventName: string, eventName2: number | string, eventName3: any) => void");
+verify.quickInfoIs("var t: (eventName: string, eventName2: string | number, eventName3: any) => void");
goTo.marker("2");
verify.quickInfoIs("var t2: (eventName?: string, eventName2?: string) => void");
From 0003c48321f025f56ac0616f9b89be5e601aedbc Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 2 May 2018 14:41:10 -0700
Subject: [PATCH 17/49] Callback return types work now
---
src/compiler/parser.ts | 18 ++++++++++++++----
.../baselines/reference/callbackTag1.symbols | 15 +++++++++++++++
tests/baselines/reference/callbackTag1.types | 19 +++++++++++++++++++
3 files changed, 48 insertions(+), 4 deletions(-)
create mode 100644 tests/baselines/reference/callbackTag1.symbols
create mode 100644 tests/baselines/reference/callbackTag1.types
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 0fddc03652733..d17e24707ac3b 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6739,10 +6739,20 @@ namespace ts {
// Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
}
- // const shouldParseReturnTag = true;
- // if (shouldParseReturnTag) {
- // jsdocSignature.type = parseReturnTag(x, y);
- // }
+ const returnTag = tryParse(() => {
+ if (parseExpectedToken(SyntaxKind.AtToken)) {
+ const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
+ atToken.end = scanner.getTextPos();
+ nextToken(); // why is this needed? Doesn't parseExpectedToken advance the token enough?
+ const name = parseJSDocIdentifierName();
+ if (name && (name.escapedText === "return" || name.escapedText === "returns")) {
+ return parseReturnTag(atToken, name);
+ }
+ }
+ });
+ if (returnTag) {
+ jsdocSignature.type = returnTag;
+ }
callbackTag.typeExpression = finishNode(jsdocSignature);
return finishNode(callbackTag);
}
diff --git a/tests/baselines/reference/callbackTag1.symbols b/tests/baselines/reference/callbackTag1.symbols
new file mode 100644
index 0000000000000..5bcc7fc540363
--- /dev/null
+++ b/tests/baselines/reference/callbackTag1.symbols
@@ -0,0 +1,15 @@
+=== tests/cases/conformance/jsdoc/cb.js ===
+// TODO: Test nested param types using the nested Object style
+/** @callback Sid
+ * @param {string} s
+ * @returns {string} What were you expecting
+ */
+var x = 1
+>x : Symbol(x, Decl(cb.js, 5, 3))
+
+/** @type {Sid} smallId */
+var sid = s => s + "!";
+>sid : Symbol(sid, Decl(cb.js, 8, 3))
+>s : Symbol(s, Decl(cb.js, 8, 9))
+>s : Symbol(s, Decl(cb.js, 8, 9))
+
diff --git a/tests/baselines/reference/callbackTag1.types b/tests/baselines/reference/callbackTag1.types
new file mode 100644
index 0000000000000..47b3394a76dd6
--- /dev/null
+++ b/tests/baselines/reference/callbackTag1.types
@@ -0,0 +1,19 @@
+=== tests/cases/conformance/jsdoc/cb.js ===
+// TODO: Test nested param types using the nested Object style
+/** @callback Sid
+ * @param {string} s
+ * @returns {string} What were you expecting
+ */
+var x = 1
+>x : number
+>1 : 1
+
+/** @type {Sid} smallId */
+var sid = s => s + "!";
+>sid : (s: string) => string
+>s => s + "!" : (s: string) => string
+>s : string
+>s + "!" : string
+>s : string
+>"!" : "!"
+
From f4ac992e5fcd8e2792c779039f5f9be66c849d83 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 2 May 2018 15:11:37 -0700
Subject: [PATCH 18/49] Fix crash in services
---
src/compiler/utilities.ts | 2 +-
tests/cases/fourslash/server/jsdocCallbackTag.ts | 9 ++++++---
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index cfc689a7ffb1d..f4c0f6e17cafc 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -6272,7 +6272,7 @@ namespace ts {
/** True if node is of a kind that may contain comment text. */
export function isJSDocCommentContainingNode(node: Node): boolean {
- return node.kind === SyntaxKind.JSDocComment || isJSDocTag(node) || isJSDocTypeLiteral(node);
+ return node.kind === SyntaxKind.JSDocComment || isJSDocTag(node) || isJSDocTypeLiteral(node) || isJSDocSignature(node);
}
// TODO: determine what this does before making it public.
diff --git a/tests/cases/fourslash/server/jsdocCallbackTag.ts b/tests/cases/fourslash/server/jsdocCallbackTag.ts
index a80aa964ce7f3..5663224dc6f38 100644
--- a/tests/cases/fourslash/server/jsdocCallbackTag.ts
+++ b/tests/cases/fourslash/server/jsdocCallbackTag.ts
@@ -8,6 +8,7 @@
//// * @param {string} eventName - So many words
//// * @param eventName2 {number | string} - Silence is golden
//// * @param eventName3 - Osterreich mos def
+//// * @return {number} - DIVEKICK
//// */
//// /**
//// * @type {FooHandler} callback
@@ -20,12 +21,14 @@
//// * @param {string} [eventName2] - i WARNED you dog
//// */
//// /**
-//// * @type {FooHandler2} callback
+//// * @type {FooH/*3*/andler2} callback
//// */
//// var t2/*2*/;
goTo.marker("1");
-verify.quickInfoIs("var t: (eventName: string, eventName2: string | number, eventName3: any) => void");
+verify.quickInfoIs("var t: (eventName: string, eventName2: string | number, eventName3: any) => number");
goTo.marker("2");
-verify.quickInfoIs("var t2: (eventName?: string, eventName2?: string) => void");
+verify.quickInfoIs("var t2: (eventName?: string, eventName2?: string) => any");
+goTo.marker("3");
+verify.quickInfoIs("type FooHandler2 = (eventName?: string, eventName2?: string) => any");
From ad7fb6482829792ead56397a337c3221079e1538 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 2 May 2018 15:24:12 -0700
Subject: [PATCH 19/49] Make github diff smaller
---
src/compiler/parser.ts | 92 +++++++++++++++++++++---------------------
1 file changed, 46 insertions(+), 46 deletions(-)
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index d17e24707ac3b..63da0e8a275bc 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6724,51 +6724,6 @@ namespace ts {
return finishNode(tag);
}
- function parseCallbackTag(atToken: AtToken, tagName: Identifier): JSDocCallbackTag {
- const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, atToken.pos) as JSDocCallbackTag;
- callbackTag.atToken = atToken;
- callbackTag.tagName = tagName;
- callbackTag.fullName = parseJSDocTypeNameWithNamespace();
- callbackTag.name = getJSDocTypeAliasName(callbackTag.fullName);
- skipWhitespace();
-
- let child: JSDocParameterTag | false;
- const start = scanner.getStartPos();
- const jsdocSignature = createNode(SyntaxKind.JSDocSignature, start) as JSDocSignature;
- while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter) as JSDocParameterTag)) {
- // Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
- jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
- }
- const returnTag = tryParse(() => {
- if (parseExpectedToken(SyntaxKind.AtToken)) {
- const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
- atToken.end = scanner.getTextPos();
- nextToken(); // why is this needed? Doesn't parseExpectedToken advance the token enough?
- const name = parseJSDocIdentifierName();
- if (name && (name.escapedText === "return" || name.escapedText === "returns")) {
- return parseReturnTag(atToken, name);
- }
- }
- });
- if (returnTag) {
- jsdocSignature.type = returnTag;
- }
- callbackTag.typeExpression = finishNode(jsdocSignature);
- return finishNode(callbackTag);
- }
-
- function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) {
- if (fullName) {
- let rightNode = fullName;
- while (true) {
- if (ts.isIdentifier(rightNode) || !rightNode.body) {
- return ts.isIdentifier(rightNode) ? rightNode : rightNode.name;
- }
- rightNode = rightNode.body;
- }
- }
- }
-
function parseTypedefTag(atToken: AtToken, tagName: Identifier): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
@@ -6815,7 +6770,52 @@ namespace ts {
return finishNode(typedefTag);
}
- function parseJSDocTypeNameWithNamespace(nested?: boolean) {
+ function parseCallbackTag(atToken: AtToken, tagName: Identifier): JSDocCallbackTag {
+ const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, atToken.pos) as JSDocCallbackTag;
+ callbackTag.atToken = atToken;
+ callbackTag.tagName = tagName;
+ callbackTag.fullName = parseJSDocTypeNameWithNamespace();
+ callbackTag.name = getJSDocTypeAliasName(callbackTag.fullName);
+ skipWhitespace();
+
+ let child: JSDocParameterTag | false;
+ const start = scanner.getStartPos();
+ const jsdocSignature = createNode(SyntaxKind.JSDocSignature, start) as JSDocSignature;
+ while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter) as JSDocParameterTag)) {
+ // Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
+ jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
+ }
+ const returnTag = tryParse(() => {
+ if (parseExpectedToken(SyntaxKind.AtToken)) {
+ const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
+ atToken.end = scanner.getTextPos();
+ nextToken(); // why is this needed? Doesn't parseExpectedToken advance the token enough?
+ const name = parseJSDocIdentifierName();
+ if (name && (name.escapedText === "return" || name.escapedText === "returns")) {
+ return parseReturnTag(atToken, name);
+ }
+ }
+ });
+ if (returnTag) {
+ jsdocSignature.type = returnTag;
+ }
+ callbackTag.typeExpression = finishNode(jsdocSignature);
+ return finishNode(callbackTag);
+ }
+
+ function getJSDocTypeAliasName(fullName: JSDocNamespaceBody | undefined) {
+ if (fullName) {
+ let rightNode = fullName;
+ while (true) {
+ if (ts.isIdentifier(rightNode) || !rightNode.body) {
+ return ts.isIdentifier(rightNode) ? rightNode : rightNode.name;
+ }
+ rightNode = rightNode.body;
+ }
+ }
+ }
+
+ function parseJSDocTypeNameWithNamespace(nested?: boolean) {
const pos = scanner.getTokenPos();
const typeNameOrNamespaceName = parseJSDocIdentifierName();
From 2410beeedad81bb6d1ffac0014eb64079ac92204 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 2 May 2018 15:29:08 -0700
Subject: [PATCH 20/49] Try to make github diff even smaller
---
src/compiler/parser.ts | 40 ++++++++++++++++++++--------------------
1 file changed, 20 insertions(+), 20 deletions(-)
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 63da0e8a275bc..ffa5cc74b28a4 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6770,6 +6770,26 @@ namespace ts {
return finishNode(typedefTag);
}
+ function parseJSDocTypeNameWithNamespace(nested?: boolean) {
+ const pos = scanner.getTokenPos();
+ const typeNameOrNamespaceName = parseJSDocIdentifierName();
+
+ if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) {
+ const jsDocNamespaceNode = createNode(SyntaxKind.ModuleDeclaration, pos);
+ if (nested) {
+ jsDocNamespaceNode.flags |= NodeFlags.NestedNamespace;
+ }
+ jsDocNamespaceNode.name = typeNameOrNamespaceName;
+ jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(/*nested*/ true);
+ return finishNode(jsDocNamespaceNode);
+ }
+
+ if (typeNameOrNamespaceName && nested) {
+ typeNameOrNamespaceName.isInJSDocNamespace = true;
+ }
+ return typeNameOrNamespaceName;
+ }
+
function parseCallbackTag(atToken: AtToken, tagName: Identifier): JSDocCallbackTag {
const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, atToken.pos) as JSDocCallbackTag;
callbackTag.atToken = atToken;
@@ -6815,26 +6835,6 @@ namespace ts {
}
}
- function parseJSDocTypeNameWithNamespace(nested?: boolean) {
- const pos = scanner.getTokenPos();
- const typeNameOrNamespaceName = parseJSDocIdentifierName();
-
- if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) {
- const jsDocNamespaceNode = createNode(SyntaxKind.ModuleDeclaration, pos);
- if (nested) {
- jsDocNamespaceNode.flags |= NodeFlags.NestedNamespace;
- }
- jsDocNamespaceNode.name = typeNameOrNamespaceName;
- jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(/*nested*/ true);
- return finishNode(jsDocNamespaceNode);
- }
-
- if (typeNameOrNamespaceName && nested) {
- typeNameOrNamespaceName.isInJSDocNamespace = true;
- }
- return typeNameOrNamespaceName;
- }
-
function escapedTextsEqual(a: EntityName, b: EntityName): boolean {
while (!ts.isIdentifier(a) || !ts.isIdentifier(b)) {
if (!ts.isIdentifier(a) && !ts.isIdentifier(b) && a.right.escapedText === b.right.escapedText) {
From 0ae190b71ded063f956708611e19b1af469a57ed Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 2 May 2018 16:16:12 -0700
Subject: [PATCH 21/49] Fix rename for callback tag
---
src/compiler/utilities.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index f4c0f6e17cafc..98b33743328b0 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -6122,7 +6122,8 @@ namespace ts {
|| kind === SyntaxKind.TypeAliasDeclaration
|| kind === SyntaxKind.TypeParameter
|| kind === SyntaxKind.VariableDeclaration
- || kind === SyntaxKind.JSDocTypedefTag;
+ || kind === SyntaxKind.JSDocTypedefTag
+ || kind === SyntaxKind.JSDocCallbackTag;
}
function isDeclarationStatementKind(kind: SyntaxKind) {
From 3ca5bf0076aa83744e9725cb91b0866630f7ffe3 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 3 May 2018 09:06:14 -0700
Subject: [PATCH 22/49] Fix nav bar for callback tag
Also clean up some now-redundant code there to find the name of typedefs.
---
src/compiler/utilities.ts | 2 ++
src/services/navigationBar.ts | 25 ++-----------------
src/services/utilities.ts | 7 +++---
.../server/jsdocCallbackTagNavigateTo.ts | 4 +--
4 files changed, 10 insertions(+), 28 deletions(-)
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 98b33743328b0..511c777085792 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -4652,6 +4652,8 @@ namespace ts {
return undefined;
}
}
+ case SyntaxKind.JSDocCallbackTag:
+ return (declaration as JSDocCallbackTag).name;
case SyntaxKind.JSDocTypedefTag:
return getNameOfJSDocTypedef(declaration as JSDocTypedefTag);
case SyntaxKind.ExportAssignment: {
diff --git a/src/services/navigationBar.ts b/src/services/navigationBar.ts
index b029796e718ef..3ce18b9100256 100644
--- a/src/services/navigationBar.ts
+++ b/src/services/navigationBar.ts
@@ -290,7 +290,7 @@ namespace ts.NavigationBar {
if (hasJSDocNodes(node)) {
forEach(node.jsDoc, jsDoc => {
forEach(jsDoc.tags, tag => {
- if (tag.kind === SyntaxKind.JSDocTypedefTag) {
+ if (isJSDocTypeAlias(tag)) {
addLeafNode(tag);
}
});
@@ -414,8 +414,6 @@ namespace ts.NavigationBar {
case SyntaxKind.ArrowFunction:
case SyntaxKind.ClassExpression:
return getFunctionOrClassName(node);
- case SyntaxKind.JSDocTypedefTag:
- return getJSDocTypedefTagName(node);
default:
return undefined;
}
@@ -460,31 +458,11 @@ namespace ts.NavigationBar {
return "()";
case SyntaxKind.IndexSignature:
return "[]";
- case SyntaxKind.JSDocTypedefTag:
- return getJSDocTypedefTagName(node);
default:
return "";
}
}
- function getJSDocTypedefTagName(node: JSDocTypedefTag): string {
- if (node.name) {
- return node.name.text;
- }
- else {
- const parentNode = node.parent && node.parent.parent;
- if (parentNode && parentNode.kind === SyntaxKind.VariableStatement) {
- if (parentNode.declarationList.declarations.length > 0) {
- const nameIdentifier = parentNode.declarationList.declarations[0].name;
- if (nameIdentifier.kind === SyntaxKind.Identifier) {
- return nameIdentifier.text;
- }
- }
- }
- return "";
- }
- }
-
/** Flattens the NavNode tree to a list, keeping only the top-level items. */
function topLevelItems(root: NavigationBarNode): NavigationBarNode[] {
const topLevel: NavigationBarNode[] = [];
@@ -511,6 +489,7 @@ namespace ts.NavigationBar {
case SyntaxKind.SourceFile:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
return true;
case SyntaxKind.Constructor:
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index 78f18869b372c..285d055f1665f 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -309,7 +309,10 @@ namespace ts {
case SyntaxKind.ClassExpression:
return ScriptElementKind.classElement;
case SyntaxKind.InterfaceDeclaration: return ScriptElementKind.interfaceElement;
- case SyntaxKind.TypeAliasDeclaration: return ScriptElementKind.typeElement;
+ case SyntaxKind.TypeAliasDeclaration:
+ case SyntaxKind.JSDocCallbackTag:
+ case SyntaxKind.JSDocTypedefTag:
+ return ScriptElementKind.typeElement;
case SyntaxKind.EnumDeclaration: return ScriptElementKind.enumElement;
case SyntaxKind.VariableDeclaration:
return getKindOfVariableDeclaration(node);
@@ -340,8 +343,6 @@ namespace ts {
case SyntaxKind.ExportSpecifier:
case SyntaxKind.NamespaceImport:
return ScriptElementKind.alias;
- case SyntaxKind.JSDocTypedefTag:
- return ScriptElementKind.typeElement;
case SyntaxKind.BinaryExpression:
const kind = getSpecialPropertyAssignmentKind(node as BinaryExpression);
const { right } = node as BinaryExpression;
diff --git a/tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts b/tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts
index e17192e65b7e6..2beaf99ed7073 100644
--- a/tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts
+++ b/tests/cases/fourslash/server/jsdocCallbackTagNavigateTo.ts
@@ -4,7 +4,7 @@
// @Filename: jsDocCallback.js
//// /**
-//// * @callback FooCallback
+//// * @callback FooCallback
//// * @param {string} eventName - What even is the navigation bar?
//// */
//// /** @type {FooCallback} */
@@ -13,7 +13,7 @@
verify.navigationBar([
{
"text": "",
- "kind": "module",
+ "kind": "script",
"childItems": [
{
"text": "FooCallback",
From 819f19dc59de6d74b525e2027794a699f8433745 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 3 May 2018 09:52:10 -0700
Subject: [PATCH 23/49] Handle ooorder callback tags
Also get rid of redundant typedef name code *in the binder*. It's
everywhere!
---
src/compiler/binder.ts | 13 ++++++-------
tests/cases/conformance/jsdoc/callbackTag1.ts | 9 +++++++++
2 files changed, 15 insertions(+), 7 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index c892b8dfcfd33..54b5cd0a0e51d 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -298,9 +298,6 @@ namespace ts {
const functionType = node.parent;
const index = functionType.parameters.indexOf(node as ParameterDeclaration);
return "arg" + index as __String;
- case SyntaxKind.JSDocTypedefTag:
- const name = getNameOfJSDocTypedef(node as JSDocTypedefTag);
- return typeof name !== "undefined" ? name.escapedText : undefined;
}
}
@@ -453,6 +450,7 @@ namespace ts {
// during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
// and this case is specially handled. Module augmentations should only be merged with original module definition
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
+ // TODO: Handle callbacks here too
if (node.kind === SyntaxKind.JSDocTypedefTag) Debug.assert(isInJavaScriptFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefTag(node)) {
if (hasModifier(node, ModifierFlags.Default) && !getDeclarationName(node)) {
@@ -710,7 +708,8 @@ namespace ts {
bindJSDocComment(node);
break;
case SyntaxKind.JSDocTypedefTag:
- bindJSDocTypedefTag(node);
+ case SyntaxKind.JSDocCallbackTag:
+ bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag);
break;
// In source files and blocks, bind functions first to match hoisting that occurs at runtime
case SyntaxKind.SourceFile:
@@ -1378,13 +1377,13 @@ namespace ts {
function bindJSDocComment(node: JSDoc) {
forEachChild(node, n => {
- if (n.kind !== SyntaxKind.JSDocTypedefTag) {
+ if (!isJSDocTypeAlias(n)) {
bind(n);
}
});
}
- function bindJSDocTypedefTag(node: JSDocTypedefTag) {
+ function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag) {
forEachChild(node, n => {
// if the node has a fullName "A.B.C", that means symbol "C" was already bound
// when we visit "fullName"; so when we visit the name "C" as the next child of
@@ -1992,7 +1991,7 @@ namespace ts {
}
for (const tag of jsDoc.tags) {
- if (tag.kind === SyntaxKind.JSDocTypedefTag) {
+ if (isJSDocTypeAlias(tag)) {
const savedParent = parent;
parent = jsDoc;
bind(tag);
diff --git a/tests/cases/conformance/jsdoc/callbackTag1.ts b/tests/cases/conformance/jsdoc/callbackTag1.ts
index 10a20e32d4baf..510a03850104f 100644
--- a/tests/cases/conformance/jsdoc/callbackTag1.ts
+++ b/tests/cases/conformance/jsdoc/callbackTag1.ts
@@ -12,3 +12,12 @@ var x = 1
/** @type {Sid} smallId */
var sid = s => s + "!";
+
+
+/** @type {NoReturn} */
+var noreturn = obj => void obj.title
+
+/**
+ * @callback NoReturn
+ * @param {{ e: number, m: number, title: string }} s - Knee deep, shores, etc
+ */
From c2d7db7df6e98e2cee4d5ad51635f5b3ff0f42b4 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 3 May 2018 09:55:07 -0700
Subject: [PATCH 24/49] Add ooorder callback tag test
---
tests/baselines/reference/callbackTag1.symbols | 14 ++++++++++++++
tests/baselines/reference/callbackTag1.types | 16 ++++++++++++++++
2 files changed, 30 insertions(+)
diff --git a/tests/baselines/reference/callbackTag1.symbols b/tests/baselines/reference/callbackTag1.symbols
index 5bcc7fc540363..e95c48735db83 100644
--- a/tests/baselines/reference/callbackTag1.symbols
+++ b/tests/baselines/reference/callbackTag1.symbols
@@ -13,3 +13,17 @@ var sid = s => s + "!";
>s : Symbol(s, Decl(cb.js, 8, 9))
>s : Symbol(s, Decl(cb.js, 8, 9))
+
+/** @type {NoReturn} */
+var noreturn = obj => void obj.title
+>noreturn : Symbol(noreturn, Decl(cb.js, 12, 3))
+>obj : Symbol(obj, Decl(cb.js, 12, 14))
+>obj.title : Symbol(title, Decl(cb.js, 16, 34))
+>obj : Symbol(obj, Decl(cb.js, 12, 14))
+>title : Symbol(title, Decl(cb.js, 16, 34))
+
+/**
+ * @callback NoReturn
+ * @param {{ e: number, m: number, title: string }} s - Knee deep, shores, etc
+ */
+
diff --git a/tests/baselines/reference/callbackTag1.types b/tests/baselines/reference/callbackTag1.types
index 47b3394a76dd6..a1c99e2e7f25f 100644
--- a/tests/baselines/reference/callbackTag1.types
+++ b/tests/baselines/reference/callbackTag1.types
@@ -17,3 +17,19 @@ var sid = s => s + "!";
>s : string
>"!" : "!"
+
+/** @type {NoReturn} */
+var noreturn = obj => void obj.title
+>noreturn : (s: { e: number; m: number; title: string; }) => any
+>obj => void obj.title : (obj: { e: number; m: number; title: string; }) => any
+>obj : { e: number; m: number; title: string; }
+>void obj.title : undefined
+>obj.title : string
+>obj : { e: number; m: number; title: string; }
+>title : string
+
+/**
+ * @callback NoReturn
+ * @param {{ e: number, m: number, title: string }} s - Knee deep, shores, etc
+ */
+
From 7c6d66ed5d967b800965712daedd628a58ca2d5a Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 3 May 2018 14:14:52 -0700
Subject: [PATCH 25/49] Parse comments for typedef/callback+display param
comments
---
src/compiler/parser.ts | 19 +++++++++++++------
src/services/jsDoc.ts | 6 +++++-
src/services/utilities.ts | 1 +
...sCorrectly.typedefTagWithChildrenTags.json | 2 +-
.../fourslash/server/jsdocCallbackTag.ts | 18 +++++++++++++-----
.../cases/fourslash/server/jsdocTypedefTag.ts | 9 ++++++---
6 files changed, 39 insertions(+), 16 deletions(-)
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index ffa5cc74b28a4..3132d5aa50af5 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6465,10 +6465,10 @@ namespace ts {
tag = parseTypeTag(atToken, tagName);
break;
case "typedef":
- tag = parseTypedefTag(atToken, tagName);
+ tag = parseTypedefTag(atToken, tagName, indent);
break;
case "callback":
- tag = parseCallbackTag(atToken, tagName);
+ tag = parseCallbackTag(atToken, tagName, indent);
break;
default:
tag = parseUnknownTag(atToken, tagName);
@@ -6483,7 +6483,10 @@ namespace ts {
// a badly malformed tag should not be added to the list of tags
return;
}
- tag.comment = parseTagComments(indent + tag.end - tag.pos);
+ if (!tag.comment) {
+ // some tags, like typedef and callback, have already parsed their comments earlier
+ tag.comment = parseTagComments(indent + tag.end - tag.pos);
+ }
addTag(tag);
}
@@ -6724,7 +6727,7 @@ namespace ts {
return finishNode(tag);
}
- function parseTypedefTag(atToken: AtToken, tagName: Identifier): JSDocTypedefTag {
+ function parseTypedefTag(atToken: AtToken, tagName: Identifier, indent: number): JSDocTypedefTag {
const typeExpression = tryParseTypeExpression();
skipWhitespace();
@@ -6735,6 +6738,7 @@ namespace ts {
typedefTag.name = getJSDocTypeAliasName(typedefTag.fullName);
skipWhitespace();
+ typedefTag.comment = parseTagComments(indent);
typedefTag.typeExpression = typeExpression;
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
let child: JSDocTypeTag | JSDocPropertyTag | false;
@@ -6790,13 +6794,14 @@ namespace ts {
return typeNameOrNamespaceName;
}
- function parseCallbackTag(atToken: AtToken, tagName: Identifier): JSDocCallbackTag {
+ function parseCallbackTag(atToken: AtToken, tagName: Identifier, indent: number): JSDocCallbackTag {
const callbackTag = createNode(SyntaxKind.JSDocCallbackTag, atToken.pos) as JSDocCallbackTag;
callbackTag.atToken = atToken;
callbackTag.tagName = tagName;
callbackTag.fullName = parseJSDocTypeNameWithNamespace();
callbackTag.name = getJSDocTypeAliasName(callbackTag.fullName);
skipWhitespace();
+ callbackTag.comment = parseTagComments(indent);
let child: JSDocParameterTag | false;
const start = scanner.getStartPos();
@@ -6812,7 +6817,9 @@ namespace ts {
nextToken(); // why is this needed? Doesn't parseExpectedToken advance the token enough?
const name = parseJSDocIdentifierName();
if (name && (name.escapedText === "return" || name.escapedText === "returns")) {
- return parseReturnTag(atToken, name);
+ const returnTag = parseReturnTag(atToken, name);
+ returnTag.comment = parseTagComments(indent);
+ return returnTag;
}
}
});
diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts
index ca0dafbc74cb4..2a8cc2245ac0f 100644
--- a/src/services/jsDoc.ts
+++ b/src/services/jsDoc.ts
@@ -5,6 +5,7 @@ namespace ts.JsDoc {
"author",
"argument",
"borrows",
+ "callback",
"class",
"constant",
"constructor",
@@ -68,10 +69,12 @@ namespace ts.JsDoc {
function getCommentHavingNodes(declaration: Declaration): ReadonlyArray {
switch (declaration.kind) {
+ case SyntaxKind.JSDocParameterTag:
case SyntaxKind.JSDocPropertyTag:
return [declaration as JSDocPropertyTag];
+ case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.JSDocTypedefTag:
- return [(declaration as JSDocTypedefTag).parent];
+ return [(declaration as JSDocTypedefTag), (declaration as JSDocTypedefTag).parent];
default:
return getJSDocCommentsAndTags(declaration);
}
@@ -97,6 +100,7 @@ namespace ts.JsDoc {
return withList((tag as JSDocTemplateTag).typeParameters);
case SyntaxKind.JSDocTypeTag:
return withNode((tag as JSDocTypeTag).typeExpression);
+ // TODO: Handle callback here
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.JSDocPropertyTag:
case SyntaxKind.JSDocParameterTag:
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index 285d055f1665f..d257e86f602aa 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -270,6 +270,7 @@ namespace ts {
}
export function getContainerNode(node: Node): Declaration {
+ // TODO: Probably need to handle jsdoccallbacktag too
if (node.kind === SyntaxKind.JSDocTypedefTag) {
// This doesn't just apply to the node immediately under the comment, but to everything in its parent's scope.
// node.parent = the JSDoc comment, node.parent.parent = the node having the comment.
diff --git a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json
index a4a69fc48a6c4..98a59931ad8a5 100644
--- a/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json
+++ b/tests/baselines/reference/JSDocParsing/DocComments.parsesCorrectly.typedefTagWithChildrenTags.json
@@ -32,7 +32,7 @@
},
"typeExpression": {
"kind": "JSDocTypeLiteral",
- "pos": 26,
+ "pos": 28,
"end": 98,
"jsDocPropertyTags": [
{
diff --git a/tests/cases/fourslash/server/jsdocCallbackTag.ts b/tests/cases/fourslash/server/jsdocCallbackTag.ts
index 5663224dc6f38..b9a2fcdf3156d 100644
--- a/tests/cases/fourslash/server/jsdocCallbackTag.ts
+++ b/tests/cases/fourslash/server/jsdocCallbackTag.ts
@@ -2,16 +2,15 @@
// @allowNonTsExtensions: true
// @Filename: jsdocCallbackTag.js
-
//// /**
-//// * @callback FooHandler
+//// * @callback FooHandler - A kind of magic
//// * @param {string} eventName - So many words
//// * @param eventName2 {number | string} - Silence is golden
//// * @param eventName3 - Osterreich mos def
//// * @return {number} - DIVEKICK
//// */
//// /**
-//// * @type {FooHandler} callback
+//// * @type {FooHa/*8*/ndler} callback
//// */
//// var t/*1*/;
////
@@ -24,11 +23,20 @@
//// * @type {FooH/*3*/andler2} callback
//// */
//// var t2/*2*/;
-
+//// t(/*4*/"!", /*5*/12, /*6*/false);
goTo.marker("1");
verify.quickInfoIs("var t: (eventName: string, eventName2: string | number, eventName3: any) => number");
goTo.marker("2");
verify.quickInfoIs("var t2: (eventName?: string, eventName2?: string) => any");
goTo.marker("3");
-verify.quickInfoIs("type FooHandler2 = (eventName?: string, eventName2?: string) => any");
+verify.quickInfoIs("type FooHandler2 = (eventName?: string, eventName2?: string) => any", "- What, another one?");
+goTo.marker("8");
+verify.quickInfoIs("type FooHandler = (eventName: string, eventName2: string | number, eventName3: any) => number", "- A kind of magic");
+goTo.marker('4');
+verify.currentSignatureHelpIs("t(eventName: string, eventName2: string | number, eventName3: any): number");
+verify.currentParameterHelpArgumentDocCommentIs("- So many words");
+goTo.marker('5');
+verify.currentParameterHelpArgumentDocCommentIs("- Silence is golden");
+goTo.marker('6');
+verify.currentParameterHelpArgumentDocCommentIs("- Osterreich mos def");
diff --git a/tests/cases/fourslash/server/jsdocTypedefTag.ts b/tests/cases/fourslash/server/jsdocTypedefTag.ts
index 9e9801148353a..1ab7060456123 100644
--- a/tests/cases/fourslash/server/jsdocTypedefTag.ts
+++ b/tests/cases/fourslash/server/jsdocTypedefTag.ts
@@ -6,7 +6,7 @@
//// /** @typedef {(string | number)} NumberLike */
////
//// /**
-//// * @typedef Animal
+//// * @typedef Animal - think Giraffes
//// * @type {Object}
//// * @property {string} animalName
//// * @property {number} animalAge
@@ -36,7 +36,7 @@
//// p.personName./*personName*/;
//// p.personAge./*personAge*/;
////
-//// /** @type {Animal} */
+//// /** @type {/*AnimalType*/Animal} */
//// var a;a./*animal*/;
//// a.animalName./*animalName*/;
//// a.animalAge./*animalAge*/;
@@ -85,4 +85,7 @@ verify.completionListContains('catAge');
goTo.marker('catName');
verify.completionListContains('charAt');
goTo.marker('catAge');
-verify.completionListContains('toExponential');
\ No newline at end of file
+verify.completionListContains('toExponential');
+
+goTo.marker("AnimalType");
+verify.quickInfoIs("type Animal = {\n animalName: string;\n animalAge: number;\n}", "- think Giraffes");
From 5e02518aca15d2035f0552b066b9cabf9415321e Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 3 May 2018 14:31:15 -0700
Subject: [PATCH 26/49] Always export callbacks
This requires almost no new code since it is basically the same as
typedefs
---
src/compiler/binder.ts | 5 ++---
.../conformance/jsdoc/callbackCrossModule.ts | 22 +++++++++++++++++++
tests/cases/conformance/jsdoc/callbackTag1.ts | 1 -
3 files changed, 24 insertions(+), 4 deletions(-)
create mode 100644 tests/cases/conformance/jsdoc/callbackCrossModule.ts
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 54b5cd0a0e51d..4c7a391230267 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -450,9 +450,8 @@ namespace ts {
// during global merging in the checker. Why? The only case when ambient module is permitted inside another module is module augmentation
// and this case is specially handled. Module augmentations should only be merged with original module definition
// and should never be merged directly with other augmentation, and the latter case would be possible if automatic merge is allowed.
- // TODO: Handle callbacks here too
- if (node.kind === SyntaxKind.JSDocTypedefTag) Debug.assert(isInJavaScriptFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
- if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypedefTag(node)) {
+ if (isJSDocTypeAlias(node)) Debug.assert(isInJavaScriptFile(node)); // We shouldn't add symbols for JSDoc nodes if not in a JS file.
+ if ((!isAmbientModule(node) && (hasExportModifier || container.flags & NodeFlags.ExportContext)) || isJSDocTypeAlias(node)) {
if (hasModifier(node, ModifierFlags.Default) && !getDeclarationName(node)) {
return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes); // No local symbol for an unnamed default!
}
diff --git a/tests/cases/conformance/jsdoc/callbackCrossModule.ts b/tests/cases/conformance/jsdoc/callbackCrossModule.ts
new file mode 100644
index 0000000000000..a2888f3627fee
--- /dev/null
+++ b/tests/cases/conformance/jsdoc/callbackCrossModule.ts
@@ -0,0 +1,22 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: mod1.js
+/** @callback Con - some kind of continuation
+ * @param {object | undefined} error
+ * @return {any} I don't even know what this should return
+ */
+module.exports = C
+function C() {
+ this.p = 1
+}
+
+// @Filename: use.js
+/** @param {import('./mod1').Con} k */
+function f(k) {
+ if (1 === 2 - 1) {
+ // I guess basic math works!
+ }
+ return k({ ok: true})
+}
+
diff --git a/tests/cases/conformance/jsdoc/callbackTag1.ts b/tests/cases/conformance/jsdoc/callbackTag1.ts
index 510a03850104f..63a90e74ff88d 100644
--- a/tests/cases/conformance/jsdoc/callbackTag1.ts
+++ b/tests/cases/conformance/jsdoc/callbackTag1.ts
@@ -3,7 +3,6 @@
// @checkJs: true
// @Filename: cb.js
-// TODO: Test nested param types using the nested Object style
/** @callback Sid
* @param {string} s
* @returns {string} What were you expecting
From 12bd83dc924658e1ed1c3f987d0231d6194d4c51 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 3 May 2018 14:36:00 -0700
Subject: [PATCH 27/49] Update baselines
---
.../reference/callbackCrossModule.symbols | 32 +++++++++++++
.../reference/callbackCrossModule.types | 47 +++++++++++++++++++
.../baselines/reference/callbackTag1.symbols | 19 ++++----
tests/baselines/reference/callbackTag1.types | 1 -
4 files changed, 88 insertions(+), 11 deletions(-)
create mode 100644 tests/baselines/reference/callbackCrossModule.symbols
create mode 100644 tests/baselines/reference/callbackCrossModule.types
diff --git a/tests/baselines/reference/callbackCrossModule.symbols b/tests/baselines/reference/callbackCrossModule.symbols
new file mode 100644
index 0000000000000..be8175c4a1157
--- /dev/null
+++ b/tests/baselines/reference/callbackCrossModule.symbols
@@ -0,0 +1,32 @@
+=== tests/cases/conformance/jsdoc/mod1.js ===
+/** @callback Con - some kind of continuation
+ * @param {object | undefined} error
+ * @return {any} I don't even know what this should return
+ */
+module.exports = C
+>module : Symbol(export=, Decl(mod1.js, 0, 0))
+>exports : Symbol(export=, Decl(mod1.js, 0, 0))
+>C : Symbol(C, Decl(mod1.js, 4, 18))
+
+function C() {
+>C : Symbol(C, Decl(mod1.js, 4, 18))
+
+ this.p = 1
+>p : Symbol(C.p, Decl(mod1.js, 5, 14))
+}
+
+=== tests/cases/conformance/jsdoc/use.js ===
+/** @param {import('./mod1').Con} k */
+function f(k) {
+>f : Symbol(f, Decl(use.js, 0, 0))
+>k : Symbol(k, Decl(use.js, 1, 11))
+
+ if (1 === 2 - 1) {
+ // I guess basic math works!
+ }
+ return k({ ok: true})
+>k : Symbol(k, Decl(use.js, 1, 11))
+>ok : Symbol(ok, Decl(use.js, 5, 14))
+}
+
+
diff --git a/tests/baselines/reference/callbackCrossModule.types b/tests/baselines/reference/callbackCrossModule.types
new file mode 100644
index 0000000000000..d1d970c555e7d
--- /dev/null
+++ b/tests/baselines/reference/callbackCrossModule.types
@@ -0,0 +1,47 @@
+=== tests/cases/conformance/jsdoc/mod1.js ===
+/** @callback Con - some kind of continuation
+ * @param {object | undefined} error
+ * @return {any} I don't even know what this should return
+ */
+module.exports = C
+>module.exports = C : typeof C
+>module.exports : any
+>module : any
+>exports : any
+>C : typeof C
+
+function C() {
+>C : typeof C
+
+ this.p = 1
+>this.p = 1 : 1
+>this.p : any
+>this : any
+>p : any
+>1 : 1
+}
+
+=== tests/cases/conformance/jsdoc/use.js ===
+/** @param {import('./mod1').Con} k */
+function f(k) {
+>f : (k: (error: any) => any) => any
+>k : (error: any) => any
+
+ if (1 === 2 - 1) {
+>1 === 2 - 1 : boolean
+>1 : 1
+>2 - 1 : number
+>2 : 2
+>1 : 1
+
+ // I guess basic math works!
+ }
+ return k({ ok: true})
+>k({ ok: true}) : any
+>k : (error: any) => any
+>{ ok: true} : { ok: boolean; }
+>ok : boolean
+>true : true
+}
+
+
diff --git a/tests/baselines/reference/callbackTag1.symbols b/tests/baselines/reference/callbackTag1.symbols
index e95c48735db83..b52ac8f92c8ab 100644
--- a/tests/baselines/reference/callbackTag1.symbols
+++ b/tests/baselines/reference/callbackTag1.symbols
@@ -1,26 +1,25 @@
=== tests/cases/conformance/jsdoc/cb.js ===
-// TODO: Test nested param types using the nested Object style
/** @callback Sid
* @param {string} s
* @returns {string} What were you expecting
*/
var x = 1
->x : Symbol(x, Decl(cb.js, 5, 3))
+>x : Symbol(x, Decl(cb.js, 4, 3))
/** @type {Sid} smallId */
var sid = s => s + "!";
->sid : Symbol(sid, Decl(cb.js, 8, 3))
->s : Symbol(s, Decl(cb.js, 8, 9))
->s : Symbol(s, Decl(cb.js, 8, 9))
+>sid : Symbol(sid, Decl(cb.js, 7, 3))
+>s : Symbol(s, Decl(cb.js, 7, 9))
+>s : Symbol(s, Decl(cb.js, 7, 9))
/** @type {NoReturn} */
var noreturn = obj => void obj.title
->noreturn : Symbol(noreturn, Decl(cb.js, 12, 3))
->obj : Symbol(obj, Decl(cb.js, 12, 14))
->obj.title : Symbol(title, Decl(cb.js, 16, 34))
->obj : Symbol(obj, Decl(cb.js, 12, 14))
->title : Symbol(title, Decl(cb.js, 16, 34))
+>noreturn : Symbol(noreturn, Decl(cb.js, 11, 3))
+>obj : Symbol(obj, Decl(cb.js, 11, 14))
+>obj.title : Symbol(title, Decl(cb.js, 15, 34))
+>obj : Symbol(obj, Decl(cb.js, 11, 14))
+>title : Symbol(title, Decl(cb.js, 15, 34))
/**
* @callback NoReturn
diff --git a/tests/baselines/reference/callbackTag1.types b/tests/baselines/reference/callbackTag1.types
index a1c99e2e7f25f..759f36640115c 100644
--- a/tests/baselines/reference/callbackTag1.types
+++ b/tests/baselines/reference/callbackTag1.types
@@ -1,5 +1,4 @@
=== tests/cases/conformance/jsdoc/cb.js ===
-// TODO: Test nested param types using the nested Object style
/** @callback Sid
* @param {string} s
* @returns {string} What were you expecting
From dafb67d55f8447358b4d95b816de995d94485fe3 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 3 May 2018 14:47:00 -0700
Subject: [PATCH 28/49] Fix support for nested namespaced callbacks
And add test
---
src/compiler/binder.ts | 4 ++--
.../reference/callbackTagNamespace.symbols | 19 +++++++++++++++++
.../reference/callbackTagNamespace.types | 21 +++++++++++++++++++
.../conformance/jsdoc/callbackTagNamespace.ts | 15 +++++++++++++
4 files changed, 57 insertions(+), 2 deletions(-)
create mode 100644 tests/baselines/reference/callbackTagNamespace.symbols
create mode 100644 tests/baselines/reference/callbackTagNamespace.types
create mode 100644 tests/cases/conformance/jsdoc/callbackTagNamespace.ts
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 4c7a391230267..6b619082e9def 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2033,10 +2033,10 @@ namespace ts {
// current "blockScopeContainer" needs to be set to its immediate namespace parent.
if ((node).isInJSDocNamespace) {
let parentNode = node.parent;
- while (parentNode && parentNode.kind !== SyntaxKind.JSDocTypedefTag) {
+ while (parentNode && !isJSDocTypeAlias(parentNode)) {
parentNode = parentNode.parent;
}
- bindBlockScopedDeclaration(parentNode, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
+ bindBlockScopedDeclaration(parentNode as Declaration, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
break;
}
// falls through
diff --git a/tests/baselines/reference/callbackTagNamespace.symbols b/tests/baselines/reference/callbackTagNamespace.symbols
new file mode 100644
index 0000000000000..b8cf7d9588638
--- /dev/null
+++ b/tests/baselines/reference/callbackTagNamespace.symbols
@@ -0,0 +1,19 @@
+=== tests/cases/conformance/jsdoc/namespaced.js ===
+/**
+ * @callback NS.Nested.Inner
+ * @param {string} space - spaaaaaaaaace
+ * @param {string} peace - peaaaaaaaaace
+ * @return {string | number}
+ */
+var x = 1;
+>x : Symbol(x, Decl(namespaced.js, 6, 3))
+
+/** @type {NS.Nested.Inner} */
+function f(space, peace) {
+>f : Symbol(f, Decl(namespaced.js, 6, 10))
+>space : Symbol(space, Decl(namespaced.js, 8, 11))
+>peace : Symbol(peace, Decl(namespaced.js, 8, 17))
+
+ return '1'
+}
+
diff --git a/tests/baselines/reference/callbackTagNamespace.types b/tests/baselines/reference/callbackTagNamespace.types
new file mode 100644
index 0000000000000..0435320795dcd
--- /dev/null
+++ b/tests/baselines/reference/callbackTagNamespace.types
@@ -0,0 +1,21 @@
+=== tests/cases/conformance/jsdoc/namespaced.js ===
+/**
+ * @callback NS.Nested.Inner
+ * @param {string} space - spaaaaaaaaace
+ * @param {string} peace - peaaaaaaaaace
+ * @return {string | number}
+ */
+var x = 1;
+>x : number
+>1 : 1
+
+/** @type {NS.Nested.Inner} */
+function f(space, peace) {
+>f : (space: string, peace: string) => string
+>space : string
+>peace : string
+
+ return '1'
+>'1' : "1"
+}
+
diff --git a/tests/cases/conformance/jsdoc/callbackTagNamespace.ts b/tests/cases/conformance/jsdoc/callbackTagNamespace.ts
new file mode 100644
index 0000000000000..ba331e79fc428
--- /dev/null
+++ b/tests/cases/conformance/jsdoc/callbackTagNamespace.ts
@@ -0,0 +1,15 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: namespaced.js
+/**
+ * @callback NS.Nested.Inner
+ * @param {string} space - spaaaaaaaaace
+ * @param {string} peace - peaaaaaaaaace
+ * @return {string | number}
+ */
+var x = 1;
+/** @type {NS.Nested.Inner} */
+function f(space, peace) {
+ return '1'
+}
From 9600db701e5796412be1df7676c6c1209e911752 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 4 May 2018 14:03:19 -0700
Subject: [PATCH 29/49] Callbacks support type parameters
1. Haven't run it with all tests
2. Haven't tested typedef tags yet
3. Still allows shared symbols when on function or class declarations.
---
src/compiler/binder.ts | 6 ++-
src/compiler/checker.ts | 34 +++++++++++-----
src/compiler/types.ts | 2 +-
src/compiler/utilities.ts | 28 ++++++++-----
tests/cases/conformance/jsdoc/callbackTag2.ts | 40 +++++++++++++++++++
5 files changed, 85 insertions(+), 25 deletions(-)
create mode 100644 tests/cases/conformance/jsdoc/callbackTag2.ts
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 6b619082e9def..22cfcd0dfea69 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1990,7 +1990,7 @@ namespace ts {
}
for (const tag of jsDoc.tags) {
- if (isJSDocTypeAlias(tag)) {
+ if (isJSDocTypeAlias(tag) || isJSDocTemplateTag(tag)) {
const savedParent = parent;
parent = jsDoc;
bind(tag);
@@ -2202,6 +2202,8 @@ namespace ts {
case SyntaxKind.ModuleBlock:
return updateStrictModeStatementList((node).statements);
+ case SyntaxKind.JSDocTemplateTag:
+ return forEach((node as JSDocTemplateTag).typeParameters, bindTypeParameter);
case SyntaxKind.JSDocParameterTag:
if (node.parent.kind === SyntaxKind.JSDocSignature) {
return bindParameter(node as JSDocParameterTag);
@@ -2699,7 +2701,7 @@ namespace ts {
}
function bindTypeParameter(node: TypeParameterDeclaration) {
- if (node.parent.kind === SyntaxKind.InferType) {
+ if (node.parent && node.parent.kind === SyntaxKind.InferType) {
const container = getInferTypeContainer(node.parent);
if (container) {
if (!container.locals) {
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index cc089a193a632..347c9035f2cd2 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1367,6 +1367,16 @@ namespace ts {
}
}
break;
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
+ // TODO: These might be stored elsewhere already if we can get the symbol
+ if (meaning & SymbolFlags.Type) {
+ const param = find(getEffectiveTypeParameterDeclarations(location as JSDocTypedefTag), param => param.name.escapedText === name);
+ if (result = getSymbolOfNode(param)) {
+ break loop;
+ }
+ }
+ break;
case SyntaxKind.ExpressionWithTypeArguments:
// The type parameters of a class are not in scope in the base class expression.
if (lastLocation === (location).expression && (location.parent).token === SyntaxKind.ExtendsKeyword) {
@@ -5065,6 +5075,8 @@ namespace ts {
case SyntaxKind.ArrowFunction:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.JSDocTemplateTag:
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.MappedType:
case SyntaxKind.ConditionalType:
const outerTypeParameters = getOuterTypeParameters(node, includeThisTypes);
@@ -5094,10 +5106,10 @@ namespace ts {
function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] {
let result: TypeParameter[];
for (const node of symbol.declarations) {
- // TODO: needs to understand jsdoc typedefs and callbacks
if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration ||
- node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration) {
- const declaration = node;
+ node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration ||
+ isJSDocTypeAlias(node)) {
+ const declaration = node;
const typeParameters = getEffectiveTypeParameterDeclarations(declaration);
if (typeParameters) {
result = appendTypeParameters(result, typeParameters);
@@ -7122,7 +7134,7 @@ namespace ts {
const classType = declaration.kind === SyntaxKind.Constructor ?
getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol))
: undefined;
- const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration);
+ const typeParameters = classType ? classType.localTypeParameters : isJSDocSignature(declaration) ? undefined : getTypeParametersFromDeclaration(declaration);
const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType);
const hasRestLikeParameter = hasRestParameter(declaration) || isInJavaScriptFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters);
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, /*resolvedTypePredicate*/ undefined, minArgumentCount, hasRestLikeParameter, hasLiteralTypes);
@@ -7584,11 +7596,11 @@ namespace ts {
const missingAugmentsTag = isJs && node.parent.kind !== SyntaxKind.JSDocAugmentsTag;
const diag = minTypeArgumentCount === typeParameters.length
? missingAugmentsTag
- ? Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag
- : Diagnostics.Generic_type_0_requires_1_type_argument_s
- : missingAugmentsTag
- ? Diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag
- : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments;
+ ? Diagnostics.Expected_0_type_arguments_provide_these_with_an_extends_tag
+ : Diagnostics.Generic_type_0_requires_1_type_argument_s
+ : missingAugmentsTag
+ ? Diagnostics.Expected_0_1_type_arguments_provide_these_with_an_extends_tag
+ : Diagnostics.Generic_type_0_requires_between_1_and_2_type_arguments;
const typeStr = typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrayAsGenericType);
error(node, diag, typeStr, minTypeArgumentCount, typeParameters.length);
if (!isJs) {
@@ -7603,7 +7615,7 @@ namespace ts {
return createTypeReference(type, typeArguments);
}
return checkNoTypeArguments(node, symbol) ? type : unknownType;
- }
+ }
function getTypeAliasInstantiation(symbol: Symbol, typeArguments: Type[]): Type {
const type = getDeclaredTypeOfSymbol(symbol);
@@ -7757,7 +7769,7 @@ namespace ts {
function getConstrainedTypeVariable(typeVariable: TypeVariable, node: Node) {
let constraints: Type[];
- while (node && !isStatement(node)) {
+ while (node && !isStatement(node) && node.kind !== SyntaxKind.JSDocComment) {
const parent = node.parent;
if (parent.kind === SyntaxKind.ConditionalType && node === (parent).trueType) {
const constraint = getImpliedConstraint(typeVariable, (parent).checkType, (parent).extendsType);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 0572106f94649..3bfa05017d29b 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2053,7 +2053,7 @@ namespace ts {
export type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypeLiteral | JSDocSignature;
+ export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag;
export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 511c777085792..5fd04b9c94cfe 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -527,10 +527,9 @@ namespace ts {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
return node.typeParameters;
- case SyntaxKind.JSDocSignature:
- case SyntaxKind.JSDocTypeLiteral:
- // TODO: Actually go find them!
- return undefined;
+ case SyntaxKind.JSDocCallbackTag:
+ case SyntaxKind.JSDocTypedefTag:
+ return getEffectiveTypeParameterDeclarations(node);
default:
assertTypeIsNever(node);
return undefined;
@@ -559,8 +558,8 @@ namespace ts {
case SyntaxKind.SetAccessor:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
- case SyntaxKind.JSDocSignature:
- case SyntaxKind.JSDocTypeLiteral:
+ case SyntaxKind.JSDocCallbackTag:
+ case SyntaxKind.JSDocTypedefTag:
return true;
default:
assertTypeIsNever(node);
@@ -1930,8 +1929,8 @@ namespace ts {
}
export function getJSDocHost(node: JSDocTag): HasJSDoc {
- while (node.parent.kind === SyntaxKind.JSDocTypeLiteral) {
- if (node.parent.parent.kind === SyntaxKind.JSDocTypedefTag) {
+ while (isJSDocTypeLiteral(node.parent) || isJSDocSignature(node.parent)) {
+ if (isJSDocTypeAlias(node.parent.parent)) {
node = node.parent.parent as JSDocTypedefTag;
}
else {
@@ -3107,9 +3106,16 @@ namespace ts {
* Gets the effective type parameters. If the node was parsed in a
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
*/
- export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
- // TODO: Make getJSDocTypeParameterDeclarations do the right thing
- if (isJSDocTypeLiteral(node) || isJSDocSignature(node)) return undefined;
+ export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): NodeArray {
+ if (isJSDocTypeAlias(node)) {
+ Debug.assert(node.parent.kind === SyntaxKind.JSDocComment);
+ const templateTags = flatMap(filter(node.parent.tags, isJSDocTemplateTag), tag => tag.typeParameters) as ReadonlyArray;
+ const templateTagNodes = templateTags as NodeArray;
+ templateTagNodes.pos = templateTagNodes.length > 0 ? first(templateTagNodes).pos : node.pos;
+ templateTagNodes.end = templateTagNodes.length > 0 ? last(templateTagNodes).end : node.end;
+ templateTagNodes.hasTrailingComma = false;
+ return templateTagNodes;
+ }
return node.typeParameters || (isInJavaScriptFile(node) ? getJSDocTypeParameterDeclarations(node) : undefined);
}
diff --git a/tests/cases/conformance/jsdoc/callbackTag2.ts b/tests/cases/conformance/jsdoc/callbackTag2.ts
new file mode 100644
index 0000000000000..0ce50ca8709e0
--- /dev/null
+++ b/tests/cases/conformance/jsdoc/callbackTag2.ts
@@ -0,0 +1,40 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: cb.js
+
+/** @template T
+ * @callback Id
+ * @param {T} t
+ * @returns {T} Maybe just return 120 and cast it?
+ */
+var x = 1
+
+/** @type {Id} I actually wanted to write `const "120"` */
+var one_twenty = s => "120";
+
+/** @template S
+ * @callback SharedId
+ * @param {S} ego
+ * @return {S}
+ */
+class SharedClass {
+ constructor() {
+ /** @type {SharedId} */
+ this.id;
+ }
+}
+/** @type {SharedId} */
+var outside = n => n + 1;
+
+/** @type {Final<{ fantasy }, { heroes }>} */
+var noreturn = (barts, tidus, noctis) => "cecil"
+
+/**
+ * @template V,X
+ * @callback Final
+ * @param {V} barts - "Barts"
+ * @param {X} tidus - Titus
+ * @param {X & V} noctis - "... Whatever"
+ * @return {"cecil" | "zidane"}
+ */
From 0c28218fbc50cacfad7c82b8e6ae44b8a17c234a Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 10:10:19 -0700
Subject: [PATCH 30/49] Template tags are now bound correctly
---
src/compiler/binder.ts | 22 ++++++-
src/compiler/checker.ts | 24 ++++----
src/compiler/utilities.ts | 27 ++++-----
.../reference/api/tsserverlibrary.d.ts | 4 +-
tests/baselines/reference/api/typescript.d.ts | 4 +-
.../reference/callbackTag2.errors.txt | 42 +++++++++++++
.../baselines/reference/callbackTag2.symbols | 52 ++++++++++++++++
tests/baselines/reference/callbackTag2.types | 60 +++++++++++++++++++
.../reference/jsdocTemplateClass.errors.txt | 5 +-
.../reference/jsdocTemplateClass.symbols | 41 ++++++-------
.../reference/jsdocTemplateClass.types | 3 +-
...sdocTemplateConstructorFunction.errors.txt | 11 ++--
.../jsdocTemplateConstructorFunction.symbols | 53 ++++++++--------
.../jsdocTemplateConstructorFunction.types | 17 +++---
tests/cases/conformance/jsdoc/callbackTag2.ts | 2 +-
.../conformance/jsdoc/jsdocTemplateClass.ts | 3 +-
.../jsdoc/jsdocTemplateConstructorFunction.ts | 9 ++-
17 files changed, 283 insertions(+), 96 deletions(-)
create mode 100644 tests/baselines/reference/callbackTag2.errors.txt
create mode 100644 tests/baselines/reference/callbackTag2.symbols
create mode 100644 tests/baselines/reference/callbackTag2.types
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 22cfcd0dfea69..49fc73eea52d6 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1376,7 +1376,7 @@ namespace ts {
function bindJSDocComment(node: JSDoc) {
forEachChild(node, n => {
- if (!isJSDocTypeAlias(n)) {
+ if (!isJSDocTypeAlias(n) && !getTypeAliasForJSDocTemplateTag(n, node.tags)) {
bind(n);
}
});
@@ -1552,6 +1552,8 @@ namespace ts {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
case SyntaxKind.JSDocFunctionType:
+ case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.TypeAliasDeclaration:
case SyntaxKind.MappedType:
// All the children of these container types are never visible through another
@@ -1990,16 +1992,32 @@ namespace ts {
}
for (const tag of jsDoc.tags) {
- if (isJSDocTypeAlias(tag) || isJSDocTemplateTag(tag)) {
+ if (isJSDocTypeAlias(tag)) {
const savedParent = parent;
parent = jsDoc;
bind(tag);
parent = savedParent;
}
+ const alias = getTypeAliasForJSDocTemplateTag(tag, jsDoc.tags);
+ if (alias) {
+ // find typedef or callback and set that to the container (TODO:manually, which is kind of a bad idea)
+ const savedContainer = container;
+ const savedParent = parent;
+ container = alias;
+ parent = jsDoc;
+ alias.locals = alias.locals || createSymbolTable();
+ bind(tag);
+ container = savedContainer;
+ parent = savedParent;
+ }
}
}
}
+ function getTypeAliasForJSDocTemplateTag(tag: Node, siblings: NodeArray) {
+ return isJSDocTemplateTag(tag) && find(siblings, isJSDocTypeAlias);
+ }
+
function updateStrictModeStatementList(statements: NodeArray) {
if (!inStrictMode) {
for (const statement of statements) {
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 347c9035f2cd2..eaf66e5c71004 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1367,16 +1367,6 @@ namespace ts {
}
}
break;
- case SyntaxKind.JSDocTypedefTag:
- case SyntaxKind.JSDocCallbackTag:
- // TODO: These might be stored elsewhere already if we can get the symbol
- if (meaning & SymbolFlags.Type) {
- const param = find(getEffectiveTypeParameterDeclarations(location as JSDocTypedefTag), param => param.name.escapedText === name);
- if (result = getSymbolOfNode(param)) {
- break loop;
- }
- }
- break;
case SyntaxKind.ExpressionWithTypeArguments:
// The type parameters of a class are not in scope in the base class expression.
if (lastLocation === (location).expression && (location.parent).token === SyntaxKind.ExtendsKeyword) {
@@ -1581,7 +1571,12 @@ namespace ts {
for (const decl of symbol.declarations) {
const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
if (decl.kind === SyntaxKind.TypeParameter && parent === container) {
- return true;
+ if (isJSDocTemplateTag(decl.parent)) {
+ return !find((decl.parent.parent as JSDoc).tags, isJSDocTypeAlias);
+ }
+ else {
+ return true;
+ }
}
}
@@ -2151,9 +2146,10 @@ namespace ts {
* name resolution won't work either.
*/
function resolveEntityNameFromJSPrototype(name: Identifier, meaning: SymbolFlags) {
- if (isJSDocTypeReference(name.parent) && isJSDocTag(name.parent.parent.parent)) {
- const host = getJSDocHost(name.parent.parent.parent as JSDocTag);
- if (isExpressionStatement(host) &&
+ if (isJSDocTypeReference(name.parent)) {
+ const host = getJSDocHost(name.parent);
+ if (host &&
+ isExpressionStatement(host) &&
isBinaryExpression(host.expression) &&
getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
const symbol = getSymbolOfNode(host.expression.left);
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 5fd04b9c94cfe..2ff837b9a2152 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1928,18 +1928,12 @@ namespace ts {
return decl && isFunctionLike(decl) ? decl : undefined;
}
- export function getJSDocHost(node: JSDocTag): HasJSDoc {
- while (isJSDocTypeLiteral(node.parent) || isJSDocSignature(node.parent)) {
- if (isJSDocTypeAlias(node.parent.parent)) {
- node = node.parent.parent as JSDocTypedefTag;
- }
- else {
- // node.parent.parent is a type expression, child of a parameter type
- node = node.parent.parent.parent as JSDocParameterTag;
- }
+ export function getJSDocHost(node: Node): HasJSDoc {
+ const comment = findAncestor(node.parent,
+ node => !(isJSDocNode(node) || node.flags & NodeFlags.JSDoc) ? "quit" : node.kind === SyntaxKind.JSDocComment);
+ if (comment) {
+ return (comment as JSDoc).parent;
}
- Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment);
- return node.parent!.parent!;
}
export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined {
@@ -3120,8 +3114,12 @@ namespace ts {
}
export function getJSDocTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
- const templateTag = getJSDocTemplateTag(node);
- return templateTag && templateTag.typeParameters;
+ const tags = filter(getJSDocTags(node), isJSDocTemplateTag);
+ for (const tag of tags) {
+ if (!(tag.parent.kind === SyntaxKind.JSDocComment && find(tag.parent.tags, isJSDocTypeAlias))) {
+ return tag.typeParameters;
+ }
+ }
}
/**
@@ -4804,7 +4802,8 @@ namespace ts {
}
/** Get the first JSDoc tag of a specified kind, or undefined if not present. */
- function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined {
+ export function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined {
+ // TODO: This shouldn't need to be exported, I think
return find(getJSDocTags(node), predicate);
}
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 3b07c3f0211d0..d37af6f07d344 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -1279,7 +1279,7 @@ declare namespace ts {
block: Block;
}
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypeLiteral | JSDocSignature;
+ type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag;
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
@@ -3148,6 +3148,8 @@ declare namespace ts {
function getJSDocReturnType(node: Node): TypeNode | undefined;
/** Get all JSDoc tags related to a node, including those on parent nodes. */
function getJSDocTags(node: Node): ReadonlyArray;
+ /** Get the first JSDoc tag of a specified kind, or undefined if not present. */
+ function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined;
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray;
}
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index f38008ea77afb..addb601009c17 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -1279,7 +1279,7 @@ declare namespace ts {
block: Block;
}
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypeLiteral | JSDocSignature;
+ type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag;
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
@@ -3148,6 +3148,8 @@ declare namespace ts {
function getJSDocReturnType(node: Node): TypeNode | undefined;
/** Get all JSDoc tags related to a node, including those on parent nodes. */
function getJSDocTags(node: Node): ReadonlyArray;
+ /** Get the first JSDoc tag of a specified kind, or undefined if not present. */
+ function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined;
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray;
}
diff --git a/tests/baselines/reference/callbackTag2.errors.txt b/tests/baselines/reference/callbackTag2.errors.txt
new file mode 100644
index 0000000000000..8be9e90102046
--- /dev/null
+++ b/tests/baselines/reference/callbackTag2.errors.txt
@@ -0,0 +1,42 @@
+tests/cases/conformance/jsdoc/cb.js(18,29): error TS2304: Cannot find name 'S'.
+
+
+==== tests/cases/conformance/jsdoc/cb.js (1 errors) ====
+ /** @template T
+ * @callback Id
+ * @param {T} t
+ * @returns {T} Maybe just return 120 and cast it?
+ */
+ var x = 1
+
+ /** @type {Id} I actually wanted to write `const "120"` */
+ var one_twenty = s => "120";
+
+ /** @template S
+ * @callback SharedId
+ * @param {S} ego
+ * @return {S}
+ */
+ class SharedClass {
+ constructor() {
+ /** @type {SharedId} */
+ ~
+!!! error TS2304: Cannot find name 'S'.
+ this.id;
+ }
+ }
+ /** @type {SharedId} */
+ var outside = n => n + 1;
+
+ /** @type {Final<{ fantasy }, { heroes }>} */
+ var noreturn = (barts, tidus, noctis) => "cecil"
+
+ /**
+ * @template V,X
+ * @callback Final
+ * @param {V} barts - "Barts"
+ * @param {X} tidus - Titus
+ * @param {X & V} noctis - "Prince Noctis Lucius Caelum"
+ * @return {"cecil" | "zidane"}
+ */
+
\ No newline at end of file
diff --git a/tests/baselines/reference/callbackTag2.symbols b/tests/baselines/reference/callbackTag2.symbols
new file mode 100644
index 0000000000000..36b810de46566
--- /dev/null
+++ b/tests/baselines/reference/callbackTag2.symbols
@@ -0,0 +1,52 @@
+=== tests/cases/conformance/jsdoc/cb.js ===
+/** @template T
+ * @callback Id
+ * @param {T} t
+ * @returns {T} Maybe just return 120 and cast it?
+ */
+var x = 1
+>x : Symbol(x, Decl(cb.js, 5, 3))
+
+/** @type {Id} I actually wanted to write `const "120"` */
+var one_twenty = s => "120";
+>one_twenty : Symbol(one_twenty, Decl(cb.js, 8, 3))
+>s : Symbol(s, Decl(cb.js, 8, 16))
+
+/** @template S
+ * @callback SharedId
+ * @param {S} ego
+ * @return {S}
+ */
+class SharedClass {
+>SharedClass : Symbol(SharedClass, Decl(cb.js, 8, 28))
+
+ constructor() {
+ /** @type {SharedId} */
+ this.id;
+>this.id : Symbol(SharedClass.id, Decl(cb.js, 16, 19))
+>this : Symbol(SharedClass, Decl(cb.js, 8, 28))
+>id : Symbol(SharedClass.id, Decl(cb.js, 16, 19))
+ }
+}
+/** @type {SharedId} */
+var outside = n => n + 1;
+>outside : Symbol(outside, Decl(cb.js, 22, 3))
+>n : Symbol(n, Decl(cb.js, 22, 13))
+>n : Symbol(n, Decl(cb.js, 22, 13))
+
+/** @type {Final<{ fantasy }, { heroes }>} */
+var noreturn = (barts, tidus, noctis) => "cecil"
+>noreturn : Symbol(noreturn, Decl(cb.js, 25, 3))
+>barts : Symbol(barts, Decl(cb.js, 25, 16))
+>tidus : Symbol(tidus, Decl(cb.js, 25, 22))
+>noctis : Symbol(noctis, Decl(cb.js, 25, 29))
+
+/**
+ * @template V,X
+ * @callback Final
+ * @param {V} barts - "Barts"
+ * @param {X} tidus - Titus
+ * @param {X & V} noctis - "Prince Noctis Lucius Caelum"
+ * @return {"cecil" | "zidane"}
+ */
+
diff --git a/tests/baselines/reference/callbackTag2.types b/tests/baselines/reference/callbackTag2.types
new file mode 100644
index 0000000000000..a6825f67524f3
--- /dev/null
+++ b/tests/baselines/reference/callbackTag2.types
@@ -0,0 +1,60 @@
+=== tests/cases/conformance/jsdoc/cb.js ===
+/** @template T
+ * @callback Id
+ * @param {T} t
+ * @returns {T} Maybe just return 120 and cast it?
+ */
+var x = 1
+>x : number
+>1 : 1
+
+/** @type {Id} I actually wanted to write `const "120"` */
+var one_twenty = s => "120";
+>one_twenty : (t: string) => string
+>s => "120" : (s: string) => string
+>s : string
+>"120" : "120"
+
+/** @template S
+ * @callback SharedId
+ * @param {S} ego
+ * @return {S}
+ */
+class SharedClass {
+>SharedClass : SharedClass
+
+ constructor() {
+ /** @type {SharedId} */
+ this.id;
+>this.id : (ego: any) => any
+>this : this
+>id : (ego: any) => any
+ }
+}
+/** @type {SharedId} */
+var outside = n => n + 1;
+>outside : (ego: number) => number
+>n => n + 1 : (n: number) => number
+>n : number
+>n + 1 : number
+>n : number
+>1 : 1
+
+/** @type {Final<{ fantasy }, { heroes }>} */
+var noreturn = (barts, tidus, noctis) => "cecil"
+>noreturn : (barts: { fantasy: any; }, tidus: { heroes: any; }, noctis: { heroes: any; } & { fantasy: any; }) => "cecil" | "zidane"
+>(barts, tidus, noctis) => "cecil" : (barts: { fantasy: any; }, tidus: { heroes: any; }, noctis: { heroes: any; } & { fantasy: any; }) => "cecil"
+>barts : { fantasy: any; }
+>tidus : { heroes: any; }
+>noctis : { heroes: any; } & { fantasy: any; }
+>"cecil" : "cecil"
+
+/**
+ * @template V,X
+ * @callback Final
+ * @param {V} barts - "Barts"
+ * @param {X} tidus - Titus
+ * @param {X & V} noctis - "Prince Noctis Lucius Caelum"
+ * @return {"cecil" | "zidane"}
+ */
+
diff --git a/tests/baselines/reference/jsdocTemplateClass.errors.txt b/tests/baselines/reference/jsdocTemplateClass.errors.txt
index 4598e9f320f6d..402992c34bdc9 100644
--- a/tests/baselines/reference/jsdocTemplateClass.errors.txt
+++ b/tests/baselines/reference/jsdocTemplateClass.errors.txt
@@ -1,4 +1,4 @@
-tests/cases/conformance/jsdoc/templateTagOnClasses.js(24,1): error TS2322: Type 'boolean' is not assignable to type 'number'.
+tests/cases/conformance/jsdoc/templateTagOnClasses.js(25,1): error TS2322: Type 'boolean' is not assignable to type 'number'.
==== tests/cases/conformance/jsdoc/templateTagOnClasses.js (1 errors) ====
@@ -6,6 +6,7 @@ tests/cases/conformance/jsdoc/templateTagOnClasses.js(24,1): error TS2322: Type
* @template {T}
* @typedef {(t: T) => T} Id
*/
+ /** @template T */
class Foo {
/** @typedef {(t: T) => T} Id2 */
/** @param {T} x */
@@ -15,7 +16,7 @@ tests/cases/conformance/jsdoc/templateTagOnClasses.js(24,1): error TS2322: Type
/**
*
* @param {T} x
- * @param {Id} y
+ * @param {Id} y
* @param {Id2} alpha
* @return {T}
*/
diff --git a/tests/baselines/reference/jsdocTemplateClass.symbols b/tests/baselines/reference/jsdocTemplateClass.symbols
index d80b73bc4ac21..b82cd8038e50b 100644
--- a/tests/baselines/reference/jsdocTemplateClass.symbols
+++ b/tests/baselines/reference/jsdocTemplateClass.symbols
@@ -3,52 +3,53 @@
* @template {T}
* @typedef {(t: T) => T} Id
*/
+/** @template T */
class Foo {
>Foo : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0))
/** @typedef {(t: T) => T} Id2 */
/** @param {T} x */
constructor (x) {
->x : Symbol(x, Decl(templateTagOnClasses.js, 7, 17))
+>x : Symbol(x, Decl(templateTagOnClasses.js, 8, 17))
this.a = x
->this.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21))
+>this.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 8, 21))
>this : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0))
->a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21))
->x : Symbol(x, Decl(templateTagOnClasses.js, 7, 17))
+>a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 8, 21))
+>x : Symbol(x, Decl(templateTagOnClasses.js, 8, 17))
}
/**
*
* @param {T} x
- * @param {Id} y
+ * @param {Id} y
* @param {Id2} alpha
* @return {T}
*/
foo(x, y, alpha) {
->foo : Symbol(Foo.foo, Decl(templateTagOnClasses.js, 9, 5))
->x : Symbol(x, Decl(templateTagOnClasses.js, 17, 8))
->y : Symbol(y, Decl(templateTagOnClasses.js, 17, 10))
->alpha : Symbol(alpha, Decl(templateTagOnClasses.js, 17, 13))
+>foo : Symbol(Foo.foo, Decl(templateTagOnClasses.js, 10, 5))
+>x : Symbol(x, Decl(templateTagOnClasses.js, 18, 8))
+>y : Symbol(y, Decl(templateTagOnClasses.js, 18, 10))
+>alpha : Symbol(alpha, Decl(templateTagOnClasses.js, 18, 13))
return alpha(y(x))
->alpha : Symbol(alpha, Decl(templateTagOnClasses.js, 17, 13))
->y : Symbol(y, Decl(templateTagOnClasses.js, 17, 10))
->x : Symbol(x, Decl(templateTagOnClasses.js, 17, 8))
+>alpha : Symbol(alpha, Decl(templateTagOnClasses.js, 18, 13))
+>y : Symbol(y, Decl(templateTagOnClasses.js, 18, 10))
+>x : Symbol(x, Decl(templateTagOnClasses.js, 18, 8))
}
}
var f = new Foo(1)
->f : Symbol(f, Decl(templateTagOnClasses.js, 21, 3))
+>f : Symbol(f, Decl(templateTagOnClasses.js, 22, 3))
>Foo : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0))
var g = new Foo(false)
->g : Symbol(g, Decl(templateTagOnClasses.js, 22, 3))
+>g : Symbol(g, Decl(templateTagOnClasses.js, 23, 3))
>Foo : Symbol(Foo, Decl(templateTagOnClasses.js, 0, 0))
f.a = g.a
->f.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21))
->f : Symbol(f, Decl(templateTagOnClasses.js, 21, 3))
->a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21))
->g.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21))
->g : Symbol(g, Decl(templateTagOnClasses.js, 22, 3))
->a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 7, 21))
+>f.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 8, 21))
+>f : Symbol(f, Decl(templateTagOnClasses.js, 22, 3))
+>a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 8, 21))
+>g.a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 8, 21))
+>g : Symbol(g, Decl(templateTagOnClasses.js, 23, 3))
+>a : Symbol(Foo.a, Decl(templateTagOnClasses.js, 8, 21))
diff --git a/tests/baselines/reference/jsdocTemplateClass.types b/tests/baselines/reference/jsdocTemplateClass.types
index aa6a685e34fae..9c028a96ff170 100644
--- a/tests/baselines/reference/jsdocTemplateClass.types
+++ b/tests/baselines/reference/jsdocTemplateClass.types
@@ -3,6 +3,7 @@
* @template {T}
* @typedef {(t: T) => T} Id
*/
+/** @template T */
class Foo {
>Foo : Foo
@@ -21,7 +22,7 @@ class Foo {
/**
*
* @param {T} x
- * @param {Id} y
+ * @param {Id} y
* @param {Id2} alpha
* @return {T}
*/
diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt b/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt
index 225d277c4fff8..9e735b780a0fc 100644
--- a/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt
+++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.errors.txt
@@ -1,11 +1,14 @@
-tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js(21,1): error TS2322: Type 'false' is not assignable to type 'number'.
+tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js(24,1): error TS2322: Type 'false' is not assignable to type 'number'.
==== tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js (1 errors) ====
/**
- * @template {T}
- * @typedef {(t: T) => T} Id
+ * @template {U}
+ * @typedef {(u: U) => U} Id
+ */
+ /**
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
/** @type {T} */
@@ -14,7 +17,7 @@ tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js(21,1): error
}
/**
* @param {T} v
- * @param {Id} id
+ * @param {Id} id
*/
Zet.prototype.add = function(v, id) {
this.u = v || this.t
diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols b/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols
index 8cf2ee99e57ea..b640d3c1e21e4 100644
--- a/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols
+++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.symbols
@@ -1,57 +1,60 @@
=== tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js ===
/**
- * @template {T}
- * @typedef {(t: T) => T} Id
+ * @template {U}
+ * @typedef {(u: U) => U} Id
+ */
+/**
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
>Zet : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0))
->t : Symbol(t, Decl(templateTagOnConstructorFunctions.js, 5, 13))
+>t : Symbol(t, Decl(templateTagOnConstructorFunctions.js, 8, 13))
/** @type {T} */
this.u
this.t = t
->t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10))
->t : Symbol(t, Decl(templateTagOnConstructorFunctions.js, 5, 13))
+>t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 10, 10))
+>t : Symbol(t, Decl(templateTagOnConstructorFunctions.js, 8, 13))
}
/**
* @param {T} v
- * @param {Id} id
+ * @param {Id} id
*/
Zet.prototype.add = function(v, id) {
->Zet.prototype : Symbol(Zet.add, Decl(templateTagOnConstructorFunctions.js, 9, 1))
+>Zet.prototype : Symbol(Zet.add, Decl(templateTagOnConstructorFunctions.js, 12, 1))
>Zet : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0))
>prototype : Symbol(Function.prototype, Decl(lib.d.ts, --, --))
->add : Symbol(Zet.add, Decl(templateTagOnConstructorFunctions.js, 9, 1))
->v : Symbol(v, Decl(templateTagOnConstructorFunctions.js, 14, 29))
->id : Symbol(id, Decl(templateTagOnConstructorFunctions.js, 14, 31))
+>add : Symbol(Zet.add, Decl(templateTagOnConstructorFunctions.js, 12, 1))
+>v : Symbol(v, Decl(templateTagOnConstructorFunctions.js, 17, 29))
+>id : Symbol(id, Decl(templateTagOnConstructorFunctions.js, 17, 31))
this.u = v || this.t
->this.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37))
+>this.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 8, 17), Decl(templateTagOnConstructorFunctions.js, 17, 37))
>this : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0))
->u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37))
->v : Symbol(v, Decl(templateTagOnConstructorFunctions.js, 14, 29))
->this.t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10))
+>u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 8, 17), Decl(templateTagOnConstructorFunctions.js, 17, 37))
+>v : Symbol(v, Decl(templateTagOnConstructorFunctions.js, 17, 29))
+>this.t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 10, 10))
>this : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0))
->t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10))
+>t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 10, 10))
return id(this.u)
->id : Symbol(id, Decl(templateTagOnConstructorFunctions.js, 14, 31))
->this.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37))
+>id : Symbol(id, Decl(templateTagOnConstructorFunctions.js, 17, 31))
+>this.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 8, 17), Decl(templateTagOnConstructorFunctions.js, 17, 37))
>this : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0))
->u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37))
+>u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 8, 17), Decl(templateTagOnConstructorFunctions.js, 17, 37))
}
var z = new Zet(1)
->z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 18, 3))
+>z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 21, 3))
>Zet : Symbol(Zet, Decl(templateTagOnConstructorFunctions.js, 0, 0))
z.t = 2
->z.t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10))
->z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 18, 3))
->t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 7, 10))
+>z.t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 10, 10))
+>z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 21, 3))
+>t : Symbol(Zet.t, Decl(templateTagOnConstructorFunctions.js, 10, 10))
z.u = false
->z.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37))
->z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 18, 3))
->u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 5, 17), Decl(templateTagOnConstructorFunctions.js, 14, 37))
+>z.u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 8, 17), Decl(templateTagOnConstructorFunctions.js, 17, 37))
+>z : Symbol(z, Decl(templateTagOnConstructorFunctions.js, 21, 3))
+>u : Symbol(Zet.u, Decl(templateTagOnConstructorFunctions.js, 8, 17), Decl(templateTagOnConstructorFunctions.js, 17, 37))
diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction.types b/tests/baselines/reference/jsdocTemplateConstructorFunction.types
index f838534aa339f..121cf846a0e39 100644
--- a/tests/baselines/reference/jsdocTemplateConstructorFunction.types
+++ b/tests/baselines/reference/jsdocTemplateConstructorFunction.types
@@ -1,8 +1,11 @@
=== tests/cases/conformance/jsdoc/templateTagOnConstructorFunctions.js ===
/**
- * @template {T}
- * @typedef {(t: T) => T} Id
+ * @template {U}
+ * @typedef {(u: U) => U} Id
+ */
+/**
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
>Zet : typeof Zet
@@ -23,18 +26,18 @@ function Zet(t) {
}
/**
* @param {T} v
- * @param {Id} id
+ * @param {Id} id
*/
Zet.prototype.add = function(v, id) {
->Zet.prototype.add = function(v, id) { this.u = v || this.t return id(this.u)} : (v: T, id: (t: T) => T) => T
+>Zet.prototype.add = function(v, id) { this.u = v || this.t return id(this.u)} : (v: T, id: (u: T) => T) => T
>Zet.prototype.add : any
>Zet.prototype : any
>Zet : typeof Zet
>prototype : any
>add : any
->function(v, id) { this.u = v || this.t return id(this.u)} : (v: T, id: (t: T) => T) => T
+>function(v, id) { this.u = v || this.t return id(this.u)} : (v: T, id: (u: T) => T) => T
>v : T
->id : (t: T) => T
+>id : (u: T) => T
this.u = v || this.t
>this.u = v || this.t : T
@@ -49,7 +52,7 @@ Zet.prototype.add = function(v, id) {
return id(this.u)
>id(this.u) : T
->id : (t: T) => T
+>id : (u: T) => T
>this.u : T
>this : Zet
>u : T
diff --git a/tests/cases/conformance/jsdoc/callbackTag2.ts b/tests/cases/conformance/jsdoc/callbackTag2.ts
index 0ce50ca8709e0..34e71b55bcd71 100644
--- a/tests/cases/conformance/jsdoc/callbackTag2.ts
+++ b/tests/cases/conformance/jsdoc/callbackTag2.ts
@@ -35,6 +35,6 @@ var noreturn = (barts, tidus, noctis) => "cecil"
* @callback Final
* @param {V} barts - "Barts"
* @param {X} tidus - Titus
- * @param {X & V} noctis - "... Whatever"
+ * @param {X & V} noctis - "Prince Noctis Lucius Caelum"
* @return {"cecil" | "zidane"}
*/
diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts b/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts
index 5cd0cde89be12..b13303d6def4f 100644
--- a/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts
+++ b/tests/cases/conformance/jsdoc/jsdocTemplateClass.ts
@@ -7,6 +7,7 @@
* @template {T}
* @typedef {(t: T) => T} Id
*/
+/** @template T */
class Foo {
/** @typedef {(t: T) => T} Id2 */
/** @param {T} x */
@@ -16,7 +17,7 @@ class Foo {
/**
*
* @param {T} x
- * @param {Id} y
+ * @param {Id} y
* @param {Id2} alpha
* @return {T}
*/
diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts
index dc44afe040a4a..a2a7a48c1cb7c 100644
--- a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts
+++ b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction.ts
@@ -4,9 +4,12 @@
// @Filename: templateTagOnConstructorFunctions.js
/**
- * @template {T}
- * @typedef {(t: T) => T} Id
+ * @template {U}
+ * @typedef {(u: U) => U} Id
+ */
+/**
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
/** @type {T} */
@@ -15,7 +18,7 @@ function Zet(t) {
}
/**
* @param {T} v
- * @param {Id} id
+ * @param {Id} id
*/
Zet.prototype.add = function(v, id) {
this.u = v || this.t
From 07cd96da8827f3b1d8d3e58a9cd5eb79cab0362d Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 10:19:51 -0700
Subject: [PATCH 31/49] Test oorder template tags
It works.
---
.../reference/jsdocTemplateConstructorFunction2.errors.txt | 2 +-
.../reference/jsdocTemplateConstructorFunction2.symbols | 2 +-
.../baselines/reference/jsdocTemplateConstructorFunction2.types | 2 +-
.../conformance/jsdoc/jsdocTemplateConstructorFunction2.ts | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt b/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt
index 6a8e45566ecab..cdcc9a056ebab 100644
--- a/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt
+++ b/tests/baselines/reference/jsdocTemplateConstructorFunction2.errors.txt
@@ -4,8 +4,8 @@ tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js(26,15): error
==== tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js (2 errors) ====
/**
- * @template {T}
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
/** @type {T} */
diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols b/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols
index c65570f84c4c5..32573a373985b 100644
--- a/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols
+++ b/tests/baselines/reference/jsdocTemplateConstructorFunction2.symbols
@@ -1,7 +1,7 @@
=== tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js ===
/**
- * @template {T}
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
>Zet : Symbol(Zet, Decl(templateTagWithNestedTypeLiteral.js, 0, 0))
diff --git a/tests/baselines/reference/jsdocTemplateConstructorFunction2.types b/tests/baselines/reference/jsdocTemplateConstructorFunction2.types
index 5737f8caaf9f1..80872e8e0eb1f 100644
--- a/tests/baselines/reference/jsdocTemplateConstructorFunction2.types
+++ b/tests/baselines/reference/jsdocTemplateConstructorFunction2.types
@@ -1,7 +1,7 @@
=== tests/cases/conformance/jsdoc/templateTagWithNestedTypeLiteral.js ===
/**
- * @template {T}
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
>Zet : typeof Zet
diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts
index a4f107190ff1c..c736962de3974 100644
--- a/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts
+++ b/tests/cases/conformance/jsdoc/jsdocTemplateConstructorFunction2.ts
@@ -4,8 +4,8 @@
// @Filename: templateTagWithNestedTypeLiteral.js
/**
- * @template {T}
* @param {T} t
+ * @template {T}
*/
function Zet(t) {
/** @type {T} */
From eb61b8b48d16288f028b5ca17c2e4a523afd53af Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 11:08:32 -0700
Subject: [PATCH 32/49] Parser cleanup
---
src/compiler/parser.ts | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 319b00de08955..b9c37239c6623 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6376,7 +6376,7 @@ namespace ts {
case SyntaxKind.AtToken:
if (state === JSDocState.BeginningOfLine || state === JSDocState.SawAsterisk) {
removeTrailingNewlines(comments);
- parseTag(indent);
+ addTag(parseTag(indent));
// NOTE: According to usejsdoc.org, a tag goes to end of line, except the last tag.
// Real-world comments may break this rule, so "BeginningOfLine" will not be a real line beginning
// for malformed examples like `/** @param {string} x @returns {number} the length */`
@@ -6493,8 +6493,7 @@ namespace ts {
case "arg":
case "argument":
case "param":
- addTag(parseParameterOrPropertyTag(atToken, tagName, PropertyLikeParse.Parameter, indent));
- return;
+ return parseParameterOrPropertyTag(atToken, tagName, PropertyLikeParse.Parameter, indent);
case "return":
case "returns":
tag = parseReturnTag(atToken, tagName);
@@ -6528,7 +6527,7 @@ namespace ts {
// some tags, like typedef and callback, have already parsed their comments earlier
tag.comment = parseTagComments(indent + tag.end - tag.pos);
}
- addTag(tag);
+ return tag;
}
function parseTagComments(indent: number): string | undefined {
@@ -6601,6 +6600,9 @@ namespace ts {
}
function addTag(tag: JSDocTag): void {
+ if (!tag) {
+ return;
+ }
if (!tags) {
tags = [tag];
tagsPos = tag.pos;
@@ -6778,8 +6780,8 @@ namespace ts {
typedefTag.fullName = parseJSDocTypeNameWithNamespace();
typedefTag.name = getJSDocTypeAliasName(typedefTag.fullName);
skipWhitespace();
-
typedefTag.comment = parseTagComments(indent);
+
typedefTag.typeExpression = typeExpression;
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
let child: JSDocTypeTag | JSDocPropertyTag | false;
@@ -6848,19 +6850,14 @@ namespace ts {
const start = scanner.getStartPos();
const jsdocSignature = createNode(SyntaxKind.JSDocSignature, start) as JSDocSignature;
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter) as JSDocParameterTag)) {
- // Debug.assert(child.kind !== SyntaxKind.JSDocTypeTag);
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
}
const returnTag = tryParse(() => {
- if (parseExpectedToken(SyntaxKind.AtToken)) {
- const atToken = createNode(SyntaxKind.AtToken, scanner.getTokenPos());
- atToken.end = scanner.getTextPos();
- nextToken(); // why is this needed? Doesn't parseExpectedToken advance the token enough?
- const name = parseJSDocIdentifierName();
- if (name && (name.escapedText === "return" || name.escapedText === "returns")) {
- const returnTag = parseReturnTag(atToken, name);
- returnTag.comment = parseTagComments(indent);
- return returnTag;
+ if (token() === SyntaxKind.AtToken) {
+ nextJSDocToken();
+ const tag = parseTag(indent);
+ if (tag && tag.kind === SyntaxKind.JSDocReturnTag) {
+ return tag as JSDocReturnTag;
}
}
});
From e6e8ebfb7ae4cacba27998c56aa0414b48a626f6 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 11:36:23 -0700
Subject: [PATCH 33/49] Cleanup types and utilities
As much as possible, and not as much as I would like.
---
src/compiler/types.ts | 3 +-
src/compiler/utilities.ts | 46 ++++---------------
src/services/codefixes/fixUnusedIdentifier.ts | 2 +-
src/services/refactors/extractSymbol.ts | 8 ++--
.../reference/api/tsserverlibrary.d.ts | 2 -
tests/baselines/reference/api/typescript.d.ts | 2 -
6 files changed, 14 insertions(+), 49 deletions(-)
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 8b852387f6663..2158d6d95f1f5 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2391,11 +2391,10 @@ namespace ts {
parent: JSDoc;
kind: SyntaxKind.JSDocCallbackTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
- name?: Identifier; // TODO: Not sure whether this rigamarole is needed for callback...but probably!
+ name?: Identifier;
typeExpression: JSDocSignature;
}
- // TODO: Could just try to reuse JSDocTypeLiteral
export interface JSDocSignature extends JSDocType, Declaration {
kind: SyntaxKind.JSDocSignature;
typeParameters?: ReadonlyArray;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index c9e21dd5d6961..872333d64b65b 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -498,37 +498,6 @@ namespace ts {
return false;
}
- export function getTypeParametersOfDeclaration(node: DeclarationWithTypeParameters): NodeArray | undefined {
- switch (node.kind) {
- case SyntaxKind.CallSignature:
- case SyntaxKind.ConstructSignature:
- case SyntaxKind.MethodSignature:
- case SyntaxKind.IndexSignature:
- case SyntaxKind.FunctionType:
- case SyntaxKind.ConstructorType:
- case SyntaxKind.JSDocFunctionType:
- case SyntaxKind.ClassDeclaration:
- case SyntaxKind.ClassExpression:
- case SyntaxKind.InterfaceDeclaration:
- case SyntaxKind.TypeAliasDeclaration:
- case SyntaxKind.JSDocTemplateTag:
- case SyntaxKind.FunctionDeclaration:
- case SyntaxKind.MethodDeclaration:
- case SyntaxKind.Constructor:
- case SyntaxKind.GetAccessor:
- case SyntaxKind.SetAccessor:
- case SyntaxKind.FunctionExpression:
- case SyntaxKind.ArrowFunction:
- return node.typeParameters;
- case SyntaxKind.JSDocCallbackTag:
- case SyntaxKind.JSDocTypedefTag:
- return getEffectiveTypeParameterDeclarations(node);
- default:
- assertTypeIsNever(node);
- return undefined;
- }
- }
-
export function isDeclarationWithTypeParameters(node: Node): node is DeclarationWithTypeParameters;
export function isDeclarationWithTypeParameters(node: DeclarationWithTypeParameters): node is DeclarationWithTypeParameters {
switch (node.kind) {
@@ -3024,7 +2993,7 @@ namespace ts {
}
export function getThisParameter(signature: SignatureDeclaration | JSDocSignature): ParameterDeclaration | undefined {
- // TODO: Maybe jsdoc signatures should support this parameters? (parameterIsThisKeyword is not going to work there, I think, since jsdoc doesn't have keywords)
+ // callback tags do not currently support this parameters
if (signature.parameters.length && !isJSDocSignature(signature)) {
const thisParameter = signature.parameters[0];
if (parameterIsThisKeyword(thisParameter)) {
@@ -3119,14 +3088,17 @@ namespace ts {
* JavaScript file, gets the return type annotation from JSDoc.
*/
export function getEffectiveReturnTypeNode(node: SignatureDeclaration | JSDocSignature): TypeNode | undefined {
- return !isJSDocSignature(node) && node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
+ if (isJSDocSignature(node)) {
+ return node.type && node.type.typeExpression && node.type.typeExpression.type;
+ }
+ return node.type || (isInJavaScriptFile(node) ? getJSDocReturnType(node) : undefined);
}
/**
* Gets the effective type parameters. If the node was parsed in a
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
*/
- export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters): NodeArray {
+ export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
if (isJSDocTypeAlias(node)) {
Debug.assert(node.parent.kind === SyntaxKind.JSDocComment);
const templateTags = flatMap(filter(node.parent.tags, isJSDocTemplateTag), tag => tag.typeParameters) as ReadonlyArray;
@@ -3143,6 +3115,7 @@ namespace ts {
const tags = filter(getJSDocTags(node), isJSDocTemplateTag);
for (const tag of tags) {
if (!(tag.parent.kind === SyntaxKind.JSDocComment && find(tag.parent.tags, isJSDocTypeAlias))) {
+ // template tags are only available when a typedef isn't already using them
return tag.typeParameters;
}
}
@@ -4765,9 +4738,6 @@ namespace ts {
/** Gets the JSDoc return tag for the node if present */
export function getJSDocReturnTag(node: Node): JSDocReturnTag | undefined {
- if (isJSDocSignature(node)) {
- return node.type;
- }
return getFirstJSDocTag(node, isJSDocReturnTag);
}
@@ -4828,7 +4798,7 @@ namespace ts {
}
/** Get the first JSDoc tag of a specified kind, or undefined if not present. */
- export function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined {
+ function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined {
// TODO: This shouldn't need to be exported, I think
return find(getJSDocTags(node), predicate);
}
diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts
index 93c1c1681af78..dc40948ed1641 100644
--- a/src/services/codefixes/fixUnusedIdentifier.ts
+++ b/src/services/codefixes/fixUnusedIdentifier.ts
@@ -125,7 +125,7 @@ namespace ts.codefix {
break;
case SyntaxKind.TypeParameter:
- const typeParameters = getTypeParametersOfDeclaration(parent.parent);
+ const typeParameters = getEffectiveTypeParameterDeclarations(parent.parent);
if (typeParameters.length === 1) {
const previousToken = getTokenAtPosition(sourceFile, typeParameters.pos - 1, /*includeJsDocComment*/ false);
const nextToken = getTokenAtPosition(sourceFile, typeParameters.end, /*includeJsDocComment*/ false);
diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts
index ffd0bdbcdef0d..42ea0eabd3a1c 100644
--- a/src/services/refactors/extractSymbol.ts
+++ b/src/services/refactors/extractSymbol.ts
@@ -1464,8 +1464,8 @@ namespace ts.refactor.extractSymbol {
}
// Note that we add the current node's type parameters *after* updating the corresponding scope.
- if (isDeclarationWithTypeParameters(curr) && getTypeParametersOfDeclaration(curr)) {
- for (const typeParameterDecl of getTypeParametersOfDeclaration(curr)) {
+ if (isDeclarationWithTypeParameters(curr) && getEffectiveTypeParameterDeclarations(curr)) {
+ for (const typeParameterDecl of getEffectiveTypeParameterDeclarations(curr)) {
const typeParameter = checker.getTypeAtLocation(typeParameterDecl) as TypeParameter;
if (allTypeParameterUsages.has(typeParameter.id.toString())) {
seenTypeParameterUsages.set(typeParameter.id.toString(), typeParameter);
@@ -1536,8 +1536,8 @@ namespace ts.refactor.extractSymbol {
function hasTypeParameters(node: Node) {
return isDeclarationWithTypeParameters(node) &&
- getTypeParametersOfDeclaration(node) &&
- getTypeParametersOfDeclaration(node).length > 0;
+ getEffectiveTypeParameterDeclarations(node) &&
+ getEffectiveTypeParameterDeclarations(node).length > 0;
}
function isInGenericContext(node: Node) {
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index 5d076f215a1b3..a4d5e9c7dd6fd 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -3167,8 +3167,6 @@ declare namespace ts {
function getJSDocReturnType(node: Node): TypeNode | undefined;
/** Get all JSDoc tags related to a node, including those on parent nodes. */
function getJSDocTags(node: Node): ReadonlyArray;
- /** Get the first JSDoc tag of a specified kind, or undefined if not present. */
- function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined;
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray;
}
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index 18a5002795b01..6437ddb4ace74 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -3167,8 +3167,6 @@ declare namespace ts {
function getJSDocReturnType(node: Node): TypeNode | undefined;
/** Get all JSDoc tags related to a node, including those on parent nodes. */
function getJSDocTags(node: Node): ReadonlyArray;
- /** Get the first JSDoc tag of a specified kind, or undefined if not present. */
- function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined;
/** Gets all JSDoc tags of a specified kind, or undefined if not present. */
function getAllJSDocTagsOfKind(node: Node, kind: SyntaxKind): ReadonlyArray;
}
From 91d95f72085618d00f193a959fc2b3bae26a2152 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 11:41:08 -0700
Subject: [PATCH 34/49] Handle callback more often in services
---
src/services/jsDoc.ts | 2 +-
src/services/utilities.ts | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/services/jsDoc.ts b/src/services/jsDoc.ts
index 2a8cc2245ac0f..c8958ad3d2f57 100644
--- a/src/services/jsDoc.ts
+++ b/src/services/jsDoc.ts
@@ -100,8 +100,8 @@ namespace ts.JsDoc {
return withList((tag as JSDocTemplateTag).typeParameters);
case SyntaxKind.JSDocTypeTag:
return withNode((tag as JSDocTypeTag).typeExpression);
- // TODO: Handle callback here
case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.JSDocPropertyTag:
case SyntaxKind.JSDocParameterTag:
const { name } = tag as JSDocTypedefTag | JSDocPropertyTag | JSDocParameterTag;
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index 861846f26d1a5..01fbfe76a2ffc 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -272,8 +272,7 @@ namespace ts {
}
export function getContainerNode(node: Node): Declaration {
- // TODO: Probably need to handle jsdoccallbacktag too
- if (node.kind === SyntaxKind.JSDocTypedefTag) {
+ if (isJSDocTypeAlias(node)) {
// This doesn't just apply to the node immediately under the comment, but to everything in its parent's scope.
// node.parent = the JSDoc comment, node.parent.parent = the node having the comment.
// Then we get parent again in the loop.
From 6973c5dd2e449b2d018bba413a8f3b977a0c875f Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 13:31:26 -0700
Subject: [PATCH 35/49] Cleanup of binder and checker
---
src/compiler/binder.ts | 8 ++--
src/compiler/checker.ts | 90 +++++++++++++----------------------------
2 files changed, 33 insertions(+), 65 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 8166cb2ca5dae..49c1a13e40383 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1380,6 +1380,7 @@ namespace ts {
function bindJSDocComment(node: JSDoc) {
forEachChild(node, n => {
+ // Skip type-alias-related tags, which are bound early.
if (!isJSDocTypeAlias(n) && !getTypeAliasForJSDocTemplateTag(n, node.tags)) {
bind(n);
}
@@ -1949,7 +1950,7 @@ namespace ts {
// Here the current node is "foo", which is a container, but the scope of "MyType" should
// not be inside "foo". Therefore we always bind @typedef before bind the parent node,
// and skip binding this tag later when binding all the other jsdoc tags.
- if (isInJavaScriptFile(node)) bindJSDocTypedefTagIfAny(node);
+ if (isInJavaScriptFile(node)) bindJSDocTypeAliasTagsIfAny(node);
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
// and then potentially add the symbol to an appropriate symbol table. Possible
@@ -1985,7 +1986,7 @@ namespace ts {
inStrictMode = saveInStrictMode;
}
- function bindJSDocTypedefTagIfAny(node: Node) {
+ function bindJSDocTypeAliasTagsIfAny(node: Node) {
if (!hasJSDocNodes(node)) {
return;
}
@@ -2002,9 +2003,10 @@ namespace ts {
bind(tag);
parent = savedParent;
}
+ // Bind template tags that have a typedef or callback tag in the same comment.
+ // The typedef/callback tag is the container of the template.
const alias = getTypeAliasForJSDocTemplateTag(tag, jsDoc.tags);
if (alias) {
- // find typedef or callback and set that to the container (TODO:manually, which is kind of a bad idea)
const savedContainer = container;
const savedParent = parent;
container = alias;
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 10ab871edd258..a1db7a626e917 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -1569,13 +1569,10 @@ namespace ts {
function isTypeParameterSymbolDeclaredInContainer(symbol: Symbol, container: Node) {
for (const decl of symbol.declarations) {
- const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
- if (decl.kind === SyntaxKind.TypeParameter && parent === container) {
- if (isJSDocTemplateTag(decl.parent)) {
- return !find((decl.parent.parent as JSDoc).tags, isJSDocTypeAlias);
- }
- else {
- return true;
+ if (decl.kind === SyntaxKind.TypeParameter) {
+ const parent = isJSDocTemplateTag(decl.parent) ? getJSDocHost(decl.parent) : decl.parent;
+ if (parent === container) {
+ return !(isJSDocTemplateTag(decl.parent) && find((decl.parent.parent as JSDoc).tags, isJSDocTypeAlias));
}
}
}
@@ -3565,34 +3562,10 @@ namespace ts {
}
function symbolToParameterDeclaration(parameterSymbol: Symbol, context: NodeBuilderContext, preserveModifierFlags?: boolean): ParameterDeclaration {
- const parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter);
+ let parameterDeclaration: ParameterDeclaration | JSDocParameterTag = getDeclarationOfKind(parameterSymbol, SyntaxKind.Parameter);
if (!parameterDeclaration && !(isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter)) {
- const paramTagDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.JSDocParameterTag);
- // now do a whole bunch of stuff with the parameter tag instead
- // TODO: Dedupe this!
- Debug.assert(!!paramTagDeclaration);
- // TODO: Might need to convert jsdoc type literal to a binding name?
- Debug.assert(isIdentifier(paramTagDeclaration.name));
- const dotDotDotToken = isRestParameter(paramTagDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined;
- const name = paramTagDeclaration.name && isIdentifier(paramTagDeclaration.name) ?
- // isIdentifier(paramTagDeclaration.name) ?
- setEmitFlags(getSynthesizedClone(paramTagDeclaration.name), EmitFlags.NoAsciiEscaping) :
- // cloneQualifiedName(paramTagDeclaration.name) :
- symbolName(parameterSymbol);
- const questionToken = isOptionalParameter2(paramTagDeclaration) ? createToken(SyntaxKind.QuestionToken) : undefined;
- const parameterTypeNode = typeToTypeNodeHelper(getTypeOfSymbol(parameterSymbol), context);
- const parameterNode = createParameter(
- /*decorators*/ undefined,
- /*modifiers*/ undefined,
- dotDotDotToken,
- name,
- questionToken,
- parameterTypeNode,
- /*initializer*/ undefined);
- return parameterNode;
+ parameterDeclaration = getDeclarationOfKind(parameterSymbol, SyntaxKind.JSDocParameterTag);
}
- Debug.assert(!!parameterDeclaration || isTransientSymbol(parameterSymbol) && !!parameterSymbol.isRestParameter);
-
let parameterType = getTypeOfSymbol(parameterSymbol);
if (parameterDeclaration && isRequiredInitializedParameter(parameterDeclaration)) {
parameterType = getOptionalType(parameterType);
@@ -3603,8 +3576,8 @@ namespace ts {
const dotDotDotToken = !parameterDeclaration || isRestParameter(parameterDeclaration) ? createToken(SyntaxKind.DotDotDotToken) : undefined;
const name = parameterDeclaration
? parameterDeclaration.name ?
- parameterDeclaration.name.kind === SyntaxKind.Identifier ?
- setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) :
+ parameterDeclaration.name.kind === SyntaxKind.Identifier ? setEmitFlags(getSynthesizedClone(parameterDeclaration.name), EmitFlags.NoAsciiEscaping) :
+ parameterDeclaration.name.kind === SyntaxKind.QualifiedName ? setEmitFlags(getSynthesizedClone(parameterDeclaration.name.right), EmitFlags.NoAsciiEscaping) :
cloneBindingName(parameterDeclaration.name) :
symbolName(parameterSymbol)
: symbolName(parameterSymbol);
@@ -3630,15 +3603,6 @@ namespace ts {
return setEmitFlags(clone, EmitFlags.SingleLine | EmitFlags.NoAsciiEscaping);
}
}
-
- // function cloneQualifiedName(node: QualifiedName): EntityName {
- // if (isIdentifier(node.left)) {
- // return setEmitFlags(getSynthesizedClone(node.left), EmitFlags.NoAsciiEscaping);
- // }
- // else {
- // return setEmitFlags(createQualifiedName(cloneQualifiedName(node.left), getSynthesizedClone(node.right)), EmitFlags.NoAsciiEscaping);
- // }
- // }
}
function lookupSymbolChain(symbol: Symbol, context: NodeBuilderContext, meaning: SymbolFlags) {
@@ -6994,8 +6958,8 @@ namespace ts {
return symbol && withAugmentations ? getMergedSymbol(symbol) : symbol;
}
- function isOptionalParameter(node: ParameterDeclaration) {
- if (hasQuestionToken(node) || isJSDocOptionalParameter(node)) {
+ function isOptionalParameter(node: ParameterDeclaration | JSDocParameterTag) {
+ if (hasQuestionToken(node) || isOptionalJSDocParameterTag(node) || isJSDocOptionalParameter(node)) {
return true;
}
@@ -7015,6 +6979,14 @@ namespace ts {
return false;
}
+ function isOptionalJSDocParameterTag(node: Node): node is JSDocParameterTag {
+ if (!isJSDocParameterTag(node)) {
+ return false;
+ }
+ const { isBracketed, typeExpression } = node;
+ return isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType;
+ }
+
function createTypePredicateFromTypePredicateNode(node: TypePredicateNode): IdentifierTypePredicate | ThisTypePredicate {
const { parameterName } = node;
const type = getTypeFromTypeNode(node.type);
@@ -7132,9 +7104,14 @@ namespace ts {
}
// Record a new minimum argument count if this is not an optional parameter
- if (!(isOptionalParameter2(param) ||
- iife && parameters.length > iife.arguments.length && !type ||
- isUntypedSignatureInJSFile)) {
+ const isOptionalParameter = isOptionalJSDocParameterTag(param) ||
+ param.initializer || param.questionToken || param.dotDotDotToken ||
+ iife && parameters.length > iife.arguments.length && !type ||
+ isUntypedSignatureInJSFile ||
+ isJSDocOptionalParameter(param);
+
+
+ if (!isOptionalParameter) {
minArgumentCount = parameters.length;
}
}
@@ -7161,18 +7138,6 @@ namespace ts {
return links.resolvedSignature;
}
- // TODO: Decide between this and the Original and B-b-b-b-best?
- function isOptionalParameter2(param: ParameterDeclaration | JSDocParameterTag) {
- if (isParameter(param)) {
- return param.initializer || param.questionToken || param.dotDotDotToken ||
- isJSDocOptionalParameter(param);
- }
- else {
- const { isBracketed, typeExpression } = param;
- return isBracketed || !!typeExpression && typeExpression.type.kind === SyntaxKind.JSDocOptionalType;
- }
- }
-
/**
* A JS function gets a synthetic rest parameter if it references `arguments` AND:
* 1. It has no parameters but at least one `@param` with a type that starts with `...`
@@ -26355,9 +26320,10 @@ namespace ts {
return false;
}
- function isRequiredInitializedParameter(parameter: ParameterDeclaration) {
+ function isRequiredInitializedParameter(parameter: ParameterDeclaration | JSDocParameterTag) {
return strictNullChecks &&
!isOptionalParameter(parameter) &&
+ !isJSDocParameterTag(parameter) &&
parameter.initializer &&
!hasModifier(parameter, ModifierFlags.ParameterPropertyModifier);
}
From 52cd1c7cca3c96114c623637687fb525cb81efec Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 14:22:17 -0700
Subject: [PATCH 36/49] More checker cleanup
---
src/compiler/checker.ts | 9 ++-------
src/compiler/types.ts | 2 +-
src/compiler/utilities.ts | 4 ++++
tests/baselines/reference/api/tsserverlibrary.d.ts | 2 +-
tests/baselines/reference/api/typescript.d.ts | 2 +-
5 files changed, 9 insertions(+), 10 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index a1db7a626e917..3a665857b6352 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4732,9 +4732,6 @@ namespace ts {
if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && declaration.typeExpression) {
return links.type = getTypeFromTypeNode(declaration.typeExpression.type);
}
- if (isInJavaScriptFile(declaration) && isJSDocPropertyLikeTag(declaration) && isJSDocSignature(declaration.parent) && !declaration.typeExpression) {
- return links.type = unknownType;
- }
// Handle variable, parameter or property
if (!pushTypeResolution(symbol, TypeSystemPropertyName.Type)) {
return unknownType;
@@ -4750,7 +4747,7 @@ namespace ts {
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
type = getWidenedTypeFromJSSpecialPropertyDeclarations(symbol);
}
- else if (isJSDocPropertyTag(declaration)
+ else if (isJSDocPropertyLikeTag(declaration)
|| isPropertyAccessExpression(declaration)
|| isIdentifier(declaration)
|| (isMethodDeclaration(declaration) && !isObjectLiteralMethod(declaration))
@@ -7109,8 +7106,6 @@ namespace ts {
iife && parameters.length > iife.arguments.length && !type ||
isUntypedSignatureInJSFile ||
isJSDocOptionalParameter(param);
-
-
if (!isOptionalParameter) {
minArgumentCount = parameters.length;
}
@@ -7130,7 +7125,7 @@ namespace ts {
const classType = declaration.kind === SyntaxKind.Constructor ?
getDeclaredTypeOfClassOrInterface(getMergedSymbol((declaration.parent).symbol))
: undefined;
- const typeParameters = classType ? classType.localTypeParameters : isJSDocSignature(declaration) ? undefined : getTypeParametersFromDeclaration(declaration);
+ const typeParameters = classType ? classType.localTypeParameters : getTypeParametersFromDeclaration(declaration);
const returnType = getSignatureReturnTypeFromDeclaration(declaration, isJSConstructSignature, classType);
const hasRestLikeParameter = hasRestParameter(declaration) || isInJavaScriptFile(declaration) && maybeAddJsSyntheticRestParameter(declaration, parameters);
links.resolvedSignature = createSignature(declaration, typeParameters, thisParameter, parameters, returnType, /*resolvedTypePredicate*/ undefined, minArgumentCount, hasRestLikeParameter, hasLiteralTypes);
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 2158d6d95f1f5..918a34c18b1b3 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2053,7 +2053,7 @@ namespace ts {
export type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag;
+ export type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag | JSDocSignature;
export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 872333d64b65b..bf5658ad27802 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -522,6 +522,7 @@ namespace ts {
case SyntaxKind.ArrowFunction:
case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.JSDocTypedefTag:
+ case SyntaxKind.JSDocSignature:
return true;
default:
assertTypeIsNever(node);
@@ -3099,6 +3100,9 @@ namespace ts {
* JavaScript file, gets the type parameters from the `@template` tag from JSDoc.
*/
export function getEffectiveTypeParameterDeclarations(node: DeclarationWithTypeParameters) {
+ if (isJSDocSignature(node)) {
+ return undefined;
+ }
if (isJSDocTypeAlias(node)) {
Debug.assert(node.parent.kind === SyntaxKind.JSDocComment);
const templateTags = flatMap(filter(node.parent.tags, isJSDocTemplateTag), tag => tag.typeParameters) as ReadonlyArray;
diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts
index a4d5e9c7dd6fd..68fcd99a54e72 100644
--- a/tests/baselines/reference/api/tsserverlibrary.d.ts
+++ b/tests/baselines/reference/api/tsserverlibrary.d.ts
@@ -1280,7 +1280,7 @@ declare namespace ts {
block: Block;
}
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag;
+ type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag | JSDocSignature;
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts
index 6437ddb4ace74..0b2ec76309948 100644
--- a/tests/baselines/reference/api/typescript.d.ts
+++ b/tests/baselines/reference/api/typescript.d.ts
@@ -1280,7 +1280,7 @@ declare namespace ts {
block: Block;
}
type ObjectTypeDeclaration = ClassLikeDeclaration | InterfaceDeclaration | TypeLiteralNode;
- type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag;
+ type DeclarationWithTypeParameters = SignatureDeclaration | ClassLikeDeclaration | InterfaceDeclaration | TypeAliasDeclaration | JSDocTemplateTag | JSDocTypedefTag | JSDocCallbackTag | JSDocSignature;
interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
name?: Identifier;
From 072ae0dfaa4bfe0edf65881d83038339f3a2b6d7 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 14:33:38 -0700
Subject: [PATCH 37/49] Remove TODOs and one more cleanup
---
src/compiler/checker.ts | 3 +--
src/compiler/parser.ts | 1 -
src/compiler/utilities.ts | 2 --
3 files changed, 1 insertion(+), 5 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 3a665857b6352..8df5b1c65e63d 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -7221,7 +7221,7 @@ namespace ts {
for (let i = 0; i < symbol.declarations.length; i++) {
const decl = symbol.declarations[i];
const node = isPropertyAccessExpression(decl) ? getAssignedJavascriptInitializer(decl) : decl;
- if (!isFunctionLike(node) && !(node && isJSDocSignature(node))) continue;
+ if (!isFunctionLike(node)) continue;
// Don't include signature if node is the implementation of an overloaded function. A node is considered
// an implementation node if it has a body and the previous node is of the same kind and immediately
// precedes the implementation node (i.e. has the same parent and ends where the implementation starts).
@@ -7615,7 +7615,6 @@ namespace ts {
*/
function getTypeFromTypeAliasReference(node: NodeWithTypeArguments, symbol: Symbol, typeArguments: Type[]): Type {
const type = getDeclaredTypeOfSymbol(symbol);
- // TODO: call getEffectiveTypeParameterDeclarations here and upgrade it to understand in-comment template tags as type parameters
const typeParameters = getSymbolLinks(symbol).typeParameters;
if (typeParameters) {
const numTypeArguments = length(node.typeArguments);
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index b9c37239c6623..06944b5e09ccf 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6668,7 +6668,6 @@ namespace ts {
createNode(SyntaxKind.JSDocParameterTag, atToken.pos);
let comment: string | undefined;
if (indent !== undefined) comment = parseTagComments(indent + scanner.getStartPos() - atToken.pos);
- // TODO: For nested parsing, CallbackParameter should change to Parameter
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name, target);
if (nestedTypeLiteral) {
typeExpression = nestedTypeLiteral;
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index bf5658ad27802..21df22e9ae3d2 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1933,7 +1933,6 @@ namespace ts {
}
export function hasRestParameter(s: SignatureDeclaration | JSDocSignature): boolean {
- // TODO: This type argument wouldn't be needed if I used NodeArray for jsdoc signatures
const last = lastOrUndefined(s.parameters);
return last && isRestParameter(last);
}
@@ -4803,7 +4802,6 @@ namespace ts {
/** Get the first JSDoc tag of a specified kind, or undefined if not present. */
function getFirstJSDocTag(node: Node, predicate: (tag: JSDocTag) => tag is T): T | undefined {
- // TODO: This shouldn't need to be exported, I think
return find(getJSDocTags(node), predicate);
}
From 4c010637f744704ef6ed4c42554e389a567e42ed Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Mon, 7 May 2018 14:51:47 -0700
Subject: [PATCH 38/49] Support parameter-less callback tags
---
src/compiler/parser.ts | 1 +
tests/baselines/reference/callbackTag3.symbols | 9 +++++++++
tests/baselines/reference/callbackTag3.types | 11 +++++++++++
tests/cases/conformance/jsdoc/callbackTag3.ts | 10 ++++++++++
4 files changed, 31 insertions(+)
create mode 100644 tests/baselines/reference/callbackTag3.symbols
create mode 100644 tests/baselines/reference/callbackTag3.types
create mode 100644 tests/cases/conformance/jsdoc/callbackTag3.ts
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 06944b5e09ccf..281443c6a4db7 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -6848,6 +6848,7 @@ namespace ts {
let child: JSDocParameterTag | false;
const start = scanner.getStartPos();
const jsdocSignature = createNode(SyntaxKind.JSDocSignature, start) as JSDocSignature;
+ jsdocSignature.parameters = [];
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter) as JSDocParameterTag)) {
jsdocSignature.parameters = append(jsdocSignature.parameters as MutableNodeArray, child);
}
diff --git a/tests/baselines/reference/callbackTag3.symbols b/tests/baselines/reference/callbackTag3.symbols
new file mode 100644
index 0000000000000..88b53a4cdaa2f
--- /dev/null
+++ b/tests/baselines/reference/callbackTag3.symbols
@@ -0,0 +1,9 @@
+=== tests/cases/conformance/jsdoc/cb.js ===
+/** @callback Miracle
+ * @returns {string} What were you expecting
+ */
+/** @type {Miracle} smallId */
+var sid = () => "!";
+>sid : Symbol(sid, Decl(cb.js, 4, 3))
+
+
diff --git a/tests/baselines/reference/callbackTag3.types b/tests/baselines/reference/callbackTag3.types
new file mode 100644
index 0000000000000..4e9bfd9a0ced0
--- /dev/null
+++ b/tests/baselines/reference/callbackTag3.types
@@ -0,0 +1,11 @@
+=== tests/cases/conformance/jsdoc/cb.js ===
+/** @callback Miracle
+ * @returns {string} What were you expecting
+ */
+/** @type {Miracle} smallId */
+var sid = () => "!";
+>sid : () => string
+>() => "!" : () => string
+>"!" : "!"
+
+
diff --git a/tests/cases/conformance/jsdoc/callbackTag3.ts b/tests/cases/conformance/jsdoc/callbackTag3.ts
new file mode 100644
index 0000000000000..cb51ed6bb46bf
--- /dev/null
+++ b/tests/cases/conformance/jsdoc/callbackTag3.ts
@@ -0,0 +1,10 @@
+// @noEmit: true
+// @allowJs: true
+// @checkJs: true
+// @Filename: cb.js
+/** @callback Miracle
+ * @returns {string} What were you expecting
+ */
+/** @type {Miracle} smallId */
+var sid = () => "!";
+
From 831f44f887070e0f27803d711c41a830a0b29c64 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 8 May 2018 10:11:31 -0700
Subject: [PATCH 39/49] Remove extra bind call on template type parameters
---
src/compiler/binder.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 49c1a13e40383..89299754fa167 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2226,8 +2226,6 @@ namespace ts {
case SyntaxKind.ModuleBlock:
return updateStrictModeStatementList((node).statements);
- case SyntaxKind.JSDocTemplateTag:
- return forEach((node as JSDocTemplateTag).typeParameters, bindTypeParameter);
case SyntaxKind.JSDocParameterTag:
if (node.parent.kind === SyntaxKind.JSDocSignature) {
return bindParameter(node as JSDocParameterTag);
From 9ec7f269a4b57c04a1f4ef2929e555e6e5c0d143 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Tue, 8 May 2018 13:16:12 -0700
Subject: [PATCH 40/49] Bind template tag containers
Doesn't quite work with typedefs, but that's because it's now stricter,
without the typedef fixes. I'm going to merge with jsdoc/callback and
see how it goes.
---
src/compiler/binder.ts | 49 +++++++++----------
src/compiler/checker.ts | 32 +++++++-----
src/compiler/utilities.ts | 4 +-
.../reference/jsdocTemplateTag2.symbols | 19 +++++++
.../reference/jsdocTemplateTag2.types | 21 ++++++++
.../conformance/jsdoc/jsdocTemplateTag2.ts | 16 ++++++
6 files changed, 102 insertions(+), 39 deletions(-)
create mode 100644 tests/baselines/reference/jsdocTemplateTag2.symbols
create mode 100644 tests/baselines/reference/jsdocTemplateTag2.types
create mode 100644 tests/cases/conformance/jsdoc/jsdocTemplateTag2.ts
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 89299754fa167..a0004d14b9b27 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -626,22 +626,6 @@ namespace ts {
}
function bindChildrenWorker(node: Node): void {
- // Binding of JsDocComment should be done before the current block scope container changes.
- // because the scope of JsDocComment should not be affected by whether the current node is a
- // container or not.
- if (hasJSDocNodes(node)) {
- if (isInJavaScriptFile(node)) {
- for (const j of node.jsDoc) {
- bind(j);
- }
- }
- else {
- for (const j of node.jsDoc) {
- setParentPointers(node, j);
- }
- }
- }
-
if (checkUnreachable(node)) {
bindEachChild(node);
return;
@@ -727,6 +711,23 @@ namespace ts {
bindEachChild(node);
break;
}
+ // Binding of JsDocComment should be done before the current block scope container changes.
+ // because the scope of JsDocComment should not be affected by whether the current node is a
+ // container or not.
+ if (hasJSDocNodes(node)) {
+ if (isInJavaScriptFile(node)) {
+ for (const j of node.jsDoc) {
+ bind(j);
+ }
+ }
+ else {
+ for (const j of node.jsDoc) {
+ setParentPointers(node, j);
+ }
+ }
+ }
+
+
}
function isNarrowingExpression(expr: Expression): boolean {
@@ -2719,19 +2720,15 @@ namespace ts {
}
function getInferTypeContainer(node: Node): ConditionalTypeNode {
- while (node) {
- const parent = node.parent;
- if (parent && parent.kind === SyntaxKind.ConditionalType && (parent).extendsType === node) {
- return parent;
- }
- node = parent;
- }
- return undefined;
+ const extendsType = findAncestor(node, n => n.parent && isConditionalTypeNode(n.parent) && n.parent.extendsType === n);
+ return extendsType && extendsType.parent as ConditionalTypeNode;
}
function bindTypeParameter(node: TypeParameterDeclaration) {
- if (node.parent && node.parent.kind === SyntaxKind.InferType) {
- const container = getInferTypeContainer(node.parent);
+ // TODO: node.parent check *might* not be necessary, although that would mean combining isJSDocTemplateTag with getTypeAliasForJSDocTemplateTag
+ // and then calling that instead
+ if (node.parent && (node.parent.kind === SyntaxKind.InferType || isJSDocTemplateTag(node.parent))) {
+ const container = node.parent.kind === SyntaxKind.InferType ? getInferTypeContainer(node.parent) : getHostSignatureFromJSDoc(node.parent);
if (container) {
if (!container.locals) {
container.locals = createSymbolTable();
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 8df5b1c65e63d..15de6ccd03fa4 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -2083,7 +2083,7 @@ namespace ts {
let symbol: Symbol;
if (name.kind === SyntaxKind.Identifier) {
const message = meaning === namespaceMeaning ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
- const symbolFromJSPrototype = isInJavaScriptFile(name) ? resolveEntityNameFromJSPrototype(name, meaning) : undefined;
+ const symbolFromJSPrototype = isInJavaScriptFile(name) ? resolveEntityNameFromJSSpecialAssignment(name, meaning) : undefined;
symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors || symbolFromJSPrototype ? undefined : message, name, /*isUse*/ true);
if (!symbol) {
return symbolFromJSPrototype;
@@ -2138,26 +2138,34 @@ namespace ts {
}
/**
- * For prototype-property methods like `A.prototype.m = function () ...`, try to resolve names in the scope of `A` too.
+ * 1. For prototype-property methods like `A.prototype.m = function () ...`, try to resolve names in the scope of `A` too.
* Note that prototype-property assignment to locations outside the current file (eg globals) doesn't work, so
* name resolution won't work either.
+ * 2. For property assignments like `{ x: function f () { } }`, try to resolve names in the scope of `f` too.
*/
- function resolveEntityNameFromJSPrototype(name: Identifier, meaning: SymbolFlags) {
+ function resolveEntityNameFromJSSpecialAssignment(name: Identifier, meaning: SymbolFlags) {
if (isJSDocTypeReference(name.parent)) {
const host = getJSDocHost(name.parent);
- if (host &&
- isExpressionStatement(host) &&
- isBinaryExpression(host.expression) &&
- getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
- const symbol = getSymbolOfNode(host.expression.left);
- if (symbol) {
- const secondaryLocation = symbol.parent.valueDeclaration;
- return resolveName(secondaryLocation, name.escapedText, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ true);
- }
+ if (host) {
+ const secondaryLocation = getJSSpecialAssignmentSymbol(getJSDocHost(name.parent.parent.parent as JSDocTag));
+ return secondaryLocation && resolveName(secondaryLocation, name.escapedText, meaning, /*nameNotFoundMessage*/ undefined, name, /*isUse*/ true);
}
}
}
+ function getJSSpecialAssignmentSymbol(host: HasJSDoc): Declaration | undefined {
+ if (isPropertyAssignment(host) && isFunctionLike(host.initializer)) {
+ const symbol = getSymbolOfNode(host.initializer);
+ return symbol && symbol.valueDeclaration;
+ }
+ else if (isExpressionStatement(host) &&
+ isBinaryExpression(host.expression) &&
+ getSpecialPropertyAssignmentKind(host.expression) === SpecialPropertyAssignmentKind.PrototypeProperty) {
+ const symbol = getSymbolOfNode(host.expression.left);
+ return symbol && symbol.parent.valueDeclaration;
+ }
+ }
+
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0);
}
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 21df22e9ae3d2..10abad383e2d6 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -1826,6 +1826,8 @@ namespace ts {
return v && v.initializer;
case SyntaxKind.PropertyDeclaration:
return (node as PropertyDeclaration).initializer;
+ case SyntaxKind.PropertyAssignment:
+ return (node as PropertyAssignment).initializer;
}
}
@@ -1907,7 +1909,7 @@ namespace ts {
return parameter && parameter.symbol;
}
- export function getHostSignatureFromJSDoc(node: JSDocParameterTag): SignatureDeclaration | undefined {
+ export function getHostSignatureFromJSDoc(node: JSDocTag): SignatureDeclaration | undefined {
const host = getJSDocHost(node);
const decl = getSourceOfDefaultedAssignment(host) ||
getSourceOfAssignment(host) ||
diff --git a/tests/baselines/reference/jsdocTemplateTag2.symbols b/tests/baselines/reference/jsdocTemplateTag2.symbols
new file mode 100644
index 0000000000000..407943934ff9f
--- /dev/null
+++ b/tests/baselines/reference/jsdocTemplateTag2.symbols
@@ -0,0 +1,19 @@
+=== tests/cases/conformance/jsdoc/github17339.js ===
+var obj = {
+>obj : Symbol(obj, Decl(github17339.js, 0, 3))
+
+ /**
+ * @template T
+ * @param {T} a
+ * @returns {T}
+ */
+ x: function (a) {
+>x : Symbol(x, Decl(github17339.js, 0, 11))
+>a : Symbol(a, Decl(github17339.js, 6, 14))
+
+ return a;
+>a : Symbol(a, Decl(github17339.js, 6, 14))
+
+ },
+};
+
diff --git a/tests/baselines/reference/jsdocTemplateTag2.types b/tests/baselines/reference/jsdocTemplateTag2.types
new file mode 100644
index 0000000000000..d35724fb9b690
--- /dev/null
+++ b/tests/baselines/reference/jsdocTemplateTag2.types
@@ -0,0 +1,21 @@
+=== tests/cases/conformance/jsdoc/github17339.js ===
+var obj = {
+>obj : { [x: string]: any; x: (a: T) => T; }
+>{ /** * @template T * @param {T} a * @returns {T} */ x: function (a) { return a; },} : { [x: string]: any; x: (a: T) => T; }
+
+ /**
+ * @template T
+ * @param {T} a
+ * @returns {T}
+ */
+ x: function (a) {
+>x : (a: T) => T
+>function (a) { return a; } : (a: T) => T
+>a : T
+
+ return a;
+>a : T
+
+ },
+};
+
diff --git a/tests/cases/conformance/jsdoc/jsdocTemplateTag2.ts b/tests/cases/conformance/jsdoc/jsdocTemplateTag2.ts
new file mode 100644
index 0000000000000..b7e706f124aa4
--- /dev/null
+++ b/tests/cases/conformance/jsdoc/jsdocTemplateTag2.ts
@@ -0,0 +1,16 @@
+// @allowJs: true
+// @checkJs: true
+// @noEmit: true
+// @lib: dom,esnext
+// @Filename: github17339.js
+
+var obj = {
+ /**
+ * @template T
+ * @param {T} a
+ * @returns {T}
+ */
+ x: function (a) {
+ return a;
+ },
+};
From 3fed87e260f3bab9c7d2834bfa32aeb1f954a962 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 9 May 2018 09:16:07 -0700
Subject: [PATCH 41/49] Fix fourslash failures
---
src/compiler/binder.ts | 24 ++++++++++++-------
src/services/codefixes/fixUnusedIdentifier.ts | 2 +-
2 files changed, 16 insertions(+), 10 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index a0004d14b9b27..3b9899b7b7bf3 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -626,6 +626,9 @@ namespace ts {
}
function bindChildrenWorker(node: Node): void {
+ if (isJSDoc(node)) {
+ return bindJSDocComment(node);
+ }
if (checkUnreachable(node)) {
bindEachChild(node);
return;
@@ -711,9 +714,6 @@ namespace ts {
bindEachChild(node);
break;
}
- // Binding of JsDocComment should be done before the current block scope container changes.
- // because the scope of JsDocComment should not be affected by whether the current node is a
- // container or not.
if (hasJSDocNodes(node)) {
if (isInJavaScriptFile(node)) {
for (const j of node.jsDoc) {
@@ -726,8 +726,6 @@ namespace ts {
}
}
}
-
-
}
function isNarrowingExpression(expr: Expression): boolean {
@@ -2725,10 +2723,18 @@ namespace ts {
}
function bindTypeParameter(node: TypeParameterDeclaration) {
- // TODO: node.parent check *might* not be necessary, although that would mean combining isJSDocTemplateTag with getTypeAliasForJSDocTemplateTag
- // and then calling that instead
- if (node.parent && (node.parent.kind === SyntaxKind.InferType || isJSDocTemplateTag(node.parent))) {
- const container = node.parent.kind === SyntaxKind.InferType ? getInferTypeContainer(node.parent) : getHostSignatureFromJSDoc(node.parent);
+ if (isJSDocTemplateTag(node.parent)) {
+ const container = getTypeAliasForJSDocTemplateTag(node.parent, (node.parent.parent as JSDoc).tags) || getHostSignatureFromJSDoc(node.parent);
+ if (container) {
+ Debug.assert(!!container.locals);
+ declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
+ }
+ else {
+ declareSymbolAndAddToSymbolTable(node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
+ }
+ }
+ else if (node.parent.kind === SyntaxKind.InferType) {
+ const container = getInferTypeContainer(node.parent);
if (container) {
if (!container.locals) {
container.locals = createSymbolTable();
diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts
index dc40948ed1641..e2ff057d1fca1 100644
--- a/src/services/codefixes/fixUnusedIdentifier.ts
+++ b/src/services/codefixes/fixUnusedIdentifier.ts
@@ -66,7 +66,7 @@ namespace ts.codefix {
}
function getToken(sourceFile: SourceFile, pos: number): Node {
- const token = findPrecedingToken(pos, sourceFile);
+ const token = findPrecedingToken(pos, sourceFile, /*startNode*/ undefined, /*includeJsDoc*/ true);
// this handles var ["computed"] = 12;
return token.kind === SyntaxKind.CloseBracketToken ? findPrecedingToken(pos - 1, sourceFile) : token;
}
From 64623f5efc231b37cfb9a71e69f1bb2fccf4910c Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 9 May 2018 14:53:06 -0700
Subject: [PATCH 42/49] Stop pre-binding js type aliases
Next up, stop pre-binding js type parameters
---
src/compiler/binder.ts | 100 +++++++++++++++++++++-----------------
src/compiler/utilities.ts | 9 +---
2 files changed, 56 insertions(+), 53 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 3b9899b7b7bf3..b283b1284ef1e 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -118,7 +118,7 @@ namespace ts {
let thisParentContainer: Node; // Container one level up
let blockScopeContainer: Node;
let lastContainer: Node;
- let delayedTypedefs: { typeAlias: JSDocTypedefTag | JSDocCallbackTag, container: Node, lastContainer: Node, blockScopeContainer: Node, parent: Node }[];
+ let delayedTypeAliases: (JSDocTypedefTag | JSDocCallbackTag)[];
let seenThisKeyword: boolean;
// state used by control flow analysis
@@ -188,7 +188,7 @@ namespace ts {
thisParentContainer = undefined;
blockScopeContainer = undefined;
lastContainer = undefined;
- delayedTypedefs = undefined;
+ delayedTypeAliases = undefined;
seenThisKeyword = false;
currentFlow = undefined;
currentBreakTarget = undefined;
@@ -627,6 +627,7 @@ namespace ts {
function bindChildrenWorker(node: Node): void {
if (isJSDoc(node)) {
+ // TODO: This won't be needed after getting rid of early-jsdoc-if-any binding, because we won't be skipping anything
return bindJSDocComment(node);
}
if (checkUnreachable(node)) {
@@ -694,9 +695,6 @@ namespace ts {
case SyntaxKind.CallExpression:
bindCallExpressionFlow(node);
break;
- case SyntaxKind.JSDocComment:
- bindJSDocComment(node);
- break;
case SyntaxKind.JSDocTypedefTag:
case SyntaxKind.JSDocCallbackTag:
bindJSDocTypeAlias(node as JSDocTypedefTag | JSDocCallbackTag);
@@ -714,18 +712,7 @@ namespace ts {
bindEachChild(node);
break;
}
- if (hasJSDocNodes(node)) {
- if (isInJavaScriptFile(node)) {
- for (const j of node.jsDoc) {
- bind(j);
- }
- }
- else {
- for (const j of node.jsDoc) {
- setParentPointers(node, j);
- }
- }
- }
+ bindJSDoc(node);
}
function isNarrowingExpression(expr: Expression): boolean {
@@ -1380,22 +1367,17 @@ namespace ts {
function bindJSDocComment(node: JSDoc) {
forEachChild(node, n => {
// Skip type-alias-related tags, which are bound early.
- if (!isJSDocTypeAlias(n) && !getTypeAliasForJSDocTemplateTag(n, node.tags)) {
+ if (!getTypeAliasForJSDocTemplateTag(n, node.tags)) {
bind(n);
}
});
}
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag) {
- forEachChild(node, n => {
- // if the node has a fullName "A.B.C", that means symbol "C" was already bound
- // when we visit "fullName"; so when we visit the name "C" as the next child of
- // the jsDocTypedefTag, we should skip binding it.
- if (node.fullName && n === node.name && node.fullName.kind !== SyntaxKind.Identifier) {
- return;
- }
- bind(n);
- });
+ if (node.fullName) {
+ // TODO: Could be not needed?
+ setParentPointers(node, node.fullName);
+ }
}
function bindCallExpressionFlow(node: CallExpression) {
@@ -1755,21 +1737,44 @@ namespace ts {
}
function delayedBindJSDocTypedefTag() {
- if (!delayedTypedefs) {
+ if (!delayedTypeAliases) {
return;
}
const saveContainer = container;
const saveLastContainer = lastContainer;
const saveBlockScopeContainer = blockScopeContainer;
const saveParent = parent;
- for (const delay of delayedTypedefs) {
- ({ container, lastContainer, blockScopeContainer, parent } = delay);
- bindBlockScopedDeclaration(delay.typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
+ const saveCurrentFlow = currentFlow;
+ for (const typeAlias of delayedTypeAliases) {
+ const host = getJSDocHost(typeAlias);
+ container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
+ blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
+ currentFlow = { flags: FlowFlags.Start }
+ if (!typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
+ parent = typeAlias.parent;
+ bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
+ parent = typeAlias;
+ bind(typeAlias.typeExpression);
+ }
+ else {
+ parent = typeAlias;
+ forEachChild(typeAlias, n => {
+ // if the node has a fullName "A.B.C", that means symbol "C" was already bound
+ // when we visit "fullName"; so when we visit the name "C" as the next child of
+ // the jsDocTypedefTag, we should skip binding it.
+ if (n === typeAlias.name) {
+ // TODO: I don't think this can ever happen
+ return;
+ }
+ bind(n);
+ });
+ }
}
container = saveContainer;
lastContainer = saveLastContainer;
blockScopeContainer = saveBlockScopeContainer;
parent = saveParent;
+ currentFlow = saveCurrentFlow;
}
// The binder visits every node in the syntax tree so it is a convenient place to perform a single localized
@@ -1981,10 +1986,26 @@ namespace ts {
}
else if (!skipTransformFlagAggregation && (node.transformFlags & TransformFlags.HasComputedFlags) === 0) {
subtreeTransformFlags |= computeTransformFlagsForNode(node, 0);
+ bindJSDoc(node);
}
inStrictMode = saveInStrictMode;
}
+ function bindJSDoc(node: Node) {
+ if (hasJSDocNodes(node)) {
+ if (isInJavaScriptFile(node)) {
+ for (const j of node.jsDoc) {
+ bind(j);
+ }
+ }
+ else {
+ for (const j of node.jsDoc) {
+ setParentPointers(node, j);
+ }
+ }
+ }
+ }
+
function bindJSDocTypeAliasTagsIfAny(node: Node) {
if (!hasJSDocNodes(node)) {
return;
@@ -1996,12 +2017,6 @@ namespace ts {
}
for (const tag of jsDoc.tags) {
- if (isJSDocTypeAlias(tag)) {
- const savedParent = parent;
- parent = jsDoc;
- bind(tag);
- parent = savedParent;
- }
// Bind template tags that have a typedef or callback tag in the same comment.
// The typedef/callback tag is the container of the template.
const alias = getTypeAliasForJSDocTemplateTag(tag, jsDoc.tags);
@@ -2240,13 +2255,8 @@ namespace ts {
SymbolFlags.Property;
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
case SyntaxKind.JSDocTypedefTag:
- case SyntaxKind.JSDocCallbackTag: {
- const { fullName } = node as JSDocTypedefTag;
- if (!fullName || fullName.kind === SyntaxKind.Identifier) {
- (delayedTypedefs || (delayedTypedefs = [])).push({ typeAlias: node as JSDocTypedefTag | JSDocCallbackTag, container, lastContainer, blockScopeContainer, parent });
- }
- break;
- }
+ case SyntaxKind.JSDocCallbackTag:
+ return (delayedTypeAliases || (delayedTypeAliases = [])).push(node as JSDocTypedefTag | JSDocCallbackTag);
}
}
@@ -3836,6 +3846,6 @@ namespace ts {
*/
function setParentPointers(parent: Node, child: Node): void {
child.parent = parent;
- forEachChild(child, (childsChild) => setParentPointers(child, childsChild));
+ forEachChild(child, grandchild => setParentPointers(child, grandchild));
}
}
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 10abad383e2d6..c37522bb01051 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -558,14 +558,7 @@ namespace ts {
// Gets the nearest enclosing block scope container that has the provided node
// as a descendant, that is not the provided node.
export function getEnclosingBlockScopeContainer(node: Node): Node {
- let current = node.parent;
- while (current) {
- if (isBlockScope(current, current.parent)) {
- return current;
- }
-
- current = current.parent;
- }
+ return findAncestor(node.parent, current => isBlockScope(current, current.parent));
}
// Return display name of an identifier
From f44be81b094eb9ddb89f4118336a1fae69e56bb0 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 9 May 2018 15:05:52 -0700
Subject: [PATCH 43/49] Further cleanup of delayed js type alias binding
---
src/compiler/binder.ts | 16 +++-------------
1 file changed, 3 insertions(+), 13 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index b283b1284ef1e..3af0d36e211fb 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1750,24 +1750,14 @@ namespace ts {
container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
currentFlow = { flags: FlowFlags.Start }
+ parent = typeAlias;
+ bind(typeAlias.typeExpression);
if (!typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
parent = typeAlias.parent;
bindBlockScopedDeclaration(typeAlias, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes);
- parent = typeAlias;
- bind(typeAlias.typeExpression);
}
else {
- parent = typeAlias;
- forEachChild(typeAlias, n => {
- // if the node has a fullName "A.B.C", that means symbol "C" was already bound
- // when we visit "fullName"; so when we visit the name "C" as the next child of
- // the jsDocTypedefTag, we should skip binding it.
- if (n === typeAlias.name) {
- // TODO: I don't think this can ever happen
- return;
- }
- bind(n);
- });
+ bind(typeAlias.fullName);
}
}
container = saveContainer;
From e59acbc1ff54c807dd9686a3cadc5c3a63276d62 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 9 May 2018 16:29:47 -0700
Subject: [PATCH 44/49] Stop prebinding template tags too
This gets rid of prebinding entirely
---
src/compiler/binder.ts | 52 ++++--------------------------------------
1 file changed, 4 insertions(+), 48 deletions(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 3af0d36e211fb..3a65875834e70 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -626,10 +626,6 @@ namespace ts {
}
function bindChildrenWorker(node: Node): void {
- if (isJSDoc(node)) {
- // TODO: This won't be needed after getting rid of early-jsdoc-if-any binding, because we won't be skipping anything
- return bindJSDocComment(node);
- }
if (checkUnreachable(node)) {
bindEachChild(node);
return;
@@ -1364,15 +1360,6 @@ namespace ts {
}
}
- function bindJSDocComment(node: JSDoc) {
- forEachChild(node, n => {
- // Skip type-alias-related tags, which are bound early.
- if (!getTypeAliasForJSDocTemplateTag(n, node.tags)) {
- bind(n);
- }
- });
- }
-
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag) {
if (node.fullName) {
// TODO: Could be not needed?
@@ -1944,7 +1931,6 @@ namespace ts {
// Here the current node is "foo", which is a container, but the scope of "MyType" should
// not be inside "foo". Therefore we always bind @typedef before bind the parent node,
// and skip binding this tag later when binding all the other jsdoc tags.
- if (isInJavaScriptFile(node)) bindJSDocTypeAliasTagsIfAny(node);
// First we bind declaration nodes to a symbol if possible. We'll both create a symbol
// and then potentially add the symbol to an appropriate symbol table. Possible
@@ -1996,38 +1982,6 @@ namespace ts {
}
}
- function bindJSDocTypeAliasTagsIfAny(node: Node) {
- if (!hasJSDocNodes(node)) {
- return;
- }
-
- for (const jsDoc of node.jsDoc) {
- if (!jsDoc.tags) {
- continue;
- }
-
- for (const tag of jsDoc.tags) {
- // Bind template tags that have a typedef or callback tag in the same comment.
- // The typedef/callback tag is the container of the template.
- const alias = getTypeAliasForJSDocTemplateTag(tag, jsDoc.tags);
- if (alias) {
- const savedContainer = container;
- const savedParent = parent;
- container = alias;
- parent = jsDoc;
- alias.locals = alias.locals || createSymbolTable();
- bind(tag);
- container = savedContainer;
- parent = savedParent;
- }
- }
- }
- }
-
- function getTypeAliasForJSDocTemplateTag(tag: Node, siblings: NodeArray) {
- return isJSDocTemplateTag(tag) && find(siblings, isJSDocTypeAlias);
- }
-
function updateStrictModeStatementList(statements: NodeArray) {
if (!inStrictMode) {
for (const statement of statements) {
@@ -2724,9 +2678,11 @@ namespace ts {
function bindTypeParameter(node: TypeParameterDeclaration) {
if (isJSDocTemplateTag(node.parent)) {
- const container = getTypeAliasForJSDocTemplateTag(node.parent, (node.parent.parent as JSDoc).tags) || getHostSignatureFromJSDoc(node.parent);
+ const container = find((node.parent.parent as JSDoc).tags, isJSDocTypeAlias) || getHostSignatureFromJSDoc(node.parent);
if (container) {
- Debug.assert(!!container.locals);
+ if (!container.locals) {
+ container.locals = createSymbolTable();
+ }
declareSymbol(container.locals, /*parent*/ undefined, node, SymbolFlags.TypeParameter, SymbolFlags.TypeParameterExcludes);
}
else {
From 7a4ac26691423025b60e86e68e971d2d009fbb31 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 9 May 2018 16:34:50 -0700
Subject: [PATCH 45/49] Remove TODO
---
src/compiler/binder.ts | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 3a65875834e70..a3641b726f18f 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1362,7 +1362,6 @@ namespace ts {
function bindJSDocTypeAlias(node: JSDocTypedefTag | JSDocCallbackTag) {
if (node.fullName) {
- // TODO: Could be not needed?
setParentPointers(node, node.fullName);
}
}
From 0e139164454e6520b0d8932641a0946b94b8d49f Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Fri, 11 May 2018 13:31:17 -0700
Subject: [PATCH 46/49] Fix lint
---
src/compiler/binder.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index a3641b726f18f..d95001b104106 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -1735,7 +1735,7 @@ namespace ts {
const host = getJSDocHost(typeAlias);
container = findAncestor(host.parent, n => !!(getContainerFlags(n) & ContainerFlags.IsContainer)) || file;
blockScopeContainer = getEnclosingBlockScopeContainer(host) || file;
- currentFlow = { flags: FlowFlags.Start }
+ currentFlow = { flags: FlowFlags.Start };
parent = typeAlias;
bind(typeAlias.typeExpression);
if (!typeAlias.fullName || typeAlias.fullName.kind === SyntaxKind.Identifier) {
From 4f61e7192e7a7d4df70c161a96bdb95d318d0fd4 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 16 May 2018 14:12:09 -0700
Subject: [PATCH 47/49] Finish merge with use-jsdoc-aliases
---
src/compiler/checker.ts | 10 ++++---
.../jsQuickInfoGenerallyAcceptableSize.ts | 6 ++--
...ParametershasInstantiatedSignatureHelp.tsx | 4 +--
.../fourslash/server/jsdocCallbackTag.ts | 30 +++++++++++++------
4 files changed, 32 insertions(+), 18 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index c2b11aab15247..021509277a3e2 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4037,8 +4037,9 @@ namespace ts {
function determineIfDeclarationIsVisible() {
switch (node.kind) {
+ case SyntaxKind.JSDocCallbackTag:
case SyntaxKind.JSDocTypedefTag:
- // Top-level jsdoc typedefs are considered exported
+ // Top-level jsdoc type aliases are considered exported
// First parent is comment node, second is hosting declaration or token; we only care about those tokens or declarations whose parent is a source file
return !!(node.parent && node.parent.parent && node.parent.parent.parent && isSourceFile(node.parent.parent.parent));
case SyntaxKind.BindingElement:
@@ -5181,9 +5182,10 @@ namespace ts {
function getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol: Symbol): TypeParameter[] {
let result: TypeParameter[];
for (const node of symbol.declarations) {
- if (node.kind === SyntaxKind.InterfaceDeclaration || node.kind === SyntaxKind.ClassDeclaration ||
- node.kind === SyntaxKind.ClassExpression || node.kind === SyntaxKind.TypeAliasDeclaration ||
- isJSDocTypeAlias(node)) {
+ if (node.kind === SyntaxKind.InterfaceDeclaration ||
+ node.kind === SyntaxKind.ClassDeclaration ||
+ node.kind === SyntaxKind.ClassExpression ||
+ isTypeAlias(node)) {
const declaration = node;
const typeParameters = getEffectiveTypeParameterDeclarations(declaration);
if (typeParameters) {
diff --git a/tests/cases/fourslash/jsQuickInfoGenerallyAcceptableSize.ts b/tests/cases/fourslash/jsQuickInfoGenerallyAcceptableSize.ts
index a6fa8059481c4..8a76a47dc8adb 100644
--- a/tests/cases/fourslash/jsQuickInfoGenerallyAcceptableSize.ts
+++ b/tests/cases/fourslash/jsQuickInfoGenerallyAcceptableSize.ts
@@ -6,7 +6,7 @@
////// Data table
/////**
//// @typedef DataTableThing
-//// @type {Thing}
+//// @type {Object}
//// @property {function(TagCollection, Location, string, string, Infotable):void} AddDataTableEntries - (arg0: tags, arg1: location, arg2: source, arg3: sourceType, arg4: values) Add multiple data table entries.
//// @property {function(TagCollection, Location, string, string, Infotable):string} AddDataTableEntry - (arg0: tags, arg1: location, arg2: source, arg3: sourceType, arg4: values) Add a new data table entry.
//// @property {function(TagCollection, Location, string, string, Infotable):void} AddOrUpdateDataTableEntries - (arg0: tags, arg1: location, arg2: source, arg3: sourceType, arg4: values) Add or update multiple data table entries.
@@ -206,7 +206,7 @@
//// * Another thing
//// * @type {SomeCallback}
//// */
-////var another/*2*/Thing = function(a, b) {}
+////var anotherThing/*2*/ = function(a, b) {}
verify.quickInfoAt("1", "var doSomething: (dataTable: DataTableThing) => void", "Do something");
-verify.quickInfoAt("2", "var anotherThing: any", "Another thing"); // TODO: #23947
+verify.quickInfoAt("2", "var anotherThing: SomeCallback", "Another thing");
diff --git a/tests/cases/fourslash/jsxWithTypeParametershasInstantiatedSignatureHelp.tsx b/tests/cases/fourslash/jsxWithTypeParametershasInstantiatedSignatureHelp.tsx
index e317fe338edb8..71859594cea04 100644
--- a/tests/cases/fourslash/jsxWithTypeParametershasInstantiatedSignatureHelp.tsx
+++ b/tests/cases/fourslash/jsxWithTypeParametershasInstantiatedSignatureHelp.tsx
@@ -14,6 +14,6 @@
//// (*2*/SFC/>);
goTo.marker("1");
-verify.currentSignatureHelpIs("SFC(_props: Record): string");
+verify.signatureHelp({ text: "SFC(_props: Record): string" });
goTo.marker("2");
-verify.currentSignatureHelpIs("SFC(_props: Record): string");
+verify.signatureHelp({ text: "SFC(_props: Record): string" });
diff --git a/tests/cases/fourslash/server/jsdocCallbackTag.ts b/tests/cases/fourslash/server/jsdocCallbackTag.ts
index b9a2fcdf3156d..700419cd6824e 100644
--- a/tests/cases/fourslash/server/jsdocCallbackTag.ts
+++ b/tests/cases/fourslash/server/jsdocCallbackTag.ts
@@ -26,17 +26,29 @@
//// t(/*4*/"!", /*5*/12, /*6*/false);
goTo.marker("1");
-verify.quickInfoIs("var t: (eventName: string, eventName2: string | number, eventName3: any) => number");
+verify.quickInfoIs("var t: FooHandler");
goTo.marker("2");
-verify.quickInfoIs("var t2: (eventName?: string, eventName2?: string) => any");
+verify.quickInfoIs("var t2: FooHandler2");
goTo.marker("3");
verify.quickInfoIs("type FooHandler2 = (eventName?: string, eventName2?: string) => any", "- What, another one?");
goTo.marker("8");
verify.quickInfoIs("type FooHandler = (eventName: string, eventName2: string | number, eventName3: any) => number", "- A kind of magic");
-goTo.marker('4');
-verify.currentSignatureHelpIs("t(eventName: string, eventName2: string | number, eventName3: any): number");
-verify.currentParameterHelpArgumentDocCommentIs("- So many words");
-goTo.marker('5');
-verify.currentParameterHelpArgumentDocCommentIs("- Silence is golden");
-goTo.marker('6');
-verify.currentParameterHelpArgumentDocCommentIs("- Osterreich mos def");
+verify.signatureHelp({
+ marker: '4',
+ text: "t(eventName: string, eventName2: string | number, eventName3: any): number",
+ parameterDocComment: "- So many words",
+ tags: [{ name: "callback", text: "FooHandler - A kind of magic" },
+ { name: "type", text: "{FooHandler} callback" }]
+});
+verify.signatureHelp({
+ marker: '5',
+ parameterDocComment: "- Silence is golden",
+ tags: [{ name: "callback", text: "FooHandler - A kind of magic" },
+ { name: "type", text: "{FooHandler} callback" }]
+});
+verify.signatureHelp({
+ marker: '6',
+ parameterDocComment: "- Osterreich mos def",
+ tags: [{ name: "callback", text: "FooHandler - A kind of magic" },
+ { name: "type", text: "{FooHandler} callback" }]
+});
From 75d4d9548bfcf39a07865a5cf326ba8460413b58 Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Wed, 16 May 2018 14:52:53 -0700
Subject: [PATCH 48/49] Update callback tag baselines
---
tests/baselines/reference/callbackTag1.types | 2 +-
tests/baselines/reference/callbackTag2.types | 8 ++++----
tests/baselines/reference/callbackTag3.types | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/tests/baselines/reference/callbackTag1.types b/tests/baselines/reference/callbackTag1.types
index 759f36640115c..2f1e64c8edcb4 100644
--- a/tests/baselines/reference/callbackTag1.types
+++ b/tests/baselines/reference/callbackTag1.types
@@ -9,7 +9,7 @@ var x = 1
/** @type {Sid} smallId */
var sid = s => s + "!";
->sid : (s: string) => string
+>sid : Sid
>s => s + "!" : (s: string) => string
>s : string
>s + "!" : string
diff --git a/tests/baselines/reference/callbackTag2.types b/tests/baselines/reference/callbackTag2.types
index a6825f67524f3..5a13438ebbe9a 100644
--- a/tests/baselines/reference/callbackTag2.types
+++ b/tests/baselines/reference/callbackTag2.types
@@ -10,7 +10,7 @@ var x = 1
/** @type {Id} I actually wanted to write `const "120"` */
var one_twenty = s => "120";
->one_twenty : (t: string) => string
+>one_twenty : Id
>s => "120" : (s: string) => string
>s : string
>"120" : "120"
@@ -26,14 +26,14 @@ class SharedClass {
constructor() {
/** @type {SharedId} */
this.id;
->this.id : (ego: any) => any
+>this.id : SharedId
>this : this
->id : (ego: any) => any
+>id : SharedId
}
}
/** @type {SharedId} */
var outside = n => n + 1;
->outside : (ego: number) => number
+>outside : SharedId
>n => n + 1 : (n: number) => number
>n : number
>n + 1 : number
diff --git a/tests/baselines/reference/callbackTag3.types b/tests/baselines/reference/callbackTag3.types
index 4e9bfd9a0ced0..836897d0f308d 100644
--- a/tests/baselines/reference/callbackTag3.types
+++ b/tests/baselines/reference/callbackTag3.types
@@ -4,7 +4,7 @@
*/
/** @type {Miracle} smallId */
var sid = () => "!";
->sid : () => string
+>sid : Miracle
>() => "!" : () => string
>"!" : "!"
From 3431563870556bc589d3d1d07a73b254e08ceffe Mon Sep 17 00:00:00 2001
From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com>
Date: Thu, 17 May 2018 09:05:41 -0700
Subject: [PATCH 49/49] Rename getTypeParametersForAliasSymbol
The real fix is *probably* to rename Type.aliasTypeArguments to
aliasTypeParameters, but I want to make sure and then put it in a
separate PR.
---
src/compiler/checker.ts | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index e32e42b603c29..4c9a98951fae8 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -8426,7 +8426,7 @@ namespace ts {
if (!links.resolvedType) {
const aliasSymbol = getAliasSymbolForTypeNode(node);
links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), UnionReduction.Literal,
- aliasSymbol, getTypeParametersForAliasSymbol(aliasSymbol));
+ aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol));
}
return links.resolvedType;
}
@@ -8537,7 +8537,7 @@ namespace ts {
if (!links.resolvedType) {
const aliasSymbol = getAliasSymbolForTypeNode(node);
links.resolvedType = getIntersectionType(map(node.types, getTypeFromTypeNode),
- aliasSymbol, getTypeParametersForAliasSymbol(aliasSymbol));
+ aliasSymbol, getTypeArgumentsForAliasSymbol(aliasSymbol));
}
return links.resolvedType;
}
@@ -8846,7 +8846,7 @@ namespace ts {
const type = createObjectType(ObjectFlags.Mapped, node.symbol);
type.declaration = node;
type.aliasSymbol = getAliasSymbolForTypeNode(node);
- type.aliasTypeArguments = getTypeParametersForAliasSymbol(type.aliasSymbol);
+ type.aliasTypeArguments = getTypeArgumentsForAliasSymbol(type.aliasSymbol);
links.resolvedType = type;
// Eagerly resolve the constraint type which forces an error if the constraint type circularly
// references itself through one or more type aliases.
@@ -8955,7 +8955,7 @@ namespace ts {
if (!links.resolvedType) {
const checkType = getTypeFromTypeNode(node.checkType);
const aliasSymbol = getAliasSymbolForTypeNode(node);
- const aliasTypeArguments = getTypeParametersForAliasSymbol(aliasSymbol);
+ const aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol);
const allOuterTypeParameters = getOuterTypeParameters(node, /*includeThisTypes*/ true);
const outerTypeParameters = aliasTypeArguments ? allOuterTypeParameters : filter(allOuterTypeParameters, tp => isPossiblyReferencedInConditionalType(tp, node));
const root: ConditionalRoot = {
@@ -9072,7 +9072,7 @@ namespace ts {
else {
let type = createObjectType(ObjectFlags.Anonymous, node.symbol);
type.aliasSymbol = aliasSymbol;
- type.aliasTypeArguments = getTypeParametersForAliasSymbol(aliasSymbol);
+ type.aliasTypeArguments = getTypeArgumentsForAliasSymbol(aliasSymbol);
if (isJSDocTypeLiteral(node) && node.isArrayType) {
type = createArrayType(type);
}
@@ -9086,7 +9086,7 @@ namespace ts {
return isTypeAlias(node.parent) ? getSymbolOfNode(node.parent) : undefined;
}
- function getTypeParametersForAliasSymbol(symbol: Symbol) {
+ function getTypeArgumentsForAliasSymbol(symbol: Symbol) {
return symbol ? getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol) : undefined;
}