Skip to content

Commit 0f09ecb

Browse files
committed
Parse private names
Signed-off-by: Max Heiber <[email protected]>
1 parent 6c413e0 commit 0f09ecb

29 files changed

+845
-595
lines changed

src/compiler/binder.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1599,7 +1599,7 @@ namespace ts {
15991599
}
16001600
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
16011601
const propertyAccess = <PropertyAccessExpression>node.expression;
1602-
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
1602+
if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
16031603
currentFlow = createFlowMutation(FlowFlags.ArrayMutation, currentFlow, node);
16041604
}
16051605
}

src/compiler/checker.ts

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11963,7 +11963,7 @@ namespace ts {
1196311963
return false;
1196411964
}
1196511965

11966-
function getPropertyNameFromIndex(indexType: Type, accessNode: StringLiteral | Identifier | ObjectBindingPattern | ArrayBindingPattern | ComputedPropertyName | NumericLiteral | IndexedAccessTypeNode | ElementAccessExpression | SyntheticExpression | undefined) {
11966+
function getPropertyNameFromIndex(indexType: Type, accessNode: StringLiteral | Identifier | PrivateName | ObjectBindingPattern | ArrayBindingPattern | ComputedPropertyName | NumericLiteral | IndexedAccessTypeNode | ElementAccessExpression | SyntheticExpression | undefined) {
1196711967
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
1196811968
return isTypeUsableAsPropertyName(indexType) ?
1196911969
getPropertyNameFromType(indexType) :
@@ -18902,9 +18902,11 @@ namespace ts {
1890218902
function isEvolvingArrayOperationTarget(node: Node) {
1890318903
const root = getReferenceRoot(node);
1890418904
const parent = root.parent;
18905-
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
18906-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
18907-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
18905+
const isLengthPushOrUnshift = isPropertyAccessExpression(parent) && (
18906+
parent.name.escapedText === "length" ||
18907+
parent.parent.kind === SyntaxKind.CallExpression
18908+
&& isIdentifier(parent.name)
18909+
&& isPushOrUnshiftIdentifier(parent.name));
1890818910
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1890918911
(<ElementAccessExpression>parent).expression === root &&
1891018912
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -23023,7 +23025,7 @@ namespace ts {
2302323025
return isCallOrNewExpression(node.parent) && node.parent.expression === node;
2302423026
}
2302523027

23026-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, leftType: Type, right: Identifier) {
23028+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, leftType: Type, right: Identifier | PrivateName) {
2302723029
const parentSymbol = getNodeLinks(left).resolvedSymbol;
2302823030
const assignmentKind = getAssignmentTargetKind(node);
2302923031
const apparentType = getApparentType(assignmentKind !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(leftType) : leftType);
@@ -23117,7 +23119,7 @@ namespace ts {
2311723119
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
2311823120
}
2311923121

23120-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
23122+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
2312123123
const { valueDeclaration } = prop;
2312223124
if (!valueDeclaration || getSourceFileOfNode(node).isDeclarationFile) {
2312323125
return;
@@ -23199,7 +23201,7 @@ namespace ts {
2319923201
return getIntersectionType(x);
2320023202
}
2320123203

23202-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
23204+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
2320323205
let errorInfo: DiagnosticMessageChain | undefined;
2320423206
let relatedInfo: Diagnostic | undefined;
2320523207
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -23243,7 +23245,7 @@ namespace ts {
2324323245
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
2324423246
}
2324523247

23246-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
23248+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
2324723249
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
2324823250
}
2324923251

@@ -29611,9 +29613,9 @@ namespace ts {
2961129613
}
2961229614
}
2961329615

29614-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
29615-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
29616-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
29616+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
29617+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
29618+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2961729619
switch (node.kind) {
2961829620
case SyntaxKind.Identifier:
2961929621
return node as Identifier;

src/compiler/diagnosticMessages.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5357,5 +5357,10 @@
53575357
"JSX expressions may not use the comma operator. Did you mean to write an array?": {
53585358
"category": "Error",
53595359
"code": 18007
5360+
},
5361+
"'#!' can only be used at the start of a file.": {
5362+
"category": "Error",
5363+
"code": 18008
53605364
}
5365+
53615366
}

src/compiler/factory.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1740,13 +1740,13 @@ namespace ts {
17401740
/**
17411741
* Gets the property name of a BindingOrAssignmentElement
17421742
*/
1743-
export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): PropertyName | undefined {
1743+
export function getPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateName> | undefined {
17441744
const propertyName = tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement);
17451745
Debug.assert(!!propertyName || isSpreadAssignment(bindingElement), "Invalid property name for binding element.");
17461746
return propertyName;
17471747
}
17481748

1749-
export function tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): PropertyName | undefined {
1749+
export function tryGetPropertyNameOfBindingOrAssignmentElement(bindingElement: BindingOrAssignmentElement): Exclude<PropertyName, PrivateName> | undefined {
17501750
switch (bindingElement.kind) {
17511751
case SyntaxKind.BindingElement:
17521752
// `a` in `let { a: b } = ...`
@@ -1755,6 +1755,9 @@ namespace ts {
17551755
// `1` in `let { 1: b } = ...`
17561756
if (bindingElement.propertyName) {
17571757
const propertyName = bindingElement.propertyName;
1758+
if (isPrivateName(propertyName)) {
1759+
return Debug.failBadSyntaxKind(propertyName);
1760+
}
17581761
return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression)
17591762
? propertyName.expression
17601763
: propertyName;
@@ -1769,6 +1772,9 @@ namespace ts {
17691772
// `1` in `({ 1: b } = ...)`
17701773
if (bindingElement.name) {
17711774
const propertyName = bindingElement.name;
1775+
if (isPrivateName(propertyName)) {
1776+
return Debug.failBadSyntaxKind(propertyName);
1777+
}
17721778
return isComputedPropertyName(propertyName) && isStringOrNumericLiteral(propertyName.expression)
17731779
? propertyName.expression
17741780
: propertyName;
@@ -1778,6 +1784,9 @@ namespace ts {
17781784

17791785
case SyntaxKind.SpreadAssignment:
17801786
// `a` in `({ ...a } = ...)`
1787+
if (bindingElement.name && isPrivateName(bindingElement.name)) {
1788+
return Debug.failBadSyntaxKind(bindingElement.name);
1789+
}
17811790
return bindingElement.name;
17821791
}
17831792

@@ -1899,4 +1908,4 @@ namespace ts {
18991908
Debug.assertNode(node, isExpression);
19001909
return node;
19011910
}
1902-
}
1911+
}

src/compiler/factoryPublic.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ namespace ts {
116116
return node;
117117
}
118118

119-
function createLiteralFromNode(sourceNode: PropertyNameLiteral): StringLiteral {
119+
function createLiteralFromNode(sourceNode: Exclude<PropertyNameLiteral, PrivateName>): StringLiteral {
120120
const node = createStringLiteral(getTextOfIdentifierOrLiteral(sourceNode));
121121
node.textSourceNode = sourceNode;
122122
return node;
@@ -1057,17 +1057,19 @@ namespace ts {
10571057
: node;
10581058
}
10591059

1060-
export function createPropertyAccess(expression: Expression, name: string | Identifier) {
1060+
export function createPropertyAccess(expression: Expression, name: string | Identifier | PrivateName) {
10611061
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
10621062
node.expression = parenthesizeForAccess(expression);
10631063
node.name = asName(name);
10641064
setEmitFlags(node, EmitFlags.NoIndentation);
10651065
return node;
10661066
}
10671067

1068-
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) {
1069-
if (isOptionalChain(node)) {
1070-
return updatePropertyAccessChain(node, expression, node.questionDotToken, name);
1068+
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateName) {
1069+
if (isOptionalChain(node) && isIdentifier(node.name) && isIdentifier(name)) {
1070+
// Not sure why this cast was necessary: the previous line should already establish that node.name is an identifier
1071+
const theNode = node as (typeof node & { name: Identifier });
1072+
return updatePropertyAccessChain(theNode, expression, node.questionDotToken, name);
10711073
}
10721074
// Because we are updating existed propertyAccess we want to inherit its emitFlags
10731075
// instead of using the default from createPropertyAccess

src/compiler/parser.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,17 @@ namespace ts {
14291429
return createIdentifier(tokenIsIdentifierOrKeyword(token()), diagnosticMessage);
14301430
}
14311431

1432+
function createPrivateName(): PrivateName {
1433+
const node = createNode(SyntaxKind.PrivateName) as PrivateName;
1434+
node.escapedText = escapeLeadingUnderscores(scanner.getTokenText());
1435+
nextToken();
1436+
return finishNode(node);
1437+
}
1438+
1439+
function parsePrivateName(): PrivateName {
1440+
return createPrivateName();
1441+
}
1442+
14321443
function isLiteralPropertyName(): boolean {
14331444
return tokenIsIdentifierOrKeyword(token()) ||
14341445
token() === SyntaxKind.StringLiteral ||
@@ -1444,6 +1455,9 @@ namespace ts {
14441455
if (allowComputedPropertyNames && token() === SyntaxKind.OpenBracketToken) {
14451456
return parseComputedPropertyName();
14461457
}
1458+
if (token() === SyntaxKind.PrivateName) {
1459+
return parsePrivateName();
1460+
}
14471461
return parseIdentifierName();
14481462
}
14491463

src/compiler/scanner.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,6 +1893,25 @@ namespace ts {
18931893
error(Diagnostics.Invalid_character);
18941894
pos++;
18951895
return token = SyntaxKind.Unknown;
1896+
case CharacterCodes.hash:
1897+
if (pos !== 0 && text[pos + 1] === "!") {
1898+
error(Diagnostics.can_only_be_used_at_the_start_of_a_file);
1899+
pos++;
1900+
return token = SyntaxKind.Unknown;
1901+
}
1902+
pos++;
1903+
if (isIdentifierStart(ch = text.charCodeAt(pos), languageVersion)) {
1904+
pos++;
1905+
while (pos < end && isIdentifierPart(ch = text.charCodeAt(pos), languageVersion)) pos++;
1906+
tokenValue = text.substring(tokenPos, pos);
1907+
if (ch === CharacterCodes.backslash) {
1908+
tokenValue += scanIdentifierParts();
1909+
}
1910+
return token = SyntaxKind.PrivateName;
1911+
}
1912+
error(Diagnostics.Invalid_character);
1913+
// no `pos++` because already advanced past the '#'
1914+
return token = SyntaxKind.Unknown;
18961915
default:
18971916
if (isIdentifierStart(ch, languageVersion)) {
18981917
pos += charSize(ch);

src/compiler/transformers/es5.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ namespace ts {
8282
* @param node A PropertyAccessExpression
8383
*/
8484
function substitutePropertyAccessExpression(node: PropertyAccessExpression): Expression {
85-
const literalName = trySubstituteReservedName(node.name);
85+
const literalName = trySubstituteReservedName(cast(node.name, isIdentifier));
8686
if (literalName) {
8787
return setTextRange(createElementAccess(node.expression, literalName), node);
8888
}

src/compiler/types.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,9 @@ namespace ts {
201201
AmpersandEqualsToken,
202202
BarEqualsToken,
203203
CaretEqualsToken,
204-
// Identifiers
204+
// Identifiers and Private Names
205205
Identifier,
206+
PrivateName,
206207
// Reserved words
207208
BreakKeyword,
208209
CaseKeyword,
@@ -831,10 +832,11 @@ namespace ts {
831832

832833
export type EntityName = Identifier | QualifiedName;
833834

834-
export type PropertyName = Identifier | StringLiteral | NumericLiteral | ComputedPropertyName;
835+
export type PropertyName = Identifier | PrivateName | StringLiteral | NumericLiteral | ComputedPropertyName;
835836

836837
export type DeclarationName =
837838
| Identifier
839+
| PrivateName
838840
| StringLiteralLike
839841
| NumericLiteral
840842
| ComputedPropertyName
@@ -886,6 +888,14 @@ namespace ts {
886888
expression: Expression;
887889
}
888890

891+
export interface PrivateName extends PrimaryExpression, Declaration {
892+
kind: SyntaxKind.PrivateName;
893+
// escaping not strictly necessary
894+
// avoids gotchas in transforms and utils
895+
escapedText: __String;
896+
}
897+
898+
889899
/* @internal */
890900
// A name that supports late-binding (used in checker)
891901
export interface LateBoundName extends ComputedPropertyName {
@@ -1831,11 +1841,12 @@ namespace ts {
18311841
kind: SyntaxKind.PropertyAccessExpression;
18321842
expression: LeftHandSideExpression;
18331843
questionDotToken?: QuestionDotToken;
1834-
name: Identifier;
1844+
name: Identifier | PrivateName;
18351845
}
18361846

18371847
export interface PropertyAccessChain extends PropertyAccessExpression {
18381848
_optionalChainBrand: any;
1849+
name: Identifier;
18391850
}
18401851

18411852
/* @internal */
@@ -1851,6 +1862,7 @@ namespace ts {
18511862
export interface PropertyAccessEntityNameExpression extends PropertyAccessExpression {
18521863
_propertyAccessExpressionLikeQualifiedNameBrand?: any;
18531864
expression: EntityNameExpression;
1865+
name: Identifier;
18541866
}
18551867

18561868
export interface ElementAccessExpression extends MemberExpression {
@@ -1909,6 +1921,7 @@ namespace ts {
19091921
/** @internal */
19101922
export interface WellKnownSymbolExpression extends PropertyAccessExpression {
19111923
expression: Identifier & { escapedText: "Symbol" };
1924+
name: Identifier;
19121925
}
19131926
/** @internal */
19141927
export type BindableObjectDefinePropertyCall = CallExpression & { arguments: { 0: BindableStaticNameExpression, 1: StringLiteralLike | NumericLiteral, 2: ObjectLiteralExpression } };

src/compiler/utilities.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,7 @@ namespace ts {
840840
export function getTextOfPropertyName(name: PropertyName | NoSubstitutionTemplateLiteral): __String {
841841
switch (name.kind) {
842842
case SyntaxKind.Identifier:
843+
case SyntaxKind.PrivateName:
843844
return name.escapedText;
844845
case SyntaxKind.StringLiteral:
845846
case SyntaxKind.NumericLiteral:
@@ -860,7 +861,12 @@ namespace ts {
860861
case SyntaxKind.QualifiedName:
861862
return entityNameToString(name.left) + "." + entityNameToString(name.right);
862863
case SyntaxKind.PropertyAccessExpression:
863-
return entityNameToString(name.expression) + "." + entityNameToString(name.name);
864+
if (isIdentifier(name.name)) {
865+
return entityNameToString(name.expression) + "." + entityNameToString(name.name);
866+
}
867+
else {
868+
return Debug.assertNever(name.name);
869+
}
864870
default:
865871
throw Debug.assertNever(name);
866872
}
@@ -2111,7 +2117,7 @@ namespace ts {
21112117
* throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand)
21122118
*/
21132119
/* @internal */
2114-
export function getElementOrPropertyAccessArgumentExpressionOrName(node: AccessExpression): Identifier | StringLiteralLike | NumericLiteral | ElementAccessExpression | undefined {
2120+
export function getElementOrPropertyAccessArgumentExpressionOrName(node: AccessExpression): Identifier | PrivateName | StringLiteralLike | NumericLiteral | ElementAccessExpression | undefined {
21152121
if (isPropertyAccessExpression(node)) {
21162122
return node.name;
21172123
}
@@ -2913,6 +2919,7 @@ namespace ts {
29132919
export function getPropertyNameForPropertyNameNode(name: PropertyName): __String | undefined {
29142920
switch (name.kind) {
29152921
case SyntaxKind.Identifier:
2922+
case SyntaxKind.PrivateName:
29162923
return name.escapedText;
29172924
case SyntaxKind.StringLiteral:
29182925
case SyntaxKind.NumericLiteral:
@@ -2931,7 +2938,7 @@ namespace ts {
29312938
}
29322939
}
29332940

2934-
export type PropertyNameLiteral = Identifier | StringLiteralLike | NumericLiteral;
2941+
export type PropertyNameLiteral = Identifier | PrivateName | StringLiteralLike | NumericLiteral;
29352942
export function isPropertyNameLiteral(node: Node): node is PropertyNameLiteral {
29362943
switch (node.kind) {
29372944
case SyntaxKind.Identifier:
@@ -2944,11 +2951,17 @@ namespace ts {
29442951
}
29452952
}
29462953
export function getTextOfIdentifierOrLiteral(node: PropertyNameLiteral): string {
2947-
return node.kind === SyntaxKind.Identifier ? idText(node) : node.text;
2954+
return isIdentifierOrPrivateName(node) ? idText(node) : node.text;
29482955
}
29492956

29502957
export function getEscapedTextOfIdentifierOrLiteral(node: PropertyNameLiteral): __String {
2951-
return node.kind === SyntaxKind.Identifier ? node.escapedText : escapeLeadingUnderscores(node.text);
2958+
switch(node.kind) {
2959+
case SyntaxKind.Identifier:
2960+
case SyntaxKind.PrivateName:
2961+
return node.escapedText;
2962+
default:
2963+
return escapeLeadingUnderscores(node.text);
2964+
}
29522965
}
29532966

29542967
export function getPropertyNameForKnownSymbolName(symbolName: string): __String {

0 commit comments

Comments
 (0)