Skip to content

Commit 3070ad7

Browse files
committed
Parse Private Names
and check that private names not used in parameters Signed-off-by: Max Heiber <[email protected]>
1 parent 7ed3896 commit 3070ad7

File tree

62 files changed

+936
-642
lines changed

Some content is hidden

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

62 files changed

+936
-642
lines changed

src/compiler/binder.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ namespace ts {
278278
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
279279
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
280280
}
281+
if (isPrivateName(node)) {
282+
return nodePosToString(node) as __String;
283+
}
281284
return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
282285
}
283286
switch (node.kind) {
@@ -1458,7 +1461,7 @@ namespace ts {
14581461
}
14591462
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
14601463
const propertyAccess = <PropertyAccessExpression>node.expression;
1461-
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
1464+
if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
14621465
currentFlow = createFlowArrayMutation(currentFlow, node);
14631466
}
14641467
}
@@ -2417,7 +2420,7 @@ namespace ts {
24172420
if (!setCommonJsModuleIndicator(node)) {
24182421
return;
24192422
}
2420-
const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
2423+
const symbol = forEachIdentifierOrPrivateNameInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
24212424
if (symbol) {
24222425
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
24232426
}
@@ -2436,7 +2439,7 @@ namespace ts {
24362439
return;
24372440
}
24382441
const lhs = node.left as PropertyAccessEntityNameExpression;
2439-
const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
2442+
const symbol = forEachIdentifierOrPrivateNameInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
24402443
if (symbol) {
24412444
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
24422445
}
@@ -2606,7 +2609,7 @@ namespace ts {
26062609
// make symbols or add declarations for intermediate containers
26072610
const flags = SymbolFlags.Module | SymbolFlags.Assignment;
26082611
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment;
2609-
namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => {
2612+
namespaceSymbol = forEachIdentifierOrPrivateNameInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => {
26102613
if (symbol) {
26112614
addDeclarationToSymbol(symbol, id, flags);
26122615
return symbol;
@@ -2694,15 +2697,15 @@ namespace ts {
26942697
}
26952698
}
26962699

2697-
function forEachIdentifierInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
2700+
function forEachIdentifierOrPrivateNameInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier | PrivateName, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
26982701
if (isExportsOrModuleExportsOrAlias(file, e)) {
26992702
return file.symbol;
27002703
}
27012704
else if (isIdentifier(e)) {
27022705
return action(e, lookupSymbolForPropertyAccess(e), parent);
27032706
}
27042707
else {
2705-
const s = forEachIdentifierInEntityName(e.expression, parent, action);
2708+
const s = forEachIdentifierOrPrivateNameInEntityName(e.expression, parent, action);
27062709
return action(e.name, s && s.exports && s.exports.get(e.name.escapedText), s);
27072710
}
27082711
}

src/compiler/checker.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16461,8 +16461,10 @@ namespace ts {
1646116461
const root = getReferenceRoot(node);
1646216462
const parent = root.parent;
1646316463
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
16464-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
16465-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
16464+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
16465+
parent.parent.kind === SyntaxKind.CallExpression
16466+
&& isIdentifier((parent as PropertyAccessExpression).name)
16467+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name as Identifier)));
1646616468
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1646716469
(<ElementAccessExpression>parent).expression === root &&
1646816470
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -20226,7 +20228,7 @@ namespace ts {
2022620228
return isCallOrNewExpression(node.parent) && node.parent.expression === node;
2022720229
}
2022820230

20229-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
20231+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
2023020232
let propType: Type;
2023120233
const leftType = checkNonNullExpression(left);
2023220234
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -20244,7 +20246,7 @@ namespace ts {
2024420246
}
2024520247
if (!prop) {
2024620248
const indexInfo = assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined;
20247-
if (!(indexInfo && indexInfo.type)) {
20249+
if (!(indexInfo && indexInfo.type) || isPrivateName(right)) {
2024820250
if (isJSLiteralType(leftType)) {
2024920251
return anyType;
2025020252
}
@@ -20317,7 +20319,7 @@ namespace ts {
2031720319
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
2031820320
}
2031920321

20320-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
20322+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
2032120323
const { valueDeclaration } = prop;
2032220324
if (!valueDeclaration) {
2032320325
return;
@@ -20399,7 +20401,7 @@ namespace ts {
2039920401
return getIntersectionType(x);
2040020402
}
2040120403

20402-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
20404+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
2040320405
let errorInfo: DiagnosticMessageChain | undefined;
2040420406
let relatedInfo: Diagnostic | undefined;
2040520407
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -20442,11 +20444,11 @@ namespace ts {
2044220444
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
2044320445
}
2044420446

20445-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
20447+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
2044620448
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
2044720449
}
2044820450

20449-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
20451+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
2045020452
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
2045120453
return suggestion && symbolName(suggestion);
2045220454
}
@@ -24638,6 +24640,9 @@ namespace ts {
2463824640
checkGrammarDecoratorsAndModifiers(node);
2463924641

2464024642
checkVariableLikeDeclaration(node);
24643+
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
24644+
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
24645+
}
2464124646
const func = getContainingFunction(node)!;
2464224647
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
2464324648
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -26309,9 +26314,9 @@ namespace ts {
2630926314
}
2631026315
}
2631126316

26312-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
26313-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
26314-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
26317+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
26318+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
26319+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2631526320
switch (node.kind) {
2631626321
case SyntaxKind.Identifier:
2631726322
return node as Identifier;
@@ -32222,6 +32227,10 @@ namespace ts {
3222232227
checkESModuleMarker(node.name);
3222332228
}
3222432229

32230+
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
32231+
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
32232+
}
32233+
3222532234
const checkLetConstNames = (isLet(node) || isVarConst(node));
3222632235

3222732236
// 1. LexicalDeclaration : LetOrConst BindingList ;

src/compiler/diagnosticMessages.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5005,5 +5005,14 @@
50055005
"Classes may not have a field named 'constructor'.": {
50065006
"category": "Error",
50075007
"code": 18006
5008+
},
5009+
"Private names are not allowed in variable declarations.": {
5010+
"category": "Error",
5011+
"code": 18007
5012+
},
5013+
"Private names cannot be used as parameters": {
5014+
"category": "Error",
5015+
"code": 18008
50085016
}
5017+
50095018
}

src/compiler/emitter.ts

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

1151+
// PrivateNames
1152+
case SyntaxKind.PrivateName:
1153+
return emitPrivateName(node as PrivateName);
1154+
11511155
// Parse tree nodes
11521156

11531157
// Names
@@ -1447,6 +1451,10 @@ namespace ts {
14471451
case SyntaxKind.Identifier:
14481452
return emitIdentifier(<Identifier>node);
14491453

1454+
// Private Names
1455+
case SyntaxKind.PrivateName:
1456+
return emitPrivateName(node as PrivateName);
1457+
14501458
// Reserved words
14511459
case SyntaxKind.FalseKeyword:
14521460
case SyntaxKind.NullKeyword:
@@ -1705,6 +1713,12 @@ namespace ts {
17051713
emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
17061714
}
17071715

1716+
function emitPrivateName(node: PrivateName) {
1717+
const writeText = node.symbol ? writeSymbol : write;
1718+
writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
1719+
emitList(node, /*typeArguments*/ undefined, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
1720+
}
1721+
17081722
//
17091723
// Names
17101724
//
@@ -4185,7 +4199,7 @@ namespace ts {
41854199
function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined): string {
41864200
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
41874201
const textSourceNode = (<StringLiteral>node).textSourceNode!;
4188-
if (isIdentifier(textSourceNode)) {
4202+
if (isIdentifierOrPrivateName(textSourceNode)) {
41894203
return neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ?
41904204
`"${escapeString(getTextOfNode(textSourceNode))}"` :
41914205
`"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`;

src/compiler/factory.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ namespace ts {
114114
return node;
115115
}
116116

117-
function createLiteralFromNode(sourceNode: PropertyNameLiteral): StringLiteral {
117+
function createLiteralFromNode(sourceNode: Exclude<PropertyNameLiteral, PrivateName>): StringLiteral {
118118
const node = createStringLiteral(getTextOfIdentifierOrLiteral(sourceNode));
119119
node.textSourceNode = sourceNode;
120120
return node;
@@ -1004,15 +1004,15 @@ namespace ts {
10041004
: node;
10051005
}
10061006

1007-
export function createPropertyAccess(expression: Expression, name: string | Identifier) {
1007+
export function createPropertyAccess(expression: Expression, name: string | Identifier | PrivateName) {
10081008
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
10091009
node.expression = parenthesizeForAccess(expression);
10101010
node.name = asName(name);
10111011
setEmitFlags(node, EmitFlags.NoIndentation);
10121012
return node;
10131013
}
10141014

1015-
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) {
1015+
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateName) {
10161016
// Because we are updating existed propertyAccess we want to inherit its emitFlags
10171017
// instead of using the default from createPropertyAccess
10181018
return node.expression !== expression
@@ -3062,7 +3062,7 @@ namespace ts {
30623062

30633063
// Utilities
30643064

3065-
function asName<T extends Identifier | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
3065+
function asName<T extends Identifier | PrivateName | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
30663066
return isString(name) ? createIdentifier(name) : name;
30673067
}
30683068

@@ -3459,7 +3459,7 @@ namespace ts {
34593459
}
34603460
else {
34613461
const expression = setTextRange(
3462-
isIdentifier(memberName)
3462+
(isIdentifier(memberName) || isPrivateName(memberName))
34633463
? createPropertyAccess(target, memberName)
34643464
: createElementAccess(target, memberName),
34653465
memberName
@@ -3915,7 +3915,7 @@ namespace ts {
39153915
}
39163916
}
39173917

3918-
export function createExpressionForPropertyName(memberName: PropertyName): Expression {
3918+
export function createExpressionForPropertyName(memberName: Exclude<PropertyName, PrivateName>): Expression {
39193919
if (isIdentifier(memberName)) {
39203920
return createLiteral(memberName);
39213921
}
@@ -3927,11 +3927,17 @@ namespace ts {
39273927
}
39283928
}
39293929

3930+
/**
3931+
* accessor declaration that can be converted to an expression (`name` field cannot be a `PrivateName`)
3932+
*/
3933+
type ExpressionableAccessorDeclaration = AccessorDeclaration & {name: Exclude<PropertyName, PrivateName>};
3934+
39303935
export function createExpressionForObjectLiteralElementLike(node: ObjectLiteralExpression, property: ObjectLiteralElementLike, receiver: Expression): Expression | undefined {
39313936
switch (property.kind) {
39323937
case SyntaxKind.GetAccessor:
39333938
case SyntaxKind.SetAccessor:
3934-
return createExpressionForAccessorDeclaration(node.properties, property, receiver, !!node.multiLine);
3939+
// type assertion `as ExpressionableAccessorDeclaration` is safe because PrivateNames are not allowed in object literals
3940+
return createExpressionForAccessorDeclaration(node.properties, property as ExpressionableAccessorDeclaration, receiver, !!node.multiLine);
39353941
case SyntaxKind.PropertyAssignment:
39363942
return createExpressionForPropertyAssignment(property, receiver);
39373943
case SyntaxKind.ShorthandPropertyAssignment:
@@ -3941,7 +3947,7 @@ namespace ts {
39413947
}
39423948
}
39433949

3944-
function createExpressionForAccessorDeclaration(properties: NodeArray<Declaration>, property: AccessorDeclaration, receiver: Expression, multiLine: boolean) {
3950+
function createExpressionForAccessorDeclaration(properties: NodeArray<Declaration>, property: ExpressionableAccessorDeclaration, receiver: Expression, multiLine: boolean) {
39453951
const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property);
39463952
if (property === firstAccessor) {
39473953
const properties: ObjectLiteralElementLike[] = [];

0 commit comments

Comments
 (0)