Skip to content

Commit ce7016e

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

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
@@ -16477,8 +16477,10 @@ namespace ts {
1647716477
const root = getReferenceRoot(node);
1647816478
const parent = root.parent;
1647916479
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
16480-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
16481-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
16480+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
16481+
parent.parent.kind === SyntaxKind.CallExpression
16482+
&& isIdentifier((parent as PropertyAccessExpression).name)
16483+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name as Identifier)));
1648216484
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1648316485
(<ElementAccessExpression>parent).expression === root &&
1648416486
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -20255,7 +20257,7 @@ namespace ts {
2025520257
return isCallOrNewExpression(node.parent) && node.parent.expression === node;
2025620258
}
2025720259

20258-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
20260+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
2025920261
let propType: Type;
2026020262
const leftType = checkNonNullExpression(left);
2026120263
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -20273,7 +20275,7 @@ namespace ts {
2027320275
}
2027420276
if (!prop) {
2027520277
const indexInfo = assignmentKind === AssignmentKind.None || !isGenericObjectType(leftType) || isThisTypeParameter(leftType) ? getIndexInfoOfType(apparentType, IndexKind.String) : undefined;
20276-
if (!(indexInfo && indexInfo.type)) {
20278+
if (!(indexInfo && indexInfo.type) || isPrivateName(right)) {
2027720279
if (isJSLiteralType(leftType)) {
2027820280
return anyType;
2027920281
}
@@ -20346,7 +20348,7 @@ namespace ts {
2034620348
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
2034720349
}
2034820350

20349-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
20351+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
2035020352
const { valueDeclaration } = prop;
2035120353
if (!valueDeclaration) {
2035220354
return;
@@ -20428,7 +20430,7 @@ namespace ts {
2042820430
return getIntersectionType(x);
2042920431
}
2043020432

20431-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
20433+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
2043220434
let errorInfo: DiagnosticMessageChain | undefined;
2043320435
let relatedInfo: Diagnostic | undefined;
2043420436
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -20471,11 +20473,11 @@ namespace ts {
2047120473
return prop !== undefined && prop.valueDeclaration && hasModifier(prop.valueDeclaration, ModifierFlags.Static);
2047220474
}
2047320475

20474-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
20476+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
2047520477
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
2047620478
}
2047720479

20478-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
20480+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
2047920481
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
2048020482
return suggestion && symbolName(suggestion);
2048120483
}
@@ -24682,6 +24684,9 @@ namespace ts {
2468224684
checkGrammarDecoratorsAndModifiers(node);
2468324685

2468424686
checkVariableLikeDeclaration(node);
24687+
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
24688+
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
24689+
}
2468524690
const func = getContainingFunction(node)!;
2468624691
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
2468724692
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -26364,9 +26369,9 @@ namespace ts {
2636426369
}
2636526370
}
2636626371

26367-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
26368-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
26369-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
26372+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
26373+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
26374+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2637026375
switch (node.kind) {
2637126376
case SyntaxKind.Identifier:
2637226377
return node as Identifier;
@@ -32283,6 +32288,10 @@ namespace ts {
3228332288
checkESModuleMarker(node.name);
3228432289
}
3228532290

32291+
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
32292+
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
32293+
}
32294+
3228632295
const checkLetConstNames = (isLet(node) || isVarConst(node));
3228732296

3228832297
// 1. LexicalDeclaration : LetOrConst BindingList ;

src/compiler/diagnosticMessages.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5013,5 +5013,14 @@
50135013
"JSX expressions may not use the comma operator. Did you mean to write an array?": {
50145014
"category": "Error",
50155015
"code": 18007
5016+
},
5017+
"Private names are not allowed in variable declarations.": {
5018+
"category": "Error",
5019+
"code": 18008
5020+
},
5021+
"Private names cannot be used as parameters": {
5022+
"category": "Error",
5023+
"code": 18009
50165024
}
5025+
50175026
}

src/compiler/emitter.ts

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

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

11541158
// Names
@@ -1448,6 +1452,10 @@ namespace ts {
14481452
case SyntaxKind.Identifier:
14491453
return emitIdentifier(<Identifier>node);
14501454

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

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