Skip to content

Commit 0b8c723

Browse files
authored
Merge pull request #4 from mheiber/syntaxkind
Parse Private Names
2 parents eac1bf8 + 548c9f8 commit 0b8c723

File tree

60 files changed

+920
-627
lines changed

Some content is hidden

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

60 files changed

+920
-627
lines changed

src/compiler/binder.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,9 @@ namespace ts {
266266
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
267267
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
268268
}
269+
if (isPrivateName(node)) {
270+
return nodePosToString(node) as __String;
271+
}
269272
return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
270273
}
271274
switch (node.kind) {
@@ -1394,7 +1397,7 @@ namespace ts {
13941397
}
13951398
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
13961399
const propertyAccess = <PropertyAccessExpression>node.expression;
1397-
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
1400+
if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
13981401
currentFlow = createFlowArrayMutation(currentFlow, node);
13991402
}
14001403
}
@@ -2359,7 +2362,7 @@ namespace ts {
23592362
return;
23602363
}
23612364
const lhs = node.left as PropertyAccessEntityNameExpression;
2362-
const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
2365+
const symbol = forEachIdentifierOrPrivateNameInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
23632366
if (symbol) {
23642367
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.JSContainer);
23652368
}
@@ -2518,7 +2521,7 @@ namespace ts {
25182521
// make symbols or add declarations for intermediate containers
25192522
const flags = SymbolFlags.Module | SymbolFlags.JSContainer;
25202523
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.JSContainer;
2521-
namespaceSymbol = forEachIdentifierInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
2524+
namespaceSymbol = forEachIdentifierOrPrivateNameInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
25222525
if (symbol) {
25232526
addDeclarationToSymbol(symbol, id, flags);
25242527
return symbol;
@@ -2590,15 +2593,15 @@ namespace ts {
25902593
}
25912594
}
25922595

2593-
function forEachIdentifierInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
2596+
function forEachIdentifierOrPrivateNameInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier | PrivateName, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
25942597
if (isExportsOrModuleExportsOrAlias(file, e)) {
25952598
return file.symbol;
25962599
}
25972600
else if (isIdentifier(e)) {
25982601
return action(e, lookupSymbolForPropertyAccess(e), parent);
25992602
}
26002603
else {
2601-
const s = forEachIdentifierInEntityName(e.expression, parent, action);
2604+
const s = forEachIdentifierOrPrivateNameInEntityName(e.expression, parent, action);
26022605
if (!s || !s.exports) return Debug.fail();
26032606
return action(e.name, s.exports.get(e.name.escapedText), s);
26042607
}

src/compiler/checker.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14346,8 +14346,10 @@ namespace ts {
1434614346
const root = getReferenceRoot(node);
1434714347
const parent = root.parent;
1434814348
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
14349-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
14350-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
14349+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
14350+
parent.parent.kind === SyntaxKind.CallExpression
14351+
&& isIdentifier((parent as PropertyAccessExpression).name)
14352+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name as Identifier)));
1435114353
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1435214354
(<ElementAccessExpression>parent).expression === root &&
1435314355
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -17972,7 +17974,7 @@ namespace ts {
1797217974
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
1797317975
}
1797417976

17975-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
17977+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
1797617978
let propType: Type;
1797717979
const leftType = checkNonNullExpression(left);
1797817980
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -17990,7 +17992,7 @@ namespace ts {
1799017992
}
1799117993
if (!prop) {
1799217994
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
17993-
if (!(indexInfo && indexInfo.type)) {
17995+
if (!(indexInfo && indexInfo.type) || isPrivateName(right)) {
1799417996
if (isJSLiteralType(leftType)) {
1799517997
return anyType;
1799617998
}
@@ -18048,7 +18050,7 @@ namespace ts {
1804818050
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1804918051
}
1805018052

18051-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
18053+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
1805218054
const { valueDeclaration } = prop;
1805318055
if (!valueDeclaration) {
1805418056
return;
@@ -18118,7 +18120,7 @@ namespace ts {
1811818120
return getIntersectionType(x);
1811918121
}
1812018122

18121-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
18123+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
1812218124
let errorInfo: DiagnosticMessageChain | undefined;
1812318125
let relatedInfo: Diagnostic | undefined;
1812418126
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -18161,11 +18163,11 @@ namespace ts {
1816118163
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
1816218164
}
1816318165

18164-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
18166+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
1816518167
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1816618168
}
1816718169

18168-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
18170+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
1816918171
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
1817018172
return suggestion && symbolName(suggestion);
1817118173
}
@@ -21876,6 +21878,9 @@ namespace ts {
2187621878
checkGrammarDecoratorsAndModifiers(node);
2187721879

2187821880
checkVariableLikeDeclaration(node);
21881+
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
21882+
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
21883+
}
2187921884
const func = getContainingFunction(node)!;
2188021885
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
2188121886
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -23523,9 +23528,9 @@ namespace ts {
2352323528
}
2352423529
}
2352523530

23526-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
23527-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
23528-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
23531+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
23532+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
23533+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2352923534
switch (node.kind) {
2353023535
case SyntaxKind.Identifier:
2353123536
return node as Identifier;
@@ -29288,6 +29293,10 @@ namespace ts {
2928829293
checkESModuleMarker(node.name);
2928929294
}
2929029295

29296+
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
29297+
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
29298+
}
29299+
2929129300
const checkLetConstNames = (isLet(node) || isVarConst(node));
2929229301

2929329302
// 1. LexicalDeclaration : LetOrConst BindingList ;

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4166,6 +4166,14 @@
41664166
"category": "Error",
41674167
"code": 18003
41684168
},
4169+
"Private names are not allowed in variable declarations.": {
4170+
"category": "Error",
4171+
"code": 18004
4172+
},
4173+
"Private names cannot be used as parameters": {
4174+
"category": "Error",
4175+
"code": 18005
4176+
},
41694177

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

src/compiler/emitter.ts

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

612+
// PrivateNames
613+
case SyntaxKind.PrivateName:
614+
return emitPrivateName(node as PrivateName);
615+
612616
// Parse tree nodes
613617

614618
// Names
@@ -878,6 +882,10 @@ namespace ts {
878882
case SyntaxKind.Identifier:
879883
return emitIdentifier(<Identifier>node);
880884

885+
// Private Names
886+
case SyntaxKind.PrivateName:
887+
return emitPrivateName(node as PrivateName);
888+
881889
// Reserved words
882890
case SyntaxKind.FalseKeyword:
883891
case SyntaxKind.NullKeyword:
@@ -1067,6 +1075,12 @@ namespace ts {
10671075
emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
10681076
}
10691077

1078+
function emitPrivateName(node: PrivateName) {
1079+
const writeText = node.symbol ? writeSymbol : write;
1080+
writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
1081+
emitList(node, /*typeArguments*/ undefined, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
1082+
}
1083+
10701084
//
10711085
// Names
10721086
//
@@ -3302,7 +3316,7 @@ namespace ts {
33023316
function getLiteralTextOfNode(node: LiteralLikeNode): string {
33033317
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
33043318
const textSourceNode = (<StringLiteral>node).textSourceNode!;
3305-
if (isIdentifier(textSourceNode)) {
3319+
if (isIdentifierOrPrivateName(textSourceNode)) {
33063320
return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ?
33073321
`"${escapeString(getTextOfNode(textSourceNode))}"` :
33083322
`"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`;

src/compiler/factory.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ namespace ts {
105105
return node;
106106
}
107107

108-
function createLiteralFromNode(sourceNode: PropertyNameLiteral): StringLiteral {
108+
function createLiteralFromNode(sourceNode: Exclude<PropertyNameLiteral, PrivateName>): StringLiteral {
109109
const node = createStringLiteral(getTextOfIdentifierOrLiteral(sourceNode));
110110
node.textSourceNode = sourceNode;
111111
return node;
@@ -995,15 +995,15 @@ namespace ts {
995995
: node;
996996
}
997997

998-
export function createPropertyAccess(expression: Expression, name: string | Identifier | undefined) {
998+
export function createPropertyAccess(expression: Expression, name: string | Identifier | PrivateName | undefined) {
999999
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
10001000
node.expression = parenthesizeForAccess(expression);
10011001
node.name = asName(name)!; // TODO: GH#18217
10021002
setEmitFlags(node, EmitFlags.NoIndentation);
10031003
return node;
10041004
}
10051005

1006-
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) {
1006+
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateName) {
10071007
// Because we are updating existed propertyAccess we want to inherit its emitFlags
10081008
// instead of using the default from createPropertyAccess
10091009
return node.expression !== expression
@@ -2714,7 +2714,7 @@ namespace ts {
27142714

27152715
// Utilities
27162716

2717-
function asName<T extends Identifier | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
2717+
function asName<T extends Identifier | PrivateName | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
27182718
return isString(name) ? createIdentifier(name) : name;
27192719
}
27202720

@@ -3099,7 +3099,7 @@ namespace ts {
30993099
}
31003100
else {
31013101
const expression = setTextRange(
3102-
isIdentifier(memberName)
3102+
(isIdentifier(memberName) || isPrivateName(memberName))
31033103
? createPropertyAccess(target, memberName)
31043104
: createElementAccess(target, memberName),
31053105
memberName
@@ -3529,7 +3529,7 @@ namespace ts {
35293529
}
35303530
}
35313531

3532-
export function createExpressionForPropertyName(memberName: PropertyName): Expression {
3532+
export function createExpressionForPropertyName(memberName: Exclude<PropertyName, PrivateName>): Expression {
35333533
if (isIdentifier(memberName)) {
35343534
return createLiteral(memberName);
35353535
}
@@ -3541,11 +3541,17 @@ namespace ts {
35413541
}
35423542
}
35433543

3544+
/**
3545+
* accessor declaration that can be converted to an expression (`name` field cannot be a `PrivateName`)
3546+
*/
3547+
type ExpressionableAccessorDeclaration = AccessorDeclaration & {name: Exclude<PropertyName, PrivateName>};
3548+
35443549
export function createExpressionForObjectLiteralElementLike(node: ObjectLiteralExpression, property: ObjectLiteralElementLike, receiver: Expression): Expression | undefined {
35453550
switch (property.kind) {
35463551
case SyntaxKind.GetAccessor:
35473552
case SyntaxKind.SetAccessor:
3548-
return createExpressionForAccessorDeclaration(node.properties, property, receiver, !!node.multiLine);
3553+
// type assertion `as ExpressionableAccessorDeclaration` is safe because PrivateNames are not allowed in object literals
3554+
return createExpressionForAccessorDeclaration(node.properties, property as ExpressionableAccessorDeclaration, receiver, !!node.multiLine);
35493555
case SyntaxKind.PropertyAssignment:
35503556
return createExpressionForPropertyAssignment(property, receiver);
35513557
case SyntaxKind.ShorthandPropertyAssignment:
@@ -3555,7 +3561,7 @@ namespace ts {
35553561
}
35563562
}
35573563

3558-
function createExpressionForAccessorDeclaration(properties: NodeArray<Declaration>, property: AccessorDeclaration, receiver: Expression, multiLine: boolean) {
3564+
function createExpressionForAccessorDeclaration(properties: NodeArray<Declaration>, property: ExpressionableAccessorDeclaration, receiver: Expression, multiLine: boolean) {
35593565
const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property);
35603566
if (property === firstAccessor) {
35613567
const properties: ObjectLiteralElementLike[] = [];

0 commit comments

Comments
 (0)