-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Add @callback jsdoc tag #9546
Add @callback jsdoc tag #9546
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -199,7 +199,7 @@ namespace ts { | |
// other kinds of value declarations take precedence over modules | ||
symbol.valueDeclaration = node; | ||
} | ||
} | ||
} | ||
} | ||
|
||
// Should not be called on a declaration with a computed property name, | ||
|
@@ -226,6 +226,7 @@ namespace ts { | |
return "__constructor"; | ||
case SyntaxKind.FunctionType: | ||
case SyntaxKind.CallSignature: | ||
case SyntaxKind.JSDocCallbackType: | ||
return "__call"; | ||
case SyntaxKind.ConstructorType: | ||
case SyntaxKind.ConstructSignature: | ||
|
@@ -264,6 +265,17 @@ namespace ts { | |
let functionType = <JSDocFunctionType>node.parent; | ||
let index = indexOf(functionType.parameters, node); | ||
return "p" + index; | ||
case SyntaxKind.JSDocParameterTag: | ||
// JSDocParameterTag can be used for either function parameter type notation | ||
// or in @callback tag declaration. We only create new symbol when the JSDocParameterTag | ||
// is part of a @callback tag declaration. | ||
Debug.assert(node.parent.kind === SyntaxKind.JSDocCallbackType, "Should not bind JSDocParameterTag if it is not part of a JSDocCallbackTag"); | ||
const preOrPostParameterName =(<JSDocParameterTag>node).preParameterName || (<JSDocParameterTag>node).postParameterName; | ||
if (preOrPostParameterName) { | ||
return preOrPostParameterName.text; | ||
} | ||
let jsDocCallbackType = <JSDocCallbackType>node.parent; | ||
return "p" + indexOf(jsDocCallbackType.parameterTags, node); | ||
case SyntaxKind.JSDocTypedefTag: | ||
const parentNode = node.parent && node.parent.parent; | ||
let nameFromParentNode: string; | ||
|
@@ -1199,6 +1211,7 @@ namespace ts { | |
case SyntaxKind.GetAccessor: | ||
case SyntaxKind.SetAccessor: | ||
case SyntaxKind.CallSignature: | ||
case SyntaxKind.JSDocCallbackType: | ||
case SyntaxKind.ConstructSignature: | ||
case SyntaxKind.IndexSignature: | ||
case SyntaxKind.FunctionType: | ||
|
@@ -1289,6 +1302,7 @@ namespace ts { | |
return declareSymbol(container.symbol.members, container.symbol, node, symbolFlags, symbolExcludes); | ||
|
||
case SyntaxKind.FunctionType: | ||
case SyntaxKind.JSDocCallbackType: | ||
case SyntaxKind.ConstructorType: | ||
case SyntaxKind.CallSignature: | ||
case SyntaxKind.ConstructSignature: | ||
|
@@ -1403,12 +1417,12 @@ namespace ts { | |
} | ||
} | ||
|
||
function bindFunctionOrConstructorType(node: SignatureDeclaration): void { | ||
function bindFunctionOrConstructorType(node: SignatureDeclaration | JSDocCallbackType): void { | ||
// For a given function symbol "<...>(...) => T" we want to generate a symbol identical | ||
// to the one we would get for: { <...>(...): T } | ||
// | ||
// We do that by making an anonymous type literal symbol, and then setting the function | ||
// symbol as its sole member. To the rest of the system, this symbol will be indistinguishable | ||
// symbol as its sole member. To the rest of the system, this symbol will be indistinguishable | ||
// from an actual type literal symbol you would have gotten had you used the long form. | ||
const symbol = createSymbol(SymbolFlags.Signature, getDeclarationName(node)); | ||
addDeclarationToSymbol(symbol, node, SymbolFlags.Signature); | ||
|
@@ -1820,7 +1834,8 @@ namespace ts { | |
case SyntaxKind.FunctionType: | ||
case SyntaxKind.ConstructorType: | ||
case SyntaxKind.JSDocFunctionType: | ||
return bindFunctionOrConstructorType(<SignatureDeclaration>node); | ||
case SyntaxKind.JSDocCallbackType: | ||
return bindFunctionOrConstructorType(<SignatureDeclaration | JSDocCallbackType>node); | ||
case SyntaxKind.TypeLiteral: | ||
case SyntaxKind.JSDocTypeLiteral: | ||
case SyntaxKind.JSDocRecordType: | ||
|
@@ -1846,8 +1861,13 @@ namespace ts { | |
case SyntaxKind.InterfaceDeclaration: | ||
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.Interface, SymbolFlags.InterfaceExcludes); | ||
case SyntaxKind.JSDocTypedefTag: | ||
case SyntaxKind.JSDocCallbackTag: | ||
case SyntaxKind.TypeAliasDeclaration: | ||
return bindBlockScopedDeclaration(<Declaration>node, SymbolFlags.TypeAlias, SymbolFlags.TypeAliasExcludes); | ||
case SyntaxKind.JSDocParameterTag: | ||
return container.kind === SyntaxKind.JSDocCallbackType | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect that this should just be an |
||
? bindJSDocParamTag(<JSDocParameterTag>node) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand why this is needed. Doesn't the addition of |
||
: undefined; | ||
case SyntaxKind.EnumDeclaration: | ||
return bindEnumDeclaration(<EnumDeclaration>node); | ||
case SyntaxKind.ModuleDeclaration: | ||
|
@@ -2113,6 +2133,10 @@ namespace ts { | |
} | ||
} | ||
|
||
function bindJSDocParamTag(node: JSDocParameterTag) { | ||
declareSymbolAndAddToSymbolTable(node, SymbolFlags.FunctionScopedVariable, SymbolFlags.ParameterExcludes); | ||
} | ||
|
||
function bindParameter(node: ParameterDeclaration) { | ||
if (!isDeclarationFile(file) && | ||
!isInAmbientContext(node) && | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2391,7 +2391,7 @@ namespace ts { | |
} | ||
|
||
function buildParameterDisplay(p: Symbol, writer: SymbolWriter, enclosingDeclaration?: Node, flags?: TypeFormatFlags, symbolStack?: Symbol[]) { | ||
const parameterNode = <ParameterDeclaration>p.valueDeclaration; | ||
const parameterNode = <ParameterDeclaration | JSDocParameterTag>p.valueDeclaration; | ||
if (isRestParameter(parameterNode)) { | ||
writePunctuation(writer, SyntaxKind.DotDotDotToken); | ||
} | ||
|
@@ -3178,6 +3178,14 @@ namespace ts { | |
type = checkExpressionCached((<BinaryExpression>declaration.parent).right); | ||
} | ||
} | ||
else if (declaration.kind == SyntaxKind.JSDocParameterTag) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when can this happen? |
||
if ((<JSDocParameterTag>declaration).typeExpression) { | ||
const jsDocType = (<JSDocParameterTag>declaration).typeExpression.type; | ||
if (jsDocType) { | ||
type = getTypeFromTypeNode(jsDocType); | ||
} | ||
} | ||
} | ||
|
||
if (type === undefined) { | ||
type = getWidenedTypeForVariableLikeDeclaration(<VariableLikeDeclaration>declaration, /*reportErrors*/ true); | ||
|
@@ -3653,18 +3661,23 @@ namespace ts { | |
} | ||
|
||
let type: Type; | ||
let declaration: JSDocTypedefTag | TypeAliasDeclaration = <JSDocTypedefTag>getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag); | ||
if (declaration) { | ||
if (declaration.jsDocTypeLiteral) { | ||
type = getTypeFromTypeNode(declaration.jsDocTypeLiteral); | ||
let declaration: Declaration; | ||
if (declaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocTypedefTag)) { | ||
const jsDocTypedefTag = <JSDocTypedefTag>declaration; | ||
if (jsDocTypedefTag.jsDocTypeLiteral) { | ||
type = getTypeFromTypeNode(jsDocTypedefTag.jsDocTypeLiteral); | ||
} | ||
else { | ||
type = getTypeFromTypeNode(declaration.typeExpression.type); | ||
else if (jsDocTypedefTag.typeExpression) { | ||
type = getTypeFromTypeNode(jsDocTypedefTag.typeExpression.type); | ||
} | ||
} | ||
else if (declaration = getDeclarationOfKind(symbol, SyntaxKind.JSDocCallbackTag)) { | ||
const jsDocCallbackTag = <JSDocCallbackTag>declaration; | ||
type = getTypeFromTypeNode(jsDocCallbackTag.type); | ||
} | ||
else { | ||
declaration = <TypeAliasDeclaration>getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); | ||
type = getTypeFromTypeNode(declaration.type); | ||
declaration = getDeclarationOfKind(symbol, SyntaxKind.TypeAliasDeclaration); | ||
type = getTypeFromTypeNode((<TypeAliasDeclaration>declaration).type); | ||
} | ||
|
||
if (popTypeResolution()) { | ||
|
@@ -3906,7 +3919,7 @@ namespace ts { | |
resolveObjectTypeMembers(type, source, typeParameters, typeArguments); | ||
} | ||
|
||
function createSignature(declaration: SignatureDeclaration, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], | ||
function createSignature(declaration: SignatureDeclaration | JSDocCallbackType, typeParameters: TypeParameter[], thisParameter: Symbol | undefined, parameters: Symbol[], | ||
resolvedReturnType: Type, typePredicate: TypePredicate, minArgumentCount: number, hasRestParameter: boolean, hasStringLiterals: boolean): Signature { | ||
const sig = new Signature(checker); | ||
sig.declaration = declaration; | ||
|
@@ -4421,26 +4434,35 @@ namespace ts { | |
return result; | ||
} | ||
|
||
function isJSDocOptionalParameterTag(node: JSDocParameterTag) { | ||
if (!node) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would rewrite this as |
||
return false; | ||
} | ||
|
||
if (node.isBracketed) { | ||
return true; | ||
} | ||
if (node.typeExpression && node.typeExpression.type) { | ||
return node.typeExpression.type.kind === SyntaxKind.JSDocOptionalType; | ||
} | ||
return false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Explicitly return |
||
} | ||
|
||
function isJSDocOptionalParameter(node: ParameterDeclaration) { | ||
if (node.flags & NodeFlags.JavaScriptFile) { | ||
if (node.type && node.type.kind === SyntaxKind.JSDocOptionalType) { | ||
return true; | ||
} | ||
|
||
const paramTag = getCorrespondingJSDocParameterTag(node); | ||
if (paramTag) { | ||
if (paramTag.isBracketed) { | ||
return true; | ||
} | ||
|
||
if (paramTag.typeExpression) { | ||
return paramTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType; | ||
} | ||
} | ||
return isJSDocOptionalParameterTag(getCorrespondingJSDocParameterTag(node)); | ||
} | ||
} | ||
|
||
function isOptionalParameter(node: ParameterDeclaration) { | ||
function isOptionalParameter(node: ParameterDeclaration | JSDocParameterTag) { | ||
if (isJSDocParameterTag(node)) { | ||
return isJSDocOptionalParameterTag(node); | ||
} | ||
|
||
if (hasQuestionToken(node) || isJSDocOptionalParameter(node)) { | ||
return true; | ||
} | ||
|
@@ -4474,7 +4496,7 @@ namespace ts { | |
} | ||
} | ||
|
||
function getSignatureFromDeclaration(declaration: SignatureDeclaration): Signature { | ||
function getSignatureFromDeclaration(declaration: SignatureDeclaration | JSDocCallbackType): Signature { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we have to getSignatureFromDeclaration for JSDocCallbackType and not for JSDocTypedefTag? |
||
const links = getNodeLinks(declaration); | ||
if (!links.resolvedSignature) { | ||
const parameters: Symbol[] = []; | ||
|
@@ -4487,8 +4509,9 @@ namespace ts { | |
// If this is a JSDoc construct signature, then skip the first parameter in the | ||
// parameter list. The first parameter represents the return type of the construct | ||
// signature. | ||
for (let i = isJSConstructSignature ? 1 : 0, n = declaration.parameters.length; i < n; i++) { | ||
const param = declaration.parameters[i]; | ||
const parameterNodes = isJSDocCallbackType(declaration) ? declaration.parameterTags : declaration.parameters; | ||
for (let i = isJSConstructSignature ? 1 : 0, n = parameterNodes.length; i < n; i++) { | ||
const param = parameterNodes[i]; | ||
|
||
let paramSymbol = param.symbol; | ||
// Include parameter symbol instead of property symbol in the signature | ||
|
@@ -4504,18 +4527,30 @@ namespace ts { | |
parameters.push(paramSymbol); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You already know whether or not you've originated from JSDoc-land or TS-land, so consider caching the value once and branching on that. |
||
if (param.type && param.type.kind === SyntaxKind.StringLiteralType) { | ||
hasStringLiterals = true; | ||
} | ||
|
||
if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) { | ||
if (minArgumentCount < 0) { | ||
minArgumentCount = i - (hasThisParameter ? 1 : 0); | ||
if (isJSDocParameterTag(param)) { | ||
if (isJSDocOptionalParameterTag(param)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Weirdly enough, this doesn't really affect anything in the type system, since specialized signatures only rejigger the order of overloads, and I don't think these JSDoc tags really cover overloads. You could consider removing this code, but I'd add a test if you're keeping it in. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is really almost the same as the code in the |
||
if (minArgumentCount < 0) { | ||
minArgumentCount = i - (hasThisParameter ? 1 : 0); | ||
} | ||
} | ||
else { | ||
minArgumentCount = -1; | ||
} | ||
} | ||
else { | ||
// If we see any required parameters, it means the prior ones were not in fact optional. | ||
minArgumentCount = -1; | ||
if (param.type && param.type.kind === SyntaxKind.StringLiteralType) { | ||
hasStringLiterals = true; | ||
} | ||
|
||
if (param.initializer || param.questionToken || param.dotDotDotToken || isJSDocOptionalParameter(param)) { | ||
if (minArgumentCount < 0) { | ||
minArgumentCount = i - (hasThisParameter ? 1 : 0); | ||
} | ||
} | ||
else { | ||
// If we see any required parameters, it means the prior ones were not in fact optional. | ||
minArgumentCount = -1; | ||
} | ||
} | ||
} | ||
|
||
|
@@ -4531,7 +4566,7 @@ namespace ts { | |
} | ||
|
||
if (minArgumentCount < 0) { | ||
minArgumentCount = declaration.parameters.length - (hasThisParameter ? 1 : 0); | ||
minArgumentCount = parameterNodes.length - (hasThisParameter ? 1 : 0); | ||
} | ||
if (isJSConstructSignature) { | ||
minArgumentCount--; | ||
|
@@ -4540,11 +4575,14 @@ namespace ts { | |
const classType = declaration.kind === SyntaxKind.Constructor ? | ||
getDeclaredTypeOfClassOrInterface(getMergedSymbol((<ClassDeclaration>declaration.parent).symbol)) | ||
: undefined; | ||
const typeParameters = classType ? classType.localTypeParameters : | ||
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : | ||
let typeParameters: TypeParameter[]; | ||
if (!isJSDocCallbackType(declaration)) { | ||
typeParameters = classType ? classType.localTypeParameters : | ||
declaration.typeParameters ? getTypeParametersFromDeclaration(declaration.typeParameters) : | ||
getTypeParametersFromJSDocTemplate(declaration); | ||
const returnType = getSignatureReturnTypeFromDeclaration(declaration, minArgumentCount, isJSConstructSignature, classType); | ||
const typePredicate = declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ? | ||
} | ||
const returnType = isJSDocCallbackType(declaration) ? voidType : getSignatureReturnTypeFromDeclaration(declaration, minArgumentCount, isJSConstructSignature, classType); | ||
const typePredicate = !isJSDocCallbackType(declaration) && declaration.type && declaration.type.kind === SyntaxKind.TypePredicate ? | ||
createTypePredicateFromTypePredicateNode(declaration.type as TypePredicateNode) : | ||
undefined; | ||
|
||
|
@@ -4603,6 +4641,7 @@ namespace ts { | |
case SyntaxKind.FunctionExpression: | ||
case SyntaxKind.ArrowFunction: | ||
case SyntaxKind.JSDocFunctionType: | ||
case SyntaxKind.JSDocCallbackType: | ||
// 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). | ||
|
@@ -4612,7 +4651,7 @@ namespace ts { | |
break; | ||
} | ||
} | ||
result.push(getSignatureFromDeclaration(<SignatureDeclaration>node)); | ||
result.push(getSignatureFromDeclaration(<SignatureDeclaration | JSDocCallbackType>node)); | ||
} | ||
} | ||
return result; | ||
|
@@ -5357,6 +5396,7 @@ namespace ts { | |
case SyntaxKind.JSDocTypeLiteral: | ||
case SyntaxKind.JSDocFunctionType: | ||
case SyntaxKind.JSDocRecordType: | ||
case SyntaxKind.JSDocCallbackType: | ||
return getTypeFromTypeLiteralOrFunctionOrConstructorTypeNode(node); | ||
// This function assumes that an identifier or qualified name is a type expression | ||
// Callers should first ensure this by calling isTypeNode | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use "arg" instead of "p". That's what Roslyn does, and what my JSDoc parser overhaul does for
JSDocFunctionType
too.