Skip to content

Commit 25d135a

Browse files
Max Heibermheiber
authored andcommitted
Parse Private Names
1 parent dc2dc8d commit 25d135a

File tree

57 files changed

+675
-456
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+675
-456
lines changed

src/compiler/binder.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,7 +1387,7 @@ namespace ts {
13871387
}
13881388
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
13891389
const propertyAccess = <PropertyAccessExpression>node.expression;
1390-
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
1390+
if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
13911391
currentFlow = createFlowArrayMutation(currentFlow, node);
13921392
}
13931393
}
@@ -2326,7 +2326,7 @@ namespace ts {
23262326
return;
23272327
}
23282328
const lhs = node.left as PropertyAccessEntityNameExpression;
2329-
const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
2329+
const symbol = forEachIdentifierOrPrivateNameInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
23302330
if (symbol) {
23312331
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.JSContainer);
23322332
}
@@ -2480,7 +2480,7 @@ namespace ts {
24802480
// make symbols or add declarations for intermediate containers
24812481
const flags = SymbolFlags.Module | SymbolFlags.JSContainer;
24822482
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.JSContainer;
2483-
namespaceSymbol = forEachIdentifierInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
2483+
namespaceSymbol = forEachIdentifierOrPrivateNameInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
24842484
if (symbol) {
24852485
addDeclarationToSymbol(symbol, id, flags);
24862486
return symbol;
@@ -2552,15 +2552,15 @@ namespace ts {
25522552
}
25532553
}
25542554

2555-
function forEachIdentifierInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
2555+
function forEachIdentifierOrPrivateNameInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier | PrivateName, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
25562556
if (isExportsOrModuleExportsOrAlias(file, e)) {
25572557
return file.symbol;
25582558
}
25592559
else if (isIdentifier(e)) {
25602560
return action(e, lookupSymbolForPropertyAccess(e), parent);
25612561
}
25622562
else {
2563-
const s = forEachIdentifierInEntityName(e.expression, parent, action);
2563+
const s = forEachIdentifierOrPrivateNameInEntityName(e.expression, parent, action);
25642564
if (!s || !s.exports) return Debug.fail();
25652565
return action(e.name, s.exports.get(e.name.escapedText), s);
25662566
}

src/compiler/checker.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13755,8 +13755,10 @@ namespace ts {
1375513755
const root = getReferenceRoot(node);
1375613756
const parent = root.parent;
1375713757
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
13758-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
13759-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
13758+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
13759+
parent.parent.kind === SyntaxKind.CallExpression
13760+
&& isIdentifier((parent as PropertyAccessExpression).name)
13761+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name)));
1376013762
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1376113763
(<ElementAccessExpression>parent).expression === root &&
1376213764
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -17381,7 +17383,7 @@ namespace ts {
1738117383
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
1738217384
}
1738317385

17384-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
17386+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
1738517387
let propType: Type;
1738617388
const leftType = checkNonNullExpression(left);
1738717389
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -17454,7 +17456,7 @@ namespace ts {
1745417456
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1745517457
}
1745617458

17457-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
17459+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
1745817460
const { valueDeclaration } = prop;
1745917461
if (!valueDeclaration) {
1746017462
return;
@@ -17516,7 +17518,7 @@ namespace ts {
1751617518
return getIntersectionType(x);
1751717519
}
1751817520

17519-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
17521+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
1752017522
let errorInfo: DiagnosticMessageChain | undefined;
1752117523
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
1752217524
for (const subtype of (containingType as UnionType).types) {
@@ -17549,7 +17551,7 @@ namespace ts {
1754917551
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
1755017552
}
1755117553

17552-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
17554+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
1755317555
const suggestion = getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1755417556
return suggestion && symbolName(suggestion);
1755517557
}
@@ -22985,9 +22987,9 @@ namespace ts {
2298522987
}
2298622988
}
2298722989

22988-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
22989-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
22990-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
22990+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
22991+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
22992+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2299122993
switch (node.kind) {
2299222994
case SyntaxKind.Identifier:
2299322995
return node as Identifier;
@@ -28723,6 +28725,9 @@ namespace ts {
2872328725
if (name.originalKeywordKind === SyntaxKind.LetKeyword) {
2872428726
return grammarErrorOnNode(name, Diagnostics.let_is_not_allowed_to_be_used_as_a_name_in_let_or_const_declarations);
2872528727
}
28728+
if (name.originalKeywordKind === SyntaxKind.PrivateName) {
28729+
return grammarErrorOnNode(name, Diagnostics.Private_names_are_not_allowed_outside_class_bodies);
28730+
}
2872628731
}
2872728732
else {
2872828733
const elements = name.elements;

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4093,6 +4093,10 @@
40934093
"category": "Error",
40944094
"code": 18003
40954095
},
4096+
"Private names are not allowed outside class bodies.": {
4097+
"category": "Error",
4098+
"code": 18004
4099+
},
40964100

40974101
"File is a CommonJS module; it may be converted to an ES6 module.": {
40984102
"category": "Suggestion",

src/compiler/emitter.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,10 @@ namespace ts {
603603
case SyntaxKind.Identifier:
604604
return emitIdentifier(<Identifier>node);
605605

606+
// PrivateNames
607+
case SyntaxKind.PrivateName:
608+
return emitPrivateName(node as PrivateName);
609+
606610
// Parse tree nodes
607611

608612
// Names
@@ -872,6 +876,10 @@ namespace ts {
872876
case SyntaxKind.Identifier:
873877
return emitIdentifier(<Identifier>node);
874878

879+
// Private Names
880+
case SyntaxKind.PrivateName:
881+
return emitPrivateName(node as PrivateName);
882+
875883
// Reserved words
876884
case SyntaxKind.FalseKeyword:
877885
case SyntaxKind.NullKeyword:
@@ -1061,6 +1069,12 @@ namespace ts {
10611069
emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
10621070
}
10631071

1072+
function emitPrivateName(node: PrivateName) {
1073+
const writeText = node.symbol ? writeSymbol : write;
1074+
writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
1075+
emitList(node, /*typeArguments*/ undefined, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
1076+
}
1077+
10641078
//
10651079
// Names
10661080
//
@@ -3296,7 +3310,7 @@ namespace ts {
32963310
function getLiteralTextOfNode(node: LiteralLikeNode): string {
32973311
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
32983312
const textSourceNode = (<StringLiteral>node).textSourceNode!;
3299-
if (isIdentifier(textSourceNode)) {
3313+
if (isIdentifierOrPrivateName(textSourceNode)) {
33003314
return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ?
33013315
`"${escapeString(getTextOfNode(textSourceNode))}"` :
33023316
`"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`;

src/compiler/factory.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ namespace ts {
6565

6666
// Literals
6767

68-
/* @internal */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote: boolean): StringLiteral; // tslint:disable-line unified-signatures
68+
/* @internal */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier | PrivateName, isSingleQuote: boolean): StringLiteral; // tslint:disable-line unified-signatures
6969
/** If a node is passed, creates a string literal whose source text is read from a source node during emit. */
70-
export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier): StringLiteral;
70+
export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier | PrivateName): StringLiteral;
7171
export function createLiteral(value: number): NumericLiteral;
7272
export function createLiteral(value: boolean): BooleanLiteral;
7373
export function createLiteral(value: string | number | boolean): PrimaryExpression;
74-
export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote?: boolean): PrimaryExpression {
74+
export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier | PrivateName, isSingleQuote?: boolean): PrimaryExpression {
7575
if (typeof value === "number") {
7676
return createNumericLiteral(value + "");
7777
}
@@ -138,6 +138,10 @@ namespace ts {
138138
: node;
139139
}
140140

141+
export function updatePrivateName(node: PrivateName): PrivateName {
142+
return node;
143+
}
144+
141145
let nextAutoGenerateId = 0;
142146

143147
/** Create a unique temporary variable. */
@@ -996,15 +1000,15 @@ namespace ts {
9961000
: node;
9971001
}
9981002

999-
export function createPropertyAccess(expression: Expression, name: string | Identifier | undefined) {
1003+
export function createPropertyAccess(expression: Expression, name: string | Identifier | PrivateName | undefined) {
10001004
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
10011005
node.expression = parenthesizeForAccess(expression);
10021006
node.name = asName(name)!; // TODO: GH#18217
10031007
setEmitFlags(node, EmitFlags.NoIndentation);
10041008
return node;
10051009
}
10061010

1007-
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) {
1011+
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateName) {
10081012
// Because we are updating existed propertyAccess we want to inherit its emitFlags
10091013
// instead of using the default from createPropertyAccess
10101014
return node.expression !== expression
@@ -2755,7 +2759,7 @@ namespace ts {
27552759

27562760
// Utilities
27572761

2758-
function asName<T extends Identifier | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
2762+
function asName<T extends Identifier | PrivateName | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
27592763
return isString(name) ? createIdentifier(name) : name;
27602764
}
27612765

@@ -3140,7 +3144,7 @@ namespace ts {
31403144
}
31413145
else {
31423146
const expression = setTextRange(
3143-
isIdentifier(memberName)
3147+
(isIdentifier(memberName) || isPrivateName(memberName))
31443148
? createPropertyAccess(target, memberName)
31453149
: createElementAccess(target, memberName),
31463150
memberName
@@ -3571,7 +3575,7 @@ namespace ts {
35713575
}
35723576

35733577
export function createExpressionForPropertyName(memberName: PropertyName): Expression {
3574-
if (isIdentifier(memberName)) {
3578+
if (isIdentifier(memberName) || isPrivateName(memberName)) {
35753579
return createLiteral(memberName);
35763580
}
35773581
else if (isComputedPropertyName(memberName)) {

src/compiler/parser.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1363,6 +1363,9 @@ namespace ts {
13631363
if (allowComputedPropertyNames && token() === SyntaxKind.OpenBracketToken) {
13641364
return parseComputedPropertyName();
13651365
}
1366+
if (token() === SyntaxKind.PrivateName) {
1367+
return parsePrivateName();
1368+
}
13661369
return parseIdentifierName();
13671370
}
13681371

@@ -1386,6 +1389,17 @@ namespace ts {
13861389
return finishNode(node);
13871390
}
13881391

1392+
function createPrivateName(): PrivateName {
1393+
const node = createNode(SyntaxKind.PrivateName) as PrivateName;
1394+
node.escapedText = escapeLeadingUnderscores(scanner.getTokenText());
1395+
nextToken();
1396+
return finishNode(node);
1397+
}
1398+
1399+
function parsePrivateName(): PrivateName {
1400+
return createPrivateName();
1401+
}
1402+
13891403
function parseContextualModifier(t: SyntaxKind): boolean {
13901404
return token() === t && tryParse(nextTokenCanFollowModifier);
13911405
}

src/compiler/scanner.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1723,6 +1723,20 @@ namespace ts {
17231723
error(Diagnostics.Invalid_character);
17241724
pos++;
17251725
return token = SyntaxKind.Unknown;
1726+
case CharacterCodes.hash:
1727+
pos++;
1728+
if (isIdentifierStart(ch = text.charCodeAt(pos), languageVersion)) {
1729+
pos++;
1730+
while (pos < end && isIdentifierPart(ch = text.charCodeAt(pos), languageVersion)) pos++;
1731+
tokenValue = text.substring(tokenPos, pos);
1732+
if (ch === CharacterCodes.backslash) {
1733+
tokenValue += scanIdentifierParts();
1734+
}
1735+
return token = SyntaxKind.PrivateName;
1736+
}
1737+
error(Diagnostics.Invalid_character);
1738+
// no `pos++` because already advanced at beginning of this `case` statement
1739+
return token = SyntaxKind.Unknown;
17261740
default:
17271741
if (isIdentifierStart(ch, languageVersion)) {
17281742
pos++;

src/compiler/transformers/es5.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ namespace ts {
8282
* @param node A PropertyAccessExpression
8383
*/
8484
function substitutePropertyAccessExpression(node: PropertyAccessExpression): Expression {
85-
const literalName = trySubstituteReservedName(node.name);
85+
if (isPrivateName(node)) {
86+
return node;
87+
}
88+
const literalName = trySubstituteReservedName(node.name as Identifier);
8689
if (literalName) {
8790
return setTextRange(createElementAccess(node.expression, literalName), node);
8891
}

src/compiler/transformers/ts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2125,7 +2125,7 @@ namespace ts {
21252125
? getGeneratedNameForNode(name)
21262126
: name.expression;
21272127
}
2128-
else if (isIdentifier(name)) {
2128+
else if (isIdentifier(name) || isPrivateName(name)) {
21292129
return createLiteral(idText(name));
21302130
}
21312131
else {

src/compiler/types.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ namespace ts {
201201
// Names
202202
QualifiedName,
203203
ComputedPropertyName,
204+
PrivateName,
204205
// Signature elements
205206
TypeParameter,
206207
Parameter,
@@ -709,9 +710,9 @@ namespace ts {
709710

710711
export type EntityName = Identifier | QualifiedName;
711712

712-
export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName;
713+
export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | PrivateName;
713714

714-
export type DeclarationName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | BindingPattern;
715+
export type DeclarationName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName | PrivateName | BindingPattern;
715716

716717
export interface Declaration extends Node {
717718
_declarationBrand: any;
@@ -741,6 +742,13 @@ namespace ts {
741742
expression: Expression;
742743
}
743744

745+
export interface PrivateName extends Declaration {
746+
kind: SyntaxKind.PrivateName;
747+
// escaping not strictly necessary
748+
// avoids gotchas in transforms and utils
749+
escapedText: __String;
750+
}
751+
744752
/* @internal */
745753
// A name that supports late-binding (used in checker)
746754
export interface LateBoundName extends ComputedPropertyName {
@@ -1176,7 +1184,7 @@ namespace ts {
11761184

11771185
export interface StringLiteral extends LiteralExpression {
11781186
kind: SyntaxKind.StringLiteral;
1179-
/* @internal */ textSourceNode?: Identifier | StringLiteralLike | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
1187+
/* @internal */ textSourceNode?: Identifier | PrivateName | StringLiteralLike | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
11801188
/** Note: this is only set when synthesizing a node, not during parsing. */
11811189
/* @internal */ singleQuote?: boolean;
11821190
}
@@ -1657,7 +1665,7 @@ namespace ts {
16571665
export interface PropertyAccessExpression extends MemberExpression, NamedDeclaration {
16581666
kind: SyntaxKind.PropertyAccessExpression;
16591667
expression: LeftHandSideExpression;
1660-
name: Identifier;
1668+
name: Identifier | PrivateName;
16611669
}
16621670

16631671
export interface SuperPropertyAccessExpression extends PropertyAccessExpression {

0 commit comments

Comments
 (0)