Skip to content

Commit 3c5ecc2

Browse files
authored
Add jsdoc support for @public/@private/@Protected (#35731)
* Add @private/@protected/@public test * Fix @declaration * draft abstraction + one usage * Fill in necessary parsing etc * make general getEffectiveModifierFlags move to utilities, make the right things call it * reorder public/private/protected * JS declaration emit works with @public/@private/@Protected * revert unneeded/incorrect changes * Update baselines and skip @public/etc when parsing 1. Update the API baselines with the new functions. 2. Do not check for @public/etc during parsing, because parent pointers aren't set, so non-local tags will be missed; this wrong answer will then be cached. * Parser: don't call hasModifier(s) anymore. Then move jsdoc modifier tag checks into getModifierFlagsNoCache where they should be. The jsdoc checks are skipped when the parent is undefined. There are 3 cases when this can happen: 1. The code is in the parser (or a few places in the binder, notably declareSymbol of module.exports assignments). 2. The node is a source file. 3. The node is synthetic, which I assume to be from the transforms. It is fine to call getModifierFlags in cases (2) and (3). It is not fine for (1), so I removed these calls and replaced them with simple iteration over the modifiers. Worth noting: Ron's uniform node construction PR removes these calls anyway; this PR just does it early. * Fix constructor emit 1. Emit protected for protected, which I missed earlier. 2. Emit a constructor, not a property named "constructor". 3. Split declaration emit tests out so that errors are properly reported there.
1 parent f7860b0 commit 3c5ecc2

16 files changed

+1023
-68
lines changed

src/compiler/checker.ts

+37-9
Original file line numberDiff line numberDiff line change
@@ -5838,6 +5838,8 @@ namespace ts {
58385838
initializer: Expression | undefined
58395839
) => T, methodKind: SyntaxKind, useAccessors: boolean): (p: Symbol, isStatic: boolean, baseType: Type | undefined) => (T | AccessorDeclaration | (T | AccessorDeclaration)[]) {
58405840
return function serializePropertySymbol(p: Symbol, isStatic: boolean, baseType: Type | undefined) {
5841+
const modifierFlags = getDeclarationModifierFlagsFromSymbol(p);
5842+
const isPrivate = !!(modifierFlags & ModifierFlags.Private);
58415843
if (isStatic && (p.flags & (SymbolFlags.Type | SymbolFlags.Namespace | SymbolFlags.Alias))) {
58425844
// Only value-only-meaning symbols can be correctly encoded as class statics, type/namespace/alias meaning symbols
58435845
// need to be merged namespace members
@@ -5849,34 +5851,35 @@ namespace ts {
58495851
&& isTypeIdenticalTo(getTypeOfSymbol(p), getTypeOfPropertyOfType(baseType, p.escapedName)!))) {
58505852
return [];
58515853
}
5852-
const staticFlag = isStatic ? ModifierFlags.Static : 0;
5854+
const flag = modifierFlags | (isStatic ? ModifierFlags.Static : 0);
58535855
const name = getPropertyNameNodeForSymbol(p, context);
58545856
const firstPropertyLikeDecl = find(p.declarations, or(isPropertyDeclaration, isAccessor, isVariableDeclaration, isPropertySignature, isBinaryExpression, isPropertyAccessExpression));
58555857
if (p.flags & SymbolFlags.Accessor && useAccessors) {
58565858
const result: AccessorDeclaration[] = [];
58575859
if (p.flags & SymbolFlags.SetAccessor) {
58585860
result.push(setTextRange(createSetAccessor(
58595861
/*decorators*/ undefined,
5860-
createModifiersFromModifierFlags(staticFlag),
5862+
createModifiersFromModifierFlags(flag),
58615863
name,
58625864
[createParameter(
58635865
/*decorators*/ undefined,
58645866
/*modifiers*/ undefined,
58655867
/*dotDotDotToken*/ undefined,
58665868
"arg",
58675869
/*questionToken*/ undefined,
5868-
serializeTypeForDeclaration(getTypeOfSymbol(p), p)
5870+
isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p)
58695871
)],
58705872
/*body*/ undefined
58715873
), find(p.declarations, isSetAccessor) || firstPropertyLikeDecl));
58725874
}
58735875
if (p.flags & SymbolFlags.GetAccessor) {
5876+
const isPrivate = modifierFlags & ModifierFlags.Private;
58745877
result.push(setTextRange(createGetAccessor(
58755878
/*decorators*/ undefined,
5876-
createModifiersFromModifierFlags(staticFlag),
5879+
createModifiersFromModifierFlags(flag),
58775880
name,
58785881
[],
5879-
serializeTypeForDeclaration(getTypeOfSymbol(p), p),
5882+
isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p),
58805883
/*body*/ undefined
58815884
), find(p.declarations, isGetAccessor) || firstPropertyLikeDecl));
58825885
}
@@ -5887,10 +5890,10 @@ namespace ts {
58875890
else if (p.flags & (SymbolFlags.Property | SymbolFlags.Variable)) {
58885891
return setTextRange(createProperty(
58895892
/*decorators*/ undefined,
5890-
createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | staticFlag),
5893+
createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag),
58915894
name,
58925895
p.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined,
5893-
serializeTypeForDeclaration(getTypeOfSymbol(p), p),
5896+
isPrivate ? undefined : serializeTypeForDeclaration(getTypeOfSymbol(p), p),
58945897
// TODO: https://github.com/microsoft/TypeScript/pull/32372#discussion_r328386357
58955898
// interface members can't have initializers, however class members _can_
58965899
/*initializer*/ undefined
@@ -5899,13 +5902,24 @@ namespace ts {
58995902
if (p.flags & (SymbolFlags.Method | SymbolFlags.Function)) {
59005903
const type = getTypeOfSymbol(p);
59015904
const signatures = getSignaturesOfType(type, SignatureKind.Call);
5905+
if (flag & ModifierFlags.Private) {
5906+
return setTextRange(createProperty(
5907+
/*decorators*/ undefined,
5908+
createModifiersFromModifierFlags((isReadonlySymbol(p) ? ModifierFlags.Readonly : 0) | flag),
5909+
name,
5910+
p.flags & SymbolFlags.Optional ? createToken(SyntaxKind.QuestionToken) : undefined,
5911+
/*type*/ undefined,
5912+
/*initializer*/ undefined
5913+
), find(p.declarations, isFunctionLikeDeclaration) || signatures[0] && signatures[0].declaration || p.declarations[0]);
5914+
}
5915+
59025916
const results = [];
59035917
for (const sig of signatures) {
59045918
// Each overload becomes a separate method declaration, in order
59055919
const decl = signatureToSignatureDeclarationHelper(sig, methodKind, context) as MethodDeclaration;
59065920
decl.name = name; // TODO: Clone
5907-
if (staticFlag) {
5908-
decl.modifiers = createNodeArray(createModifiersFromModifierFlags(staticFlag));
5921+
if (flag) {
5922+
decl.modifiers = createNodeArray(createModifiersFromModifierFlags(flag));
59095923
}
59105924
if (p.flags & SymbolFlags.Optional) {
59115925
decl.questionToken = createToken(SyntaxKind.QuestionToken);
@@ -6093,6 +6107,20 @@ namespace ts {
60936107
}
60946108
}
60956109
}
6110+
let privateProtected: ModifierFlags = 0;
6111+
for (const s of signatures) {
6112+
if (s.declaration) {
6113+
privateProtected |= getSelectedModifierFlags(s.declaration, ModifierFlags.Private | ModifierFlags.Protected);
6114+
}
6115+
}
6116+
if (privateProtected) {
6117+
return [setTextRange(createConstructor(
6118+
/*decorators*/ undefined,
6119+
createModifiersFromModifierFlags(privateProtected),
6120+
/*parameters*/ [],
6121+
/*body*/ undefined,
6122+
), signatures[0].declaration)];
6123+
}
60966124
}
60976125

60986126
const results = [];

src/compiler/parser.ts

+28-11
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,9 @@ namespace ts {
506506
return forEach((node as JSDocTypeLiteral).jsDocPropertyTags, cbNode);
507507
case SyntaxKind.JSDocTag:
508508
case SyntaxKind.JSDocClassTag:
509+
case SyntaxKind.JSDocPublicTag:
510+
case SyntaxKind.JSDocPrivateTag:
511+
case SyntaxKind.JSDocProtectedTag:
509512
return visitNode(cbNode, (node as JSDocTag).tagName);
510513
case SyntaxKind.PartiallyEmittedExpression:
511514
return visitNode(cbNode, (<PartiallyEmittedExpression>node).expression);
@@ -2581,7 +2584,7 @@ namespace ts {
25812584
// FormalParameter [Yield,Await]:
25822585
// BindingElement[?Yield,?Await]
25832586
node.name = parseIdentifierOrPattern();
2584-
if (getFullWidth(node.name) === 0 && !hasModifiers(node) && isModifierKind(token())) {
2587+
if (getFullWidth(node.name) === 0 && !node.modifiers && isModifierKind(token())) {
25852588
// in cases like
25862589
// 'use strict'
25872590
// function foo(static)
@@ -3600,7 +3603,7 @@ namespace ts {
36003603
return undefined;
36013604
}
36023605

3603-
const isAsync = hasModifier(arrowFunction, ModifierFlags.Async);
3606+
const isAsync = hasModifierOfKind(arrowFunction, SyntaxKind.AsyncKeyword);
36043607

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

50045007
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
5005-
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
5008+
const isAsync = hasModifierOfKind(node, SyntaxKind.AsyncKeyword) ? SignatureFlags.Await : SignatureFlags.None;
50065009
node.name =
50075010
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
50085011
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
@@ -5831,9 +5834,9 @@ namespace ts {
58315834
node.kind = SyntaxKind.FunctionDeclaration;
58325835
parseExpected(SyntaxKind.FunctionKeyword);
58335836
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);
5834-
node.name = hasModifier(node, ModifierFlags.Default) ? parseOptionalIdentifier() : parseIdentifier();
5837+
node.name = hasModifierOfKind(node, SyntaxKind.DefaultKeyword) ? parseOptionalIdentifier() : parseIdentifier();
58355838
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
5836-
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
5839+
const isAsync = hasModifierOfKind(node, SyntaxKind.AsyncKeyword) ? SignatureFlags.Await : SignatureFlags.None;
58375840
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
58385841
node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected);
58395842
return finishNode(node);
@@ -5866,7 +5869,7 @@ namespace ts {
58665869
node.kind = SyntaxKind.MethodDeclaration;
58675870
node.asteriskToken = asteriskToken;
58685871
const isGenerator = asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
5869-
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
5872+
const isAsync = hasModifierOfKind(node, SyntaxKind.AsyncKeyword) ? SignatureFlags.Await : SignatureFlags.None;
58705873
fillSignature(SyntaxKind.ColonToken, isGenerator | isAsync, node);
58715874
node.body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, diagnosticMessage);
58725875
return finishNode(node);
@@ -6508,7 +6511,7 @@ namespace ts {
65086511
}
65096512

65106513
function isAnExternalModuleIndicatorNode(node: Node) {
6511-
return hasModifier(node, ModifierFlags.Export)
6514+
return hasModifierOfKind(node, SyntaxKind.ExportKeyword)
65126515
|| node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference
65136516
|| node.kind === SyntaxKind.ImportDeclaration
65146517
|| node.kind === SyntaxKind.ExportAssignment
@@ -6525,6 +6528,11 @@ namespace ts {
65256528
return isImportMeta(node) ? node : forEachChild(node, walkTreeForExternalModuleIndicators);
65266529
}
65276530

6531+
/** Do not use hasModifier inside the parser; it relies on parent pointers. Use this instead. */
6532+
function hasModifierOfKind(node: Node, kind: SyntaxKind) {
6533+
return some(node.modifiers, m => m.kind === kind);
6534+
}
6535+
65286536
function isImportMeta(node: Node): boolean {
65296537
return isMetaProperty(node) && node.keywordToken === SyntaxKind.ImportKeyword && node.name.escapedText === "meta";
65306538
}
@@ -6826,7 +6834,16 @@ namespace ts {
68266834
break;
68276835
case "class":
68286836
case "constructor":
6829-
tag = parseClassTag(start, tagName);
6837+
tag = parseSimpleTag(start, SyntaxKind.JSDocClassTag, tagName);
6838+
break;
6839+
case "public":
6840+
tag = parseSimpleTag(start, SyntaxKind.JSDocPublicTag, tagName);
6841+
break;
6842+
case "private":
6843+
tag = parseSimpleTag(start, SyntaxKind.JSDocPrivateTag, tagName);
6844+
break;
6845+
case "protected":
6846+
tag = parseSimpleTag(start, SyntaxKind.JSDocProtectedTag, tagName);
68306847
break;
68316848
case "this":
68326849
tag = parseThisTag(start, tagName);
@@ -7192,8 +7209,8 @@ namespace ts {
71927209
return node;
71937210
}
71947211

7195-
function parseClassTag(start: number, tagName: Identifier): JSDocClassTag {
7196-
const tag = <JSDocClassTag>createNode(SyntaxKind.JSDocClassTag, start);
7212+
function parseSimpleTag(start: number, kind: SyntaxKind, tagName: Identifier): JSDocTag {
7213+
const tag = <JSDocTag>createNode(kind, start);
71977214
tag.tagName = tagName;
71987215
return finishNode(tag);
71997216
}

src/compiler/transformers/declarations.ts

+6-9
Original file line numberDiff line numberDiff line change
@@ -840,13 +840,11 @@ namespace ts {
840840
ensureType(input, input.type)
841841
));
842842
case SyntaxKind.Constructor: {
843-
const isPrivate = hasModifier(input, ModifierFlags.Private);
844843
// A constructor declaration may not have a type annotation
845844
const ctor = createSignatureDeclaration(
846845
SyntaxKind.Constructor,
847-
isPrivate ? undefined : ensureTypeParams(input, input.typeParameters),
848-
// TODO: GH#18217
849-
isPrivate ? undefined! : updateParamsList(input, input.parameters, ModifierFlags.None),
846+
ensureTypeParams(input, input.typeParameters),
847+
updateParamsList(input, input.parameters, ModifierFlags.None),
850848
/*type*/ undefined
851849
);
852850
ctor.modifiers = createNodeArray(ensureModifiers(input));
@@ -865,15 +863,14 @@ namespace ts {
865863
return cleanup(sig);
866864
}
867865
case SyntaxKind.GetAccessor: {
868-
const isPrivate = hasModifier(input, ModifierFlags.Private);
869866
const accessorType = getTypeAnnotationFromAllAccessorDeclarations(input, resolver.getAllAccessorDeclarations(input));
870867
return cleanup(updateGetAccessor(
871868
input,
872869
/*decorators*/ undefined,
873870
ensureModifiers(input),
874871
input.name,
875-
updateAccessorParamsList(input, isPrivate),
876-
!isPrivate ? ensureType(input, accessorType) : undefined,
872+
updateAccessorParamsList(input, hasModifier(input, ModifierFlags.Private)),
873+
ensureType(input, accessorType),
877874
/*body*/ undefined));
878875
}
879876
case SyntaxKind.SetAccessor: {
@@ -892,7 +889,7 @@ namespace ts {
892889
ensureModifiers(input),
893890
input.name,
894891
input.questionToken,
895-
!hasModifier(input, ModifierFlags.Private) ? ensureType(input, input.type) : undefined,
892+
ensureType(input, input.type),
896893
ensureNoInitializer(input)
897894
));
898895
case SyntaxKind.PropertySignature:
@@ -901,7 +898,7 @@ namespace ts {
901898
ensureModifiers(input),
902899
input.name,
903900
input.questionToken,
904-
!hasModifier(input, ModifierFlags.Private) ? ensureType(input, input.type) : undefined,
901+
ensureType(input, input.type),
905902
ensureNoInitializer(input)
906903
));
907904
case SyntaxKind.MethodSignature: {

src/compiler/types.ts

+15
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,9 @@ namespace ts {
468468
JSDocAugmentsTag,
469469
JSDocAuthorTag,
470470
JSDocClassTag,
471+
JSDocPublicTag,
472+
JSDocPrivateTag,
473+
JSDocProtectedTag,
471474
JSDocCallbackTag,
472475
JSDocEnumTag,
473476
JSDocParameterTag,
@@ -2617,6 +2620,18 @@ namespace ts {
26172620
kind: SyntaxKind.JSDocClassTag;
26182621
}
26192622

2623+
export interface JSDocPublicTag extends JSDocTag {
2624+
kind: SyntaxKind.JSDocPublicTag;
2625+
}
2626+
2627+
export interface JSDocPrivateTag extends JSDocTag {
2628+
kind: SyntaxKind.JSDocPrivateTag;
2629+
}
2630+
2631+
export interface JSDocProtectedTag extends JSDocTag {
2632+
kind: SyntaxKind.JSDocProtectedTag;
2633+
}
2634+
26202635
export interface JSDocEnumTag extends JSDocTag, Declaration {
26212636
parent: JSDoc;
26222637
kind: SyntaxKind.JSDocEnumTag;

src/compiler/utilities.ts

+9
Original file line numberDiff line numberDiff line change
@@ -4125,6 +4125,15 @@ namespace ts {
41254125
}
41264126
}
41274127

4128+
if (isInJSFile(node) && !!node.parent) {
4129+
// getModifierFlagsNoCache should only be called when parent pointers are set,
4130+
// or when !(node.flags & NodeFlags.Synthesized) && node.kind !== SyntaxKind.SourceFile)
4131+
const tags = (getJSDocPublicTag(node) ? ModifierFlags.Public : ModifierFlags.None)
4132+
| (getJSDocPrivateTag(node) ? ModifierFlags.Private : ModifierFlags.None)
4133+
| (getJSDocProtectedTag(node) ? ModifierFlags.Protected : ModifierFlags.None);
4134+
flags |= tags;
4135+
}
4136+
41284137
if (node.flags & NodeFlags.NestedNamespace || (node.kind === SyntaxKind.Identifier && (<Identifier>node).isInJSDocNamespace)) {
41294138
flags |= ModifierFlags.Export;
41304139
}

0 commit comments

Comments
 (0)