Skip to content

Commit a3a4098

Browse files
Max Heibermheiber
Max Heiber
authored andcommitted
Parse Private Names
and check that private names not used in parameters Signed-off-by: Max Heiber <[email protected]>
1 parent caa005b commit a3a4098

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
@@ -14372,8 +14372,10 @@ namespace ts {
1437214372
const root = getReferenceRoot(node);
1437314373
const parent = root.parent;
1437414374
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
14375-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
14376-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
14375+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
14376+
parent.parent.kind === SyntaxKind.CallExpression
14377+
&& isIdentifier((parent as PropertyAccessExpression).name)
14378+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name as Identifier)));
1437714379
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1437814380
(<ElementAccessExpression>parent).expression === root &&
1437914381
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -17999,7 +18001,7 @@ namespace ts {
1799918001
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
1800018002
}
1800118003

18002-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
18004+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
1800318005
let propType: Type;
1800418006
const leftType = checkNonNullExpression(left);
1800518007
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -18017,7 +18019,7 @@ namespace ts {
1801718019
}
1801818020
if (!prop) {
1801918021
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
18020-
if (!(indexInfo && indexInfo.type)) {
18022+
if (!(indexInfo && indexInfo.type) || isPrivateName(right)) {
1802118023
if (isJSLiteralType(leftType)) {
1802218024
return anyType;
1802318025
}
@@ -18075,7 +18077,7 @@ namespace ts {
1807518077
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1807618078
}
1807718079

18078-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
18080+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
1807918081
const { valueDeclaration } = prop;
1808018082
if (!valueDeclaration) {
1808118083
return;
@@ -18145,7 +18147,7 @@ namespace ts {
1814518147
return getIntersectionType(x);
1814618148
}
1814718149

18148-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
18150+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
1814918151
let errorInfo: DiagnosticMessageChain | undefined;
1815018152
let relatedInfo: Diagnostic | undefined;
1815118153
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -18188,11 +18190,11 @@ namespace ts {
1818818190
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
1818918191
}
1819018192

18191-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
18193+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
1819218194
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1819318195
}
1819418196

18195-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
18197+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
1819618198
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
1819718199
return suggestion && symbolName(suggestion);
1819818200
}
@@ -21909,6 +21911,9 @@ namespace ts {
2190921911
checkGrammarDecoratorsAndModifiers(node);
2191021912

2191121913
checkVariableLikeDeclaration(node);
21914+
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
21915+
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
21916+
}
2191221917
const func = getContainingFunction(node)!;
2191321918
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
2191421919
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -23556,9 +23561,9 @@ namespace ts {
2355623561
}
2355723562
}
2355823563

23559-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
23560-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
23561-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
23564+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
23565+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
23566+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2356223567
switch (node.kind) {
2356323568
case SyntaxKind.Identifier:
2356423569
return node as Identifier;
@@ -29322,6 +29327,10 @@ namespace ts {
2932229327
checkESModuleMarker(node.name);
2932329328
}
2932429329

29330+
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
29331+
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
29332+
}
29333+
2932529334
const checkLetConstNames = (isLet(node) || isVarConst(node));
2932629335

2932729336
// 1. LexicalDeclaration : LetOrConst BindingList ;

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4162,6 +4162,14 @@
41624162
"category": "Error",
41634163
"code": 18003
41644164
},
4165+
"Private names are not allowed in variable declarations.": {
4166+
"category": "Error",
4167+
"code": 18004
4168+
},
4169+
"Private names cannot be used as parameters": {
4170+
"category": "Error",
4171+
"code": 18005
4172+
},
41654173

41664174
"File is a CommonJS module; it may be converted to an ES6 module.": {
41674175
"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
//
@@ -3306,7 +3320,7 @@ namespace ts {
33063320
function getLiteralTextOfNode(node: LiteralLikeNode): string {
33073321
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
33083322
const textSourceNode = (<StringLiteral>node).textSourceNode!;
3309-
if (isIdentifier(textSourceNode)) {
3323+
if (isIdentifierOrPrivateName(textSourceNode)) {
33103324
return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ?
33113325
`"${escapeString(getTextOfNode(textSourceNode))}"` :
33123326
`"${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)