mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-09 07:55:10 -05:00
Merge pull request #17352 from Microsoft/jsdoc-param-type-literals
Parse jsdoc `@param` type literals
This commit is contained in:
@@ -1504,9 +1504,9 @@ namespace ts {
|
||||
return declareSymbol(container.symbol.exports, container.symbol, node, symbolFlags, symbolExcludes);
|
||||
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
case SyntaxKind.InterfaceDeclaration:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.JsxAttributes:
|
||||
// Interface/Object-types always have their children added to the 'members' of
|
||||
// their container. They are only accessible through an instance of their
|
||||
@@ -2103,8 +2103,9 @@ namespace ts {
|
||||
case SyntaxKind.ConstructorType:
|
||||
return bindFunctionOrConstructorType(<SignatureDeclaration>node);
|
||||
case SyntaxKind.TypeLiteral:
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
case SyntaxKind.MappedType:
|
||||
return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode);
|
||||
return bindAnonymousTypeWorker(node as TypeLiteralNode | MappedTypeNode | JSDocTypeLiteral);
|
||||
case SyntaxKind.ObjectLiteralExpression:
|
||||
return bindObjectLiteralExpression(<ObjectLiteralExpression>node);
|
||||
case SyntaxKind.FunctionExpression:
|
||||
@@ -2162,13 +2163,17 @@ namespace ts {
|
||||
case SyntaxKind.ModuleBlock:
|
||||
return updateStrictModeStatementList((<Block | ModuleBlock>node).statements);
|
||||
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
if (node.parent.kind !== SyntaxKind.JSDocTypeLiteral) {
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
return declareSymbolAndAddToSymbolTable(node as JSDocPropertyTag,
|
||||
(node as JSDocPropertyTag).isBracketed || ((node as JSDocPropertyTag).typeExpression && (node as JSDocPropertyTag).typeExpression.type.kind === SyntaxKind.JSDocOptionalType) ?
|
||||
SymbolFlags.Property | SymbolFlags.Optional : SymbolFlags.Property,
|
||||
SymbolFlags.PropertyExcludes);
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
return bindAnonymousTypeWorker(node as JSDocTypeLiteral);
|
||||
const propTag = node as JSDocPropertyLikeTag;
|
||||
const flags = propTag.isBracketed || propTag.typeExpression.type.kind === SyntaxKind.JSDocOptionalType ?
|
||||
SymbolFlags.Property | SymbolFlags.Optional :
|
||||
SymbolFlags.Property;
|
||||
return declareSymbolAndAddToSymbolTable(propTag, flags, SymbolFlags.PropertyExcludes);
|
||||
case SyntaxKind.JSDocTypedefTag: {
|
||||
const { fullName } = node as JSDocTypedefTag;
|
||||
if (!fullName || fullName.kind === SyntaxKind.Identifier) {
|
||||
|
||||
@@ -4493,8 +4493,8 @@ namespace ts {
|
||||
if (declaration.kind === SyntaxKind.ExportAssignment) {
|
||||
return links.type = checkExpression((<ExportAssignment>declaration).expression);
|
||||
}
|
||||
if (isInJavaScriptFile(declaration) && declaration.kind === SyntaxKind.JSDocPropertyTag && (<JSDocPropertyTag>declaration).typeExpression) {
|
||||
return links.type = getTypeFromTypeNode((<JSDocPropertyTag>declaration).typeExpression.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)) {
|
||||
@@ -5074,20 +5074,9 @@ namespace ts {
|
||||
return unknownType;
|
||||
}
|
||||
|
||||
let declaration: JSDocTypedefTag | TypeAliasDeclaration = getDeclarationOfKind<JSDocTypedefTag>(symbol, SyntaxKind.JSDocTypedefTag);
|
||||
let type: Type;
|
||||
if (declaration) {
|
||||
if (declaration.jsDocTypeLiteral) {
|
||||
type = getTypeFromTypeNode(declaration.jsDocTypeLiteral);
|
||||
}
|
||||
else {
|
||||
type = getTypeFromTypeNode(declaration.typeExpression.type);
|
||||
}
|
||||
}
|
||||
else {
|
||||
declaration = getDeclarationOfKind<TypeAliasDeclaration>(symbol, SyntaxKind.TypeAliasDeclaration);
|
||||
type = getTypeFromTypeNode(declaration.type);
|
||||
}
|
||||
const declaration = <JSDocTypedefTag | TypeAliasDeclaration>findDeclaration(
|
||||
symbol, d => d.kind === SyntaxKind.JSDocTypedefTag || d.kind === SyntaxKind.TypeAliasDeclaration);
|
||||
let type = getTypeFromTypeNode(declaration.kind === SyntaxKind.JSDocTypedefTag ? declaration.typeExpression : declaration.type);
|
||||
|
||||
if (popTypeResolution()) {
|
||||
const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
|
||||
@@ -7662,9 +7651,12 @@ namespace ts {
|
||||
links.resolvedType = emptyTypeLiteralType;
|
||||
}
|
||||
else {
|
||||
const type = createObjectType(ObjectFlags.Anonymous, node.symbol);
|
||||
let type = createObjectType(ObjectFlags.Anonymous, node.symbol);
|
||||
type.aliasSymbol = aliasSymbol;
|
||||
type.aliasTypeArguments = getAliasTypeArgumentsForTypeNode(node);
|
||||
if (isJSDocTypeLiteral(node) && node.isArrayType) {
|
||||
type = createArrayType(type);
|
||||
}
|
||||
links.resolvedType = type;
|
||||
}
|
||||
}
|
||||
@@ -7898,7 +7890,8 @@ namespace ts {
|
||||
case SyntaxKind.ParenthesizedType:
|
||||
case SyntaxKind.JSDocNonNullableType:
|
||||
case SyntaxKind.JSDocOptionalType:
|
||||
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode>node).type);
|
||||
case SyntaxKind.JSDocTypeExpression:
|
||||
return getTypeFromTypeNode((<ParenthesizedTypeNode | JSDocTypeReferencingNode | JSDocTypeExpression>node).type);
|
||||
case SyntaxKind.FunctionType:
|
||||
case SyntaxKind.ConstructorType:
|
||||
case SyntaxKind.TypeLiteral:
|
||||
@@ -22646,8 +22639,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
if (entityName.parent!.kind === SyntaxKind.JSDocParameterTag) {
|
||||
const parameter = getParameterFromJSDoc(entityName.parent as JSDocParameterTag);
|
||||
return parameter && parameter.symbol;
|
||||
return getParameterSymbolFromJSDoc(entityName.parent as JSDocParameterTag);
|
||||
}
|
||||
|
||||
if (entityName.parent.kind === SyntaxKind.TypeParameter && entityName.parent.parent.kind === SyntaxKind.JSDocTemplateTag) {
|
||||
|
||||
@@ -59,6 +59,9 @@ namespace ts {
|
||||
* @param node a given node to visit its children
|
||||
* @param cbNode a callback to be invoked for all child nodes
|
||||
* @param cbNodes a callback to be invoked for embedded array
|
||||
*
|
||||
* @remarks `forEachChild` must visit the children of a node in the order
|
||||
* that they appear in the source code. The language service depends on this property to locate nodes by position.
|
||||
*/
|
||||
export function forEachChild<T>(node: Node, cbNode: (node: Node) => T | undefined, cbNodes?: (nodes: NodeArray<Node>) => T | undefined): T | undefined {
|
||||
if (!node || node.kind <= SyntaxKind.LastToken) {
|
||||
@@ -407,9 +410,15 @@ namespace ts {
|
||||
case SyntaxKind.JSDocComment:
|
||||
return visitNodes(cbNode, cbNodes, (<JSDoc>node).tags);
|
||||
case SyntaxKind.JSDocParameterTag:
|
||||
return visitNode(cbNode, (<JSDocParameterTag>node).preParameterName) ||
|
||||
visitNode(cbNode, (<JSDocParameterTag>node).typeExpression) ||
|
||||
visitNode(cbNode, (<JSDocParameterTag>node).postParameterName);
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
if ((node as JSDocPropertyLikeTag).isNameFirst) {
|
||||
return visitNode(cbNode, (<JSDocPropertyLikeTag>node).name) ||
|
||||
visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression);
|
||||
}
|
||||
else {
|
||||
return visitNode(cbNode, (<JSDocPropertyLikeTag>node).typeExpression) ||
|
||||
visitNode(cbNode, (<JSDocPropertyLikeTag>node).name);
|
||||
}
|
||||
case SyntaxKind.JSDocReturnTag:
|
||||
return visitNode(cbNode, (<JSDocReturnTag>node).typeExpression);
|
||||
case SyntaxKind.JSDocTypeTag:
|
||||
@@ -419,15 +428,20 @@ namespace ts {
|
||||
case SyntaxKind.JSDocTemplateTag:
|
||||
return visitNodes(cbNode, cbNodes, (<JSDocTemplateTag>node).typeParameters);
|
||||
case SyntaxKind.JSDocTypedefTag:
|
||||
return visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) ||
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).fullName) ||
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).name) ||
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).jsDocTypeLiteral);
|
||||
if ((node as JSDocTypedefTag).typeExpression &&
|
||||
(node as JSDocTypedefTag).typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
|
||||
return visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression) ||
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).fullName);
|
||||
}
|
||||
else {
|
||||
return visitNode(cbNode, (<JSDocTypedefTag>node).fullName) ||
|
||||
visitNode(cbNode, (<JSDocTypedefTag>node).typeExpression);
|
||||
}
|
||||
case SyntaxKind.JSDocTypeLiteral:
|
||||
return visitNodes(cbNode, cbNodes, (<JSDocTypeLiteral>node).jsDocPropertyTags);
|
||||
case SyntaxKind.JSDocPropertyTag:
|
||||
return visitNode(cbNode, (<JSDocPropertyTag>node).typeExpression) ||
|
||||
visitNode(cbNode, (<JSDocPropertyTag>node).name);
|
||||
for (const tag of (node as JSDocTypeLiteral).jsDocPropertyTags) {
|
||||
visitNode(cbNode, tag);
|
||||
}
|
||||
return;
|
||||
case SyntaxKind.PartiallyEmittedExpression:
|
||||
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
|
||||
}
|
||||
@@ -1949,14 +1963,18 @@ namespace ts {
|
||||
break;
|
||||
}
|
||||
dotPos = scanner.getStartPos();
|
||||
const node: QualifiedName = <QualifiedName>createNode(SyntaxKind.QualifiedName, entity.pos);
|
||||
node.left = entity;
|
||||
node.right = parseRightSideOfDot(allowReservedWords);
|
||||
entity = finishNode(node);
|
||||
entity = createQualifiedName(entity, parseRightSideOfDot(allowReservedWords));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
function createQualifiedName(entity: EntityName, name: Identifier): QualifiedName {
|
||||
const node = createNode(SyntaxKind.QualifiedName, entity.pos) as QualifiedName;
|
||||
node.left = entity;
|
||||
node.right = name;
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseRightSideOfDot(allowIdentifierNames: boolean): Identifier {
|
||||
// Technically a keyword is valid here as all identifiers and keywords are identifier names.
|
||||
// However, often we'll encounter this in error situations when the identifier or keyword
|
||||
@@ -6163,6 +6181,11 @@ namespace ts {
|
||||
SavingComments,
|
||||
}
|
||||
|
||||
const enum PropertyLikeParse {
|
||||
Property,
|
||||
Parameter,
|
||||
}
|
||||
|
||||
export function parseJSDocCommentWorker(start: number, length: number): JSDoc {
|
||||
const content = sourceText;
|
||||
start = start || 0;
|
||||
@@ -6342,7 +6365,7 @@ namespace ts {
|
||||
case "arg":
|
||||
case "argument":
|
||||
case "param":
|
||||
tag = parseParameterOrPropertyTag(atToken, tagName, /*shouldParseParamTag*/ true);
|
||||
tag = parseParameterOrPropertyTag(atToken, tagName, PropertyLikeParse.Parameter);
|
||||
break;
|
||||
case "return":
|
||||
case "returns":
|
||||
@@ -6465,10 +6488,10 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function parseBracketNameInPropertyAndParamTag(): { name: Identifier, isBracketed: boolean } {
|
||||
// Looking for something like '[foo]' or 'foo'
|
||||
function parseBracketNameInPropertyAndParamTag(): { name: EntityName, isBracketed: boolean } {
|
||||
// Looking for something like '[foo]', 'foo', '[foo.bar]' or 'foo.bar'
|
||||
const isBracketed = parseOptional(SyntaxKind.OpenBracketToken);
|
||||
const name = parseJSDocIdentifierName(/*createIfMissing*/ true);
|
||||
const name = parseJSDocEntityName();
|
||||
if (isBracketed) {
|
||||
skipWhitespace();
|
||||
|
||||
@@ -6483,33 +6506,72 @@ namespace ts {
|
||||
return { name, isBracketed };
|
||||
}
|
||||
|
||||
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, shouldParseParamTag: boolean): JSDocPropertyTag | JSDocParameterTag {
|
||||
function isObjectOrObjectArrayTypeReference(node: TypeNode): boolean {
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ObjectKeyword:
|
||||
return true;
|
||||
case SyntaxKind.ArrayType:
|
||||
return isObjectOrObjectArrayTypeReference((node as ArrayTypeNode).elementType);
|
||||
default:
|
||||
return isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.escapedText === "Object";
|
||||
}
|
||||
}
|
||||
|
||||
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse.Parameter): JSDocParameterTag;
|
||||
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse.Property): JSDocPropertyTag;
|
||||
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse): JSDocPropertyLikeTag {
|
||||
let typeExpression = tryParseTypeExpression();
|
||||
let isNameFirst = !typeExpression;
|
||||
skipWhitespace();
|
||||
|
||||
const { name, isBracketed } = parseBracketNameInPropertyAndParamTag();
|
||||
skipWhitespace();
|
||||
|
||||
let preName: Identifier, postName: Identifier;
|
||||
if (typeExpression) {
|
||||
postName = name;
|
||||
}
|
||||
else {
|
||||
preName = name;
|
||||
if (isNameFirst) {
|
||||
typeExpression = tryParseTypeExpression();
|
||||
}
|
||||
|
||||
const result = shouldParseParamTag ?
|
||||
const result: JSDocPropertyLikeTag = target === PropertyLikeParse.Parameter ?
|
||||
<JSDocParameterTag>createNode(SyntaxKind.JSDocParameterTag, atToken.pos) :
|
||||
<JSDocPropertyTag>createNode(SyntaxKind.JSDocPropertyTag, atToken.pos);
|
||||
const nestedTypeLiteral = parseNestedTypeLiteral(typeExpression, name);
|
||||
if (nestedTypeLiteral) {
|
||||
typeExpression = nestedTypeLiteral;
|
||||
isNameFirst = true;
|
||||
}
|
||||
result.atToken = atToken;
|
||||
result.tagName = tagName;
|
||||
result.preParameterName = preName;
|
||||
result.typeExpression = typeExpression;
|
||||
result.postParameterName = postName;
|
||||
result.name = postName || preName;
|
||||
result.name = name;
|
||||
result.isNameFirst = isNameFirst;
|
||||
result.isBracketed = isBracketed;
|
||||
return finishNode(result);
|
||||
|
||||
}
|
||||
|
||||
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression, name: EntityName) {
|
||||
if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) {
|
||||
const typeLiteralExpression = <JSDocTypeExpression>createNode(SyntaxKind.JSDocTypeExpression, scanner.getTokenPos());
|
||||
let child: JSDocParameterTag | false;
|
||||
let jsdocTypeLiteral: JSDocTypeLiteral;
|
||||
const start = scanner.getStartPos();
|
||||
let children: JSDocParameterTag[];
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Parameter, name))) {
|
||||
if (!children) {
|
||||
children = [];
|
||||
}
|
||||
children.push(child);
|
||||
}
|
||||
if (children) {
|
||||
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
|
||||
jsdocTypeLiteral.jsDocPropertyTags = children;
|
||||
if (typeExpression.type.kind === SyntaxKind.ArrayType) {
|
||||
jsdocTypeLiteral.isArrayType = true;
|
||||
}
|
||||
typeLiteralExpression.type = finishNode(jsdocTypeLiteral);
|
||||
return finishNode(typeLiteralExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseReturnTag(atToken: AtToken, tagName: Identifier): JSDocReturnTag {
|
||||
@@ -6573,69 +6635,44 @@ namespace ts {
|
||||
rightNode = rightNode.body;
|
||||
}
|
||||
}
|
||||
typedefTag.typeExpression = typeExpression;
|
||||
skipWhitespace();
|
||||
|
||||
if (typeExpression) {
|
||||
if (isObjectTypeReference(typeExpression.type)) {
|
||||
typedefTag.jsDocTypeLiteral = scanChildTags();
|
||||
typedefTag.typeExpression = typeExpression;
|
||||
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
|
||||
let child: JSDocTypeTag | JSDocPropertyTag | false;
|
||||
let jsdocTypeLiteral: JSDocTypeLiteral;
|
||||
let alreadyHasTypeTag = false;
|
||||
const start = scanner.getStartPos();
|
||||
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.Property))) {
|
||||
if (!jsdocTypeLiteral) {
|
||||
jsdocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, start);
|
||||
}
|
||||
if (child.kind === SyntaxKind.JSDocTypeTag) {
|
||||
if (alreadyHasTypeTag) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
jsdocTypeLiteral.jsDocTypeTag = child;
|
||||
alreadyHasTypeTag = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!jsdocTypeLiteral.jsDocPropertyTags) {
|
||||
jsdocTypeLiteral.jsDocPropertyTags = [] as MutableNodeArray<JSDocPropertyTag>;
|
||||
}
|
||||
(jsdocTypeLiteral.jsDocPropertyTags as MutableNodeArray<JSDocPropertyTag>).push(child);
|
||||
}
|
||||
}
|
||||
if (!typedefTag.jsDocTypeLiteral) {
|
||||
typedefTag.jsDocTypeLiteral = <JSDocTypeLiteral>typeExpression.type;
|
||||
if (jsdocTypeLiteral) {
|
||||
if (typeExpression && typeExpression.type.kind === SyntaxKind.ArrayType) {
|
||||
jsdocTypeLiteral.isArrayType = true;
|
||||
}
|
||||
typedefTag.typeExpression = finishNode(jsdocTypeLiteral);
|
||||
}
|
||||
}
|
||||
else {
|
||||
typedefTag.jsDocTypeLiteral = scanChildTags();
|
||||
}
|
||||
|
||||
return finishNode(typedefTag);
|
||||
|
||||
function isObjectTypeReference(node: TypeNode) {
|
||||
return node.kind === SyntaxKind.ObjectKeyword ||
|
||||
isTypeReferenceNode(node) && ts.isIdentifier(node.typeName) && node.typeName.escapedText === "Object";
|
||||
}
|
||||
|
||||
function scanChildTags(): JSDocTypeLiteral {
|
||||
const jsDocTypeLiteral = <JSDocTypeLiteral>createNode(SyntaxKind.JSDocTypeLiteral, scanner.getStartPos());
|
||||
let resumePos = scanner.getStartPos();
|
||||
let canParseTag = true;
|
||||
let seenAsterisk = false;
|
||||
let parentTagTerminated = false;
|
||||
|
||||
while (token() !== SyntaxKind.EndOfFileToken && !parentTagTerminated) {
|
||||
nextJSDocToken();
|
||||
switch (token()) {
|
||||
case SyntaxKind.AtToken:
|
||||
if (canParseTag) {
|
||||
parentTagTerminated = !tryParseChildTag(jsDocTypeLiteral);
|
||||
if (!parentTagTerminated) {
|
||||
resumePos = scanner.getStartPos();
|
||||
}
|
||||
}
|
||||
seenAsterisk = false;
|
||||
break;
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
resumePos = scanner.getStartPos() - 1;
|
||||
canParseTag = true;
|
||||
seenAsterisk = false;
|
||||
break;
|
||||
case SyntaxKind.AsteriskToken:
|
||||
if (seenAsterisk) {
|
||||
canParseTag = false;
|
||||
}
|
||||
seenAsterisk = true;
|
||||
break;
|
||||
case SyntaxKind.Identifier:
|
||||
canParseTag = false;
|
||||
break;
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
break;
|
||||
}
|
||||
}
|
||||
scanner.setTextPos(resumePos);
|
||||
return finishNode(jsDocTypeLiteral);
|
||||
}
|
||||
|
||||
function parseJSDocTypeNameWithNamespace(flags: NodeFlags) {
|
||||
const pos = scanner.getTokenPos();
|
||||
const typeNameOrNamespaceName = parseJSDocIdentifierName();
|
||||
@@ -6655,8 +6692,58 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
a = a.left;
|
||||
b = b.left;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return a.escapedText === b.escapedText;
|
||||
}
|
||||
|
||||
function tryParseChildTag(parentTag: JSDocTypeLiteral): boolean {
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Property): JSDocTypeTag | JSDocPropertyTag | false;
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse.Parameter, name: EntityName): JSDocParameterTag | false;
|
||||
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
|
||||
let canParseTag = true;
|
||||
let seenAsterisk = false;
|
||||
while (true) {
|
||||
nextJSDocToken();
|
||||
switch (token()) {
|
||||
case SyntaxKind.AtToken:
|
||||
if (canParseTag) {
|
||||
const child = tryParseChildTag(target);
|
||||
if (child && child.kind === SyntaxKind.JSDocParameterTag &&
|
||||
(ts.isIdentifier(child.name) || !escapedTextsEqual(name, child.name.left))) {
|
||||
return false;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
seenAsterisk = false;
|
||||
break;
|
||||
case SyntaxKind.NewLineTrivia:
|
||||
canParseTag = true;
|
||||
seenAsterisk = false;
|
||||
break;
|
||||
case SyntaxKind.AsteriskToken:
|
||||
if (seenAsterisk) {
|
||||
canParseTag = false;
|
||||
}
|
||||
seenAsterisk = true;
|
||||
break;
|
||||
case SyntaxKind.Identifier:
|
||||
canParseTag = false;
|
||||
break;
|
||||
case SyntaxKind.EndOfFileToken:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function tryParseChildTag(target: PropertyLikeParse): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
|
||||
Debug.assert(token() === SyntaxKind.AtToken);
|
||||
const atToken = <AtToken>createNode(SyntaxKind.AtToken, scanner.getStartPos());
|
||||
atToken.end = scanner.getTextPos();
|
||||
@@ -6667,27 +6754,16 @@ namespace ts {
|
||||
if (!tagName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (tagName.escapedText) {
|
||||
case "type":
|
||||
if (parentTag.jsDocTypeTag) {
|
||||
// already has a @type tag, terminate the parent tag now.
|
||||
return false;
|
||||
}
|
||||
parentTag.jsDocTypeTag = parseTypeTag(atToken, tagName);
|
||||
return true;
|
||||
return target === PropertyLikeParse.Property && parseTypeTag(atToken, tagName);
|
||||
case "prop":
|
||||
case "property":
|
||||
const propertyTag = parseParameterOrPropertyTag(atToken, tagName, /*shouldParseParamTag*/ false) as JSDocPropertyTag;
|
||||
if (propertyTag) {
|
||||
if (!parentTag.jsDocPropertyTags) {
|
||||
parentTag.jsDocPropertyTags = <MutableNodeArray<JSDocPropertyTag>>[];
|
||||
}
|
||||
(parentTag.jsDocPropertyTags as MutableNodeArray<JSDocPropertyTag>).push(propertyTag);
|
||||
return true;
|
||||
}
|
||||
// Error parsing property tag
|
||||
return false;
|
||||
return target === PropertyLikeParse.Property && parseParameterOrPropertyTag(atToken, tagName, target);
|
||||
case "arg":
|
||||
case "argument":
|
||||
case "param":
|
||||
return target === PropertyLikeParse.Parameter && parseParameterOrPropertyTag(atToken, tagName, target);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -6736,6 +6812,24 @@ namespace ts {
|
||||
return currentToken = scanner.scanJSDocToken();
|
||||
}
|
||||
|
||||
function parseJSDocEntityName(): EntityName {
|
||||
let entity: EntityName = parseJSDocIdentifierName(/*createIfMissing*/ true);
|
||||
if (parseOptional(SyntaxKind.OpenBracketToken)) {
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
// Note that y[] is accepted as an entity name, but the postfix brackets are not saved for checking.
|
||||
// Technically usejsdoc.org requires them for specifying a property of a type equivalent to Array<{ x: ...}>
|
||||
// but it's not worth it to enforce that restriction.
|
||||
}
|
||||
while (parseOptional(SyntaxKind.DotToken)) {
|
||||
const name = parseJSDocIdentifierName(/*createIfMissing*/ true);
|
||||
if (parseOptional(SyntaxKind.OpenBracketToken)) {
|
||||
parseExpected(SyntaxKind.CloseBracketToken);
|
||||
}
|
||||
entity = createQualifiedName(entity, name);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
function parseJSDocIdentifierName(createIfMissing = false): Identifier {
|
||||
return createJSDocIdentifier(tokenIsIdentifierOrKeyword(token()), createIfMissing);
|
||||
}
|
||||
|
||||
@@ -760,6 +760,7 @@ namespace ts {
|
||||
// SyntaxKind.ShorthandPropertyAssignment
|
||||
// SyntaxKind.EnumMember
|
||||
// SyntaxKind.JSDocPropertyTag
|
||||
// SyntaxKind.JSDocParameterTag
|
||||
export interface VariableLikeDeclaration extends NamedDeclaration {
|
||||
propertyName?: PropertyName;
|
||||
dotDotDotToken?: DotDotDotToken;
|
||||
@@ -2036,7 +2037,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
// represents a top level: { type } expression in a JSDoc comment.
|
||||
export interface JSDocTypeExpression extends Node {
|
||||
export interface JSDocTypeExpression extends TypeNode {
|
||||
kind: SyntaxKind.JSDocTypeExpression;
|
||||
type: TypeNode;
|
||||
}
|
||||
@@ -2125,38 +2126,32 @@ namespace ts {
|
||||
kind: SyntaxKind.JSDocTypedefTag;
|
||||
fullName?: JSDocNamespaceDeclaration | Identifier;
|
||||
name?: Identifier;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
jsDocTypeLiteral?: JSDocTypeLiteral;
|
||||
typeExpression?: JSDocTypeExpression | JSDocTypeLiteral;
|
||||
}
|
||||
|
||||
export interface JSDocPropertyTag extends JSDocTag, TypeElement {
|
||||
export interface JSDocPropertyLikeTag extends JSDocTag, Declaration {
|
||||
parent: JSDoc;
|
||||
kind: SyntaxKind.JSDocPropertyTag;
|
||||
name: Identifier;
|
||||
/** the parameter name, if provided *before* the type (TypeScript-style) */
|
||||
preParameterName?: Identifier;
|
||||
/** the parameter name, if provided *after* the type (JSDoc-standard) */
|
||||
postParameterName?: Identifier;
|
||||
name: EntityName;
|
||||
typeExpression: JSDocTypeExpression;
|
||||
/** Whether the property name came before the type -- non-standard for JSDoc, but Typescript-like */
|
||||
isNameFirst: boolean;
|
||||
isBracketed: boolean;
|
||||
}
|
||||
|
||||
export interface JSDocPropertyTag extends JSDocPropertyLikeTag {
|
||||
kind: SyntaxKind.JSDocPropertyTag;
|
||||
}
|
||||
|
||||
export interface JSDocParameterTag extends JSDocPropertyLikeTag {
|
||||
kind: SyntaxKind.JSDocParameterTag;
|
||||
}
|
||||
|
||||
export interface JSDocTypeLiteral extends JSDocType {
|
||||
kind: SyntaxKind.JSDocTypeLiteral;
|
||||
jsDocPropertyTags?: NodeArray<JSDocPropertyTag>;
|
||||
jsDocPropertyTags?: ReadonlyArray<JSDocPropertyLikeTag>;
|
||||
jsDocTypeTag?: JSDocTypeTag;
|
||||
}
|
||||
|
||||
export interface JSDocParameterTag extends JSDocTag {
|
||||
kind: SyntaxKind.JSDocParameterTag;
|
||||
/** the parameter name, if provided *before* the type (TypeScript-style) */
|
||||
preParameterName?: Identifier;
|
||||
typeExpression?: JSDocTypeExpression;
|
||||
/** the parameter name, if provided *after* the type (JSDoc-standard) */
|
||||
postParameterName?: Identifier;
|
||||
/** the parameter name, regardless of the location it was provided */
|
||||
name: Identifier;
|
||||
isBracketed: boolean;
|
||||
/** If true, then this type literal represents an *array* of its type. */
|
||||
isArrayType?: boolean;
|
||||
}
|
||||
|
||||
export const enum FlowFlags {
|
||||
|
||||
@@ -1547,25 +1547,29 @@ namespace ts {
|
||||
export function getJSDocParameterTags(param: ParameterDeclaration): JSDocParameterTag[] | undefined {
|
||||
if (param.name && isIdentifier(param.name)) {
|
||||
const name = param.name.escapedText;
|
||||
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && tag.name.escapedText === name) as JSDocParameterTag[];
|
||||
}
|
||||
else {
|
||||
// TODO: it's a destructured parameter, so it should look up an "object type" series of multiple lines
|
||||
// But multi-line object types aren't supported yet either
|
||||
return undefined;
|
||||
return getJSDocTags(param.parent).filter((tag): tag is JSDocParameterTag => isJSDocParameterTag(tag) && isIdentifier(tag.name) && tag.name.escapedText === name) as JSDocParameterTag[];
|
||||
}
|
||||
// a binding pattern doesn't have a name, so it's not possible to match it a jsdoc parameter, which is identified by name
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/** Does the opposite of `getJSDocParameterTags`: given a JSDoc parameter, finds the parameter corresponding to it. */
|
||||
export function getParameterFromJSDoc(node: JSDocParameterTag): ParameterDeclaration | undefined {
|
||||
const name = node.name.escapedText;
|
||||
const grandParent = node.parent!.parent!;
|
||||
Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment);
|
||||
if (!isFunctionLike(grandParent)) {
|
||||
export function getParameterSymbolFromJSDoc(node: JSDocParameterTag): Symbol | undefined {
|
||||
if (node.symbol) {
|
||||
return node.symbol;
|
||||
}
|
||||
if (!isIdentifier(node.name)) {
|
||||
return undefined;
|
||||
}
|
||||
return find(grandParent.parameters, p =>
|
||||
const name = node.name.escapedText;
|
||||
Debug.assert(node.parent!.kind === SyntaxKind.JSDocComment);
|
||||
const func = node.parent!.parent!;
|
||||
if (!isFunctionLike(func)) {
|
||||
return undefined;
|
||||
}
|
||||
const parameter = find(func.parameters, p =>
|
||||
p.name.kind === SyntaxKind.Identifier && p.name.escapedText === name);
|
||||
return parameter && parameter.symbol;
|
||||
}
|
||||
|
||||
export function getTypeParameterFromJsDoc(node: TypeParameterDeclaration & { parent: JSDocTemplateTag }): TypeParameterDeclaration | undefined {
|
||||
@@ -4057,6 +4061,9 @@ namespace ts {
|
||||
if (!declaration) {
|
||||
return undefined;
|
||||
}
|
||||
if (isJSDocPropertyLikeTag(declaration) && declaration.name.kind === SyntaxKind.QualifiedName) {
|
||||
return declaration.name.right;
|
||||
}
|
||||
if (declaration.kind === SyntaxKind.BinaryExpression) {
|
||||
const expr = declaration as BinaryExpression;
|
||||
switch (getSpecialPropertyAssignmentKind(expr)) {
|
||||
@@ -4715,6 +4722,10 @@ namespace ts {
|
||||
return node.kind === SyntaxKind.JSDocPropertyTag;
|
||||
}
|
||||
|
||||
export function isJSDocPropertyLikeTag(node: Node): node is JSDocPropertyLikeTag {
|
||||
return node.kind === SyntaxKind.JSDocPropertyTag || node.kind === SyntaxKind.JSDocParameterTag;
|
||||
}
|
||||
|
||||
export function isJSDocTypeLiteral(node: Node): node is JSDocTypeLiteral {
|
||||
return node.kind === SyntaxKind.JSDocTypeLiteral;
|
||||
}
|
||||
|
||||
@@ -755,10 +755,10 @@ namespace ts {
|
||||
return;
|
||||
|
||||
function processJSDocParameterTag(tag: JSDocParameterTag) {
|
||||
if (tag.preParameterName) {
|
||||
pushCommentRange(pos, tag.preParameterName.pos - pos);
|
||||
pushClassification(tag.preParameterName.pos, tag.preParameterName.end - tag.preParameterName.pos, ClassificationType.parameterName);
|
||||
pos = tag.preParameterName.end;
|
||||
if (tag.isNameFirst) {
|
||||
pushCommentRange(pos, tag.name.pos - pos);
|
||||
pushClassification(tag.name.pos, tag.name.end - tag.name.pos, ClassificationType.parameterName);
|
||||
pos = tag.name.end;
|
||||
}
|
||||
|
||||
if (tag.typeExpression) {
|
||||
@@ -767,10 +767,10 @@ namespace ts {
|
||||
pos = tag.typeExpression.end;
|
||||
}
|
||||
|
||||
if (tag.postParameterName) {
|
||||
pushCommentRange(pos, tag.postParameterName.pos - pos);
|
||||
pushClassification(tag.postParameterName.pos, tag.postParameterName.end - tag.postParameterName.pos, ClassificationType.parameterName);
|
||||
pos = tag.postParameterName.end;
|
||||
if (!tag.isNameFirst) {
|
||||
pushCommentRange(pos, tag.name.pos - pos);
|
||||
pushClassification(tag.name.pos, tag.name.end - tag.name.pos, ClassificationType.parameterName);
|
||||
pos = tag.name.end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,7 +420,7 @@ namespace ts.Completions {
|
||||
if (tag.tagName.pos <= position && position <= tag.tagName.end) {
|
||||
request = { kind: "JsDocTagName" };
|
||||
}
|
||||
if (isTagWithTypeExpression(tag) && tag.typeExpression) {
|
||||
if (isTagWithTypeExpression(tag) && tag.typeExpression && tag.typeExpression.kind === SyntaxKind.JSDocTypeExpression) {
|
||||
currentToken = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ true);
|
||||
if (!currentToken ||
|
||||
(!isDeclarationName(currentToken) &&
|
||||
|
||||
@@ -120,6 +120,9 @@ namespace ts.JsDoc {
|
||||
}
|
||||
|
||||
export function getJSDocParameterNameCompletions(tag: JSDocParameterTag): CompletionEntry[] {
|
||||
if (!isIdentifier(tag.name)) {
|
||||
return emptyArray;
|
||||
}
|
||||
const nameThusFar = tag.name.text;
|
||||
const jsdoc = tag.parent;
|
||||
const fn = jsdoc.parent;
|
||||
@@ -129,7 +132,7 @@ namespace ts.JsDoc {
|
||||
if (!isIdentifier(param.name)) return undefined;
|
||||
|
||||
const name = param.name.text;
|
||||
if (jsdoc.tags.some(t => t !== tag && isJSDocParameterTag(t) && t.name.escapedText === name)
|
||||
if (jsdoc.tags.some(t => t !== tag && isJSDocParameterTag(t) && isIdentifier(t.name) && t.name.escapedText === name)
|
||||
|| nameThusFar !== undefined && !startsWith(name, nameThusFar)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user