Skip to content

Checkerchanges #11

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

Open
wants to merge 6 commits into
base: checker-before-msft-feedback
Choose a base branch
from
Open
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
17 changes: 9 additions & 8 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,6 @@ 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 @@ -270,6 +266,11 @@ namespace ts {
Debug.assert(isWellKnownSymbolSyntactically(nameExpression));
return getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>nameExpression).name));
}
if (isPrivateName(name)) {
// containingClass exists because private names only allowed inside classes
const containingClassSymbol = getContainingClass(name.parent)!.symbol;
return getPropertyNameForPrivateName(containingClassSymbol, name.escapedText);
}
return isPropertyNameLiteral(name) ? getEscapedTextOfIdentifierOrLiteral(name) : undefined;
}
switch (node.kind) {
Expand Down Expand Up @@ -326,6 +327,10 @@ namespace ts {

const isDefaultExport = hasModifier(node, ModifierFlags.Default);

// need this before getDeclarationName
if (isNamedDeclaration(node)) {
node.name.parent = node;
}
// The exported symbol for an export default function/class node is always named "default"
const name = isDefaultExport && parent ? InternalSymbolName.Default : getDeclarationName(node);

Expand Down Expand Up @@ -378,10 +383,6 @@ namespace ts {
symbolTable.set(name, symbol = createSymbol(SymbolFlags.None, name));
}
else {
if (isNamedDeclaration(node)) {
node.name.parent = node;
}

// Report errors every position with duplicate declaration
// Report errors on previous encountered declarations
let message = symbol.flags & SymbolFlags.BlockScopedVariable
Expand Down
31 changes: 14 additions & 17 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2594,15 +2594,16 @@ namespace ts {
return getUnionType(arrayFrom(typeofEQFacts.keys(), getLiteralType));
}

// A reserved member name starts with two underscores, but the third character cannot be an underscore
// or the @ symbol. A third underscore indicates an escaped form of an identifer that started
// A reserved member name starts with two underscores, but the third character cannot be an underscore,
// @ or #. A third underscore indicates an escaped form of an identifer that started
// with at least two underscores. The @ character indicates that the name is denoted by a well known ES
// Symbol instance.
// Symbol instance and the # indicates that the name is a PrivateName.
function isReservedMemberName(name: __String) {
return (name as string).charCodeAt(0) === CharacterCodes._ &&
(name as string).charCodeAt(1) === CharacterCodes._ &&
(name as string).charCodeAt(2) !== CharacterCodes._ &&
(name as string).charCodeAt(2) !== CharacterCodes.at;
(name as string).charCodeAt(2) !== CharacterCodes.at &&
(name as string).charCodeAt(2) !== CharacterCodes.hash;
}

function getNamedMembers(members: SymbolTable): Symbol[] {
Expand Down Expand Up @@ -7276,11 +7277,12 @@ namespace ts {
* @param type a type to look up property from
* @param name a name of property to look up in a given type
*/
function getPropertyOfType(type: Type, name: __String): Symbol | undefined {
function getPropertyOfType(type: Type, name: __String, isPrivateName = false): Symbol | undefined {
type = getApparentType(type);
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
const symbol = resolved.members.get(name);
const symbolTableKey = isPrivateName ? getPropertyNameForPrivateName(type.symbol, name) : name;
const symbol = resolved.members.get(symbolTableKey);
if (symbol && symbolIsValue(symbol)) {
return symbol;
}
Expand Down Expand Up @@ -9042,7 +9044,9 @@ namespace ts {
}

function getLiteralTypeFromPropertyName(prop: Symbol, include: TypeFlags) {
if (!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) {
const hasNonPublicModifier = !!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier);
const hasPrivateName = prop.valueDeclaration && isNamedDeclaration(prop.valueDeclaration) && isPrivateName(prop.valueDeclaration.name);
if (!hasNonPublicModifier && !hasPrivateName) {
let type = getLateBoundSymbol(prop).nameType;
if (!type && !isKnownSymbol(prop)) {
const name = prop.valueDeclaration && getNameOfDeclaration(prop.valueDeclaration);
Expand Down Expand Up @@ -13187,9 +13191,6 @@ 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 @@ -17866,7 +17867,7 @@ 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)!));
error(errorNode, Diagnostics.Property_0_is_not_accessible_outside_class_1_because_it_has_a_private_name, symbolToString(prop), typeToString(getDeclaringClass(prop)!));
return false;
}
return true;
Expand Down Expand Up @@ -17999,13 +18000,13 @@ namespace ts {
return apparentType;
}
const assignmentKind = getAssignmentTargetKind(node);
const prop = getPropertyOfType(apparentType, right.escapedText);
const prop = getPropertyOfType(apparentType, right.escapedText, isPrivateName(right));
if (isIdentifier(left) && parentSymbol && !(prop && isConstEnumOrConstEnumOnlyModule(prop))) {
markAliasReferenced(parentSymbol, node);
}
if (!prop) {
const indexInfo = getIndexInfoOfType(apparentType, IndexKind.String);
if (!(indexInfo && indexInfo.type) || isPrivateName(right)) {
if (!(indexInfo && indexInfo.type)) {
if (isJSLiteralType(leftType)) {
return anyType;
}
Expand Down Expand Up @@ -25442,10 +25443,6 @@ 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
2 changes: 1 addition & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -4178,7 +4178,7 @@
"category": "Error",
"code": 18006
},
"Property '{0}' is only accessible within class '{1}' because it has a private name.": {
"Property '{0}' is not accessible outside class '{1}' because it has a private name.": {
"category": "Error",
"code": 18007
},
Expand Down
1 change: 0 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3433,7 +3433,6 @@ 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
4 changes: 4 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2672,6 +2672,10 @@ namespace ts {
return "__@" + symbolName as __String;
}

export function getPropertyNameForPrivateName(containingClassSymbol: Symbol, escapedText: __String): __String {
return `__#${getSymbolId(containingClassSymbol)}@${escapedText}` as __String;
}

export function isKnownSymbol(symbol: Symbol): boolean {
return startsWith(symbol.escapedName as string, "__@");
}
Expand Down
1 change: 0 additions & 1 deletion tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2063,7 +2063,6 @@ declare namespace ts {
Transient = 33554432,
JSContainer = 67108864,
ModuleExports = 134217728,
PrivateNamed = 268435456,
Enum = 384,
Variable = 3,
Value = 67216319,
Expand Down
1 change: 0 additions & 1 deletion tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2063,7 +2063,6 @@ declare namespace ts {
Transient = 33554432,
JSContainer = 67108864,
ModuleExports = 134217728,
PrivateNamed = 268435456,
Enum = 384,
Variable = 3,
Value = 67216319,
Expand Down
13 changes: 0 additions & 13 deletions tests/baselines/reference/privateNameAndIndexSignature.errors.txt

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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(5,9): error TS18007: Property '#foo' is not accessible outside class 'A' because it has a private name.


==== tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOutsideDefiningClass.ts (1 errors) ====
Expand All @@ -8,5 +8,5 @@ tests/cases/conformance/classes/members/privateNames/privateNameNotAccessibleOut

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

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"

29 changes: 29 additions & 0 deletions tests/baselines/reference/privateNamesInGenericClasses.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts(10,3): error TS18007: Property '#foo' is not accessible outside class 'C<T>' because it has a private name.
tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts(11,1): error TS2322: Type 'C<string>' is not assignable to type 'C<number>'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts(12,1): error TS2322: Type 'C<number>' is not assignable to type 'C<string>'.
Type 'number' is not assignable to type 'string'.


==== tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts (3 errors) ====
class C<T> {
#foo: T;
bar(x: C<T>) { return x.#foo; } // OK
baz(x: C<number>) { return x.#foo; } // OK
quux(x: C<string>) { return x.#foo; } // OK
}

declare let a: C<number>;
declare let b: C<string>;
a.#foo; // OK
~~~~
!!! error TS18007: Property '#foo' is not accessible outside class 'C<T>' because it has a private name.
a = b; // Error
~
!!! error TS2322: Type 'C<string>' is not assignable to type 'C<number>'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
b = a; // Error
~
!!! error TS2322: Type 'C<number>' is not assignable to type 'C<string>'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.

27 changes: 27 additions & 0 deletions tests/baselines/reference/privateNamesInGenericClasses.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//// [privateNamesInGenericClasses.ts]
class C<T> {
#foo: T;
bar(x: C<T>) { return x.#foo; } // OK
baz(x: C<number>) { return x.#foo; } // OK
quux(x: C<string>) { return x.#foo; } // OK
}

declare let a: C<number>;
declare let b: C<string>;
a.#foo; // OK
a = b; // Error
b = a; // Error


//// [privateNamesInGenericClasses.js]
var C = /** @class */ (function () {
function C() {
}
C.prototype.bar = function (x) { return x.#foo; }; // OK
C.prototype.baz = function (x) { return x.#foo; }; // OK
C.prototype.quux = function (x) { return x.#foo; }; // OK
return C;
}());
a.#foo; // OK
a = b; // Error
b = a; // Error
52 changes: 52 additions & 0 deletions tests/baselines/reference/privateNamesInGenericClasses.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
=== tests/cases/conformance/classes/members/privateNames/privateNamesInGenericClasses.ts ===
class C<T> {
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
>T : Symbol(T, Decl(privateNamesInGenericClasses.ts, 0, 8))

#foo: T;
>#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
>T : Symbol(T, Decl(privateNamesInGenericClasses.ts, 0, 8))

bar(x: C<T>) { return x.#foo; } // OK
>bar : Symbol(C.bar, Decl(privateNamesInGenericClasses.ts, 1, 10))
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 2, 6))
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
>T : Symbol(T, Decl(privateNamesInGenericClasses.ts, 0, 8))
>x.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 2, 6))

baz(x: C<number>) { return x.#foo; } // OK
>baz : Symbol(C.baz, Decl(privateNamesInGenericClasses.ts, 2, 33))
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 3, 6))
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
>x.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 3, 6))

quux(x: C<string>) { return x.#foo; } // OK
>quux : Symbol(C.quux, Decl(privateNamesInGenericClasses.ts, 3, 38))
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 4, 7))
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))
>x.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
>x : Symbol(x, Decl(privateNamesInGenericClasses.ts, 4, 7))
}

declare let a: C<number>;
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))

declare let b: C<string>;
>b : Symbol(b, Decl(privateNamesInGenericClasses.ts, 8, 11))
>C : Symbol(C, Decl(privateNamesInGenericClasses.ts, 0, 0))

a.#foo; // OK
>a.#foo : Symbol(C[#foo], Decl(privateNamesInGenericClasses.ts, 0, 12))
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))

a = b; // Error
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))
>b : Symbol(b, Decl(privateNamesInGenericClasses.ts, 8, 11))

b = a; // Error
>b : Symbol(b, Decl(privateNamesInGenericClasses.ts, 8, 11))
>a : Symbol(a, Decl(privateNamesInGenericClasses.ts, 7, 11))

Loading