Skip to content

Checker no mangle #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ namespace ts {
node.symbol = symbol;
symbol.declarations = append(symbol.declarations, node);

if (isNamedDeclaration(node) && isPrivateName(node.name)) {
symbol.flags |= SymbolFlags.PrivateNamed;
}

if (symbolFlags & (SymbolFlags.Class | SymbolFlags.Enum | SymbolFlags.Module | SymbolFlags.Variable) && !symbol.exports) {
symbol.exports = createSymbolTable();
}
Expand Down Expand Up @@ -266,9 +270,6 @@ namespace ts {
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
}
if (isPrivateName(node)) {
return nodePosToString(node) as __String;
}
return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
}
switch (node.kind) {
Expand Down Expand Up @@ -1798,6 +1799,18 @@ namespace ts {
return Diagnostics.Identifier_expected_0_is_a_reserved_word_in_strict_mode;
}

// The binder visits every node, so this is a good place to check for
// the reserved private name (there is only one)
function checkPrivateName(node: PrivateName) {
if (node.escapedText === "#constructor") {
// Report error only if there are no parse errors in file
if (!file.parseDiagnostics.length) {
file.bindDiagnostics.push(createDiagnosticForNode(node,
Diagnostics.constructor_is_a_reserved_word, declarationNameToString(node)));
}
}
}

function checkStrictModeBinaryExpression(node: BinaryExpression) {
if (inStrictMode && isLeftHandSideExpression(node.left) && isAssignmentOperator(node.operatorToken.kind)) {
// ECMA 262 (Annex C) The identifier eval or arguments may not appear as the LeftHandSideExpression of an
Expand Down Expand Up @@ -2070,6 +2083,8 @@ namespace ts {
node.flowNode = currentFlow;
}
return checkStrictModeIdentifier(<Identifier>node);
case SyntaxKind.PrivateName:
return checkPrivateName(node as PrivateName);
case SyntaxKind.PropertyAccessExpression:
case SyntaxKind.ElementAccessExpression:
if (currentFlow && isNarrowableReference(<Expression>node)) {
Expand Down
37 changes: 28 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13187,6 +13187,9 @@ namespace ts {
const properties = target.flags & TypeFlags.Intersection ? getPropertiesOfUnionOrIntersectionType(<IntersectionType>target) : getPropertiesOfObjectType(target);
for (const targetProp of properties) {
if (requireOptionalProperties || !(targetProp.flags & SymbolFlags.Optional)) {
if (targetProp.flags & SymbolFlags.PrivateNamed) {
return targetProp;
}
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
if (!sourceProp) {
return targetProp;
Expand Down Expand Up @@ -17860,6 +17863,16 @@ namespace ts {
}
}

if (node.kind === SyntaxKind.PropertyAccessExpression && isPrivateName(node.name)) {
const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!)!;
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
error(errorNode, Diagnostics.Property_0_is_only_accessible_within_class_1_because_it_has_a_private_name, symbolToString(prop), typeToString(getDeclaringClass(prop)!));
return false;
}
return true;
}


// Public properties are otherwise accessible.
if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) {
return true;
Expand Down Expand Up @@ -20725,10 +20738,16 @@ namespace ts {
error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference);
return booleanType;
}
if (expr.kind === SyntaxKind.PropertyAccessExpression && isPrivateName((expr as PropertyAccessExpression).name)) {
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_private_name);

}
const links = getNodeLinks(expr);
const symbol = getExportSymbolOfValueSymbolIfExported(links.resolvedSymbol);
if (symbol && isReadonlySymbol(symbol)) {
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
if (symbol) {
if (isReadonlySymbol(symbol)) {
error(expr, Diagnostics.The_operand_of_a_delete_operator_cannot_be_a_read_only_property);
}
}
return booleanType;
}
Expand Down Expand Up @@ -21878,9 +21897,6 @@ namespace ts {
checkGrammarDecoratorsAndModifiers(node);

checkVariableLikeDeclaration(node);
if (node.name && isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
error(node, Diagnostics.Private_names_cannot_be_used_as_parameters);
}
const func = getContainingFunction(node)!;
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
if (!(func.kind === SyntaxKind.Constructor && nodeIsPresent(func.body))) {
Expand Down Expand Up @@ -25426,6 +25442,10 @@ namespace ts {
const declaredProp = member.name && getSymbolAtLocation(member.name) || getSymbolAtLocation(member);
if (declaredProp) {
const prop = getPropertyOfType(typeWithThis, declaredProp.escapedName);
// skip inheritance checks because private names are unique to each class
if (member.name && isPrivateName(member.name)) {
return;
}
const baseProp = getPropertyOfType(baseWithThis, declaredProp.escapedName);
if (prop && baseProp) {
const rootChain = () => chainDiagnosticMessages(
Expand Down Expand Up @@ -28515,6 +28535,9 @@ namespace ts {
else if (node.kind === SyntaxKind.Parameter && (flags & ModifierFlags.ParameterPropertyModifier) && (<ParameterDeclaration>node).dotDotDotToken) {
return grammarErrorOnNode(node, Diagnostics.A_parameter_property_cannot_be_declared_using_a_rest_parameter);
}
else if (isNamedDeclaration(node) && (flags & ModifierFlags.AccessibilityModifier) && node.name.kind === SyntaxKind.PrivateName) {
return grammarErrorOnNode(node, Diagnostics.Accessibility_modifiers_cannot_be_used_with_private_names);
}
if (flags & ModifierFlags.Async) {
return checkGrammarAsyncModifier(node, lastAsync!);
}
Expand Down Expand Up @@ -29293,10 +29316,6 @@ namespace ts {
checkESModuleMarker(node.name);
}

if (isIdentifier(node.name) && node.name.originalKeywordKind === SyntaxKind.PrivateName) {
return grammarErrorOnNode(node.name, Diagnostics.Private_names_are_not_allowed_in_variable_declarations);
}

const checkLetConstNames = (isLet(node) || isVarConst(node));

// 1. LexicalDeclaration : LetOrConst BindingList ;
Expand Down
13 changes: 11 additions & 2 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4166,14 +4166,23 @@
"category": "Error",
"code": 18003
},
"Private names are not allowed in variable declarations.": {
"Accessibility modifiers cannot be used with private names.": {
"category": "Error",
"code": 18004
},
"Private names cannot be used as parameters": {
"The operand of a delete operator cannot be a private name.": {
"category": "Error",
"code": 18005
},
"#constructor is a reserved word.": {
"category": "Error",
"code": 18006
},
"Property '{0}' is only accessible within class '{1}' because it has a private name.": {
"category": "Error",
"code": 18007
},


"File is a CommonJS module; it may be converted to an ES6 module.": {
"category": "Suggestion",
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3433,6 +3433,7 @@ namespace ts {
Transient = 1 << 25, // Transient symbol (created during type check)
JSContainer = 1 << 26, // Contains Javascript special declarations
ModuleExports = 1 << 27, // Symbol for CommonJS `module` of `module.exports`
PrivateNamed = 1 << 28, // Symbol's name is a private name (example: `#foo`)

/* @internal */
All = FunctionScopedVariable | BlockScopedVariable | Property | EnumMember | Function | Class | Interface | ConstEnum | RegularEnum | ValueModule | NamespaceModule | TypeLiteral
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
tests/cases/conformance/classes/members/privateNames/privateNameConstructorReserved.ts(1,7): error TS2300: Duplicate identifier 'A'.
tests/cases/conformance/classes/members/privateNames/privateNameConstructorReserved.ts(2,5): error TS18006: #constructor is a reserved word.
tests/cases/conformance/classes/members/privateNames/privateNameConstructorReserved.ts(5,7): error TS2300: Duplicate identifier 'A'.
tests/cases/conformance/classes/members/privateNames/privateNameConstructorReserved.ts(6,5): error TS18006: #constructor is a reserved word.


==== tests/cases/conformance/classes/members/privateNames/privateNameConstructorReserved.ts (4 errors) ====
class A {
~
!!! error TS2300: Duplicate identifier 'A'.
#constructor() {} // Error: `#constructor` is a reserved word.
~~~~~~~~~~~~
!!! error TS18006: #constructor is a reserved word.
}

class A {
~
!!! error TS2300: Duplicate identifier 'A'.
#constructor = 5 // Error: `#constructor` is a reserved word.
~~~~~~~~~~~~
!!! error TS18006: #constructor is a reserved word.
}
22 changes: 22 additions & 0 deletions tests/baselines/reference/privateNameConstructorReserved.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//// [privateNameConstructorReserved.ts]
class A {
#constructor() {} // Error: `#constructor` is a reserved word.
}

class A {
#constructor = 5 // Error: `#constructor` is a reserved word.
}

//// [privateNameConstructorReserved.js]
var A = /** @class */ (function () {
function A() {
}
A.prototype.#constructor = function () { }; // Error: `#constructor` is a reserved word.
return A;
}());
var A = /** @class */ (function () {
function A() {
this.#constructor = 5; // Error: `#constructor` is a reserved word.
}
return A;
}());
14 changes: 14 additions & 0 deletions tests/baselines/reference/privateNameConstructorReserved.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameConstructorReserved.ts ===
class A {
>A : Symbol(A, Decl(privateNameConstructorReserved.ts, 0, 0))

#constructor() {} // Error: `#constructor` is a reserved word.
>#constructor : Symbol(A[#constructor], Decl(privateNameConstructorReserved.ts, 0, 9))
}

class A {
>A : Symbol(A, Decl(privateNameConstructorReserved.ts, 2, 1))

#constructor = 5 // Error: `#constructor` is a reserved word.
>#constructor : Symbol(A[#constructor], Decl(privateNameConstructorReserved.ts, 4, 9))
}
15 changes: 15 additions & 0 deletions tests/baselines/reference/privateNameConstructorReserved.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameConstructorReserved.ts ===
class A {
>A : A

#constructor() {} // Error: `#constructor` is a reserved word.
>#constructor : () => void
}

class A {
>A : A

#constructor = 5 // Error: `#constructor` is a reserved word.
>#constructor : number
>5 : 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts(5,9): error TS18007: Property '#foo' is only accessible within class 'A' because it has a private name.


==== tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts (1 errors) ====
class A {
#foo: number = 3;
}

new A().#foo = 4; // Error
~~~~
!!! error TS18007: Property '#foo' is only accessible within class 'A' because it has a private name.

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//// [privateNameNotAccessibleOutsideDefiningClass.ts]
class A {
#foo: number = 3;
}

new A().#foo = 4; // Error


//// [privateNameNotAccessibleOutsideDefiningClass.js]
var A = /** @class */ (function () {
function A() {
this.#foo = 3;
}
return A;
}());
new A().#foo = 4; // Error
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts ===
class A {
>A : Symbol(A, Decl(privateNameNotAccessibleOutsideDefiningClass.ts, 0, 0))

#foo: number = 3;
>#foo : Symbol(A[#foo], Decl(privateNameNotAccessibleOutsideDefiningClass.ts, 0, 9))
}

new A().#foo = 4; // Error
>new A().#foo : Symbol(A[#foo], Decl(privateNameNotAccessibleOutsideDefiningClass.ts, 0, 9))
>A : Symbol(A, Decl(privateNameNotAccessibleOutsideDefiningClass.ts, 0, 0))

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts ===
class A {
>A : A

#foo: number = 3;
>#foo : number
>3 : 3
}

new A().#foo = 4; // Error
>new A().#foo = 4 : 4
>new A().#foo : number
>new A() : A
>A : typeof A
>4 : 4

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
tests/cases/conformance/classes/members/privateNames/privateNameNotAllowedOutsideClass.ts(1,7): error TS1134: Variable declaration expected.
tests/cases/conformance/classes/members/privateNames/privateNameNotAllowedOutsideClass.ts(1,12): error TS1134: Variable declaration expected.
tests/cases/conformance/classes/members/privateNames/privateNameNotAllowedOutsideClass.ts(1,14): error TS1134: Variable declaration expected.


==== tests/cases/conformance/classes/members/privateNames/privateNameNotAllowedOutsideClass.ts (3 errors) ====
const #foo = 3;
~~~~
!!! error TS1134: Variable declaration expected.
~
!!! error TS1134: Variable declaration expected.
~
!!! error TS1134: Variable declaration expected.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
//// [privateNameNotAllowedOutsideClass.ts]
const #foo = 3;

//// [privateNameNotAllowedOutsideClass.js]
var ;
3;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameNotAllowedOutsideClass.ts ===
const #foo = 3;
No type information for this code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== tests/cases/conformance/classes/members/privateNames/privateNameNotAllowedOutsideClass.ts ===
const #foo = 3;
>3 : 3

16 changes: 16 additions & 0 deletions tests/baselines/reference/privateNamesAndkeyof.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//// [privateNamesAndkeyof.ts]
class A {
#foo;
bar;
baz;
}

type T = keyof A // should not include '#foo'


//// [privateNamesAndkeyof.js]
var A = /** @class */ (function () {
function A() {
}
return A;
}());
18 changes: 18 additions & 0 deletions tests/baselines/reference/privateNamesAndkeyof.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
=== tests/cases/conformance/classes/members/privateNames/privateNamesAndkeyof.ts ===
class A {
>A : Symbol(A, Decl(privateNamesAndkeyof.ts, 0, 0))

#foo;
>#foo : Symbol(A[#foo], Decl(privateNamesAndkeyof.ts, 0, 9))

bar;
>bar : Symbol(A.bar, Decl(privateNamesAndkeyof.ts, 1, 9))

baz;
>baz : Symbol(A.baz, Decl(privateNamesAndkeyof.ts, 2, 8))
}

type T = keyof A // should not include '#foo'
>T : Symbol(T, Decl(privateNamesAndkeyof.ts, 4, 1))
>A : Symbol(A, Decl(privateNamesAndkeyof.ts, 0, 0))

17 changes: 17 additions & 0 deletions tests/baselines/reference/privateNamesAndkeyof.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
=== tests/cases/conformance/classes/members/privateNames/privateNamesAndkeyof.ts ===
class A {
>A : A

#foo;
>#foo : any

bar;
>bar : any

baz;
>baz : any
}

type T = keyof A // should not include '#foo'
>T : "bar" | "baz"

Loading