Skip to content

Commit b385921

Browse files
Max Heibermheiber
authored andcommitted
Parse Private Names
and check that private names not used in parameters Signed-off-by: Max Heiber <max.heiber@gmail.com>
1 parent 42456f6 commit b385921

File tree

52 files changed

+668
-458
lines changed

Some content is hidden

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

52 files changed

+668
-458
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) {
@@ -1392,7 +1395,7 @@ namespace ts {
13921395
}
13931396
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
13941397
const propertyAccess = <PropertyAccessExpression>node.expression;
1395-
if (isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
1398+
if (isIdentifier(propertyAccess.name) && isNarrowableOperand(propertyAccess.expression) && isPushOrUnshiftIdentifier(propertyAccess.name)) {
13961399
currentFlow = createFlowArrayMutation(currentFlow, node);
13971400
}
13981401
}
@@ -2353,7 +2356,7 @@ namespace ts {
23532356
return;
23542357
}
23552358
const lhs = node.left as PropertyAccessEntityNameExpression;
2356-
const symbol = forEachIdentifierInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
2359+
const symbol = forEachIdentifierOrPrivateNameInEntityName(lhs.expression, /*parent*/ undefined, (id, symbol) => {
23572360
if (symbol) {
23582361
addDeclarationToSymbol(symbol, id, SymbolFlags.Module | SymbolFlags.JSContainer);
23592362
}
@@ -2507,7 +2510,7 @@ namespace ts {
25072510
// make symbols or add declarations for intermediate containers
25082511
const flags = SymbolFlags.Module | SymbolFlags.JSContainer;
25092512
const excludeFlags = SymbolFlags.ValueModuleExcludes & ~SymbolFlags.JSContainer;
2510-
namespaceSymbol = forEachIdentifierInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
2513+
namespaceSymbol = forEachIdentifierOrPrivateNameInEntityName(propertyAccess.expression, namespaceSymbol, (id, symbol, parent) => {
25112514
if (symbol) {
25122515
addDeclarationToSymbol(symbol, id, flags);
25132516
return symbol;
@@ -2579,15 +2582,15 @@ namespace ts {
25792582
}
25802583
}
25812584

2582-
function forEachIdentifierInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
2585+
function forEachIdentifierOrPrivateNameInEntityName(e: EntityNameExpression, parent: Symbol | undefined, action: (e: Identifier | PrivateName, symbol: Symbol | undefined, parent: Symbol | undefined) => Symbol | undefined): Symbol | undefined {
25832586
if (isExportsOrModuleExportsOrAlias(file, e)) {
25842587
return file.symbol;
25852588
}
25862589
else if (isIdentifier(e)) {
25872590
return action(e, lookupSymbolForPropertyAccess(e), parent);
25882591
}
25892592
else {
2590-
const s = forEachIdentifierInEntityName(e.expression, parent, action);
2593+
const s = forEachIdentifierOrPrivateNameInEntityName(e.expression, parent, action);
25912594
if (!s || !s.exports) return Debug.fail();
25922595
return action(e.name, s.exports.get(e.name.escapedText), s);
25932596
}

src/compiler/checker.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14160,8 +14160,10 @@ namespace ts {
1416014160
const root = getReferenceRoot(node);
1416114161
const parent = root.parent;
1416214162
const isLengthPushOrUnshift = parent.kind === SyntaxKind.PropertyAccessExpression && (
14163-
(<PropertyAccessExpression>parent).name.escapedText === "length" ||
14164-
parent.parent.kind === SyntaxKind.CallExpression && isPushOrUnshiftIdentifier((<PropertyAccessExpression>parent).name));
14163+
(<PropertyAccessExpression>parent).name.escapedText === "length" || (
14164+
parent.parent.kind === SyntaxKind.CallExpression
14165+
&& isIdentifier((parent as PropertyAccessExpression).name)
14166+
&& isPushOrUnshiftIdentifier((parent as PropertyAccessExpression).name)));
1416514167
const isElementAssignment = parent.kind === SyntaxKind.ElementAccessExpression &&
1416614168
(<ElementAccessExpression>parent).expression === root &&
1416714169
parent.parent.kind === SyntaxKind.BinaryExpression &&
@@ -17760,7 +17762,7 @@ namespace ts {
1776017762
return checkPropertyAccessExpressionOrQualifiedName(node, node.left, node.right);
1776117763
}
1776217764

17763-
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
17765+
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier | PrivateName) {
1776417766
let propType: Type;
1776517767
const leftType = checkNonNullExpression(left);
1776617768
const parentSymbol = getNodeLinks(left).resolvedSymbol;
@@ -17833,7 +17835,7 @@ namespace ts {
1783317835
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
1783417836
}
1783517837

17836-
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier): void {
17838+
function checkPropertyNotUsedBeforeDeclaration(prop: Symbol, node: PropertyAccessExpression | QualifiedName, right: Identifier | PrivateName): void {
1783717839
const { valueDeclaration } = prop;
1783817840
if (!valueDeclaration) {
1783917841
return;
@@ -17903,7 +17905,7 @@ namespace ts {
1790317905
return getIntersectionType(x);
1790417906
}
1790517907

17906-
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
17908+
function reportNonexistentProperty(propNode: Identifier | PrivateName, containingType: Type) {
1790717909
let errorInfo: DiagnosticMessageChain | undefined;
1790817910
let relatedInfo: Diagnostic | undefined;
1790917911
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
@@ -17937,11 +17939,11 @@ namespace ts {
1793717939
diagnostics.add(resultDiagnostic);
1793817940
}
1793917941

17940-
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
17942+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): Symbol | undefined {
1794117943
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1794217944
}
1794317945

17944-
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
17946+
function getSuggestionForNonexistentProperty(name: Identifier | PrivateName | string, containingType: Type): string | undefined {
1794517947
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
1794617948
return suggestion && symbolName(suggestion);
1794717949
}
@@ -21880,6 +21882,9 @@ namespace ts {
2188021882
checkGrammarDecoratorsAndModifiers(node);
2188121883

2188221884
checkVariableLikeDeclaration(node);
21885+
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
21886+
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
21887+
}
2188321888
const func = getContainingFunction(node)!;
2188421889
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
2188521890
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
@@ -23526,9 +23531,9 @@ namespace ts {
2352623531
}
2352723532
}
2352823533

23529-
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier;
23530-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined;
23531-
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | undefined {
23534+
function getIdentifierFromEntityNameExpression(node: Identifier | PropertyAccessExpression): Identifier | PrivateName;
23535+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined;
23536+
function getIdentifierFromEntityNameExpression(node: Expression): Identifier | PrivateName | undefined {
2353223537
switch (node.kind) {
2353323538
case SyntaxKind.Identifier:
2353423539
return node as Identifier;
@@ -29239,6 +29244,10 @@ namespace ts {
2923929244
checkESModuleMarker(node.name);
2924029245
}
2924129246

29247+
if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
29248+
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
29249+
}
29250+
2924229251
const checkLetConstNames = (isLet(node) || isVarConst(node));
2924329252

2924429253
// 1. LexicalDeclaration : LetOrConst BindingList ;

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4134,6 +4134,14 @@
41344134
"category": "Error",
41354135
"code": 18003
41364136
},
4137+
"Private names are not allowed in variable declarations.": {
4138+
"category": "Error",
4139+
"code": 18004
4140+
},
4141+
"Private names cannot be used as parameters": {
4142+
"category": "Error",
4143+
"code": 18005
4144+
},
41374145

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

src/compiler/emitter.ts

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

606+
// PrivateNames
607+
case SyntaxKind.PrivateName:
608+
return emitPrivateName(node as PrivateName);
609+
606610
// Parse tree nodes
607611

608612
// Names
@@ -872,6 +876,10 @@ namespace ts {
872876
case SyntaxKind.Identifier:
873877
return emitIdentifier(<Identifier>node);
874878

879+
// Private Names
880+
case SyntaxKind.PrivateName:
881+
return emitPrivateName(node as PrivateName);
882+
875883
// Reserved words
876884
case SyntaxKind.FalseKeyword:
877885
case SyntaxKind.NullKeyword:
@@ -1061,6 +1069,12 @@ namespace ts {
10611069
emitList(node, node.typeArguments, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
10621070
}
10631071

1072+
function emitPrivateName(node: PrivateName) {
1073+
const writeText = node.symbol ? writeSymbol : write;
1074+
writeText(getTextOfNode(node, /*includeTrivia*/ false), node.symbol);
1075+
emitList(node, /*typeArguments*/ undefined, ListFormat.TypeParameters); // Call emitList directly since it could be an array of TypeParameterDeclarations _or_ type arguments
1076+
}
1077+
10641078
//
10651079
// Names
10661080
//
@@ -3296,7 +3310,7 @@ namespace ts {
32963310
function getLiteralTextOfNode(node: LiteralLikeNode): string {
32973311
if (node.kind === SyntaxKind.StringLiteral && (<StringLiteral>node).textSourceNode) {
32983312
const textSourceNode = (<StringLiteral>node).textSourceNode!;
3299-
if (isIdentifier(textSourceNode)) {
3313+
if (isIdentifierOrPrivateName(textSourceNode)) {
33003314
return getEmitFlags(node) & EmitFlags.NoAsciiEscaping ?
33013315
`"${escapeString(getTextOfNode(textSourceNode))}"` :
33023316
`"${escapeNonAsciiString(getTextOfNode(textSourceNode))}"`;

src/compiler/factory.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ namespace ts {
6565

6666
// Literals
6767

68-
/* @internal */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote: boolean): StringLiteral; // tslint:disable-line unified-signatures
68+
/* @internal */ export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier | PrivateName, isSingleQuote: boolean): StringLiteral; // tslint:disable-line unified-signatures
6969
/** If a node is passed, creates a string literal whose source text is read from a source node during emit. */
70-
export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier): StringLiteral;
70+
export function createLiteral(value: string | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier | PrivateName): StringLiteral;
7171
export function createLiteral(value: number): NumericLiteral;
7272
export function createLiteral(value: boolean): BooleanLiteral;
7373
export function createLiteral(value: string | number | boolean): PrimaryExpression;
74-
export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier, isSingleQuote?: boolean): PrimaryExpression {
74+
export function createLiteral(value: string | number | boolean | StringLiteral | NoSubstitutionTemplateLiteral | NumericLiteral | Identifier | PrivateName, isSingleQuote?: boolean): PrimaryExpression {
7575
if (typeof value === "number") {
7676
return createNumericLiteral(value + "");
7777
}
@@ -138,6 +138,10 @@ namespace ts {
138138
: node;
139139
}
140140

141+
export function updatePrivateName(node: PrivateName): PrivateName {
142+
return node;
143+
}
144+
141145
let nextAutoGenerateId = 0;
142146

143147
/** Create a unique temporary variable. */
@@ -995,15 +999,15 @@ namespace ts {
995999
: node;
9961000
}
9971001

998-
export function createPropertyAccess(expression: Expression, name: string | Identifier | undefined) {
1002+
export function createPropertyAccess(expression: Expression, name: string | Identifier | PrivateName | undefined) {
9991003
const node = <PropertyAccessExpression>createSynthesizedNode(SyntaxKind.PropertyAccessExpression);
10001004
node.expression = parenthesizeForAccess(expression);
10011005
node.name = asName(name)!; // TODO: GH#18217
10021006
setEmitFlags(node, EmitFlags.NoIndentation);
10031007
return node;
10041008
}
10051009

1006-
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier) {
1010+
export function updatePropertyAccess(node: PropertyAccessExpression, expression: Expression, name: Identifier | PrivateName) {
10071011
// Because we are updating existed propertyAccess we want to inherit its emitFlags
10081012
// instead of using the default from createPropertyAccess
10091013
return node.expression !== expression
@@ -2754,7 +2758,7 @@ namespace ts {
27542758

27552759
// Utilities
27562760

2757-
function asName<T extends Identifier | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
2761+
function asName<T extends Identifier | PrivateName | BindingName | PropertyName | EntityName | ThisTypeNode | undefined>(name: string | T): T | Identifier {
27582762
return isString(name) ? createIdentifier(name) : name;
27592763
}
27602764

@@ -3139,7 +3143,7 @@ namespace ts {
31393143
}
31403144
else {
31413145
const expression = setTextRange(
3142-
isIdentifier(memberName)
3146+
(isIdentifier(memberName) || isPrivateName(memberName))
31433147
? createPropertyAccess(target, memberName)
31443148
: createElementAccess(target, memberName),
31453149
memberName
@@ -3570,7 +3574,7 @@ namespace ts {
35703574
}
35713575

35723576
export function createExpressionForPropertyName(memberName: PropertyName): Expression {
3573-
if (isIdentifier(memberName)) {
3577+
if (isIdentifier(memberName) || isPrivateName(memberName)) {
35743578
return createLiteral(memberName);
35753579
}
35763580
else if (isComputedPropertyName(memberName)) {

0 commit comments

Comments
 (0)