Skip to content

Add callback tag, with type parameters #23947

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 56 commits into from
May 17, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
f9f9157
Add initial tests
sandersn May 1, 2018
5fab72f
Add types
sandersn May 1, 2018
37076b3
Half of parsing (builds but does not pass tests)
sandersn May 1, 2018
248cd06
Parsing done; types are uglier; doesn't crash but doesn't pass
sandersn May 1, 2018
2ce53ba
Bind callback tag
sandersn May 1, 2018
8991502
Only bind param tags inside callback tags
sandersn May 1, 2018
7737c7c
Fix binding switch to only handle param tags once
sandersn May 1, 2018
ec7ddf8
Checking is 1/3 done or so.
sandersn May 1, 2018
7d2233a
Rename typeExpression to type (for some jsdoc)
sandersn May 1, 2018
f41a96b
Rename the rest of typeExpressions
sandersn May 1, 2018
64631e5
Few more checker changes
sandersn May 1, 2018
009c411
Revert "Rename the rest of typeExpressions"
sandersn May 1, 2018
0de72c2
Revert "Rename typeExpression to type (for some jsdoc)"
sandersn May 1, 2018
8ae8939
Finish undoing typeExpression rename
sandersn May 1, 2018
a96bdfd
Rename and improve getTypeParametersForAliasSymbol
sandersn May 2, 2018
6a0a5eb
Core checking works, but is flabbergastingly messy
sandersn May 2, 2018
0003c48
Callback return types work now
sandersn May 2, 2018
f4ac992
Fix crash in services
sandersn May 2, 2018
ad7fb64
Make github diff smaller
sandersn May 2, 2018
2410bee
Try to make github diff even smaller
sandersn May 2, 2018
0ae190b
Fix rename for callback tag
sandersn May 2, 2018
3ca5bf0
Fix nav bar for callback tag
sandersn May 3, 2018
819f19d
Handle ooorder callback tags
sandersn May 3, 2018
c2d7db7
Add ooorder callback tag test
sandersn May 3, 2018
7c6d66e
Parse comments for typedef/callback+display param comments
sandersn May 3, 2018
5e02518
Always export callbacks
sandersn May 3, 2018
12bd83d
Update baselines
sandersn May 3, 2018
dafb67d
Fix support for nested namespaced callbacks
sandersn May 3, 2018
9600db7
Callbacks support type parameters
sandersn May 4, 2018
0c28218
Template tags are now bound correctly
sandersn May 7, 2018
07cd96d
Test oorder template tags
sandersn May 7, 2018
1986b4d
Merge branch 'master' into jsdoc/callback
sandersn May 7, 2018
eb61b8b
Parser cleanup
sandersn May 7, 2018
e6e8ebf
Cleanup types and utilities
sandersn May 7, 2018
91d95f7
Handle callback more often in services
sandersn May 7, 2018
57fa3a5
Merge branch 'master' into jsdoc/callback
sandersn May 7, 2018
6973c5d
Cleanup of binder and checker
sandersn May 7, 2018
52cd1c7
More checker cleanup
sandersn May 7, 2018
072ae0d
Remove TODOs and one more cleanup
sandersn May 7, 2018
4c01063
Support parameter-less callback tags
sandersn May 7, 2018
831f44f
Remove extra bind call on template type parameters
sandersn May 8, 2018
9ec7f26
Bind template tag containers
sandersn May 8, 2018
3fed87e
Fix fourslash failures
sandersn May 9, 2018
64623f5
Stop pre-binding js type aliases
sandersn May 9, 2018
f44be81
Further cleanup of delayed js type alias binding
sandersn May 9, 2018
e59acbc
Stop prebinding template tags too
sandersn May 9, 2018
7a4ac26
Remove TODO
sandersn May 9, 2018
a073966
Merge branch 'master' into jsdoc/callback
sandersn May 11, 2018
0e13916
Fix lint
sandersn May 11, 2018
f75abe1
Merge branch 'master' into jsdoc/callback
sandersn May 16, 2018
4f61e71
Finish merge with use-jsdoc-aliases
sandersn May 16, 2018
cc011a6
Merge branch 'master' into jsdoc/callback
sandersn May 16, 2018
75d4d95
Update callback tag baselines
sandersn May 16, 2018
fcdc499
Merge branch 'master' into jsdoc/callback
sandersn May 16, 2018
4116fea
Merge branch 'master' into jsdoc/callback
sandersn May 17, 2018
3431563
Rename getTypeParametersForAliasSymbol
sandersn May 17, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 75 additions & 88 deletions src/compiler/binder.ts

Large diffs are not rendered by default.

172 changes: 103 additions & 69 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

133 changes: 94 additions & 39 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -489,6 +489,15 @@ namespace ts {
return visitNode(cbNode, (<JSDocTypedefTag>node).fullName) ||
visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression);
}
case SyntaxKind.JSDocCallbackTag:
return visitNode(cbNode, (node as JSDocCallbackTag).fullName) ||
visitNode(cbNode, (node as JSDocCallbackTag).typeExpression);
case SyntaxKind.JSDocSignature:
return visitNodes(cbNode, cbNodes, node.decorators) ||
visitNodes(cbNode, cbNodes, node.modifiers) ||
visitNodes(cbNode, cbNodes, (<SignatureDeclaration>node).typeParameters) ||
visitNodes(cbNode, cbNodes, (<SignatureDeclaration>node).parameters) ||
visitNode(cbNode, (<SignatureDeclaration>node).type);
case SyntaxKind.JSDocTypeLiteral:
if ((node as JSDocTypeLiteral).jsDocPropertyTags) {
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
Expand Down Expand Up @@ -6331,8 +6340,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 {
Expand Down Expand Up @@ -6386,7 +6396,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 */`
Expand Down Expand Up @@ -6503,8 +6513,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);
Expand All @@ -6516,7 +6525,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, indent);
break;
default:
tag = parseUnknownTag(atToken, tagName);
Expand All @@ -6531,8 +6543,11 @@ namespace ts {
// a badly malformed tag should not be added to the list of tags
return;
}
tag.comment = parseTagComments(indent + tag.end - tag.pos);
addTag(tag);
if (!tag.comment) {
// some tags, like typedef and callback, have already parsed their comments earlier
tag.comment = parseTagComments(indent + tag.end - tag.pos);
}
return tag;
}

function parseTagComments(indent: number): string | undefined {
Expand Down Expand Up @@ -6605,6 +6620,9 @@ namespace ts {
}

function addTag(tag: JSDocTag): void {
if (!tag) {
return;
}
if (!tags) {
tags = [tag];
tagsPos = tag.pos;
Expand Down Expand Up @@ -6665,9 +6683,9 @@ namespace ts {
typeExpression = tryParseTypeExpression();
}

const result = target === PropertyLikeParse.Parameter ?
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos) :
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
const result = target === PropertyLikeParse.Property ?
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos) :
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos);
let comment: string | undefined;
if (indent !== undefined) comment = parseTagComments(indent + scanner.getStartPos() - atToken.pos);
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name, target);
Expand Down Expand Up @@ -6771,27 +6789,17 @@ 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();

const typedefTag = <JSDocTypedefTag>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.comment = parseTagComments(indent);

typedefTag.typeExpression = typeExpression;
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
Expand Down Expand Up @@ -6826,23 +6834,69 @@ namespace ts {
}

return finishNode(typedefTag);
}

function parseJSDocTypeNameWithNamespace(flags: NodeFlags) {
const pos = scanner.getTokenPos();
const typeNameOrNamespaceName = parseJSDocIdentifierName();
function parseJSDocTypeNameWithNamespace(nested?: boolean) {
const pos = scanner.getTokenPos();
const typeNameOrNamespaceName = parseJSDocIdentifierName();

if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) {
const jsDocNamespaceNode = <JSDocNamespaceDeclaration>createNode(SyntaxKind.ModuleDeclaration, pos);
jsDocNamespaceNode.flags |= flags;
jsDocNamespaceNode.name = typeNameOrNamespaceName;
jsDocNamespaceNode.body = parseJSDocTypeNameWithNamespace(NodeFlags.NestedNamespace);
return finishNode(jsDocNamespaceNode);
if (typeNameOrNamespaceName && parseOptional(SyntaxKind.DotToken)) {
const jsDocNamespaceNode = <JSDocNamespaceDeclaration>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;
}

if (typeNameOrNamespaceName && flags & NodeFlags.NestedNamespace) {
typeNameOrNamespaceName.isInJSDocNamespace = true;
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();
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<JSDocParameterTag>, child);
}
const returnTag = tryParse(() => {
if (token() === SyntaxKind.AtToken) {
nextJSDocToken();
const tag = parseTag(indent);
if (tag && tag.kind === SyntaxKind.JSDocReturnTag) {
return tag as JSDocReturnTag;
}
}
});
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;
}
return typeNameOrNamespaceName;
}
}

Expand Down Expand Up @@ -6872,6 +6926,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;
}
Expand Down Expand Up @@ -6920,12 +6975,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);
Expand Down
21 changes: 19 additions & 2 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -413,9 +413,11 @@ namespace ts {
JSDocVariadicType,
JSDocComment,
JSDocTypeLiteral,
JSDocSignature,
JSDocTag,
JSDocAugmentsTag,
JSDocClassTag,
JSDocCallbackTag,
JSDocParameterTag,
JSDocReturnTag,
JSDocTypeTag,
Expand Down Expand Up @@ -2053,7 +2055,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 | JSDocTypedefTag | JSDocCallbackTag | JSDocSignature;

export interface ClassLikeDeclarationBase extends NamedDeclaration, JSDocContainer {
kind: SyntaxKind.ClassDeclaration | SyntaxKind.ClassExpression;
Expand Down Expand Up @@ -2387,6 +2389,21 @@ namespace ts {
typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
}

export interface JSDocCallbackTag extends JSDocTag, NamedDeclaration {
parent: JSDoc;
kind: SyntaxKind.JSDocCallbackTag;
fullName?: JSDocNamespaceDeclaration | Identifier;
name?: Identifier;
typeExpression: JSDocSignature;
}

export interface JSDocSignature extends JSDocType, Declaration {
kind: SyntaxKind.JSDocSignature;
typeParameters?: ReadonlyArray<JSDocTemplateTag>;
parameters: ReadonlyArray<JSDocParameterTag>;
type: JSDocReturnTag | undefined;
}

export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
parent: JSDoc;
name: EntityName;
Expand Down Expand Up @@ -4032,7 +4049,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 */
Expand Down
Loading