Skip to content

Commit 853486f

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 5701839 commit 853486f

File tree

61 files changed

+921
-628
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

+921
-628
lines changed

src/compiler/binder.ts

Lines changed: 8 additions & 5 deletions
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
}
@@ -2358,7 +2361,7 @@ namespace ts {
23582361
return;
23592362
}
23602363
const lhs = node.left as PropertyAccessEntityNameExpression;
2361-
const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
2364+
const symbol = forEachIdentifierOrPrivateNameInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
23622365
if (symbol) {
23632366
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.Assignment);
23642367
}
@@ -2516,7 +2519,7 @@ namespace ts {
25162519
// make symbols or add declarations for intermediate containers
25172520
const flags = SymbolFlags.Module | SymbolFlags.Assignment;
25182521
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.Assignment;
2519-
namespaceSymbol = forEachIdentifierInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
2522+
namespaceSymbol = forEachIdentifierOrPrivateNameInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
25202523
if (symbol) {
25212524
addDeclarationToSymbol(symbol, id, flags);
25222525
return symbol;
@@ -2588,15 +2591,15 @@ namespace ts {
25882591
}
25892592
}
25902593

2591-
function forEachIdentifierInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
2594+
function forEachIdentifierOrPrivateNameInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier | PrivateName, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
25922595
if (isExportsOrModuleExportsOrAlias(file, e)) {
25932596
return file.symbol;
25942597
}
25952598
else if (isIdentifier(e)) {
25962599
return action(e, lookupSymbolForPropertyAccess(e), parent);
25972600
}
25982601
else {
2599-
const s = forEachIdentifierInEntityName(e.expression, parent, action);
2602+
const s = forEachIdentifierOrPrivateNameInEntityName(e.expression, parent, action);
26002603
if (!s || !s.exports) return Debug.fail();
26012604
return action(e.name, s.exports.get(e.name.escapedText), s);
26022605
}

src/compiler/checker.ts

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14773,8 +14773,10 @@ namespace ts {
1477314773
const root = getReferenceRoot(node);
1477414774
const parent = root.parent;
1477514775
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
14776-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
14777-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
14776+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
14777+
parent.parent.kind === SyntaxKind.CallExpression
14778+
&& isIdentifier((parent as PropertyAccessExpression).name)
14779+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name as Identifier)));
1477814780
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1477914781
(<ElementAccessExpression>parent).expression === root &&
1478014782
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -18619,7 +18621,7 @@ namespace ts {
1861918621
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
1862018622
}
1862118623

18622-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
18624+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
1862318625
let propType: Type;
1862418626
const leftType = checkNonNullExpression(left);
1862518627
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -18637,7 +18639,7 @@ namespace ts {
1863718639
}
1863818640
if (!prop) {
1863918641
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
18640-
if (!(indexInfo && indexInfo.type)) {
18642+
if (!(indexInfo && indexInfo.type) || isPrivateName(right)) {
1864118643
if (isJSLiteralType(leftType)) {
1864218644
return anyType;
1864318645
}
@@ -18701,7 +18703,7 @@ namespace ts {
1870118703
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1870218704
}
1870318705

18704-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
18706+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
1870518707
const { valueDeclaration } = prop;
1870618708
if (!valueDeclaration) {
1870718709
return;
@@ -18771,7 +18773,7 @@ namespace ts {
1877118773
return getIntersectionType(x);
1877218774
}
1877318775

18774-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
18776+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
1877518777
let errorInfo: DiagnosticMessageChain | undefined;
1877618778
let relatedInfo: Diagnostic | undefined;
1877718779
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -18814,11 +18816,11 @@ namespace ts {
1881418816
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
1881518817
}
1881618818

18817-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
18819+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
1881818820
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1881918821
}
1882018822

18821-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
18823+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
1882218824
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
1882318825
return suggestion && symbolName(suggestion);
1882418826
}
@@ -22640,6 +22642,9 @@ namespace ts {
2264022642
checkGrammarDecoratorsAndModifiers(node);
2264122643

2264222644
checkVariableLikeDeclaration(node);
22645+
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
22646+
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
22647+
}
2264322648
const func = getContainingFunction(node)!;
2264422649
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
2264522650
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -24287,9 +24292,9 @@ namespace ts {
2428724292
}
2428824293
}
2428924294

24290-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
24291-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
24292-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
24295+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
24296+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
24297+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2429324298
switch (node.kind) {
2429424299
case SyntaxKind.Identifier:
2429524300
return node as Identifier;
@@ -30141,6 +30146,10 @@ namespace ts {
3014130146
checkESModuleMarker(node.name);
3014230147
}
3014330148

30149+
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
30150+
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
30151+
}
30152+
3014430153
const checkLetConstNames = (isLet(node) || isVarConst(node));
3014530154

3014630155
// 1. LexicalDeclaration : LetOrConst BindingList ;

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4262,6 +4262,14 @@
42624262
"category": "Error",
42634263
"code": 18003
42644264
},
4265+
"Private names are not allowed in variable declarations.": {
4266+
"category": "Error",
4267+
"code": 18004
4268+
},
4269+
"Private names cannot be used as parameters": {
4270+
"category": "Error",
4271+
"code": 18005
4272+
},
42654273

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

src/compiler/emitter.ts

Lines changed: 15 additions & 1 deletion
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
@@ -889,6 +893,10 @@ namespace ts {
889893
case SyntaxKind.Identifier:
890894
return emitIdentifier(<Identifier>node);
891895

896+
// Private Names
897+
case SyntaxKind.PrivateName:
898+
return emitPrivateName(node as PrivateName);
899+
892900
// Reserved words
893901
case SyntaxKind.FalseKeyword:
894902
case SyntaxKind.NullKeyword:
@@ -1078,6 +1086,12 @@ namespace ts {
10781086
emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
10791087
}
10801088

1089+
function emitPrivateName(node: PrivateName) {
1090+
const writeText = node.symbol ? writeSymbol : write;
1091+
writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
1092+
emitList(node, /*typeArguments*/ undefined, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
1093+
}
1094+
10811095
//
10821096
// Names
10831097
//
@@ -3320,7 +3334,7 @@ namespace ts {
33203334
function getLiteralTextOfNode(node: LiteralLikeNode, neverAsciiEscape: boolean | undefined): string {
33213335
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
33223336
const textSourceNode = (<StringLiteral>node).textSourceNode!;
3323-
if (isIdentifier(textSourceNode)) {
3337+
if (isIdentifierOrPrivateName(textSourceNode)) {
33243338
return neverAsciiEscape || (getEmitFlags(node) & EmitFlags.NoAsciiEscaping) ?
33253339
`"${escapeString(getTextOfNode(textSourceNode))}"` :
33263340
`"${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)