Skip to content
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 jsdoc support for @public/@private/@protected #35731

Merged
merged 12 commits into from
Dec 18, 2019
46 changes: 37 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5838,6 +5838,8 @@ namespace ts {
initializer: Expression | undefined
) => T, methodKind: SyntaxKind, useAccessors: boolean): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => (T | AccessorDeclaration | (T | AccessorDeclaration)[]) {
return function serializePropertySymbol(p: Symbol, isStatic: boolean, baseType: Type | undefined) {
const modifierFlags = getDeclarationModifierFlagsFromSymbol(p);
const isPrivate = !!(modifierFlags & ModifierFlags.Private);
if (isStatic && (p.flags & (SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias))) {
// Only value-only-meaning symbols can be correctly encoded as class statics, type/namespace/alias meaning symbols
// need to be merged namespace members
Expand All @@ -5849,34 +5851,35 @@ namespace ts {
&& isTypeIdenticalTo(getTypeOfSymbol(p), getTypeOfPropertyOfType(baseType, p.escapedName)!))) {
return [];
}
const staticFlag = isStatic ? ModifierFlags.Static : 0;
const flag = modifierFlags | (isStatic ? ModifierFlags.Static : 0);
const name = getPropertyNameNodeForSymbol(p, context);
const firstPropertyLikeDecl = find(p.declarations, or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression));
if (p.flags & SymbolFlags.Accessor && useAccessors) {
const result: AccessorDeclaration[] = [];
if (p.flags & SymbolFlags.SetAccessor) {
result.push(setTextRange(createSetAccessor(
/*decorators*/ undefined,
createModifiersFromModifierFlags(staticFlag),
createModifiersFromModifierFlags(flag),
name,
[createParameter(
/*decorators*/ undefined,
/*modifiers*/ undefined,
/*dotDotDotToken*/ undefined,
"arg",
/*questionToken*/ undefined,
serializeTypeForDeclaration(getTypeOfSymbol(p), p)
isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p)
)],
/*body*/ undefined
), find(p.declarations, isSetAccessor) || firstPropertyLikeDecl));
}
if (p.flags & SymbolFlags.GetAccessor) {
const isPrivate = modifierFlags & ModifierFlags.Private;
result.push(setTextRange(createGetAccessor(
/*decorators*/ undefined,
createModifiersFromModifierFlags(staticFlag),
createModifiersFromModifierFlags(flag),
name,
[],
serializeTypeForDeclaration(getTypeOfSymbol(p), p),
isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p),
/*body*/ undefined
), find(p.declarations, isGetAccessor) || firstPropertyLikeDecl));
}
Expand All @@ -5887,10 +5890,10 @@ namespace ts {
else if (p.flags & (SymbolFlags.Property | SymbolFlags.Variable)) {
return setTextRange(createProperty(
/*decorators*/ undefined,
createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | staticFlag),
createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag),
name,
p.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined,
serializeTypeForDeclaration(getTypeOfSymbol(p), p),
isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p),
// TODO: https://github.com/microsoft/TypeScript/pull/32372#discussion_r328386357
// interface members can't have initializers, however class members _can_
/*initializer*/ undefined
Expand All @@ -5899,13 +5902,24 @@ namespace ts {
if (p.flags & (SymbolFlags.Method | SymbolFlags.Function)) {
const type = getTypeOfSymbol(p);
const signatures = getSignaturesOfType(type, SignatureKind.Call);
if (flag & ModifierFlags.Private) {
return setTextRange(createProperty(
/*decorators*/ undefined,
createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag),
name,
p.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined,
/*type*/ undefined,
/*initializer*/ undefined
), find(p.declarations, isFunctionLikeDeclaration) || signatures[0] && signatures[0].declaration || p.declarations[0]);
}

const results = [];
for (const sig of signatures) {
// Each overload becomes a separate method declaration, in order
const decl = signatureToSignatureDeclarationHelper(sig, methodKind, context) as MethodDeclaration;
decl.name = name; // TODO: Clone
if (staticFlag) {
decl.modifiers = createNodeArray(createModifiersFromModifierFlags(staticFlag));
if (flag) {
decl.modifiers = createNodeArray(createModifiersFromModifierFlags(flag));
}
if (p.flags & SymbolFlags.Optional) {
decl.questionToken = createToken(SyntaxKind.QuestionToken);
Expand Down Expand Up @@ -6093,6 +6107,20 @@ namespace ts {
}
}
}
let privateProtected: ModifierFlags = 0;
sandersn marked this conversation as resolved.
Show resolved Hide resolved
for (const s of signatures) {
sandersn marked this conversation as resolved.
Show resolved Hide resolved
if (s.declaration) {
privateProtected |= getSelectedModifierFlags(s.declaration, ModifierFlags.Private | ModifierFlags.Protected);
}
}
if (privateProtected) {
return [setTextRange(createConstructor(
/*decorators*/ undefined,
createModifiersFromModifierFlags(privateProtected),
/*parameters*/ [],
/*body*/ undefined,
), signatures[0].declaration)];
}
}

const results = [];
Expand Down
39 changes: 28 additions & 11 deletions src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,9 @@ namespace ts {
return forEach((node as JSDocTypeLiteral).jsDocPropertyTags, cbNode);
case SyntaxKind.JSDocTag:
case SyntaxKind.JSDocClassTag:
case SyntaxKind.JSDocPublicTag:
case SyntaxKind.JSDocPrivateTag:
case SyntaxKind.JSDocProtectedTag:
return visitNode(cbNode, (node as JSDocTag).tagName);
case SyntaxKind.PartiallyEmittedExpression:
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
Expand Down Expand Up @@ -2581,7 +2584,7 @@ namespace ts {
// FormalParameter [Yield,Await]:
// BindingElement[?Yield,?Await]
node.name = parseIdentifierOrPattern();
if (getFullWidth(node.name) === 0 && !hasModifiers(node) && isModifierKind(token())) {
if (getFullWidth(node.name) === 0 && !node.modifiers && isModifierKind(token())) {
// in cases like
// 'use strict'
// function foo(static)
Expand Down Expand Up @@ -3600,7 +3603,7 @@ namespace ts {
return undefined;
}

const isAsync = hasModifier(arrowFunction, ModifierFlags.Async);
const isAsync = hasModifierOfKind(arrowFunction, SyntaxKind.AsyncKeyword);

// If we have an arrow, then try to parse the body. Even if not, try to parse if we
// have an opening brace, just in case we're in an error state.
Expand Down Expand Up @@ -3806,7 +3809,7 @@ namespace ts {
function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction | undefined {
const node = <ArrowFunction>createNodeWithJSDoc(SyntaxKind.ArrowFunction);
node.modifiers = parseModifiersForArrowFunction();
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifierOfKind(node, SyntaxKind.AsyncKeyword) ? SignatureFlags.Await : SignatureFlags.None;
// Arrow functions are never generators.
//
// If we're speculatively parsing a signature for a parenthesized arrow function, then
Expand Down Expand Up @@ -5002,7 +5005,7 @@ namespace ts {
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);

const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifierOfKind(node, SyntaxKind.AsyncKeyword) ? SignatureFlags.Await : SignatureFlags.None;
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
Expand Down Expand Up @@ -5831,9 +5834,9 @@ namespace ts {
node.kind = SyntaxKind.FunctionDeclaration;
parseExpected(SyntaxKind.FunctionKeyword);
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);
node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier();
node.name = hasModifierOfKind(node, SyntaxKind.DefaultKeyword) ? parseOptionalIdentifier() : parseIdentifier();
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifierOfKind(node, SyntaxKind.AsyncKeyword) ? SignatureFlags.Await : SignatureFlags.None;
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected);
return finishNode(node);
Expand Down Expand Up @@ -5866,7 +5869,7 @@ namespace ts {
node.kind = SyntaxKind.MethodDeclaration;
node.asteriskToken = asteriskToken;
const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifierOfKind(node, SyntaxKind.AsyncKeyword) ? SignatureFlags.Await : SignatureFlags.None;
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage);
return finishNode(node);
Expand Down Expand Up @@ -6508,7 +6511,7 @@ namespace ts {
}

function isAnExternalModuleIndicatorNode(node: Node) {
return hasModifier(node, ModifierFlags.Export)
return hasModifierOfKind(node, SyntaxKind.ExportKeyword)
|| node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference
|| node.kind === SyntaxKind.ImportDeclaration
|| node.kind === SyntaxKind.ExportAssignment
Expand All @@ -6525,6 +6528,11 @@ namespace ts {
return isImportMeta(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators);
}

/** Do not use hasModifier inside the parser; it relies on parent pointers. Use this instead. */
function hasModifierOfKind(node: Node, kind: SyntaxKind) {
return some(node.modifiers, m => m.kind === kind);
}

function isImportMeta(node: Node): boolean {
return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta";
}
Expand Down Expand Up @@ -6826,7 +6834,16 @@ namespace ts {
break;
case "class":
case "constructor":
tag = parseClassTag(start, tagName);
tag = parseSimpleTag(start, SyntaxKind.JSDocClassTag, tagName);
break;
case "public":
tag = parseSimpleTag(start, SyntaxKind.JSDocPublicTag, tagName);
break;
case "private":
tag = parseSimpleTag(start, SyntaxKind.JSDocPrivateTag, tagName);
break;
case "protected":
tag = parseSimpleTag(start, SyntaxKind.JSDocProtectedTag, tagName);
break;
case "this":
tag = parseThisTag(start, tagName);
Expand Down Expand Up @@ -7192,8 +7209,8 @@ namespace ts {
return node;
}

function parseClassTag(start: number, tagName: Identifier): JSDocClassTag {
const tag = <JSDocClassTag>createNode(SyntaxKind.JSDocClassTag, start);
function parseSimpleTag(start: number, kind: SyntaxKind, tagName: Identifier): JSDocTag {
const tag = <JSDocTag>createNode(kind, start);
tag.tagName = tagName;
return finishNode(tag);
}
Expand Down
15 changes: 6 additions & 9 deletions src/compiler/transformers/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -840,13 +840,11 @@ namespace ts {
ensureType(input, input.type)
));
case SyntaxKind.Constructor: {
const isPrivate = hasModifier(input, ModifierFlags.Private);
// A constructor declaration may not have a type annotation
const ctor = createSignatureDeclaration(
SyntaxKind.Constructor,
isPrivate ? undefined : ensureTypeParams(input, input.typeParameters),
// TODO: GH#18217
isPrivate ? undefined! : updateParamsList(input, input.parameters, ModifierFlags.None),
ensureTypeParams(input, input.typeParameters),
updateParamsList(input, input.parameters, ModifierFlags.None),
/*type*/ undefined
);
ctor.modifiers = createNodeArray(ensureModifiers(input));
Expand All @@ -865,15 +863,14 @@ namespace ts {
return cleanup(sig);
}
case SyntaxKind.GetAccessor: {
const isPrivate = hasModifier(input, ModifierFlags.Private);
const accessorType = getTypeAnnotationFromAllAccessorDeclarations(input, resolver.getAllAccessorDeclarations(input));
return cleanup(updateGetAccessor(
input,
/*decorators*/ undefined,
ensureModifiers(input),
input.name,
updateAccessorParamsList(input, isPrivate),
!isPrivate ? ensureType(input, accessorType) : undefined,
updateAccessorParamsList(input, hasModifier(input, ModifierFlags.Private)),
ensureType(input, accessorType),
/*body*/ undefined));
}
case SyntaxKind.SetAccessor: {
Expand All @@ -892,7 +889,7 @@ namespace ts {
ensureModifiers(input),
input.name,
input.questionToken,
!hasModifier(input, ModifierFlags.Private) ? ensureType(input, input.type) : undefined,
ensureType(input, input.type),
ensureNoInitializer(input)
));
case SyntaxKind.PropertySignature:
Expand All @@ -901,7 +898,7 @@ namespace ts {
ensureModifiers(input),
input.name,
input.questionToken,
!hasModifier(input, ModifierFlags.Private) ? ensureType(input, input.type) : undefined,
ensureType(input, input.type),
ensureNoInitializer(input)
));
case SyntaxKind.MethodSignature: {
Expand Down
15 changes: 15 additions & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,9 @@ namespace ts {
JSDocAugmentsTag,
JSDocAuthorTag,
JSDocClassTag,
JSDocPublicTag,
JSDocPrivateTag,
JSDocProtectedTag,
JSDocCallbackTag,
JSDocEnumTag,
JSDocParameterTag,
Expand Down Expand Up @@ -2617,6 +2620,18 @@ namespace ts {
kind: SyntaxKind.JSDocClassTag;
}

export interface JSDocPublicTag extends JSDocTag {
kind: SyntaxKind.JSDocPublicTag;
}

export interface JSDocPrivateTag extends JSDocTag {
kind: SyntaxKind.JSDocPrivateTag;
}

export interface JSDocProtectedTag extends JSDocTag {
kind: SyntaxKind.JSDocProtectedTag;
}

export interface JSDocEnumTag extends JSDocTag, Declaration {
parent: JSDoc;
kind: SyntaxKind.JSDocEnumTag;
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4125,6 +4125,15 @@ namespace ts {
}
}

if (isInJSFile(node) && !!node.parent) {
// getModifierFlagsNoCache should only be called when parent pointers are set,
// or when !(node.flags & NodeFlags.Synthesized) && node.kind !== SyntaxKind.SourceFile)
const tags = (getJSDocPublicTag(node) ? ModifierFlags.Public : ModifierFlags.None)
| (getJSDocPrivateTag(node) ? ModifierFlags.Private : ModifierFlags.None)
| (getJSDocProtectedTag(node) ? ModifierFlags.Protected : ModifierFlags.None);
flags |= tags;
}

if (node.flags & NodeFlags.NestedNamespace || (node.kind === SyntaxKind.Identifier && (<Identifier>node).isInJSDocNamespace)) {
flags |= ModifierFlags.Export;
}
Expand Down
Loading