Skip to content

Commit 221111e

Browse files
author
Max Heiber
committed
Parse Private Names
and check that private names not used in parameters Signed-off-by: Max Heiber <[email protected]> Signed-off-by: Max Heiber <[email protected]>
1 parent 0af8860 commit 221111e

File tree

61 files changed

+922
-629
lines changed

Some content is hidden

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

61 files changed

+922
-629
lines changed

src/compiler/binder.ts

+9-6
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ namespace ts {
271271
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
272272
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
273273
}
274+
if (isPrivateName(node)) {
275+
return nodePosToString(node) as __String;
276+
}
274277
return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
275278
}
276279
switch (node.kind) {
@@ -1402,7 +1405,7 @@ namespace ts {
14021405
}
14031406
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
14041407
const propertyAccess = <PropertyAccessExpression>node.expression;
1405-
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
1408+
if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
14061409
currentFlow = createFlowArrayMutation(currentFlow, node);
14071410
}
14081411
}
@@ -2368,7 +2371,7 @@ namespace ts {
23682371
if (!setCommonJsModuleIndicator(node)) {
23692372
return;
23702373
}
2371-
const symbol = forEachIdentifierInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
2374+
const symbol = forEachIdentifierOrPrivateNameInEntityName(node.arguments[0], /*parent*/ undefined, (id, symbol) => {
23722375
if (symbol) {
23732376
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
23742377
}
@@ -2387,7 +2390,7 @@ namespace ts {
23872390
return;
23882391
}
23892392
const lhs = node.left as PropertyAccessEntityNameExpression;
2390-
const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
2393+
const symbol = forEachIdentifierOrPrivateNameInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
23912394
if (symbol) {
23922395
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
23932396
}
@@ -2552,7 +2555,7 @@ namespace ts {
25522555
// make symbols or add declarations for intermediate containers
25532556
const flags = SymbolFlags.Module | SymbolFlags.Assignment;
25542557
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment;
2555-
namespaceSymbol = forEachIdentifierInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => {
2558+
namespaceSymbol = forEachIdentifierOrPrivateNameInEntityName(entityName, namespaceSymbol, (id, symbol, parent) => {
25562559
if (symbol) {
25572560
addDeclarationToSymbol(symbol, id, flags);
25582561
return symbol;
@@ -2640,15 +2643,15 @@ namespace ts {
26402643
}
26412644
}
26422645

2643-
function forEachIdentifierInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
2646+
function forEachIdentifierOrPrivateNameInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier | PrivateName, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
26442647
if (isExportsOrModuleExportsOrAlias(file, e)) {
26452648
return file.symbol;
26462649
}
26472650
else if (isIdentifier(e)) {
26482651
return action(e, lookupSymbolForPropertyAccess(e), parent);
26492652
}
26502653
else {
2651-
const s = forEachIdentifierInEntityName(e.expression, parent, action);
2654+
const s = forEachIdentifierOrPrivateNameInEntityName(e.expression, parent, action);
26522655
if (!s || !s.exports) return Debug.fail();
26532656
return action(e.name, s.exports.get(e.name.escapedText), s);
26542657
}

src/compiler/checker.ts

+20-11
Original file line numberDiff line numberDiff line change
@@ -14883,8 +14883,10 @@ namespace ts {
1488314883
const root = getReferenceRoot(node);
1488414884
const parent = root.parent;
1488514885
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
14886-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
14887-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
14886+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
14887+
parent.parent.kind === SyntaxKind.CallExpression
14888+
&& isIdentifier((parent as PropertyAccessExpression).name)
14889+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name as Identifier)));
1488814890
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1488914891
(<ElementAccessExpression>parent).expression === root &&
1489014892
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -18418,7 +18420,7 @@ namespace ts {
1841818420
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
1841918421
}
1842018422

18421-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
18423+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
1842218424
let propType: Type;
1842318425
const leftType = checkNonNullExpression(left);
1842418426
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -18436,7 +18438,7 @@ namespace ts {
1843618438
}
1843718439
if (!prop) {
1843818440
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
18439-
if (!(indexInfo && indexInfo.type)) {
18441+
if (!(indexInfo && indexInfo.type) || isPrivateName(right)) {
1844018442
if (isJSLiteralType(leftType)) {
1844118443
return anyType;
1844218444
}
@@ -18500,7 +18502,7 @@ namespace ts {
1850018502
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1850118503
}
1850218504

18503-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
18505+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
1850418506
const { valueDeclaration } = prop;
1850518507
if (!valueDeclaration) {
1850618508
return;
@@ -18570,7 +18572,7 @@ namespace ts {
1857018572
return getIntersectionType(x);
1857118573
}
1857218574

18573-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
18575+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
1857418576
let errorInfo: DiagnosticMessageChain | undefined;
1857518577
let relatedInfo: Diagnostic | undefined;
1857618578
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -18613,11 +18615,11 @@ namespace ts {
1861318615
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
1861418616
}
1861518617

18616-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
18618+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
1861718619
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1861818620
}
1861918621

18620-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
18622+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
1862118623
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
1862218624
return suggestion && symbolName(suggestion);
1862318625
}
@@ -22524,6 +22526,9 @@ namespace ts {
2252422526
checkGrammarDecoratorsAndModifiers(node);
2252522527

2252622528
checkVariableLikeDeclaration(node);
22529+
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
22530+
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
22531+
}
2252722532
const func = getContainingFunction(node)!;
2252822533
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
2252922534
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -24171,9 +24176,9 @@ namespace ts {
2417124176
}
2417224177
}
2417324178

24174-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
24175-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
24176-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
24179+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
24180+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
24181+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2417724182
switch (node.kind) {
2417824183
case SyntaxKind.Identifier:
2417924184
return node as Identifier;
@@ -30027,6 +30032,10 @@ namespace ts {
3002730032
checkESModuleMarker(node.name);
3002830033
}
3002930034

30035+
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
30036+
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
30037+
}
30038+
3003030039
const checkLetConstNames = (isLet(node) || isVarConst(node));
3003130040

3003230041
// 1. LexicalDeclaration : LetOrConst BindingList ;

src/compiler/diagnosticMessages.json

+8
Original file line numberDiff line numberDiff line change
@@ -4299,6 +4299,14 @@
42994299
"category": "Error",
43004300
"code": 18003
43014301
},
4302+
"Private names are not allowed in variable declarations.": {
4303+
"category": "Error",
4304+
"code": 18004
4305+
},
4306+
"Private names cannot be used as parameters": {
4307+
"category": "Error",
4308+
"code": 18005
4309+
},
43024310

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

src/compiler/emitter.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,10 @@ namespace ts {
620620
case SyntaxKind.Identifier:
621621
return emitIdentifier(<Identifier>node);
622622

623+
// PrivateNames
624+
case SyntaxKind.PrivateName:
625+
return emitPrivateName(node as PrivateName);
626+
623627
// Parse tree nodes
624628

625629
// Names
@@ -916,6 +920,10 @@ namespace ts {
916920
case SyntaxKind.Identifier:
917921
return emitIdentifier(<Identifier>node);
918922

923+
// Private Names
924+
case SyntaxKind.PrivateName:
925+
return emitPrivateName(node as PrivateName);
926+
919927
// Reserved words
920928
case SyntaxKind.FalseKeyword:
921929
case SyntaxKind.NullKeyword:
@@ -1105,6 +1113,12 @@ namespace ts {
11051113
emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
11061114
}
11071115

1116+
function emitPrivateName(node: PrivateName) {
1117+
const writeText = node.symbol ? writeSymbol : write;
1118+
writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
1119+
emitList(node, /*typeArguments*/ undefined, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
1120+
}
1121+
11081122
//
11091123
// Names
11101124
//
@@ -3505,7 +3519,7 @@ namespace ts {
35053519
function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined): string {
35063520
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
35073521
const textSourceNode = (<StringLiteral>node).textSourceNode!;
3508-
if (isIdentifier(textSourceNode)) {
3522+
if (isIdentifierOrPrivateName(textSourceNode)) {
35093523
return neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ?
35103524
`"${escapeString(getTextOfNode(textSourceNode))}"` :
35113525
`"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`;

src/compiler/factory.ts

+14-8
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
@@ -2765,7 +2765,7 @@ namespace ts {
27652765

27662766
// Utilities
27672767

2768-
function asName<T extends Identifier | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
2768+
function asName<T extends Identifier | PrivateName | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
27692769
return isString(name) ? createIdentifier(name) : name;
27702770
}
27712771

@@ -3150,7 +3150,7 @@ namespace ts {
31503150
}
31513151
else {
31523152
const expression = setTextRange(
3153-
isIdentifier(memberName)
3153+
(isIdentifier(memberName) || isPrivateName(memberName))
31543154
? createPropertyAccess(target, memberName)
31553155
: createElementAccess(target, memberName),
31563156
memberName
@@ -3580,7 +3580,7 @@ namespace ts {
35803580
}
35813581
}
35823582

3583-
export function createExpressionForPropertyName(memberName: PropertyName): Expression {
3583+
export function createExpressionForPropertyName(memberName: Exclude<PropertyName, PrivateName>): Expression {
35843584
if (isIdentifier(memberName)) {
35853585
return createLiteral(memberName);
35863586
}
@@ -3592,11 +3592,17 @@ namespace ts {
35923592
}
35933593
}
35943594

3595+
/**
3596+
* accessor declaration that can be converted to an expression (`name` field cannot be a `PrivateName`)
3597+
*/
3598+
type ExpressionableAccessorDeclaration = AccessorDeclaration & {name: Exclude<PropertyName, PrivateName>};
3599+
35953600
export function createExpressionForObjectLiteralElementLike(node: ObjectLiteralExpression, property: ObjectLiteralElementLike, receiver: Expression): Expression | undefined {
35963601
switch (property.kind) {
35973602
case SyntaxKind.GetAccessor:
35983603
case SyntaxKind.SetAccessor:
3599-
return createExpressionForAccessorDeclaration(node.properties, property, receiver, !!node.multiLine);
3604+
// type assertion `as ExpressionableAccessorDeclaration` is safe because PrivateNames are not allowed in object literals
3605+
return createExpressionForAccessorDeclaration(node.properties, property as ExpressionableAccessorDeclaration, receiver, !!node.multiLine);
36003606
case SyntaxKind.PropertyAssignment:
36013607
return createExpressionForPropertyAssignment(property, receiver);
36023608
case SyntaxKind.ShorthandPropertyAssignment:
@@ -3606,7 +3612,7 @@ namespace ts {
36063612
}
36073613
}
36083614

3609-
function createExpressionForAccessorDeclaration(properties: NodeArray<Declaration>, property: AccessorDeclaration, receiver: Expression, multiLine: boolean) {
3615+
function createExpressionForAccessorDeclaration(properties: NodeArray<Declaration>, property: ExpressionableAccessorDeclaration, receiver: Expression, multiLine: boolean) {
36103616
const { firstAccessor, getAccessor, setAccessor } = getAllAccessorDeclarations(properties, property);
36113617
if (property === firstAccessor) {
36123618
const properties: ObjectLiteralElementLike[] = [];

0 commit comments

Comments
 (0)