From f04f22c732ef68e99da5b99e9373ef6036d7f172 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 17 Jun 2021 20:13:24 +0100 Subject: [PATCH 01/41] es private fields in in (#52) add support for the 'private-fields-in-in' TC39 proposal Signed-off-by: Ashley Claymore --- src/compiler/binder.ts | 2 + src/compiler/checker.ts | 98 ++++++- src/compiler/emitter.ts | 20 ++ src/compiler/factory/emitHelpers.ts | 18 ++ src/compiler/factory/nodeFactory.ts | 30 ++ src/compiler/factory/nodeTests.ts | 9 + src/compiler/parser.ts | 33 ++- src/compiler/transformers/classFields.ts | 23 ++ src/compiler/types.ts | 11 + src/compiler/utilities.ts | 2 + src/compiler/utilitiesPublic.ts | 1 + src/compiler/visitorPublic.ts | 7 + src/services/codefixes/fixSpelling.ts | 5 + .../reference/api/tsserverlibrary.d.ts | 263 +++++++++-------- tests/baselines/reference/api/typescript.d.ts | 263 +++++++++-------- .../privateNameInInExpression.errors.txt | 140 +++++++++ .../reference/privateNameInInExpression.js | 210 ++++++++++++++ .../privateNameInInExpression.symbols | 233 +++++++++++++++ .../reference/privateNameInInExpression.types | 274 ++++++++++++++++++ ...vateNameInInExpressionTransform.errors.txt | 67 +++++ .../privateNameInInExpressionTransform.js | 97 +++++++ ...privateNameInInExpressionTransform.symbols | 102 +++++++ .../privateNameInInExpressionTransform.types | 130 +++++++++ ...privateNameInInExpressionUnused.errors.txt | 16 + .../privateNameInInExpressionUnused.js | 22 ++ .../privateNameInInExpressionUnused.symbols | 22 ++ .../privateNameInInExpressionUnused.types | 21 ++ .../privateNames/privateNameInInExpression.ts | 113 ++++++++ .../privateNameInInExpressionTransform.ts | 47 +++ .../privateNameInInExpressionUnused.ts | 13 + .../codeFixSpellingPrivateNameInIn.ts | 19 ++ .../findAllRefsPrivateNameProperties.ts | 7 +- 32 files changed, 2060 insertions(+), 258 deletions(-) create mode 100644 tests/baselines/reference/privateNameInInExpression.errors.txt create mode 100644 tests/baselines/reference/privateNameInInExpression.js create mode 100644 tests/baselines/reference/privateNameInInExpression.symbols create mode 100644 tests/baselines/reference/privateNameInInExpression.types create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.errors.txt create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.js create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.symbols create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform.types create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.errors.txt create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.js create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.symbols create mode 100644 tests/baselines/reference/privateNameInInExpressionUnused.types create mode 100644 tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts create mode 100644 tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts create mode 100644 tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts create mode 100644 tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 78bca28c26b15..7f902330f8286 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -879,6 +879,8 @@ namespace ts { return (expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr as PrefixUnaryExpression).operand); case SyntaxKind.TypeOfExpression: return isNarrowingExpression((expr as TypeOfExpression).expression); + case SyntaxKind.PrivateIdentifierInInExpression: + return isNarrowingExpression((expr as PrivateIdentifierInInExpression).expression); } return false; } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 44cd5f0608345..4b4e35c826cb9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23590,6 +23590,32 @@ namespace ts { return type; } + function narrowTypeByPrivateIdentifierInInExpression(type: Type, expr: PrivateIdentifierInInExpression, assumeTrue: boolean): Type { + const target = getReferenceCandidate(expr.expression); + if (!isMatchingReference(reference, target)) { + return type; + } + + const privateId = expr.name; + const symbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); + if (symbol === undefined) { + return type; + } + const classSymbol = symbol.parent!; + const classType = getTypeOfSymbol(classSymbol) as InterfaceType; + const classDecl = symbol.valueDeclaration; + Debug.assert(classDecl, "should always have a declaration"); + let targetType: Type; + if (hasStaticModifier(classDecl)) { + targetType = classType; + } + else { + const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); + targetType = classInstanceType; + } + return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); + } + function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type { // We are in a branch of obj?.foo === value (or any one of the other equality operators). We narrow obj as follows: // When operator is === and type of value excludes undefined, null and undefined is removed from type of obj in true branch. @@ -24022,6 +24048,8 @@ namespace ts { return narrowType(type, (expr as ParenthesizedExpression | NonNullExpression).expression, assumeTrue); case SyntaxKind.BinaryExpression: return narrowTypeByBinaryExpression(type, expr as BinaryExpression, assumeTrue); + case SyntaxKind.PrivateIdentifierInInExpression: + return narrowTypeByPrivateIdentifierInInExpression(type, expr as PrivateIdentifierInInExpression, assumeTrue); case SyntaxKind.PrefixUnaryExpression: if ((expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken) { return narrowType(type, (expr as PrefixUnaryExpression).operand, !assumeTrue); @@ -27726,6 +27754,7 @@ namespace ts { } function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { + const originalName = name; let props = getPropertiesOfType(containingType); if (typeof name !== "string") { const parent = name.parent; @@ -27734,7 +27763,23 @@ namespace ts { } name = idText(name); } - return getSpellingSuggestionForName(name, props, SymbolFlags.Value); + const suggestion = getSpellingSuggestionForName(name, props, SymbolFlags.Value); + if (suggestion) { + return suggestion; + } + // If we have `#typo in expr` then we can still look up potential privateIdentifiers from the surrounding classes + if (typeof originalName !== "string" && isPrivateIdentifierInInExpression(originalName.parent)) { + const privateIdentifiers: Symbol[] = []; + forEachEnclosingClass(originalName, (klass: ClassLikeDeclaration) => { + forEach(klass.members, member => { + if (isPrivateIdentifierClassElementDeclaration(member)) { + privateIdentifiers.push(member.symbol); + } + }); + }); + return getSpellingSuggestionForName(name, privateIdentifiers, SymbolFlags.Value); + } + return undefined; } function getSuggestedSymbolForNonexistentJSXAttribute(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { @@ -31494,6 +31539,11 @@ namespace ts { isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } + checkInExpressionRHS(right, rightType); + return booleanType; + } + + function checkInExpressionRHS(right: Expression, rightType: Type) { const rightTypeConstraint = getConstraintOfType(rightType); if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) || rightTypeConstraint && ( @@ -31503,7 +31553,6 @@ namespace ts { ) { error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_not_be_a_primitive); } - return booleanType; } function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, rightIsThis?: boolean): Type { @@ -31740,6 +31789,40 @@ namespace ts { return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } + function checkPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, checkMode?: CheckMode) { + const privateId = node.name; + const exp = node.expression; + let rightType = checkExpression(exp, checkMode); + + const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); + if (lexicallyScopedSymbol === undefined) { + if (!getContainingClass(node)) { + error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + } + else { + const suggestion = getSuggestedSymbolForNonexistentProperty(privateId, rightType); + if (suggestion) { + const suggestedName = symbolName(suggestion); + error(privateId, Diagnostics.Cannot_find_name_0_Did_you_mean_1, diagnosticName(privateId), suggestedName); + } + else { + error(privateId, Diagnostics.Cannot_find_name_0, diagnosticName(privateId)); + } + } + return anyType; + } + + markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); + getNodeLinks(node).resolvedSymbol = lexicallyScopedSymbol; + + if (rightType === silentNeverType) { + return silentNeverType; + } + rightType = checkNonNullType(rightType, exp); + checkInExpressionRHS(exp, rightType); + return booleanType; + } + function createCheckBinaryExpression() { interface WorkArea { readonly checkMode: CheckMode | undefined; @@ -32921,6 +33004,8 @@ namespace ts { return checkPostfixUnaryExpression(node as PostfixUnaryExpression); case SyntaxKind.BinaryExpression: return checkBinaryExpression(node as BinaryExpression, checkMode); + case SyntaxKind.PrivateIdentifierInInExpression: + return checkPrivateIdentifierInInExpression(node as PrivateIdentifierInInExpression, checkMode); case SyntaxKind.ConditionalExpression: return checkConditionalExpression(node as ConditionalExpression, checkMode); case SyntaxKind.SpreadElement: @@ -39416,6 +39501,15 @@ namespace ts { return resolveEntityName(name as Identifier, /*meaning*/ SymbolFlags.FunctionScopedVariable); } + if (isPrivateIdentifier(name) && isPrivateIdentifierInInExpression(name.parent)) { + const links = getNodeLinks(name.parent); + if (links.resolvedSymbol) { + return links.resolvedSymbol; + } + checkPrivateIdentifierInInExpression(name.parent); + return links.resolvedSymbol; + } + return undefined; } diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index fc945a4cac70f..de343e520ec0d 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1716,6 +1716,8 @@ namespace ts { return emitPostfixUnaryExpression(node as PostfixUnaryExpression); case SyntaxKind.BinaryExpression: return emitBinaryExpression(node as BinaryExpression); + case SyntaxKind.PrivateIdentifierInInExpression: + return emitPrivateIdentifierInInExpression(node as PrivateIdentifierInInExpression); case SyntaxKind.ConditionalExpression: return emitConditionalExpression(node as ConditionalExpression); case SyntaxKind.TemplateExpression: @@ -2690,6 +2692,24 @@ namespace ts { } } + function emitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { + const linesBeforeIn = getLinesBetweenNodes(node, node.name, node.inToken); + const linesAfterIn = getLinesBetweenNodes(node, node.inToken, node.expression); + + emitLeadingCommentsOfPosition(node.name.pos); + emitPrivateIdentifier(node.name); + emitTrailingCommentsOfPosition(node.name.end); + + writeLinesAndIndent(linesBeforeIn, /*writeSpaceIfNotIndenting*/ true); + emitLeadingCommentsOfPosition(node.inToken.pos); + writeTokenNode(node.inToken, writeKeyword); + emitTrailingCommentsOfPosition(node.inToken.end, /*prefixSpace*/ true); + writeLinesAndIndent(linesAfterIn, /*writeSpaceIfNotIndenting*/ true); + + emit(node.expression); + decreaseIndentIf(linesBeforeIn, linesAfterIn); + } + function emitConditionalExpression(node: ConditionalExpression) { const linesBeforeQuestion = getLinesBetweenNodes(node, node.condition, node.questionToken); const linesAfterQuestion = getLinesBetweenNodes(node, node.questionToken, node.whenTrue); diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index f3e0004efd407..9df1d4126cd43 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -34,6 +34,7 @@ namespace ts { // Class Fields Helpers createClassPrivateFieldGetHelper(receiver: Expression, state: Identifier, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; createClassPrivateFieldSetHelper(receiver: Expression, state: Identifier, value: Expression, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; + createClassPrivateFieldInHelper(receiver: Expression, state: Identifier): Expression; } export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory { @@ -72,6 +73,7 @@ namespace ts { // Class Fields Helpers createClassPrivateFieldGetHelper, createClassPrivateFieldSetHelper, + createClassPrivateFieldInHelper }; /** @@ -392,6 +394,10 @@ namespace ts { return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, args); } + function createClassPrivateFieldInHelper(receiver: Expression, state: Identifier) { + context.requestEmitHelper(classPrivateFieldInHelper); + return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [receiver, state]); + } } /* @internal */ @@ -954,6 +960,17 @@ namespace ts { };` }; + export const classPrivateFieldInHelper: UnscopedEmitHelper = { + name: "typescript:classPrivateFieldIn", + importName: "__classPrivateFieldIn", + scoped: false, + text: ` + var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, state) { + if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); + return typeof state === "function" ? receiver === state : state.has(receiver); + };` + }; + let allUnscopedEmitHelpers: ReadonlyESMap | undefined; export function getAllUnscopedEmitHelpers() { @@ -979,6 +996,7 @@ namespace ts { exportStarHelper, classPrivateFieldGetHelper, classPrivateFieldSetHelper, + classPrivateFieldInHelper, createBindingHelper, setModuleDefaultHelper ], helper => helper.name)); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 4b75321a84ee2..63341684535ca 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -443,6 +443,8 @@ namespace ts { createMergeDeclarationMarker, createSyntheticReferenceExpression, updateSyntheticReferenceExpression, + createPrivateIdentifierInInExpression, + updatePrivateIdentifierInInExpression, cloneNode, // Lazily load factory methods for common operator factories and utilities @@ -3066,6 +3068,34 @@ namespace ts { : node; } + // @api + function createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression) { + const node = createBaseExpression(SyntaxKind.PrivateIdentifierInInExpression); + node.name = name; + node.inToken = inToken; + node.expression = expression; + node.transformFlags |= + propagateChildFlags(node.name) | + propagateChildFlags(node.inToken) | + propagateChildFlags(node.expression) | + TransformFlags.ContainsESNext; + return node; + } + + // @api + function updatePrivateIdentifierInInExpression( + node: PrivateIdentifierInInExpression, + name: PrivateIdentifier, + inToken: Token, + expression: Expression + ): PrivateIdentifierInInExpression { + return node.name !== name + || node.inToken !== inToken + || node.expression !== expression + ? update(createPrivateIdentifierInInExpression(name, inToken, expression), node) + : node; + } + // // Misc // diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index fbe8a1ccd69fa..8b42bf3f09e06 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -87,6 +87,11 @@ namespace ts { return node.kind === SyntaxKind.EqualsGreaterThanToken; } + /*@internal*/ + export function isInKeyword(node: Node): node is InKeyword { + return node.kind === SyntaxKind.InKeyword; + } + // Identifiers export function isIdentifier(node: Node): node is Identifier { @@ -444,6 +449,10 @@ namespace ts { return node.kind === SyntaxKind.CommaListExpression; } + export function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression { + return node.kind === SyntaxKind.PrivateIdentifierInInExpression; + } + // Misc export function isTemplateSpan(node: Node): node is TemplateSpan { diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 8a6743090f9dc..2488f514a9f3d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -442,6 +442,9 @@ namespace ts { return visitNodes(cbNode, cbNodes, node.decorators); case SyntaxKind.CommaListExpression: return visitNodes(cbNode, cbNodes, (node as CommaListExpression).elements); + case SyntaxKind.PrivateIdentifierInInExpression: + return visitNode(cbNode, (node as PrivateIdentifierInInExpression).name) || + visitNode(cbNode, (node as PrivateIdentifierInInExpression).expression); case SyntaxKind.JsxElement: return visitNode(cbNode, (node as JsxElement).openingElement) || @@ -4446,9 +4449,32 @@ namespace ts { ); } + function parsePrivateIdentifierInInExpression(pos: number): Expression { + // PrivateIdentifierInInExpression[in]: + // [+in] PrivateIdentifier in RelationalExpression[?in] + + Debug.assert(token() === SyntaxKind.PrivateIdentifier, "parsePrivateIdentifierInInExpression should only have been called if we had a privateIdentifier"); + Debug.assert(inDisallowInContext() === false, "parsePrivateIdentifierInInExpression should only have been called if 'in' is allowed"); + const id = parsePrivateIdentifier(); + if (token() !== SyntaxKind.InKeyword) { + return createMissingNode(SyntaxKind.InKeyword, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, tokenToString(SyntaxKind.InKeyword)); + } + + const inToken = parseTokenNode>(); + const exp = parseBinaryExpressionOrHigher(OperatorPrecedence.Relational); + return finishNode(factory.createPrivateIdentifierInInExpression(id, inToken, exp), pos); + } + function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { + // parse a BinaryExpression the LHS is either: + // 1) a PrivateIdentifierInInExpression when 'in' flag allowed and lookahead matches 'PrivateIdentifier in' + // 2) a UnaryExpression + const pos = getNodePos(); - const leftOperand = parseUnaryExpressionOrHigher(); + const tryPrivateIdentifierInIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext() && lookAhead(nextTokenIsInKeyword); + const leftOperand = tryPrivateIdentifierInIn + ? parsePrivateIdentifierInInExpression(pos) + : parseUnaryExpressionOrHigher(); return parseBinaryExpressionRest(precedence, leftOperand, pos); } @@ -5977,6 +6003,11 @@ namespace ts { return (tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral || token() === SyntaxKind.StringLiteral) && !scanner.hasPrecedingLineBreak(); } + function nextTokenIsInKeyword() { + nextToken(); + return token() === SyntaxKind.InKeyword; + } + function isDeclaration(): boolean { while (true) { switch (token()) { diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index e820851dde949..cda2b609dc5a2 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -176,6 +176,8 @@ namespace ts { return visitForStatement(node as ForStatement); case SyntaxKind.TaggedTemplateExpression: return visitTaggedTemplateExpression(node as TaggedTemplateExpression); + case SyntaxKind.PrivateIdentifierInInExpression: + return visitPrivateIdentifierInInExpression(node as PrivateIdentifierInInExpression); } return visitEachChild(node, visitor, context); } @@ -201,6 +203,27 @@ namespace ts { return setOriginalNode(factory.createIdentifier(""), node); } + /** + * Visits `#id in expr` + */ + function visitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { + if (!shouldTransformPrivateElements) { + return node; + } + const info = accessPrivateIdentifier(node.name); + if (info) { + const receiver = visitNode(node.expression, visitor, isExpression); + + return setOriginalNode( + context.getEmitHelperFactory().createClassPrivateFieldInHelper(receiver, info.brandCheckIdentifier), + node + ); + } + + // Private name has not been declared. Subsequent transformers will handle this error + return visitEachChild(node, visitor, context); + } + /** * Visits the members of a class that has fields. * diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a7ef444811925..d2120fc07d505 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -271,6 +271,7 @@ namespace ts { NonNullExpression, MetaProperty, SyntheticExpression, + PrivateIdentifierInInExpression, // Misc TemplateSpan, @@ -1035,6 +1036,7 @@ namespace ts { export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; + export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; @@ -2328,6 +2330,13 @@ namespace ts { // see: https://tc39.github.io/ecma262/#prod-SuperProperty export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; + export interface PrivateIdentifierInInExpression extends Expression { + readonly kind: SyntaxKind.PrivateIdentifierInInExpression; + readonly name: PrivateIdentifier; + readonly inToken: Token; + readonly expression: Expression; + } + export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -7190,6 +7199,8 @@ namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; + createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; + updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; // // Misc diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 83ee8d9da3606..13a5c1f636dd1 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1901,6 +1901,7 @@ namespace ts { case SyntaxKind.YieldExpression: case SyntaxKind.AwaitExpression: case SyntaxKind.MetaProperty: + case SyntaxKind.PrivateIdentifierInInExpression: return true; case SyntaxKind.QualifiedName: while (node.parent.kind === SyntaxKind.QualifiedName) { @@ -3625,6 +3626,7 @@ namespace ts { return OperatorPrecedence.Member; case SyntaxKind.AsExpression: + case SyntaxKind.PrivateIdentifierInInExpression: return OperatorPrecedence.Relational; case SyntaxKind.ThisKeyword: diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index b156908537953..fe04a682d398c 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1562,6 +1562,7 @@ namespace ts { case SyntaxKind.OmittedExpression: case SyntaxKind.CommaListExpression: case SyntaxKind.PartiallyEmittedExpression: + case SyntaxKind.PrivateIdentifierInInExpression: return true; default: return isUnaryExpressionKind(kind); diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 1758aa4397680..61bd204647187 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -793,6 +793,13 @@ namespace ts { nodeVisitor(node.operatorToken, tokenVisitor, isBinaryOperatorToken), nodeVisitor(node.right, visitor, isExpression)); + case SyntaxKind.PrivateIdentifierInInExpression: + Debug.type(node); + return factory.updatePrivateIdentifierInInExpression(node, + nodeVisitor((node).name, visitor, isMemberName), + nodeVisitor((node).inToken, tokenVisitor, isToken), + nodeVisitor((node).expression, visitor, isExpression)); + case SyntaxKind.ConditionalExpression: Debug.type(node); return factory.updateConditionalExpression(node, diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 885f5d7c46188..705bf947eb416 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -54,6 +54,11 @@ namespace ts.codefix { } suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType); } + else if (isPrivateIdentifierInInExpression(parent) && parent.name === node) { + const receiverType = checker.getTypeAtLocation(parent.expression); + Debug.assert(isPrivateIdentifier(node), "Expected a privateIdentifier for spelling (in)"); + suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, receiverType); + } else if (isQualifiedName(parent) && parent.right === node) { const symbol = checker.getSymbolAtLocation(parent.left); if (symbol && symbol.flags & SymbolFlags.Module) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 98f85301f5c3c..4ef574ab1f0bf 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -335,126 +335,127 @@ declare namespace ts { NonNullExpression = 227, MetaProperty = 228, SyntheticExpression = 229, - TemplateSpan = 230, - SemicolonClassElement = 231, - Block = 232, - EmptyStatement = 233, - VariableStatement = 234, - ExpressionStatement = 235, - IfStatement = 236, - DoStatement = 237, - WhileStatement = 238, - ForStatement = 239, - ForInStatement = 240, - ForOfStatement = 241, - ContinueStatement = 242, - BreakStatement = 243, - ReturnStatement = 244, - WithStatement = 245, - SwitchStatement = 246, - LabeledStatement = 247, - ThrowStatement = 248, - TryStatement = 249, - DebuggerStatement = 250, - VariableDeclaration = 251, - VariableDeclarationList = 252, - FunctionDeclaration = 253, - ClassDeclaration = 254, - InterfaceDeclaration = 255, - TypeAliasDeclaration = 256, - EnumDeclaration = 257, - ModuleDeclaration = 258, - ModuleBlock = 259, - CaseBlock = 260, - NamespaceExportDeclaration = 261, - ImportEqualsDeclaration = 262, - ImportDeclaration = 263, - ImportClause = 264, - NamespaceImport = 265, - NamedImports = 266, - ImportSpecifier = 267, - ExportAssignment = 268, - ExportDeclaration = 269, - NamedExports = 270, - NamespaceExport = 271, - ExportSpecifier = 272, - MissingDeclaration = 273, - ExternalModuleReference = 274, - JsxElement = 275, - JsxSelfClosingElement = 276, - JsxOpeningElement = 277, - JsxClosingElement = 278, - JsxFragment = 279, - JsxOpeningFragment = 280, - JsxClosingFragment = 281, - JsxAttribute = 282, - JsxAttributes = 283, - JsxSpreadAttribute = 284, - JsxExpression = 285, - CaseClause = 286, - DefaultClause = 287, - HeritageClause = 288, - CatchClause = 289, - PropertyAssignment = 290, - ShorthandPropertyAssignment = 291, - SpreadAssignment = 292, - EnumMember = 293, - UnparsedPrologue = 294, - UnparsedPrepend = 295, - UnparsedText = 296, - UnparsedInternalText = 297, - UnparsedSyntheticReference = 298, - SourceFile = 299, - Bundle = 300, - UnparsedSource = 301, - InputFiles = 302, - JSDocTypeExpression = 303, - JSDocNameReference = 304, - JSDocMemberName = 305, - JSDocAllType = 306, - JSDocUnknownType = 307, - JSDocNullableType = 308, - JSDocNonNullableType = 309, - JSDocOptionalType = 310, - JSDocFunctionType = 311, - JSDocVariadicType = 312, - JSDocNamepathType = 313, - JSDocComment = 314, - JSDocText = 315, - JSDocTypeLiteral = 316, - JSDocSignature = 317, - JSDocLink = 318, - JSDocLinkCode = 319, - JSDocLinkPlain = 320, - JSDocTag = 321, - JSDocAugmentsTag = 322, - JSDocImplementsTag = 323, - JSDocAuthorTag = 324, - JSDocDeprecatedTag = 325, - JSDocClassTag = 326, - JSDocPublicTag = 327, - JSDocPrivateTag = 328, - JSDocProtectedTag = 329, - JSDocReadonlyTag = 330, - JSDocOverrideTag = 331, - JSDocCallbackTag = 332, - JSDocEnumTag = 333, - JSDocParameterTag = 334, - JSDocReturnTag = 335, - JSDocThisTag = 336, - JSDocTypeTag = 337, - JSDocTemplateTag = 338, - JSDocTypedefTag = 339, - JSDocSeeTag = 340, - JSDocPropertyTag = 341, - SyntaxList = 342, - NotEmittedStatement = 343, - PartiallyEmittedExpression = 344, - CommaListExpression = 345, - MergeDeclarationMarker = 346, - EndOfDeclarationMarker = 347, - SyntheticReferenceExpression = 348, - Count = 349, + PrivateIdentifierInInExpression = 230, + TemplateSpan = 231, + SemicolonClassElement = 232, + Block = 233, + EmptyStatement = 234, + VariableStatement = 235, + ExpressionStatement = 236, + IfStatement = 237, + DoStatement = 238, + WhileStatement = 239, + ForStatement = 240, + ForInStatement = 241, + ForOfStatement = 242, + ContinueStatement = 243, + BreakStatement = 244, + ReturnStatement = 245, + WithStatement = 246, + SwitchStatement = 247, + LabeledStatement = 248, + ThrowStatement = 249, + TryStatement = 250, + DebuggerStatement = 251, + VariableDeclaration = 252, + VariableDeclarationList = 253, + FunctionDeclaration = 254, + ClassDeclaration = 255, + InterfaceDeclaration = 256, + TypeAliasDeclaration = 257, + EnumDeclaration = 258, + ModuleDeclaration = 259, + ModuleBlock = 260, + CaseBlock = 261, + NamespaceExportDeclaration = 262, + ImportEqualsDeclaration = 263, + ImportDeclaration = 264, + ImportClause = 265, + NamespaceImport = 266, + NamedImports = 267, + ImportSpecifier = 268, + ExportAssignment = 269, + ExportDeclaration = 270, + NamedExports = 271, + NamespaceExport = 272, + ExportSpecifier = 273, + MissingDeclaration = 274, + ExternalModuleReference = 275, + JsxElement = 276, + JsxSelfClosingElement = 277, + JsxOpeningElement = 278, + JsxClosingElement = 279, + JsxFragment = 280, + JsxOpeningFragment = 281, + JsxClosingFragment = 282, + JsxAttribute = 283, + JsxAttributes = 284, + JsxSpreadAttribute = 285, + JsxExpression = 286, + CaseClause = 287, + DefaultClause = 288, + HeritageClause = 289, + CatchClause = 290, + PropertyAssignment = 291, + ShorthandPropertyAssignment = 292, + SpreadAssignment = 293, + EnumMember = 294, + UnparsedPrologue = 295, + UnparsedPrepend = 296, + UnparsedText = 297, + UnparsedInternalText = 298, + UnparsedSyntheticReference = 299, + SourceFile = 300, + Bundle = 301, + UnparsedSource = 302, + InputFiles = 303, + JSDocTypeExpression = 304, + JSDocNameReference = 305, + JSDocMemberName = 306, + JSDocAllType = 307, + JSDocUnknownType = 308, + JSDocNullableType = 309, + JSDocNonNullableType = 310, + JSDocOptionalType = 311, + JSDocFunctionType = 312, + JSDocVariadicType = 313, + JSDocNamepathType = 314, + JSDocComment = 315, + JSDocText = 316, + JSDocTypeLiteral = 317, + JSDocSignature = 318, + JSDocLink = 319, + JSDocLinkCode = 320, + JSDocLinkPlain = 321, + JSDocTag = 322, + JSDocAugmentsTag = 323, + JSDocImplementsTag = 324, + JSDocAuthorTag = 325, + JSDocDeprecatedTag = 326, + JSDocClassTag = 327, + JSDocPublicTag = 328, + JSDocPrivateTag = 329, + JSDocProtectedTag = 330, + JSDocReadonlyTag = 331, + JSDocOverrideTag = 332, + JSDocCallbackTag = 333, + JSDocEnumTag = 334, + JSDocParameterTag = 335, + JSDocReturnTag = 336, + JSDocThisTag = 337, + JSDocTypeTag = 338, + JSDocTemplateTag = 339, + JSDocTypedefTag = 340, + JSDocSeeTag = 341, + JSDocPropertyTag = 342, + SyntaxList = 343, + NotEmittedStatement = 344, + PartiallyEmittedExpression = 345, + CommaListExpression = 346, + MergeDeclarationMarker = 347, + EndOfDeclarationMarker = 348, + SyntheticReferenceExpression = 349, + Count = 350, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -479,13 +480,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 234, - LastStatement = 250, + FirstStatement = 235, + LastStatement = 251, FirstNode = 159, - FirstJSDocNode = 303, - LastJSDocNode = 341, - FirstJSDocTagNode = 321, - LastJSDocTagNode = 341, + FirstJSDocNode = 304, + LastJSDocNode = 342, + FirstJSDocTagNode = 322, + LastJSDocTagNode = 342, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -597,6 +598,7 @@ declare namespace ts { } export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; + export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; /** @deprecated Use `AssertsKeyword` instead. */ @@ -1256,6 +1258,12 @@ declare namespace ts { readonly expression: SuperExpression; } export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; + export interface PrivateIdentifierInInExpression extends Expression { + readonly kind: SyntaxKind.PrivateIdentifierInInExpression; + readonly name: PrivateIdentifier; + readonly inToken: Token; + readonly expression: Expression; + } export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -3419,6 +3427,8 @@ declare namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; + createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; + updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; createTemplateSpan(expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; createSemicolonClassElement(): SemicolonClassElement; @@ -4517,6 +4527,7 @@ declare namespace ts { function isSyntheticExpression(node: Node): node is SyntheticExpression; function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression; function isCommaListExpression(node: Node): node is CommaListExpression; + function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression; function isTemplateSpan(node: Node): node is TemplateSpan; function isSemicolonClassElement(node: Node): node is SemicolonClassElement; function isBlock(node: Node): node is Block; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index cc33707287259..f43cc392ad545 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -335,126 +335,127 @@ declare namespace ts { NonNullExpression = 227, MetaProperty = 228, SyntheticExpression = 229, - TemplateSpan = 230, - SemicolonClassElement = 231, - Block = 232, - EmptyStatement = 233, - VariableStatement = 234, - ExpressionStatement = 235, - IfStatement = 236, - DoStatement = 237, - WhileStatement = 238, - ForStatement = 239, - ForInStatement = 240, - ForOfStatement = 241, - ContinueStatement = 242, - BreakStatement = 243, - ReturnStatement = 244, - WithStatement = 245, - SwitchStatement = 246, - LabeledStatement = 247, - ThrowStatement = 248, - TryStatement = 249, - DebuggerStatement = 250, - VariableDeclaration = 251, - VariableDeclarationList = 252, - FunctionDeclaration = 253, - ClassDeclaration = 254, - InterfaceDeclaration = 255, - TypeAliasDeclaration = 256, - EnumDeclaration = 257, - ModuleDeclaration = 258, - ModuleBlock = 259, - CaseBlock = 260, - NamespaceExportDeclaration = 261, - ImportEqualsDeclaration = 262, - ImportDeclaration = 263, - ImportClause = 264, - NamespaceImport = 265, - NamedImports = 266, - ImportSpecifier = 267, - ExportAssignment = 268, - ExportDeclaration = 269, - NamedExports = 270, - NamespaceExport = 271, - ExportSpecifier = 272, - MissingDeclaration = 273, - ExternalModuleReference = 274, - JsxElement = 275, - JsxSelfClosingElement = 276, - JsxOpeningElement = 277, - JsxClosingElement = 278, - JsxFragment = 279, - JsxOpeningFragment = 280, - JsxClosingFragment = 281, - JsxAttribute = 282, - JsxAttributes = 283, - JsxSpreadAttribute = 284, - JsxExpression = 285, - CaseClause = 286, - DefaultClause = 287, - HeritageClause = 288, - CatchClause = 289, - PropertyAssignment = 290, - ShorthandPropertyAssignment = 291, - SpreadAssignment = 292, - EnumMember = 293, - UnparsedPrologue = 294, - UnparsedPrepend = 295, - UnparsedText = 296, - UnparsedInternalText = 297, - UnparsedSyntheticReference = 298, - SourceFile = 299, - Bundle = 300, - UnparsedSource = 301, - InputFiles = 302, - JSDocTypeExpression = 303, - JSDocNameReference = 304, - JSDocMemberName = 305, - JSDocAllType = 306, - JSDocUnknownType = 307, - JSDocNullableType = 308, - JSDocNonNullableType = 309, - JSDocOptionalType = 310, - JSDocFunctionType = 311, - JSDocVariadicType = 312, - JSDocNamepathType = 313, - JSDocComment = 314, - JSDocText = 315, - JSDocTypeLiteral = 316, - JSDocSignature = 317, - JSDocLink = 318, - JSDocLinkCode = 319, - JSDocLinkPlain = 320, - JSDocTag = 321, - JSDocAugmentsTag = 322, - JSDocImplementsTag = 323, - JSDocAuthorTag = 324, - JSDocDeprecatedTag = 325, - JSDocClassTag = 326, - JSDocPublicTag = 327, - JSDocPrivateTag = 328, - JSDocProtectedTag = 329, - JSDocReadonlyTag = 330, - JSDocOverrideTag = 331, - JSDocCallbackTag = 332, - JSDocEnumTag = 333, - JSDocParameterTag = 334, - JSDocReturnTag = 335, - JSDocThisTag = 336, - JSDocTypeTag = 337, - JSDocTemplateTag = 338, - JSDocTypedefTag = 339, - JSDocSeeTag = 340, - JSDocPropertyTag = 341, - SyntaxList = 342, - NotEmittedStatement = 343, - PartiallyEmittedExpression = 344, - CommaListExpression = 345, - MergeDeclarationMarker = 346, - EndOfDeclarationMarker = 347, - SyntheticReferenceExpression = 348, - Count = 349, + PrivateIdentifierInInExpression = 230, + TemplateSpan = 231, + SemicolonClassElement = 232, + Block = 233, + EmptyStatement = 234, + VariableStatement = 235, + ExpressionStatement = 236, + IfStatement = 237, + DoStatement = 238, + WhileStatement = 239, + ForStatement = 240, + ForInStatement = 241, + ForOfStatement = 242, + ContinueStatement = 243, + BreakStatement = 244, + ReturnStatement = 245, + WithStatement = 246, + SwitchStatement = 247, + LabeledStatement = 248, + ThrowStatement = 249, + TryStatement = 250, + DebuggerStatement = 251, + VariableDeclaration = 252, + VariableDeclarationList = 253, + FunctionDeclaration = 254, + ClassDeclaration = 255, + InterfaceDeclaration = 256, + TypeAliasDeclaration = 257, + EnumDeclaration = 258, + ModuleDeclaration = 259, + ModuleBlock = 260, + CaseBlock = 261, + NamespaceExportDeclaration = 262, + ImportEqualsDeclaration = 263, + ImportDeclaration = 264, + ImportClause = 265, + NamespaceImport = 266, + NamedImports = 267, + ImportSpecifier = 268, + ExportAssignment = 269, + ExportDeclaration = 270, + NamedExports = 271, + NamespaceExport = 272, + ExportSpecifier = 273, + MissingDeclaration = 274, + ExternalModuleReference = 275, + JsxElement = 276, + JsxSelfClosingElement = 277, + JsxOpeningElement = 278, + JsxClosingElement = 279, + JsxFragment = 280, + JsxOpeningFragment = 281, + JsxClosingFragment = 282, + JsxAttribute = 283, + JsxAttributes = 284, + JsxSpreadAttribute = 285, + JsxExpression = 286, + CaseClause = 287, + DefaultClause = 288, + HeritageClause = 289, + CatchClause = 290, + PropertyAssignment = 291, + ShorthandPropertyAssignment = 292, + SpreadAssignment = 293, + EnumMember = 294, + UnparsedPrologue = 295, + UnparsedPrepend = 296, + UnparsedText = 297, + UnparsedInternalText = 298, + UnparsedSyntheticReference = 299, + SourceFile = 300, + Bundle = 301, + UnparsedSource = 302, + InputFiles = 303, + JSDocTypeExpression = 304, + JSDocNameReference = 305, + JSDocMemberName = 306, + JSDocAllType = 307, + JSDocUnknownType = 308, + JSDocNullableType = 309, + JSDocNonNullableType = 310, + JSDocOptionalType = 311, + JSDocFunctionType = 312, + JSDocVariadicType = 313, + JSDocNamepathType = 314, + JSDocComment = 315, + JSDocText = 316, + JSDocTypeLiteral = 317, + JSDocSignature = 318, + JSDocLink = 319, + JSDocLinkCode = 320, + JSDocLinkPlain = 321, + JSDocTag = 322, + JSDocAugmentsTag = 323, + JSDocImplementsTag = 324, + JSDocAuthorTag = 325, + JSDocDeprecatedTag = 326, + JSDocClassTag = 327, + JSDocPublicTag = 328, + JSDocPrivateTag = 329, + JSDocProtectedTag = 330, + JSDocReadonlyTag = 331, + JSDocOverrideTag = 332, + JSDocCallbackTag = 333, + JSDocEnumTag = 334, + JSDocParameterTag = 335, + JSDocReturnTag = 336, + JSDocThisTag = 337, + JSDocTypeTag = 338, + JSDocTemplateTag = 339, + JSDocTypedefTag = 340, + JSDocSeeTag = 341, + JSDocPropertyTag = 342, + SyntaxList = 343, + NotEmittedStatement = 344, + PartiallyEmittedExpression = 345, + CommaListExpression = 346, + MergeDeclarationMarker = 347, + EndOfDeclarationMarker = 348, + SyntheticReferenceExpression = 349, + Count = 350, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -479,13 +480,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 234, - LastStatement = 250, + FirstStatement = 235, + LastStatement = 251, FirstNode = 159, - FirstJSDocNode = 303, - LastJSDocNode = 341, - FirstJSDocTagNode = 321, - LastJSDocTagNode = 341, + FirstJSDocNode = 304, + LastJSDocNode = 342, + FirstJSDocTagNode = 322, + LastJSDocTagNode = 342, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -597,6 +598,7 @@ declare namespace ts { } export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; + export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; /** @deprecated Use `AssertsKeyword` instead. */ @@ -1256,6 +1258,12 @@ declare namespace ts { readonly expression: SuperExpression; } export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; + export interface PrivateIdentifierInInExpression extends Expression { + readonly kind: SyntaxKind.PrivateIdentifierInInExpression; + readonly name: PrivateIdentifier; + readonly inToken: Token; + readonly expression: Expression; + } export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -3419,6 +3427,8 @@ declare namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; + createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; + updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; createTemplateSpan(expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; createSemicolonClassElement(): SemicolonClassElement; @@ -4517,6 +4527,7 @@ declare namespace ts { function isSyntheticExpression(node: Node): node is SyntheticExpression; function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression; function isCommaListExpression(node: Node): node is CommaListExpression; + function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression; function isTemplateSpan(node: Node): node is TemplateSpan; function isSemicolonClassElement(node: Node): node is SemicolonClassElement; function isBlock(node: Node): node is Block; diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt new file mode 100644 index 0000000000000..66152059feb4e --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -0,0 +1,140 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2552: Cannot find name '#fiel'. Did you mean '#field'? +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2304: Cannot find name '#field'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(29,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(43,27): error TS2531: Object is possibly 'null'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(108,12): error TS18016: Private identifiers are not allowed outside class bodies. + + +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== + class Foo { + #field = 1; + static #staticField = 2; + #method() {} + static #staticMethod() {} + + goodRhs(v: any) { + const a = #field in v; + + const b = #field in v.p1.p2; + + const c = #field in (v as {}); + + const d = #field in (v as Foo); + + const e = #field in (v as never); + + for (let f in #field in v as any) { /**/ } // unlikely but valid + } + badRhs(v: any) { + const a = #field in (v as unknown); // Bad - RHS of in must be object type or any + ~~~~~~~~~~~~~~ +!!! error TS2571: Object is of type 'unknown'. + + const b = #fiel in v; // Bad - typo in privateID + ~~~~~ +!!! error TS2552: Cannot find name '#fiel'. Did you mean '#field'? + + const c = (#field) in v; // Bad - privateID is not an expression on its own + ~~~~~~ +!!! error TS2304: Cannot find name '#field'. + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + + for (#field in v) { /**/ } // Bad - 'in' not allowed + ~~~~~~ +!!! error TS2304: Cannot find name '#field'. + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + + for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any + ~~~~~~~~~~~ +!!! error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'. + } + whitespace(v: any) { + const a = v && /*0*/#field/*1*/ + /*2*/in/*3*/ + /*4*/v/*5*/ + } + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + + if (typeof u === 'object') { + if (#field in n) { + n; // good n is never + } + + if (#field in u) { + ~ +!!! error TS2531: Object is possibly 'null'. + u; // good u is Foo + } else { + u; // good u is object | null + } + + if (u !== null) { + if (#field in u) { + u; // good u is Foo + } else { + u; // good u is object + } + + if (#method in u) { + u; // good u is Foo + } + + if (#staticField in u) { + u; // good u is typeof Foo + } + + if (#staticMethod in u) { + u; // good u is typeof Foo + } + } + } + + if (#field in fb) { + fb; // good fb is Foo + } else { + fb; // good fb is Bar + } + + if (#field in fs) { + fs; // good fs is FooSub + } else { + fs; // good fs is never + } + + if (#field in b) { + b; // good b is 'Bar & Foo' + } else { + b; // good b is Bar + } + + if (#field in fsb) { + fsb; // good fsb is FooSub + } else { + fsb; // good fsb is Bar + } + + class Nested { + m(v: any) { + if (#field in v) { + v; // good v is Foo + } + } + } + } + } + + class FooSub extends Foo { subTypeOfFoo = true } + class Bar { notFoo = true } + + function badSyntax(v: Foo) { + return #field in v; // Bad - outside of class + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + } + \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js new file mode 100644 index 0000000000000..000620cb6d561 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -0,0 +1,210 @@ +//// [privateNameInInExpression.ts] +class Foo { + #field = 1; + static #staticField = 2; + #method() {} + static #staticMethod() {} + + goodRhs(v: any) { + const a = #field in v; + + const b = #field in v.p1.p2; + + const c = #field in (v as {}); + + const d = #field in (v as Foo); + + const e = #field in (v as never); + + for (let f in #field in v as any) { /**/ } // unlikely but valid + } + badRhs(v: any) { + const a = #field in (v as unknown); // Bad - RHS of in must be object type or any + + const b = #fiel in v; // Bad - typo in privateID + + const c = (#field) in v; // Bad - privateID is not an expression on its own + + for (#field in v) { /**/ } // Bad - 'in' not allowed + + for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any + } + whitespace(v: any) { + const a = v && /*0*/#field/*1*/ + /*2*/in/*3*/ + /*4*/v/*5*/ + } + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + + if (typeof u === 'object') { + if (#field in n) { + n; // good n is never + } + + if (#field in u) { + u; // good u is Foo + } else { + u; // good u is object | null + } + + if (u !== null) { + if (#field in u) { + u; // good u is Foo + } else { + u; // good u is object + } + + if (#method in u) { + u; // good u is Foo + } + + if (#staticField in u) { + u; // good u is typeof Foo + } + + if (#staticMethod in u) { + u; // good u is typeof Foo + } + } + } + + if (#field in fb) { + fb; // good fb is Foo + } else { + fb; // good fb is Bar + } + + if (#field in fs) { + fs; // good fs is FooSub + } else { + fs; // good fs is never + } + + if (#field in b) { + b; // good b is 'Bar & Foo' + } else { + b; // good b is Bar + } + + if (#field in fsb) { + fsb; // good fsb is FooSub + } else { + fsb; // good fsb is Bar + } + + class Nested { + m(v: any) { + if (#field in v) { + v; // good v is Foo + } + } + } + } +} + +class FooSub extends Foo { subTypeOfFoo = true } +class Bar { notFoo = true } + +function badSyntax(v: Foo) { + return #field in v; // Bad - outside of class +} + + +//// [privateNameInInExpression.js] +"use strict"; +class Foo { + #field = 1; + static #staticField = 2; + #method() { } + static #staticMethod() { } + goodRhs(v) { + const a = #field in v; + const b = #field in v.p1.p2; + const c = #field in v; + const d = #field in v; + const e = #field in v; + for (let f in #field in v) { /**/ } // unlikely but valid + } + badRhs(v) { + const a = #field in v; // Bad - RHS of in must be object type or any + const b = #fiel in v; // Bad - typo in privateID + const c = (#field) in v; // Bad - privateID is not an expression on its own + for (#field in v) { /**/ } // Bad - 'in' not allowed + for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any + } + whitespace(v) { + const a = v && /*0*/ #field/*1*/ + /*2*/ in /*3*/ + /*4*/ v; /*5*/ + } + flow(u, n, fb, fs, b, fsb) { + if (typeof u === 'object') { + if (#field in n) { + n; // good n is never + } + if (#field in u) { + u; // good u is Foo + } + else { + u; // good u is object | null + } + if (u !== null) { + if (#field in u) { + u; // good u is Foo + } + else { + u; // good u is object + } + if (#method in u) { + u; // good u is Foo + } + if (#staticField in u) { + u; // good u is typeof Foo + } + if (#staticMethod in u) { + u; // good u is typeof Foo + } + } + } + if (#field in fb) { + fb; // good fb is Foo + } + else { + fb; // good fb is Bar + } + if (#field in fs) { + fs; // good fs is FooSub + } + else { + fs; // good fs is never + } + if (#field in b) { + b; // good b is 'Bar & Foo' + } + else { + b; // good b is Bar + } + if (#field in fsb) { + fsb; // good fsb is FooSub + } + else { + fsb; // good fsb is Bar + } + class Nested { + m(v) { + if (#field in v) { + v; // good v is Foo + } + } + } + } +} +class FooSub extends Foo { + subTypeOfFoo = true; +} +class Bar { + notFoo = true; +} +function badSyntax(v) { + return #field in v; // Bad - outside of class +} diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols new file mode 100644 index 0000000000000..30a02dddacea9 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -0,0 +1,233 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === +class Foo { +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) + + #field = 1; +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) + + static #staticField = 2; +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpression.ts, 1, 15)) + + #method() {} +>#method : Symbol(Foo.#method, Decl(privateNameInInExpression.ts, 2, 28)) + + static #staticMethod() {} +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 3, 16)) + + goodRhs(v: any) { +>goodRhs : Symbol(Foo.goodRhs, Decl(privateNameInInExpression.ts, 4, 29)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) + + const a = #field in v; +>a : Symbol(a, Decl(privateNameInInExpression.ts, 7, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) + + const b = #field in v.p1.p2; +>b : Symbol(b, Decl(privateNameInInExpression.ts, 9, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) + + const c = #field in (v as {}); +>c : Symbol(c, Decl(privateNameInInExpression.ts, 11, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) + + const d = #field in (v as Foo); +>d : Symbol(d, Decl(privateNameInInExpression.ts, 13, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) + + const e = #field in (v as never); +>e : Symbol(e, Decl(privateNameInInExpression.ts, 15, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) + + for (let f in #field in v as any) { /**/ } // unlikely but valid +>f : Symbol(f, Decl(privateNameInInExpression.ts, 17, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) + } + badRhs(v: any) { +>badRhs : Symbol(Foo.badRhs, Decl(privateNameInInExpression.ts, 18, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) + + const a = #field in (v as unknown); // Bad - RHS of in must be object type or any +>a : Symbol(a, Decl(privateNameInInExpression.ts, 20, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) + + const b = #fiel in v; // Bad - typo in privateID +>b : Symbol(b, Decl(privateNameInInExpression.ts, 22, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) + + const c = (#field) in v; // Bad - privateID is not an expression on its own +>c : Symbol(c, Decl(privateNameInInExpression.ts, 24, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) + + for (#field in v) { /**/ } // Bad - 'in' not allowed +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) + + for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any +>d : Symbol(d, Decl(privateNameInInExpression.ts, 28, 16)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) + } + whitespace(v: any) { +>whitespace : Symbol(Foo.whitespace, Decl(privateNameInInExpression.ts, 29, 5)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15)) + + const a = v && /*0*/#field/*1*/ +>a : Symbol(a, Decl(privateNameInInExpression.ts, 31, 13)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15)) + + /*2*/in/*3*/ + /*4*/v/*5*/ +>v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15)) + } + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { +>flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 34, 5)) +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) +>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20)) +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) + + if (typeof u === 'object') { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + if (#field in n) { +>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20)) + + n; // good n is never +>n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20)) + } + + if (#field in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + u; // good u is Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + } else { + u; // good u is object | null +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + } + + if (u !== null) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + if (#field in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + u; // good u is Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + } else { + u; // good u is object +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + } + + if (#method in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + u; // good u is Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + } + + if (#staticField in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + u; // good u is typeof Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + } + + if (#staticMethod in u) { +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + + u; // good u is typeof Foo +>u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) + } + } + } + + if (#field in fb) { +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) + + fb; // good fb is Foo +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) + + } else { + fb; // good fb is Bar +>fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) + } + + if (#field in fs) { +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) + + fs; // good fs is FooSub +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) + + } else { + fs; // good fs is never +>fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) + } + + if (#field in b) { +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) + + b; // good b is 'Bar & Foo' +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) + + } else { + b; // good b is Bar +>b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) + } + + if (#field in fsb) { +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) + + fsb; // good fsb is FooSub +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) + + } else { + fsb; // good fsb is Bar +>fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) + } + + class Nested { +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 91, 9)) + + m(v: any) { +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 93, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) + + if (#field in v) { +>v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) + + v; // good v is Foo +>v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) + } + } + } + } +} + +class FooSub extends Foo { subTypeOfFoo = true } +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) +>subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 103, 26)) + +class Bar { notFoo = true } +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 104, 11)) + +function badSyntax(v: Foo) { +>badSyntax : Symbol(badSyntax, Decl(privateNameInInExpression.ts, 104, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 106, 19)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) + + return #field in v; // Bad - outside of class +>v : Symbol(v, Decl(privateNameInInExpression.ts, 106, 19)) +} + diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types new file mode 100644 index 0000000000000..f323b2a6a2dc3 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -0,0 +1,274 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts === +class Foo { +>Foo : Foo + + #field = 1; +>#field : number +>1 : 1 + + static #staticField = 2; +>#staticField : number +>2 : 2 + + #method() {} +>#method : () => void + + static #staticMethod() {} +>#staticMethod : () => void + + goodRhs(v: any) { +>goodRhs : (v: any) => void +>v : any + + const a = #field in v; +>a : boolean +>#field in v : boolean +>v : any + + const b = #field in v.p1.p2; +>b : boolean +>#field in v.p1.p2 : boolean +>v.p1.p2 : any +>v.p1 : any +>v : any +>p1 : any +>p2 : any + + const c = #field in (v as {}); +>c : boolean +>#field in (v as {}) : boolean +>(v as {}) : {} +>v as {} : {} +>v : any + + const d = #field in (v as Foo); +>d : boolean +>#field in (v as Foo) : boolean +>(v as Foo) : Foo +>v as Foo : Foo +>v : any + + const e = #field in (v as never); +>e : boolean +>#field in (v as never) : boolean +>(v as never) : never +>v as never : never +>v : any + + for (let f in #field in v as any) { /**/ } // unlikely but valid +>f : string +>#field in v as any : any +>#field in v : boolean +>v : any + } + badRhs(v: any) { +>badRhs : (v: any) => void +>v : any + + const a = #field in (v as unknown); // Bad - RHS of in must be object type or any +>a : boolean +>#field in (v as unknown) : boolean +>(v as unknown) : unknown +>v as unknown : unknown +>v : any + + const b = #fiel in v; // Bad - typo in privateID +>b : any +>#fiel in v : any +>v : any + + const c = (#field) in v; // Bad - privateID is not an expression on its own +>c : boolean +>(#field) in v : boolean +>(#field) : any +>#field : any +>v : any + + for (#field in v) { /**/ } // Bad - 'in' not allowed +>#field : any +>v : any + + for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any +>d : string +>#field in v : boolean +>v : any + } + whitespace(v: any) { +>whitespace : (v: any) => void +>v : any + + const a = v && /*0*/#field/*1*/ +>a : any +>v && /*0*/#field/*1*/ /*2*/in/*3*/ /*4*/v : any +>v : any +>#field/*1*/ /*2*/in/*3*/ /*4*/v : boolean + + /*2*/in/*3*/ + /*4*/v/*5*/ +>v : any + } + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { +>flow : (u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) => void +>u : unknown +>n : never +>fb : Foo | Bar +>fs : FooSub +>b : Bar +>fsb : Bar | FooSub + + if (typeof u === 'object') { +>typeof u === 'object' : boolean +>typeof u : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>u : unknown +>'object' : "object" + + if (#field in n) { +>#field in n : boolean +>n : never + + n; // good n is never +>n : never + } + + if (#field in u) { +>#field in u : boolean +>u : object | null + + u; // good u is Foo +>u : Foo + + } else { + u; // good u is object | null +>u : object | null + } + + if (u !== null) { +>u !== null : boolean +>u : object | null +>null : null + + if (#field in u) { +>#field in u : boolean +>u : object + + u; // good u is Foo +>u : Foo + + } else { + u; // good u is object +>u : object + } + + if (#method in u) { +>#method in u : boolean +>u : object + + u; // good u is Foo +>u : Foo + } + + if (#staticField in u) { +>#staticField in u : boolean +>u : object + + u; // good u is typeof Foo +>u : typeof Foo + } + + if (#staticMethod in u) { +>#staticMethod in u : boolean +>u : object + + u; // good u is typeof Foo +>u : typeof Foo + } + } + } + + if (#field in fb) { +>#field in fb : boolean +>fb : Foo | Bar + + fb; // good fb is Foo +>fb : Foo + + } else { + fb; // good fb is Bar +>fb : Bar + } + + if (#field in fs) { +>#field in fs : boolean +>fs : FooSub + + fs; // good fs is FooSub +>fs : FooSub + + } else { + fs; // good fs is never +>fs : never + } + + if (#field in b) { +>#field in b : boolean +>b : Bar + + b; // good b is 'Bar & Foo' +>b : Bar & Foo + + } else { + b; // good b is Bar +>b : Bar + } + + if (#field in fsb) { +>#field in fsb : boolean +>fsb : Bar | FooSub + + fsb; // good fsb is FooSub +>fsb : FooSub + + } else { + fsb; // good fsb is Bar +>fsb : Bar + } + + class Nested { +>Nested : Nested + + m(v: any) { +>m : (v: any) => void +>v : any + + if (#field in v) { +>#field in v : boolean +>v : any + + v; // good v is Foo +>v : Foo + } + } + } + } +} + +class FooSub extends Foo { subTypeOfFoo = true } +>FooSub : FooSub +>Foo : Foo +>subTypeOfFoo : boolean +>true : true + +class Bar { notFoo = true } +>Bar : Bar +>notFoo : boolean +>true : true + +function badSyntax(v: Foo) { +>badSyntax : (v: Foo) => any +>v : Foo + + return #field in v; // Bad - outside of class +>#field in v : any +>v : Foo +} + diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt new file mode 100644 index 0000000000000..0368b9ef750bc --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt @@ -0,0 +1,67 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. + + +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (6 errors) ==== + class Foo { + #field = 1; + #method() {} + static #staticField= 2; + static #staticMethod() {} + + check(v: any) { + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor + } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + ~~~~~~~~~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + ~~~~~~ +!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + ~~~~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + } + invalidLHS(v: any) { + 'prop' in v = 10; + ~ +!!! error TS1005: ';' expected. + #field in v = 10; + ~ +!!! error TS1005: ';' expected. + } + } + + class Bar { + #field = 1; + check(v: any) { + #field in v; // expect Bar's 'field' WeakMap + } + } + + function syntaxError(v: Foo) { + return #field in v; // expect `return in v` so runtime will have a syntax error + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + } + + export { } + \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.js b/tests/baselines/reference/privateNameInInExpressionTransform.js new file mode 100644 index 0000000000000..cd7d3dbc61db2 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.js @@ -0,0 +1,97 @@ +//// [privateNameInInExpressionTransform.ts] +class Foo { + #field = 1; + #method() {} + static #staticField= 2; + static #staticMethod() {} + + check(v: any) { + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor + } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + } + invalidLHS(v: any) { + 'prop' in v = 10; + #field in v = 10; + } +} + +class Bar { + #field = 1; + check(v: any) { + #field in v; // expect Bar's 'field' WeakMap + } +} + +function syntaxError(v: Foo) { + return #field in v; // expect `return in v` so runtime will have a syntax error +} + +export { } + + +//// [privateNameInInExpressionTransform.js] +var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, state) { + if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); + return typeof state === "function" ? receiver === state : state.has(receiver); +}; +var _Foo_instances, _a, _Foo_field, _Foo_method, _Foo_staticField, _Foo_staticMethod, _Bar_field; +class Foo { + constructor() { + _Foo_instances.add(this); + _Foo_field.set(this, 1); + } + check(v) { + __classPrivateFieldIn(v, _Foo_field); // expect Foo's 'field' WeakMap + __classPrivateFieldIn(v, _Foo_instances); // expect Foo's 'method' WeakSet + __classPrivateFieldIn(v, _a); // expect Foo's constructor + __classPrivateFieldIn(v, _a); // expect Foo's constructor + } + precedence(v) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + v == __classPrivateFieldIn(v, _Foo_field) == v; // Good precedence: ((v == (#field in v)) == v) + v << __classPrivateFieldIn(v << v, _Foo_field); // Good precedence: (v << (#field in (v << v))) + v << __classPrivateFieldIn(v, _Foo_field) == v; // Good precedence: ((v << (#field in v)) == v) + v == __classPrivateFieldIn(v, _Foo_field) < v; // Good precedence: (v == ((#field in v) < v)) + __classPrivateFieldIn(v, _Foo_field) && __classPrivateFieldIn(v, _Foo_field); // Good precedence: ((#field in v) && (#field in v)) + } + invalidLHS(v) { + 'prop' in v; + 10; + __classPrivateFieldIn(v, _Foo_field); + 10; + } +} +_a = Foo, _Foo_field = new WeakMap(), _Foo_instances = new WeakSet(), _Foo_method = function _Foo_method() { }, _Foo_staticMethod = function _Foo_staticMethod() { }; +_Foo_staticField = { value: 2 }; +class Bar { + constructor() { + _Bar_field.set(this, 1); + } + check(v) { + __classPrivateFieldIn(v, _Bar_field); // expect Bar's 'field' WeakMap + } +} +_Bar_field = new WeakMap(); +function syntaxError(v) { + return in v; // expect `return in v` so runtime will have a syntax error +} +export {}; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.symbols b/tests/baselines/reference/privateNameInInExpressionTransform.symbols new file mode 100644 index 0000000000000..c0cc9938cd8d2 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.symbols @@ -0,0 +1,102 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === +class Foo { +>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) + + #field = 1; +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) + + #method() {} +>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15)) + + static #staticField= 2; +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16)) + + static #staticMethod() {} +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27)) + + check(v: any) { +>check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 4, 29)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #field in v; // expect Foo's 'field' WeakMap +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #method in v; // expect Foo's 'method' WeakSet +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #staticField in v; // expect Foo's constructor +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #staticMethod in v; // expect Foo's constructor +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + } + precedence(v: any) { +>precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 11, 5)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + } + invalidLHS(v: any) { +>invalidLHS : Symbol(Foo.invalidLHS, Decl(privateNameInInExpressionTransform.ts, 26, 5)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) + + 'prop' in v = 10; +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) + + #field in v = 10; +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) + } +} + +class Bar { +>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 31, 1)) + + #field = 1; +>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11)) + + check(v: any) { +>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 34, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) + + #field in v; // expect Bar's 'field' WeakMap +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) + } +} + +function syntaxError(v: Foo) { +>syntaxError : Symbol(syntaxError, Decl(privateNameInInExpressionTransform.ts, 38, 1)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21)) +>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) + + return #field in v; // expect `return in v` so runtime will have a syntax error +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21)) +} + +export { } + diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.types b/tests/baselines/reference/privateNameInInExpressionTransform.types new file mode 100644 index 0000000000000..5c68549be8fd4 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform.types @@ -0,0 +1,130 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === +class Foo { +>Foo : Foo + + #field = 1; +>#field : number +>1 : 1 + + #method() {} +>#method : () => void + + static #staticField= 2; +>#staticField : number +>2 : 2 + + static #staticMethod() {} +>#staticMethod : () => void + + check(v: any) { +>check : (v: any) => void +>v : any + + #field in v; // expect Foo's 'field' WeakMap +>#field in v : boolean +>v : any + + #method in v; // expect Foo's 'method' WeakSet +>#method in v : boolean +>v : any + + #staticField in v; // expect Foo's constructor +>#staticField in v : boolean +>v : any + + #staticMethod in v; // expect Foo's constructor +>#staticMethod in v : boolean +>v : any + } + precedence(v: any) { +>precedence : (v: any) => void +>v : any + + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) +>v == #field in v == v : boolean +>v == #field in v : boolean +>v : any +>#field in v : boolean +>v : any +>v : any + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) +>v << #field in v << v : number +>v : any +>#field in v << v : boolean +>v << v : number +>v : any +>v : any + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) +>v << #field in v == v : boolean +>v << #field in v : number +>v : any +>#field in v : boolean +>v : any +>v : any + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) +>v == #field in v < v : boolean +>v : any +>#field in v < v : boolean +>#field in v : boolean +>v : any +>v : any + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) +>#field in v && #field in v : boolean +>#field in v : boolean +>v : any +>#field in v : boolean +>v : Foo + } + invalidLHS(v: any) { +>invalidLHS : (v: any) => void +>v : any + + 'prop' in v = 10; +>'prop' in v : boolean +>'prop' : "prop" +>v : any +>10 : 10 + + #field in v = 10; +>#field in v : boolean +>v : any +>10 : 10 + } +} + +class Bar { +>Bar : Bar + + #field = 1; +>#field : number +>1 : 1 + + check(v: any) { +>check : (v: any) => void +>v : any + + #field in v; // expect Bar's 'field' WeakMap +>#field in v : boolean +>v : any + } +} + +function syntaxError(v: Foo) { +>syntaxError : (v: Foo) => any +>v : Foo + + return #field in v; // expect `return in v` so runtime will have a syntax error +>#field in v : any +>v : Foo +} + +export { } + diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt b/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt new file mode 100644 index 0000000000000..94d56876d297b --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts(2,5): error TS6133: '#unused' is declared but its value is never read. + + +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts (1 errors) ==== + class Foo { + #unused: undefined; // expect unused error + ~~~~~~~ +!!! error TS6133: '#unused' is declared but its value is never read. + #brand: undefined; // expect no error + + isFoo(v: any): v is Foo { + // This should count as using/reading '#brand' + return #brand in v; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.js b/tests/baselines/reference/privateNameInInExpressionUnused.js new file mode 100644 index 0000000000000..651f2932d1669 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.js @@ -0,0 +1,22 @@ +//// [privateNameInInExpressionUnused.ts] +class Foo { + #unused: undefined; // expect unused error + #brand: undefined; // expect no error + + isFoo(v: any): v is Foo { + // This should count as using/reading '#brand' + return #brand in v; + } +} + + +//// [privateNameInInExpressionUnused.js] +"use strict"; +class Foo { + #unused; // expect unused error + #brand; // expect no error + isFoo(v) { + // This should count as using/reading '#brand' + return #brand in v; + } +} diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.symbols b/tests/baselines/reference/privateNameInInExpressionUnused.symbols new file mode 100644 index 0000000000000..03c516ae887f9 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.symbols @@ -0,0 +1,22 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts === +class Foo { +>Foo : Symbol(Foo, Decl(privateNameInInExpressionUnused.ts, 0, 0)) + + #unused: undefined; // expect unused error +>#unused : Symbol(Foo.#unused, Decl(privateNameInInExpressionUnused.ts, 0, 11)) + + #brand: undefined; // expect no error +>#brand : Symbol(Foo.#brand, Decl(privateNameInInExpressionUnused.ts, 1, 23)) + + isFoo(v: any): v is Foo { +>isFoo : Symbol(Foo.isFoo, Decl(privateNameInInExpressionUnused.ts, 2, 22)) +>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10)) +>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10)) +>Foo : Symbol(Foo, Decl(privateNameInInExpressionUnused.ts, 0, 0)) + + // This should count as using/reading '#brand' + return #brand in v; +>v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10)) + } +} + diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.types b/tests/baselines/reference/privateNameInInExpressionUnused.types new file mode 100644 index 0000000000000..74eea43e013b8 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionUnused.types @@ -0,0 +1,21 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts === +class Foo { +>Foo : Foo + + #unused: undefined; // expect unused error +>#unused : undefined + + #brand: undefined; // expect no error +>#brand : undefined + + isFoo(v: any): v is Foo { +>isFoo : (v: any) => v is Foo +>v : any + + // This should count as using/reading '#brand' + return #brand in v; +>#brand in v : boolean +>v : any + } +} + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts new file mode 100644 index 0000000000000..abea3ed544e64 --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -0,0 +1,113 @@ +// @strict: true +// @target: esnext +// @useDefineForClassFields: true + +class Foo { + #field = 1; + static #staticField = 2; + #method() {} + static #staticMethod() {} + + goodRhs(v: any) { + const a = #field in v; + + const b = #field in v.p1.p2; + + const c = #field in (v as {}); + + const d = #field in (v as Foo); + + const e = #field in (v as never); + + for (let f in #field in v as any) { /**/ } // unlikely but valid + } + badRhs(v: any) { + const a = #field in (v as unknown); // Bad - RHS of in must be object type or any + + const b = #fiel in v; // Bad - typo in privateID + + const c = (#field) in v; // Bad - privateID is not an expression on its own + + for (#field in v) { /**/ } // Bad - 'in' not allowed + + for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any + } + whitespace(v: any) { + const a = v && /*0*/#field/*1*/ + /*2*/in/*3*/ + /*4*/v/*5*/ + } + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + + if (typeof u === 'object') { + if (#field in n) { + n; // good n is never + } + + if (#field in u) { + u; // good u is Foo + } else { + u; // good u is object | null + } + + if (u !== null) { + if (#field in u) { + u; // good u is Foo + } else { + u; // good u is object + } + + if (#method in u) { + u; // good u is Foo + } + + if (#staticField in u) { + u; // good u is typeof Foo + } + + if (#staticMethod in u) { + u; // good u is typeof Foo + } + } + } + + if (#field in fb) { + fb; // good fb is Foo + } else { + fb; // good fb is Bar + } + + if (#field in fs) { + fs; // good fs is FooSub + } else { + fs; // good fs is never + } + + if (#field in b) { + b; // good b is 'Bar & Foo' + } else { + b; // good b is Bar + } + + if (#field in fsb) { + fsb; // good fsb is FooSub + } else { + fsb; // good fsb is Bar + } + + class Nested { + m(v: any) { + if (#field in v) { + v; // good v is Foo + } + } + } + } +} + +class FooSub extends Foo { subTypeOfFoo = true } +class Bar { notFoo = true } + +function badSyntax(v: Foo) { + return #field in v; // Bad - outside of class +} diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts new file mode 100644 index 0000000000000..2518012b3e1b0 --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -0,0 +1,47 @@ +// @target: es2020 + +class Foo { + #field = 1; + #method() {} + static #staticField= 2; + static #staticMethod() {} + + check(v: any) { + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor + } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + } + invalidLHS(v: any) { + 'prop' in v = 10; + #field in v = 10; + } +} + +class Bar { + #field = 1; + check(v: any) { + #field in v; // expect Bar's 'field' WeakMap + } +} + +function syntaxError(v: Foo) { + return #field in v; // expect `return in v` so runtime will have a syntax error +} + +export { } diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts new file mode 100644 index 0000000000000..4b214a2b9e78f --- /dev/null +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionUnused.ts @@ -0,0 +1,13 @@ +// @strict: true +// @noUnusedLocals: true +// @target: esnext + +class Foo { + #unused: undefined; // expect unused error + #brand: undefined; // expect no error + + isFoo(v: any): v is Foo { + // This should count as using/reading '#brand' + return #brand in v; + } +} diff --git a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts new file mode 100644 index 0000000000000..3d3e48ede332d --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts @@ -0,0 +1,19 @@ +/// + +////class A { +//// #foo: number; +//// static isA(v: any) { +//// [|return #fo in v;|] +//// } +////} + +verify.codeFixAvailable([ + { description: "Change spelling to '#foo'" }, + { description: "Remove unused declaration for: '#foo'" }, +]); + +verify.codeFix({ + index: 0, + description: "Change spelling to '#foo'", + newRangeContent: "return #foo in v;" +}); diff --git a/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts b/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts index 8470772e9bf52..06c886b7e4468 100644 --- a/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts +++ b/tests/cases/fourslash/findAllRefsPrivateNameProperties.ts @@ -4,6 +4,7 @@ //// [|[|{|"isDefinition": true, "isWriteAccess": true, "contextRangeIndex": 0 |}#foo|] = 10;|] //// constructor() { //// this.[|{|"isWriteAccess": true|}#foo|] = 20; +//// [|#foo|] in this; //// } ////} ////class D extends C { @@ -13,12 +14,12 @@ //// } ////} ////class E { -//// [|[|{|"isDefinition": true, "contextRangeIndex": 3 |}#foo|]: number;|] +//// [|[|{|"isDefinition": true, "contextRangeIndex": 4 |}#foo|]: number;|] //// constructor() { //// this.[|{|"isWriteAccess": true|}#foo|] = 20; //// } ////} -const [rC0Def, rC0, rC1, rE0Def, rE0, rE1] = test.ranges(); -verify.singleReferenceGroup("(property) C.#foo: number", [rC0, rC1]); +const [rC0Def, rC0, rC1, rC2, rE0Def, rE0, rE1] = test.ranges(); +verify.singleReferenceGroup("(property) C.#foo: number", [rC0, rC1, rC2]); verify.singleReferenceGroup("(property) E.#foo: number", [rE0, rE1]); From 30dde524c6c747d98ed490d826fa9a090288b0f6 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 18 Jun 2021 15:42:37 +0100 Subject: [PATCH 02/41] [fixup] include inToken when walking forEachChild(node, cb) --- src/compiler/parser.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 2488f514a9f3d..0e9c042d5ff8c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -444,6 +444,7 @@ namespace ts { return visitNodes(cbNode, cbNodes, (node as CommaListExpression).elements); case SyntaxKind.PrivateIdentifierInInExpression: return visitNode(cbNode, (node as PrivateIdentifierInInExpression).name) || + visitNode(cbNode, (node as PrivateIdentifierInInExpression).inToken) || visitNode(cbNode, (node as PrivateIdentifierInInExpression).expression); case SyntaxKind.JsxElement: From 61c677be3e7fa89d18f26158f5b1825c83feb747 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 30 Jun 2021 18:48:45 +0100 Subject: [PATCH 03/41] [squash] re-accept lib definition baseline changes --- .../reference/api/tsserverlibrary.d.ts | 255 +++++++++--------- tests/baselines/reference/api/typescript.d.ts | 255 +++++++++--------- 2 files changed, 256 insertions(+), 254 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index de097679b25a5..aad84266505b8 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -336,126 +336,127 @@ declare namespace ts { NonNullExpression = 228, MetaProperty = 229, SyntheticExpression = 230, - TemplateSpan = 231, - SemicolonClassElement = 232, - Block = 233, - EmptyStatement = 234, - VariableStatement = 235, - ExpressionStatement = 236, - IfStatement = 237, - DoStatement = 238, - WhileStatement = 239, - ForStatement = 240, - ForInStatement = 241, - ForOfStatement = 242, - ContinueStatement = 243, - BreakStatement = 244, - ReturnStatement = 245, - WithStatement = 246, - SwitchStatement = 247, - LabeledStatement = 248, - ThrowStatement = 249, - TryStatement = 250, - DebuggerStatement = 251, - VariableDeclaration = 252, - VariableDeclarationList = 253, - FunctionDeclaration = 254, - ClassDeclaration = 255, - InterfaceDeclaration = 256, - TypeAliasDeclaration = 257, - EnumDeclaration = 258, - ModuleDeclaration = 259, - ModuleBlock = 260, - CaseBlock = 261, - NamespaceExportDeclaration = 262, - ImportEqualsDeclaration = 263, - ImportDeclaration = 264, - ImportClause = 265, - NamespaceImport = 266, - NamedImports = 267, - ImportSpecifier = 268, - ExportAssignment = 269, - ExportDeclaration = 270, - NamedExports = 271, - NamespaceExport = 272, - ExportSpecifier = 273, - MissingDeclaration = 274, - ExternalModuleReference = 275, - JsxElement = 276, - JsxSelfClosingElement = 277, - JsxOpeningElement = 278, - JsxClosingElement = 279, - JsxFragment = 280, - JsxOpeningFragment = 281, - JsxClosingFragment = 282, - JsxAttribute = 283, - JsxAttributes = 284, - JsxSpreadAttribute = 285, - JsxExpression = 286, - CaseClause = 287, - DefaultClause = 288, - HeritageClause = 289, - CatchClause = 290, - PropertyAssignment = 291, - ShorthandPropertyAssignment = 292, - SpreadAssignment = 293, - EnumMember = 294, - UnparsedPrologue = 295, - UnparsedPrepend = 296, - UnparsedText = 297, - UnparsedInternalText = 298, - UnparsedSyntheticReference = 299, - SourceFile = 300, - Bundle = 301, - UnparsedSource = 302, - InputFiles = 303, - JSDocTypeExpression = 304, - JSDocNameReference = 305, - JSDocMemberName = 306, - JSDocAllType = 307, - JSDocUnknownType = 308, - JSDocNullableType = 309, - JSDocNonNullableType = 310, - JSDocOptionalType = 311, - JSDocFunctionType = 312, - JSDocVariadicType = 313, - JSDocNamepathType = 314, - JSDocComment = 315, - JSDocText = 316, - JSDocTypeLiteral = 317, - JSDocSignature = 318, - JSDocLink = 319, - JSDocLinkCode = 320, - JSDocLinkPlain = 321, - JSDocTag = 322, - JSDocAugmentsTag = 323, - JSDocImplementsTag = 324, - JSDocAuthorTag = 325, - JSDocDeprecatedTag = 326, - JSDocClassTag = 327, - JSDocPublicTag = 328, - JSDocPrivateTag = 329, - JSDocProtectedTag = 330, - JSDocReadonlyTag = 331, - JSDocOverrideTag = 332, - JSDocCallbackTag = 333, - JSDocEnumTag = 334, - JSDocParameterTag = 335, - JSDocReturnTag = 336, - JSDocThisTag = 337, - JSDocTypeTag = 338, - JSDocTemplateTag = 339, - JSDocTypedefTag = 340, - JSDocSeeTag = 341, - JSDocPropertyTag = 342, - SyntaxList = 343, - NotEmittedStatement = 344, - PartiallyEmittedExpression = 345, - CommaListExpression = 346, - MergeDeclarationMarker = 347, - EndOfDeclarationMarker = 348, - SyntheticReferenceExpression = 349, - Count = 350, + PrivateIdentifierInInExpression = 231, + TemplateSpan = 232, + SemicolonClassElement = 233, + Block = 234, + EmptyStatement = 235, + VariableStatement = 236, + ExpressionStatement = 237, + IfStatement = 238, + DoStatement = 239, + WhileStatement = 240, + ForStatement = 241, + ForInStatement = 242, + ForOfStatement = 243, + ContinueStatement = 244, + BreakStatement = 245, + ReturnStatement = 246, + WithStatement = 247, + SwitchStatement = 248, + LabeledStatement = 249, + ThrowStatement = 250, + TryStatement = 251, + DebuggerStatement = 252, + VariableDeclaration = 253, + VariableDeclarationList = 254, + FunctionDeclaration = 255, + ClassDeclaration = 256, + InterfaceDeclaration = 257, + TypeAliasDeclaration = 258, + EnumDeclaration = 259, + ModuleDeclaration = 260, + ModuleBlock = 261, + CaseBlock = 262, + NamespaceExportDeclaration = 263, + ImportEqualsDeclaration = 264, + ImportDeclaration = 265, + ImportClause = 266, + NamespaceImport = 267, + NamedImports = 268, + ImportSpecifier = 269, + ExportAssignment = 270, + ExportDeclaration = 271, + NamedExports = 272, + NamespaceExport = 273, + ExportSpecifier = 274, + MissingDeclaration = 275, + ExternalModuleReference = 276, + JsxElement = 277, + JsxSelfClosingElement = 278, + JsxOpeningElement = 279, + JsxClosingElement = 280, + JsxFragment = 281, + JsxOpeningFragment = 282, + JsxClosingFragment = 283, + JsxAttribute = 284, + JsxAttributes = 285, + JsxSpreadAttribute = 286, + JsxExpression = 287, + CaseClause = 288, + DefaultClause = 289, + HeritageClause = 290, + CatchClause = 291, + PropertyAssignment = 292, + ShorthandPropertyAssignment = 293, + SpreadAssignment = 294, + EnumMember = 295, + UnparsedPrologue = 296, + UnparsedPrepend = 297, + UnparsedText = 298, + UnparsedInternalText = 299, + UnparsedSyntheticReference = 300, + SourceFile = 301, + Bundle = 302, + UnparsedSource = 303, + InputFiles = 304, + JSDocTypeExpression = 305, + JSDocNameReference = 306, + JSDocMemberName = 307, + JSDocAllType = 308, + JSDocUnknownType = 309, + JSDocNullableType = 310, + JSDocNonNullableType = 311, + JSDocOptionalType = 312, + JSDocFunctionType = 313, + JSDocVariadicType = 314, + JSDocNamepathType = 315, + JSDocComment = 316, + JSDocText = 317, + JSDocTypeLiteral = 318, + JSDocSignature = 319, + JSDocLink = 320, + JSDocLinkCode = 321, + JSDocLinkPlain = 322, + JSDocTag = 323, + JSDocAugmentsTag = 324, + JSDocImplementsTag = 325, + JSDocAuthorTag = 326, + JSDocDeprecatedTag = 327, + JSDocClassTag = 328, + JSDocPublicTag = 329, + JSDocPrivateTag = 330, + JSDocProtectedTag = 331, + JSDocReadonlyTag = 332, + JSDocOverrideTag = 333, + JSDocCallbackTag = 334, + JSDocEnumTag = 335, + JSDocParameterTag = 336, + JSDocReturnTag = 337, + JSDocThisTag = 338, + JSDocTypeTag = 339, + JSDocTemplateTag = 340, + JSDocTypedefTag = 341, + JSDocSeeTag = 342, + JSDocPropertyTag = 343, + SyntaxList = 344, + NotEmittedStatement = 345, + PartiallyEmittedExpression = 346, + CommaListExpression = 347, + MergeDeclarationMarker = 348, + EndOfDeclarationMarker = 349, + SyntheticReferenceExpression = 350, + Count = 351, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -480,13 +481,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 235, - LastStatement = 251, + FirstStatement = 236, + LastStatement = 252, FirstNode = 159, - FirstJSDocNode = 304, - LastJSDocNode = 342, - FirstJSDocTagNode = 322, - LastJSDocTagNode = 342, + FirstJSDocNode = 305, + LastJSDocNode = 343, + FirstJSDocTagNode = 323, + LastJSDocTagNode = 343, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -11288,4 +11289,4 @@ declare namespace ts { } export = ts; -export as namespace ts; +export as namespace ts; \ No newline at end of file diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8df8521309794..5b4f765e6b4d4 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -336,126 +336,127 @@ declare namespace ts { NonNullExpression = 228, MetaProperty = 229, SyntheticExpression = 230, - TemplateSpan = 231, - SemicolonClassElement = 232, - Block = 233, - EmptyStatement = 234, - VariableStatement = 235, - ExpressionStatement = 236, - IfStatement = 237, - DoStatement = 238, - WhileStatement = 239, - ForStatement = 240, - ForInStatement = 241, - ForOfStatement = 242, - ContinueStatement = 243, - BreakStatement = 244, - ReturnStatement = 245, - WithStatement = 246, - SwitchStatement = 247, - LabeledStatement = 248, - ThrowStatement = 249, - TryStatement = 250, - DebuggerStatement = 251, - VariableDeclaration = 252, - VariableDeclarationList = 253, - FunctionDeclaration = 254, - ClassDeclaration = 255, - InterfaceDeclaration = 256, - TypeAliasDeclaration = 257, - EnumDeclaration = 258, - ModuleDeclaration = 259, - ModuleBlock = 260, - CaseBlock = 261, - NamespaceExportDeclaration = 262, - ImportEqualsDeclaration = 263, - ImportDeclaration = 264, - ImportClause = 265, - NamespaceImport = 266, - NamedImports = 267, - ImportSpecifier = 268, - ExportAssignment = 269, - ExportDeclaration = 270, - NamedExports = 271, - NamespaceExport = 272, - ExportSpecifier = 273, - MissingDeclaration = 274, - ExternalModuleReference = 275, - JsxElement = 276, - JsxSelfClosingElement = 277, - JsxOpeningElement = 278, - JsxClosingElement = 279, - JsxFragment = 280, - JsxOpeningFragment = 281, - JsxClosingFragment = 282, - JsxAttribute = 283, - JsxAttributes = 284, - JsxSpreadAttribute = 285, - JsxExpression = 286, - CaseClause = 287, - DefaultClause = 288, - HeritageClause = 289, - CatchClause = 290, - PropertyAssignment = 291, - ShorthandPropertyAssignment = 292, - SpreadAssignment = 293, - EnumMember = 294, - UnparsedPrologue = 295, - UnparsedPrepend = 296, - UnparsedText = 297, - UnparsedInternalText = 298, - UnparsedSyntheticReference = 299, - SourceFile = 300, - Bundle = 301, - UnparsedSource = 302, - InputFiles = 303, - JSDocTypeExpression = 304, - JSDocNameReference = 305, - JSDocMemberName = 306, - JSDocAllType = 307, - JSDocUnknownType = 308, - JSDocNullableType = 309, - JSDocNonNullableType = 310, - JSDocOptionalType = 311, - JSDocFunctionType = 312, - JSDocVariadicType = 313, - JSDocNamepathType = 314, - JSDocComment = 315, - JSDocText = 316, - JSDocTypeLiteral = 317, - JSDocSignature = 318, - JSDocLink = 319, - JSDocLinkCode = 320, - JSDocLinkPlain = 321, - JSDocTag = 322, - JSDocAugmentsTag = 323, - JSDocImplementsTag = 324, - JSDocAuthorTag = 325, - JSDocDeprecatedTag = 326, - JSDocClassTag = 327, - JSDocPublicTag = 328, - JSDocPrivateTag = 329, - JSDocProtectedTag = 330, - JSDocReadonlyTag = 331, - JSDocOverrideTag = 332, - JSDocCallbackTag = 333, - JSDocEnumTag = 334, - JSDocParameterTag = 335, - JSDocReturnTag = 336, - JSDocThisTag = 337, - JSDocTypeTag = 338, - JSDocTemplateTag = 339, - JSDocTypedefTag = 340, - JSDocSeeTag = 341, - JSDocPropertyTag = 342, - SyntaxList = 343, - NotEmittedStatement = 344, - PartiallyEmittedExpression = 345, - CommaListExpression = 346, - MergeDeclarationMarker = 347, - EndOfDeclarationMarker = 348, - SyntheticReferenceExpression = 349, - Count = 350, + PrivateIdentifierInInExpression = 231, + TemplateSpan = 232, + SemicolonClassElement = 233, + Block = 234, + EmptyStatement = 235, + VariableStatement = 236, + ExpressionStatement = 237, + IfStatement = 238, + DoStatement = 239, + WhileStatement = 240, + ForStatement = 241, + ForInStatement = 242, + ForOfStatement = 243, + ContinueStatement = 244, + BreakStatement = 245, + ReturnStatement = 246, + WithStatement = 247, + SwitchStatement = 248, + LabeledStatement = 249, + ThrowStatement = 250, + TryStatement = 251, + DebuggerStatement = 252, + VariableDeclaration = 253, + VariableDeclarationList = 254, + FunctionDeclaration = 255, + ClassDeclaration = 256, + InterfaceDeclaration = 257, + TypeAliasDeclaration = 258, + EnumDeclaration = 259, + ModuleDeclaration = 260, + ModuleBlock = 261, + CaseBlock = 262, + NamespaceExportDeclaration = 263, + ImportEqualsDeclaration = 264, + ImportDeclaration = 265, + ImportClause = 266, + NamespaceImport = 267, + NamedImports = 268, + ImportSpecifier = 269, + ExportAssignment = 270, + ExportDeclaration = 271, + NamedExports = 272, + NamespaceExport = 273, + ExportSpecifier = 274, + MissingDeclaration = 275, + ExternalModuleReference = 276, + JsxElement = 277, + JsxSelfClosingElement = 278, + JsxOpeningElement = 279, + JsxClosingElement = 280, + JsxFragment = 281, + JsxOpeningFragment = 282, + JsxClosingFragment = 283, + JsxAttribute = 284, + JsxAttributes = 285, + JsxSpreadAttribute = 286, + JsxExpression = 287, + CaseClause = 288, + DefaultClause = 289, + HeritageClause = 290, + CatchClause = 291, + PropertyAssignment = 292, + ShorthandPropertyAssignment = 293, + SpreadAssignment = 294, + EnumMember = 295, + UnparsedPrologue = 296, + UnparsedPrepend = 297, + UnparsedText = 298, + UnparsedInternalText = 299, + UnparsedSyntheticReference = 300, + SourceFile = 301, + Bundle = 302, + UnparsedSource = 303, + InputFiles = 304, + JSDocTypeExpression = 305, + JSDocNameReference = 306, + JSDocMemberName = 307, + JSDocAllType = 308, + JSDocUnknownType = 309, + JSDocNullableType = 310, + JSDocNonNullableType = 311, + JSDocOptionalType = 312, + JSDocFunctionType = 313, + JSDocVariadicType = 314, + JSDocNamepathType = 315, + JSDocComment = 316, + JSDocText = 317, + JSDocTypeLiteral = 318, + JSDocSignature = 319, + JSDocLink = 320, + JSDocLinkCode = 321, + JSDocLinkPlain = 322, + JSDocTag = 323, + JSDocAugmentsTag = 324, + JSDocImplementsTag = 325, + JSDocAuthorTag = 326, + JSDocDeprecatedTag = 327, + JSDocClassTag = 328, + JSDocPublicTag = 329, + JSDocPrivateTag = 330, + JSDocProtectedTag = 331, + JSDocReadonlyTag = 332, + JSDocOverrideTag = 333, + JSDocCallbackTag = 334, + JSDocEnumTag = 335, + JSDocParameterTag = 336, + JSDocReturnTag = 337, + JSDocThisTag = 338, + JSDocTypeTag = 339, + JSDocTemplateTag = 340, + JSDocTypedefTag = 341, + JSDocSeeTag = 342, + JSDocPropertyTag = 343, + SyntaxList = 344, + NotEmittedStatement = 345, + PartiallyEmittedExpression = 346, + CommaListExpression = 347, + MergeDeclarationMarker = 348, + EndOfDeclarationMarker = 349, + SyntheticReferenceExpression = 350, + Count = 351, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -480,13 +481,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 235, - LastStatement = 251, + FirstStatement = 236, + LastStatement = 252, FirstNode = 159, - FirstJSDocNode = 304, - LastJSDocNode = 342, - FirstJSDocTagNode = 322, - LastJSDocTagNode = 342, + FirstJSDocNode = 305, + LastJSDocNode = 343, + FirstJSDocTagNode = 323, + LastJSDocTagNode = 343, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -7483,4 +7484,4 @@ declare namespace ts { const isIdentifierOrPrivateIdentifier: (node: Node) => node is MemberName; } -export = ts; +export = ts; \ No newline at end of file From 8292ee22d669b45e0f0c85a6193bcb30a3908ddf Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 15 Sep 2021 10:50:12 +0100 Subject: [PATCH 04/41] [squash] reduce if/else to ternary Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e0485b7cf4035..cef87a89fcbb8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23665,17 +23665,11 @@ namespace ts { return type; } const classSymbol = symbol.parent!; - const classType = getTypeOfSymbol(classSymbol) as InterfaceType; const classDecl = symbol.valueDeclaration; Debug.assert(classDecl, "should always have a declaration"); - let targetType: Type; - if (hasStaticModifier(classDecl)) { - targetType = classType; - } - else { - const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); - targetType = classInstanceType; - } + const targetType = hasStaticModifier(classDecl) + ? getTypeOfSymbol(classSymbol) as InterfaceType + : getDeclaredTypeOfSymbol(classSymbol); return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); } From 4723380a0be30f6ab3f1fd1a125ebba6777fa4b0 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 15 Sep 2021 11:12:57 +0100 Subject: [PATCH 05/41] [squash] drop 'originalName' and rename parameter instead Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cef87a89fcbb8..1ab9a436e57a9 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27882,24 +27882,27 @@ namespace ts { return getSpellingSuggestionForName(name, arrayFrom(getMembersOfSymbol(baseType.symbol).values()), SymbolFlags.ClassMember); } - function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { - const originalName = name; + function getSuggestedSymbolForNonexistentProperty(propertyName: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { let props = getPropertiesOfType(containingType); - if (typeof name !== "string") { - const parent = name.parent; + let name: string; + if (typeof propertyName === "string") { + name = propertyName; + } + else { + name = idText(propertyName); + const parent = propertyName.parent; if (isPropertyAccessExpression(parent)) { props = filter(props, prop => isValidPropertyAccessForCompletions(parent, containingType, prop)); } - name = idText(name); } const suggestion = getSpellingSuggestionForName(name, props, SymbolFlags.Value); if (suggestion) { return suggestion; } // If we have `#typo in expr` then we can still look up potential privateIdentifiers from the surrounding classes - if (typeof originalName !== "string" && isPrivateIdentifierInInExpression(originalName.parent)) { + if (typeof propertyName !== "string" && isPrivateIdentifierInInExpression(propertyName.parent)) { const privateIdentifiers: Symbol[] = []; - forEachEnclosingClass(originalName, (klass: ClassLikeDeclaration) => { + forEachEnclosingClass(propertyName, (klass: ClassLikeDeclaration) => { forEach(klass.members, member => { if (isPrivateIdentifierClassElementDeclaration(member)) { privateIdentifiers.push(member.symbol); From 9f1c1764bb7ccfe4a59c1a7a97b035c431509cc3 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 15 Sep 2021 11:59:15 +0100 Subject: [PATCH 06/41] [squash] extend spelling suggestion to all privateIdentifiers Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 4 ++-- ...codeFixSpellingPrivatePropertyNameOnAny.ts | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1ab9a436e57a9..052f5f2d8b287 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27899,8 +27899,8 @@ namespace ts { if (suggestion) { return suggestion; } - // If we have `#typo in expr` then we can still look up potential privateIdentifiers from the surrounding classes - if (typeof propertyName !== "string" && isPrivateIdentifierInInExpression(propertyName.parent)) { + + if (typeof propertyName !== "string" && isPrivateIdentifier(propertyName)) { const privateIdentifiers: Symbol[] = []; forEachEnclosingClass(propertyName, (klass: ClassLikeDeclaration) => { forEach(klass.members, member => { diff --git a/tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts b/tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts new file mode 100644 index 0000000000000..4bad070e5827a --- /dev/null +++ b/tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts @@ -0,0 +1,19 @@ +/// + +////class A { +//// #foo = 0; +//// method(v: any) { +//// [|v.#fo = 1;|] +//// } +////} + +verify.codeFixAvailable([ + { description: "Change spelling to '#foo'" }, + { description: "Remove unused declaration for: '#foo'" }, +]); + +verify.codeFix({ + index: 0, + description: "Change spelling to '#foo'", + newRangeContent: "v.#foo = 1;" +}); From 511ac22da7de24e23c549a22874c0d691ef37e04 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 16 Sep 2021 12:15:35 +0100 Subject: [PATCH 07/41] [squash] revert the added lexical spelling suggestions logic Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 30 ++++--------------- .../codeFixSpellingPrivateNameInIn.ts | 2 +- ...codeFixSpellingPrivatePropertyNameOnAny.ts | 19 ------------ 3 files changed, 6 insertions(+), 45 deletions(-) delete mode 100644 tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 052f5f2d8b287..785b34f25d9df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27882,36 +27882,16 @@ namespace ts { return getSpellingSuggestionForName(name, arrayFrom(getMembersOfSymbol(baseType.symbol).values()), SymbolFlags.ClassMember); } - function getSuggestedSymbolForNonexistentProperty(propertyName: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { + function getSuggestedSymbolForNonexistentProperty(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { let props = getPropertiesOfType(containingType); - let name: string; - if (typeof propertyName === "string") { - name = propertyName; - } - else { - name = idText(propertyName); - const parent = propertyName.parent; + if (typeof name !== "string") { + const parent = name.parent; if (isPropertyAccessExpression(parent)) { props = filter(props, prop => isValidPropertyAccessForCompletions(parent, containingType, prop)); } + name = idText(name); } - const suggestion = getSpellingSuggestionForName(name, props, SymbolFlags.Value); - if (suggestion) { - return suggestion; - } - - if (typeof propertyName !== "string" && isPrivateIdentifier(propertyName)) { - const privateIdentifiers: Symbol[] = []; - forEachEnclosingClass(propertyName, (klass: ClassLikeDeclaration) => { - forEach(klass.members, member => { - if (isPrivateIdentifierClassElementDeclaration(member)) { - privateIdentifiers.push(member.symbol); - } - }); - }); - return getSpellingSuggestionForName(name, privateIdentifiers, SymbolFlags.Value); - } - return undefined; + return getSpellingSuggestionForName(name, props, SymbolFlags.Value); } function getSuggestedSymbolForNonexistentJSXAttribute(name: Identifier | PrivateIdentifier | string, containingType: Type): Symbol | undefined { diff --git a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts index 3d3e48ede332d..daba84dde2012 100644 --- a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts +++ b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts @@ -2,7 +2,7 @@ ////class A { //// #foo: number; -//// static isA(v: any) { +//// static isA(v: A) { //// [|return #fo in v;|] //// } ////} diff --git a/tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts b/tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts deleted file mode 100644 index 4bad070e5827a..0000000000000 --- a/tests/cases/fourslash/codeFixSpellingPrivatePropertyNameOnAny.ts +++ /dev/null @@ -1,19 +0,0 @@ -/// - -////class A { -//// #foo = 0; -//// method(v: any) { -//// [|v.#fo = 1;|] -//// } -////} - -verify.codeFixAvailable([ - { description: "Change spelling to '#foo'" }, - { description: "Remove unused declaration for: '#foo'" }, -]); - -verify.codeFix({ - index: 0, - description: "Change spelling to '#foo'", - newRangeContent: "v.#foo = 1;" -}); From 337f7e8885aa162ea365f4fc3946bf053cdc2074 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 16 Sep 2021 12:22:34 +0100 Subject: [PATCH 08/41] [squash] update baseline Signed-off-by: Ashley Claymore --- .../baselines/reference/privateNameInInExpression.errors.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 66152059feb4e..96f54bf1f3212 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2552: Cannot find name '#fiel'. Did you mean '#field'? +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2304: Cannot find name '#fiel'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS2304: Cannot find name '#field'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2304: Cannot find name '#field'. @@ -36,7 +36,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #fiel in v; // Bad - typo in privateID ~~~~~ -!!! error TS2552: Cannot find name '#fiel'. Did you mean '#field'? +!!! error TS2304: Cannot find name '#fiel'. const c = (#field) in v; // Bad - privateID is not an expression on its own ~~~~~~ From d059c1594f81bf11ea45d99d3e01ed4ae4fec34b Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 16 Sep 2021 12:23:16 +0100 Subject: [PATCH 09/41] [squash] inline variable as per PR suggestion Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 785b34f25d9df..5dd7e762faa6d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23665,9 +23665,7 @@ namespace ts { return type; } const classSymbol = symbol.parent!; - const classDecl = symbol.valueDeclaration; - Debug.assert(classDecl, "should always have a declaration"); - const targetType = hasStaticModifier(classDecl) + const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration")) ? getTypeOfSymbol(classSymbol) as InterfaceType : getDeclaredTypeOfSymbol(classSymbol); return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); From 79eeebe93aa7bfb21ffced281f235a40a6a0f48f Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 16 Sep 2021 14:30:43 +0100 Subject: [PATCH 10/41] [squash] test targets both esnext and es2020 as per PR comment Signed-off-by: Ashley Claymore --- ...essionTransform(target=es2020).errors.txt} | 0 ...InInExpressionTransform(target=es2020).js} | 0 ...xpressionTransform(target=es2020).symbols} | 0 ...nExpressionTransform(target=es2020).types} | 0 ...ressionTransform(target=esnext).errors.txt | 70 ++++++++++ ...eInInExpressionTransform(target=esnext).js | 87 ++++++++++++ ...ExpressionTransform(target=esnext).symbols | 102 ++++++++++++++ ...InExpressionTransform(target=esnext).types | 130 ++++++++++++++++++ .../privateNameInInExpressionTransform.ts | 2 +- 9 files changed, 390 insertions(+), 1 deletion(-) rename tests/baselines/reference/{privateNameInInExpressionTransform.errors.txt => privateNameInInExpressionTransform(target=es2020).errors.txt} (100%) rename tests/baselines/reference/{privateNameInInExpressionTransform.js => privateNameInInExpressionTransform(target=es2020).js} (100%) rename tests/baselines/reference/{privateNameInInExpressionTransform.symbols => privateNameInInExpressionTransform(target=es2020).symbols} (100%) rename tests/baselines/reference/{privateNameInInExpressionTransform.types => privateNameInInExpressionTransform(target=es2020).types} (100%) create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols create mode 100644 tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt similarity index 100% rename from tests/baselines/reference/privateNameInInExpressionTransform.errors.txt rename to tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.js b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js similarity index 100% rename from tests/baselines/reference/privateNameInInExpressionTransform.js rename to tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols similarity index 100% rename from tests/baselines/reference/privateNameInInExpressionTransform.symbols rename to tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols diff --git a/tests/baselines/reference/privateNameInInExpressionTransform.types b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types similarity index 100% rename from tests/baselines/reference/privateNameInInExpressionTransform.types rename to tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt new file mode 100644 index 0000000000000..efcb361b3c4d7 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt @@ -0,0 +1,70 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(4,26): error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. + + +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (7 errors) ==== + class Foo { + #field = 1; + #method() {} + static #staticField= 2; + ~ +!!! error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag. + static #staticMethod() {} + + check(v: any) { + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor + } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + ~~~~~~~~~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + ~~~~~~ +!!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + ~~~~~~~~~~~ +!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + } + invalidLHS(v: any) { + 'prop' in v = 10; + ~ +!!! error TS1005: ';' expected. + #field in v = 10; + ~ +!!! error TS1005: ';' expected. + } + } + + class Bar { + #field = 1; + check(v: any) { + #field in v; // expect Bar's 'field' WeakMap + } + } + + function syntaxError(v: Foo) { + return #field in v; // expect `return in v` so runtime will have a syntax error + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. + } + + export { } + \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js new file mode 100644 index 0000000000000..8772d5e3933a3 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js @@ -0,0 +1,87 @@ +//// [privateNameInInExpressionTransform.ts] +class Foo { + #field = 1; + #method() {} + static #staticField= 2; + static #staticMethod() {} + + check(v: any) { + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor + } + precedence(v: any) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + } + invalidLHS(v: any) { + 'prop' in v = 10; + #field in v = 10; + } +} + +class Bar { + #field = 1; + check(v: any) { + #field in v; // expect Bar's 'field' WeakMap + } +} + +function syntaxError(v: Foo) { + return #field in v; // expect `return in v` so runtime will have a syntax error +} + +export { } + + +//// [privateNameInInExpressionTransform.js] +class Foo { + #field = 1; + #method() { } + static #staticField = 2; + static #staticMethod() { } + check(v) { + #field in v; // expect Foo's 'field' WeakMap + #method in v; // expect Foo's 'method' WeakSet + #staticField in v; // expect Foo's constructor + #staticMethod in v; // expect Foo's constructor + } + precedence(v) { + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + } + invalidLHS(v) { + 'prop' in v; + 10; + #field in v; + 10; + } +} +class Bar { + #field = 1; + check(v) { + #field in v; // expect Bar's 'field' WeakMap + } +} +function syntaxError(v) { + return #field in v; // expect `return in v` so runtime will have a syntax error +} +export {}; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols new file mode 100644 index 0000000000000..c0cc9938cd8d2 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols @@ -0,0 +1,102 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === +class Foo { +>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) + + #field = 1; +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) + + #method() {} +>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15)) + + static #staticField= 2; +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16)) + + static #staticMethod() {} +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27)) + + check(v: any) { +>check : Symbol(Foo.check, Decl(privateNameInInExpressionTransform.ts, 4, 29)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #field in v; // expect Foo's 'field' WeakMap +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #method in v; // expect Foo's 'method' WeakSet +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #staticField in v; // expect Foo's constructor +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + + #staticMethod in v; // expect Foo's constructor +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) + } + precedence(v: any) { +>precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 11, 5)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) + } + invalidLHS(v: any) { +>invalidLHS : Symbol(Foo.invalidLHS, Decl(privateNameInInExpressionTransform.ts, 26, 5)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) + + 'prop' in v = 10; +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) + + #field in v = 10; +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) + } +} + +class Bar { +>Bar : Symbol(Bar, Decl(privateNameInInExpressionTransform.ts, 31, 1)) + + #field = 1; +>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11)) + + check(v: any) { +>check : Symbol(Bar.check, Decl(privateNameInInExpressionTransform.ts, 34, 15)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) + + #field in v; // expect Bar's 'field' WeakMap +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) + } +} + +function syntaxError(v: Foo) { +>syntaxError : Symbol(syntaxError, Decl(privateNameInInExpressionTransform.ts, 38, 1)) +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21)) +>Foo : Symbol(Foo, Decl(privateNameInInExpressionTransform.ts, 0, 0)) + + return #field in v; // expect `return in v` so runtime will have a syntax error +>v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 40, 21)) +} + +export { } + diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types new file mode 100644 index 0000000000000..5c68549be8fd4 --- /dev/null +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types @@ -0,0 +1,130 @@ +=== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts === +class Foo { +>Foo : Foo + + #field = 1; +>#field : number +>1 : 1 + + #method() {} +>#method : () => void + + static #staticField= 2; +>#staticField : number +>2 : 2 + + static #staticMethod() {} +>#staticMethod : () => void + + check(v: any) { +>check : (v: any) => void +>v : any + + #field in v; // expect Foo's 'field' WeakMap +>#field in v : boolean +>v : any + + #method in v; // expect Foo's 'method' WeakSet +>#method in v : boolean +>v : any + + #staticField in v; // expect Foo's constructor +>#staticField in v : boolean +>v : any + + #staticMethod in v; // expect Foo's constructor +>#staticMethod in v : boolean +>v : any + } + precedence(v: any) { +>precedence : (v: any) => void +>v : any + + // '==' has lower precedence than 'in' + // '<' has same precedence than 'in' + // '<<' has higher precedence than 'in' + + v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) +>v == #field in v == v : boolean +>v == #field in v : boolean +>v : any +>#field in v : boolean +>v : any +>v : any + + v << #field in v << v; // Good precedence: (v << (#field in (v << v))) +>v << #field in v << v : number +>v : any +>#field in v << v : boolean +>v << v : number +>v : any +>v : any + + v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) +>v << #field in v == v : boolean +>v << #field in v : number +>v : any +>#field in v : boolean +>v : any +>v : any + + v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) +>v == #field in v < v : boolean +>v : any +>#field in v < v : boolean +>#field in v : boolean +>v : any +>v : any + + #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) +>#field in v && #field in v : boolean +>#field in v : boolean +>v : any +>#field in v : boolean +>v : Foo + } + invalidLHS(v: any) { +>invalidLHS : (v: any) => void +>v : any + + 'prop' in v = 10; +>'prop' in v : boolean +>'prop' : "prop" +>v : any +>10 : 10 + + #field in v = 10; +>#field in v : boolean +>v : any +>10 : 10 + } +} + +class Bar { +>Bar : Bar + + #field = 1; +>#field : number +>1 : 1 + + check(v: any) { +>check : (v: any) => void +>v : any + + #field in v; // expect Bar's 'field' WeakMap +>#field in v : boolean +>v : any + } +} + +function syntaxError(v: Foo) { +>syntaxError : (v: Foo) => any +>v : Foo + + return #field in v; // expect `return in v` so runtime will have a syntax error +>#field in v : any +>v : Foo +} + +export { } + diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index 2518012b3e1b0..5c07e0ad87f67 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -1,4 +1,4 @@ -// @target: es2020 +// @target: esnext, es2020 class Foo { #field = 1; From 125df938c458daa066e61e839136509db0f87542 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 17 Sep 2021 13:55:06 +0100 Subject: [PATCH 11/41] switch to using a binary expression Signed-off-by: Ashley Claymore --- src/compiler/binder.ts | 4 +- src/compiler/checker.ts | 26 +- src/compiler/emitter.ts | 22 +- src/compiler/factory/nodeFactory.ts | 33 +-- src/compiler/factory/nodeTests.ts | 9 - src/compiler/factory/parenthesizerRules.ts | 7 +- src/compiler/parser.ts | 13 +- src/compiler/transformers/classFields.ts | 11 +- src/compiler/types.ts | 11 - src/compiler/utilities.ts | 2 - src/compiler/utilitiesPublic.ts | 1 - src/compiler/visitorPublic.ts | 7 - src/services/codefixes/fixSpelling.ts | 4 +- .../reference/api/tsserverlibrary.d.ts | 263 +++++++++--------- tests/baselines/reference/api/typescript.d.ts | 263 +++++++++--------- .../reference/privateNameInInExpression.js | 2 +- 16 files changed, 293 insertions(+), 385 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index f7ccfe4efe344..1f1efb43c00f8 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -881,8 +881,6 @@ namespace ts { return (expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken && isNarrowingExpression((expr as PrefixUnaryExpression).operand); case SyntaxKind.TypeOfExpression: return isNarrowingExpression((expr as TypeOfExpression).expression); - case SyntaxKind.PrivateIdentifierInInExpression: - return isNarrowingExpression((expr as PrivateIdentifierInInExpression).expression); } return false; } @@ -919,7 +917,7 @@ namespace ts { } function isNarrowableInOperands(left: Expression, right: Expression) { - return isStringLiteralLike(left) && isNarrowingExpression(right); + return (isStringLiteralLike(left) || isPrivateIdentifier(left)) && isNarrowingExpression(right); } function isNarrowingBinaryExpression(expr: BinaryExpression) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5dd7e762faa6d..86484722e432b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23635,6 +23635,9 @@ namespace ts { return narrowByInKeyword(type, expr.left, assumeTrue); } } + else if (isPrivateIdentifier(expr.left)) { + return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue); + } break; case SyntaxKind.CommaToken: return narrowType(type, expr.right, assumeTrue); @@ -23653,13 +23656,13 @@ namespace ts { return type; } - function narrowTypeByPrivateIdentifierInInExpression(type: Type, expr: PrivateIdentifierInInExpression, assumeTrue: boolean): Type { - const target = getReferenceCandidate(expr.expression); + function narrowTypeByPrivateIdentifierInInExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type { + const target = getReferenceCandidate(expr.right); if (!isMatchingReference(reference, target)) { return type; } - const privateId = expr.name; + const privateId = expr.left as Node as PrivateIdentifier; const symbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); if (symbol === undefined) { return type; @@ -24119,8 +24122,6 @@ namespace ts { return narrowType(type, (expr as ParenthesizedExpression | NonNullExpression).expression, assumeTrue); case SyntaxKind.BinaryExpression: return narrowTypeByBinaryExpression(type, expr as BinaryExpression, assumeTrue); - case SyntaxKind.PrivateIdentifierInInExpression: - return narrowTypeByPrivateIdentifierInInExpression(type, expr as PrivateIdentifierInInExpression, assumeTrue); case SyntaxKind.PrefixUnaryExpression: if ((expr as PrefixUnaryExpression).operator === SyntaxKind.ExclamationToken) { return narrowType(type, (expr as PrefixUnaryExpression).operand, !assumeTrue); @@ -31938,9 +31939,9 @@ namespace ts { return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } - function checkPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, checkMode?: CheckMode) { - const privateId = node.name; - const exp = node.expression; + function checkPrivateIdentifierInInExpression(node: BinaryExpression, checkMode?: CheckMode) { + const privateId = node.left as Node as PrivateIdentifier; + const exp = node.right; let rightType = checkExpression(exp, checkMode); const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); @@ -32023,6 +32024,11 @@ namespace ts { setLastResult(state, checkDestructuringAssignment(node.left, checkExpression(node.right, checkMode), checkMode, node.right.kind === SyntaxKind.ThisKeyword)); return state; } + else if (node.operatorToken.kind === SyntaxKind.InKeyword && isPrivateIdentifier(node.left)) { + state.skip = true; + setLastResult(state, checkPrivateIdentifierInInExpression(node, checkMode)); + return state; + } return state; } @@ -33162,8 +33168,6 @@ namespace ts { return checkPostfixUnaryExpression(node as PostfixUnaryExpression); case SyntaxKind.BinaryExpression: return checkBinaryExpression(node as BinaryExpression, checkMode); - case SyntaxKind.PrivateIdentifierInInExpression: - return checkPrivateIdentifierInInExpression(node as PrivateIdentifierInInExpression, checkMode); case SyntaxKind.ConditionalExpression: return checkConditionalExpression(node as ConditionalExpression, checkMode); case SyntaxKind.SpreadElement: @@ -39663,7 +39667,7 @@ namespace ts { return resolveEntityName(name as Identifier, /*meaning*/ SymbolFlags.FunctionScopedVariable); } - if (isPrivateIdentifier(name) && isPrivateIdentifierInInExpression(name.parent)) { + if (isPrivateIdentifier(name) && isBinaryExpression(name.parent)) { const links = getNodeLinks(name.parent); if (links.resolvedSymbol) { return links.resolvedSymbol; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index d546be6f69e8a..cf81e499a56a2 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1680,6 +1680,8 @@ namespace ts { // Identifiers case SyntaxKind.Identifier: return emitIdentifier(node as Identifier); + case SyntaxKind.PrivateIdentifier: + return emitPrivateIdentifier(node as PrivateIdentifier); // Expressions case SyntaxKind.ArrayLiteralExpression: @@ -1718,8 +1720,6 @@ namespace ts { return emitPostfixUnaryExpression(node as PostfixUnaryExpression); case SyntaxKind.BinaryExpression: return emitBinaryExpression(node as BinaryExpression); - case SyntaxKind.PrivateIdentifierInInExpression: - return emitPrivateIdentifierInInExpression(node as PrivateIdentifierInInExpression); case SyntaxKind.ConditionalExpression: return emitConditionalExpression(node as ConditionalExpression); case SyntaxKind.TemplateExpression: @@ -2721,24 +2721,6 @@ namespace ts { } } - function emitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { - const linesBeforeIn = getLinesBetweenNodes(node, node.name, node.inToken); - const linesAfterIn = getLinesBetweenNodes(node, node.inToken, node.expression); - - emitLeadingCommentsOfPosition(node.name.pos); - emitPrivateIdentifier(node.name); - emitTrailingCommentsOfPosition(node.name.end); - - writeLinesAndIndent(linesBeforeIn, /*writeSpaceIfNotIndenting*/ true); - emitLeadingCommentsOfPosition(node.inToken.pos); - writeTokenNode(node.inToken, writeKeyword); - emitTrailingCommentsOfPosition(node.inToken.end, /*prefixSpace*/ true); - writeLinesAndIndent(linesAfterIn, /*writeSpaceIfNotIndenting*/ true); - - emit(node.expression); - decreaseIndentIf(linesBeforeIn, linesAfterIn); - } - function emitConditionalExpression(node: ConditionalExpression) { const linesBeforeQuestion = getLinesBetweenNodes(node, node.condition, node.questionToken); const linesAfterQuestion = getLinesBetweenNodes(node, node.questionToken, node.whenTrue); diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index ee6632ec1a0cd..9ec2605302a34 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -445,8 +445,6 @@ namespace ts { createMergeDeclarationMarker, createSyntheticReferenceExpression, updateSyntheticReferenceExpression, - createPrivateIdentifierInInExpression, - updatePrivateIdentifierInInExpression, cloneNode, // Lazily load factory methods for common operator factories and utilities @@ -2788,6 +2786,9 @@ namespace ts { else if (isLogicalOrCoalescingAssignmentOperator(operatorKind)) { node.transformFlags |= TransformFlags.ContainsES2021; } + else if (operatorKind === SyntaxKind.InKeyword && isPrivateIdentifier(left)) { + node.transformFlags |= TransformFlags.ContainsESNext; + } return node; } @@ -3125,34 +3126,6 @@ namespace ts { : node; } - // @api - function createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression) { - const node = createBaseExpression(SyntaxKind.PrivateIdentifierInInExpression); - node.name = name; - node.inToken = inToken; - node.expression = expression; - node.transformFlags |= - propagateChildFlags(node.name) | - propagateChildFlags(node.inToken) | - propagateChildFlags(node.expression) | - TransformFlags.ContainsESNext; - return node; - } - - // @api - function updatePrivateIdentifierInInExpression( - node: PrivateIdentifierInInExpression, - name: PrivateIdentifier, - inToken: Token, - expression: Expression - ): PrivateIdentifierInInExpression { - return node.name !== name - || node.inToken !== inToken - || node.expression !== expression - ? update(createPrivateIdentifierInInExpression(name, inToken, expression), node) - : node; - } - // // Misc // diff --git a/src/compiler/factory/nodeTests.ts b/src/compiler/factory/nodeTests.ts index bc421a09ad737..270fded5c920b 100644 --- a/src/compiler/factory/nodeTests.ts +++ b/src/compiler/factory/nodeTests.ts @@ -87,11 +87,6 @@ namespace ts { return node.kind === SyntaxKind.EqualsGreaterThanToken; } - /*@internal*/ - export function isInKeyword(node: Node): node is InKeyword { - return node.kind === SyntaxKind.InKeyword; - } - // Identifiers export function isIdentifier(node: Node): node is Identifier { @@ -453,10 +448,6 @@ namespace ts { return node.kind === SyntaxKind.CommaListExpression; } - export function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression { - return node.kind === SyntaxKind.PrivateIdentifierInInExpression; - } - // Misc export function isTemplateSpan(node: Node): node is TemplateSpan { diff --git a/src/compiler/factory/parenthesizerRules.ts b/src/compiler/factory/parenthesizerRules.ts index 782a0110aba33..dd4f63881aa47 100644 --- a/src/compiler/factory/parenthesizerRules.ts +++ b/src/compiler/factory/parenthesizerRules.ts @@ -81,6 +81,11 @@ namespace ts { const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator); const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator); const emittedOperand = skipPartiallyEmittedExpressions(operand); + // If a privateIdentifier is on the left side, this is not a real expression and + // should not become parenthesized + if (isLeftSideOfBinary && isPrivateIdentifier(operand)) { + return false; + } if (!isLeftSideOfBinary && operand.kind === SyntaxKind.ArrowFunction && binaryOperatorPrecedence > OperatorPrecedence.Assignment) { // We need to parenthesize arrow functions on the right side to avoid it being // parsed as parenthesized expression: `a && (() => {})` @@ -452,4 +457,4 @@ namespace ts { parenthesizeConstituentTypesOfUnionOrIntersectionType: nodes => cast(nodes, isNodeArray), parenthesizeTypeArguments: nodes => nodes && cast(nodes, isNodeArray), }; -} \ No newline at end of file +} diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b9ba406b22e5c..4581f5ac69448 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -446,10 +446,6 @@ namespace ts { return visitNodes(cbNode, cbNodes, node.decorators); case SyntaxKind.CommaListExpression: return visitNodes(cbNode, cbNodes, (node as CommaListExpression).elements); - case SyntaxKind.PrivateIdentifierInInExpression: - return visitNode(cbNode, (node as PrivateIdentifierInInExpression).name) || - visitNode(cbNode, (node as PrivateIdentifierInInExpression).inToken) || - visitNode(cbNode, (node as PrivateIdentifierInInExpression).expression); case SyntaxKind.JsxElement: return visitNode(cbNode, (node as JsxElement).openingElement) || @@ -4472,7 +4468,7 @@ namespace ts { const inToken = parseTokenNode>(); const exp = parseBinaryExpressionOrHigher(OperatorPrecedence.Relational); - return finishNode(factory.createPrivateIdentifierInInExpression(id, inToken, exp), pos); + return finishNode(factory.createBinaryExpression(id as Node as Expression, inToken, exp), pos); } function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { @@ -4482,9 +4478,10 @@ namespace ts { const pos = getNodePos(); const tryPrivateIdentifierInIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext() && lookAhead(nextTokenIsInKeyword); - const leftOperand = tryPrivateIdentifierInIn - ? parsePrivateIdentifierInInExpression(pos) - : parseUnaryExpressionOrHigher(); + const leftOperand = + tryPrivateIdentifierInIn + ? parsePrivateIdentifierInInExpression(pos) : + parseUnaryExpressionOrHigher(); return parseBinaryExpressionRest(precedence, leftOperand, pos); } diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 942b264311f71..20b83703f36e5 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -207,8 +207,6 @@ namespace ts { return visitPreOrPostfixUnaryExpression(node as PrefixUnaryExpression | PostfixUnaryExpression, valueIsDiscarded); case SyntaxKind.BinaryExpression: return visitBinaryExpression(node as BinaryExpression, valueIsDiscarded); - case SyntaxKind.PrivateIdentifierInInExpression: - return visitPrivateIdentifierInInExpression(node as PrivateIdentifierInInExpression); case SyntaxKind.CallExpression: return visitCallExpression(node as CallExpression); case SyntaxKind.TaggedTemplateExpression: @@ -280,13 +278,13 @@ namespace ts { /** * Visits `#id in expr` */ - function visitPrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression) { + function visitPrivateIdentifierInInExpression(node: BinaryExpression) { if (!shouldTransformPrivateElementsOrClassStaticBlocks) { return node; } - const info = accessPrivateIdentifier(node.name); + const info = accessPrivateIdentifier(node.left as Node as PrivateIdentifier); if (info) { - const receiver = visitNode(node.expression, visitor, isExpression); + const receiver = visitNode(node.right, visitor, isExpression); return setOriginalNode( context.getEmitHelperFactory().createClassPrivateFieldInHelper(receiver, info.brandCheckIdentifier), @@ -850,6 +848,9 @@ namespace ts { } } } + if (node.operatorToken.kind === SyntaxKind.InKeyword && isPrivateIdentifier(node.left)) { + return visitPrivateIdentifierInInExpression(node); + } return visitEachChild(node, visitor, context); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 2ceba09bd8ee0..8db830a649428 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -272,7 +272,6 @@ namespace ts { NonNullExpression, MetaProperty, SyntheticExpression, - PrivateIdentifierInInExpression, // Misc TemplateSpan, @@ -1041,7 +1040,6 @@ namespace ts { export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; - export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; @@ -2345,13 +2343,6 @@ namespace ts { // see: https://tc39.github.io/ecma262/#prod-SuperProperty export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; - export interface PrivateIdentifierInInExpression extends Expression { - readonly kind: SyntaxKind.PrivateIdentifierInInExpression; - readonly name: PrivateIdentifier; - readonly inToken: Token; - readonly expression: Expression; - } - export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -7224,8 +7215,6 @@ namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; - createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; - updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; // // Misc diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 85d5a54223537..52ed8f1d77c40 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1921,7 +1921,6 @@ namespace ts { case SyntaxKind.YieldExpression: case SyntaxKind.AwaitExpression: case SyntaxKind.MetaProperty: - case SyntaxKind.PrivateIdentifierInInExpression: return true; case SyntaxKind.QualifiedName: while (node.parent.kind === SyntaxKind.QualifiedName) { @@ -3646,7 +3645,6 @@ namespace ts { return OperatorPrecedence.Member; case SyntaxKind.AsExpression: - case SyntaxKind.PrivateIdentifierInInExpression: return OperatorPrecedence.Relational; case SyntaxKind.ThisKeyword: diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 92c9cbe6e5bdb..b9236521cba67 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1585,7 +1585,6 @@ namespace ts { case SyntaxKind.OmittedExpression: case SyntaxKind.CommaListExpression: case SyntaxKind.PartiallyEmittedExpression: - case SyntaxKind.PrivateIdentifierInInExpression: return true; default: return isUnaryExpressionKind(kind); diff --git a/src/compiler/visitorPublic.ts b/src/compiler/visitorPublic.ts index 51e4175a921ad..94ab1afbb8aa0 100644 --- a/src/compiler/visitorPublic.ts +++ b/src/compiler/visitorPublic.ts @@ -802,13 +802,6 @@ namespace ts { nodeVisitor(node.operatorToken, tokenVisitor, isBinaryOperatorToken), nodeVisitor(node.right, visitor, isExpression)); - case SyntaxKind.PrivateIdentifierInInExpression: - Debug.type(node); - return factory.updatePrivateIdentifierInInExpression(node, - nodeVisitor((node).name, visitor, isMemberName), - nodeVisitor((node).inToken, tokenVisitor, isToken), - nodeVisitor((node).expression, visitor, isExpression)); - case SyntaxKind.ConditionalExpression: Debug.type(node); return factory.updateConditionalExpression(node, diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 5036745b6826b..2f928df86e50f 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -56,8 +56,8 @@ namespace ts.codefix { } suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType); } - else if (isPrivateIdentifierInInExpression(parent) && parent.name === node) { - const receiverType = checker.getTypeAtLocation(parent.expression); + else if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.InKeyword && isPrivateIdentifier(parent.left) && parent.left === node) { + const receiverType = checker.getTypeAtLocation(parent.right); Debug.assert(isPrivateIdentifier(node), "Expected a privateIdentifier for spelling (in)"); suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, receiverType); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index aad84266505b8..8b537d952f339 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -336,127 +336,126 @@ declare namespace ts { NonNullExpression = 228, MetaProperty = 229, SyntheticExpression = 230, - PrivateIdentifierInInExpression = 231, - TemplateSpan = 232, - SemicolonClassElement = 233, - Block = 234, - EmptyStatement = 235, - VariableStatement = 236, - ExpressionStatement = 237, - IfStatement = 238, - DoStatement = 239, - WhileStatement = 240, - ForStatement = 241, - ForInStatement = 242, - ForOfStatement = 243, - ContinueStatement = 244, - BreakStatement = 245, - ReturnStatement = 246, - WithStatement = 247, - SwitchStatement = 248, - LabeledStatement = 249, - ThrowStatement = 250, - TryStatement = 251, - DebuggerStatement = 252, - VariableDeclaration = 253, - VariableDeclarationList = 254, - FunctionDeclaration = 255, - ClassDeclaration = 256, - InterfaceDeclaration = 257, - TypeAliasDeclaration = 258, - EnumDeclaration = 259, - ModuleDeclaration = 260, - ModuleBlock = 261, - CaseBlock = 262, - NamespaceExportDeclaration = 263, - ImportEqualsDeclaration = 264, - ImportDeclaration = 265, - ImportClause = 266, - NamespaceImport = 267, - NamedImports = 268, - ImportSpecifier = 269, - ExportAssignment = 270, - ExportDeclaration = 271, - NamedExports = 272, - NamespaceExport = 273, - ExportSpecifier = 274, - MissingDeclaration = 275, - ExternalModuleReference = 276, - JsxElement = 277, - JsxSelfClosingElement = 278, - JsxOpeningElement = 279, - JsxClosingElement = 280, - JsxFragment = 281, - JsxOpeningFragment = 282, - JsxClosingFragment = 283, - JsxAttribute = 284, - JsxAttributes = 285, - JsxSpreadAttribute = 286, - JsxExpression = 287, - CaseClause = 288, - DefaultClause = 289, - HeritageClause = 290, - CatchClause = 291, - PropertyAssignment = 292, - ShorthandPropertyAssignment = 293, - SpreadAssignment = 294, - EnumMember = 295, - UnparsedPrologue = 296, - UnparsedPrepend = 297, - UnparsedText = 298, - UnparsedInternalText = 299, - UnparsedSyntheticReference = 300, - SourceFile = 301, - Bundle = 302, - UnparsedSource = 303, - InputFiles = 304, - JSDocTypeExpression = 305, - JSDocNameReference = 306, - JSDocMemberName = 307, - JSDocAllType = 308, - JSDocUnknownType = 309, - JSDocNullableType = 310, - JSDocNonNullableType = 311, - JSDocOptionalType = 312, - JSDocFunctionType = 313, - JSDocVariadicType = 314, - JSDocNamepathType = 315, - JSDocComment = 316, - JSDocText = 317, - JSDocTypeLiteral = 318, - JSDocSignature = 319, - JSDocLink = 320, - JSDocLinkCode = 321, - JSDocLinkPlain = 322, - JSDocTag = 323, - JSDocAugmentsTag = 324, - JSDocImplementsTag = 325, - JSDocAuthorTag = 326, - JSDocDeprecatedTag = 327, - JSDocClassTag = 328, - JSDocPublicTag = 329, - JSDocPrivateTag = 330, - JSDocProtectedTag = 331, - JSDocReadonlyTag = 332, - JSDocOverrideTag = 333, - JSDocCallbackTag = 334, - JSDocEnumTag = 335, - JSDocParameterTag = 336, - JSDocReturnTag = 337, - JSDocThisTag = 338, - JSDocTypeTag = 339, - JSDocTemplateTag = 340, - JSDocTypedefTag = 341, - JSDocSeeTag = 342, - JSDocPropertyTag = 343, - SyntaxList = 344, - NotEmittedStatement = 345, - PartiallyEmittedExpression = 346, - CommaListExpression = 347, - MergeDeclarationMarker = 348, - EndOfDeclarationMarker = 349, - SyntheticReferenceExpression = 350, - Count = 351, + TemplateSpan = 231, + SemicolonClassElement = 232, + Block = 233, + EmptyStatement = 234, + VariableStatement = 235, + ExpressionStatement = 236, + IfStatement = 237, + DoStatement = 238, + WhileStatement = 239, + ForStatement = 240, + ForInStatement = 241, + ForOfStatement = 242, + ContinueStatement = 243, + BreakStatement = 244, + ReturnStatement = 245, + WithStatement = 246, + SwitchStatement = 247, + LabeledStatement = 248, + ThrowStatement = 249, + TryStatement = 250, + DebuggerStatement = 251, + VariableDeclaration = 252, + VariableDeclarationList = 253, + FunctionDeclaration = 254, + ClassDeclaration = 255, + InterfaceDeclaration = 256, + TypeAliasDeclaration = 257, + EnumDeclaration = 258, + ModuleDeclaration = 259, + ModuleBlock = 260, + CaseBlock = 261, + NamespaceExportDeclaration = 262, + ImportEqualsDeclaration = 263, + ImportDeclaration = 264, + ImportClause = 265, + NamespaceImport = 266, + NamedImports = 267, + ImportSpecifier = 268, + ExportAssignment = 269, + ExportDeclaration = 270, + NamedExports = 271, + NamespaceExport = 272, + ExportSpecifier = 273, + MissingDeclaration = 274, + ExternalModuleReference = 275, + JsxElement = 276, + JsxSelfClosingElement = 277, + JsxOpeningElement = 278, + JsxClosingElement = 279, + JsxFragment = 280, + JsxOpeningFragment = 281, + JsxClosingFragment = 282, + JsxAttribute = 283, + JsxAttributes = 284, + JsxSpreadAttribute = 285, + JsxExpression = 286, + CaseClause = 287, + DefaultClause = 288, + HeritageClause = 289, + CatchClause = 290, + PropertyAssignment = 291, + ShorthandPropertyAssignment = 292, + SpreadAssignment = 293, + EnumMember = 294, + UnparsedPrologue = 295, + UnparsedPrepend = 296, + UnparsedText = 297, + UnparsedInternalText = 298, + UnparsedSyntheticReference = 299, + SourceFile = 300, + Bundle = 301, + UnparsedSource = 302, + InputFiles = 303, + JSDocTypeExpression = 304, + JSDocNameReference = 305, + JSDocMemberName = 306, + JSDocAllType = 307, + JSDocUnknownType = 308, + JSDocNullableType = 309, + JSDocNonNullableType = 310, + JSDocOptionalType = 311, + JSDocFunctionType = 312, + JSDocVariadicType = 313, + JSDocNamepathType = 314, + JSDocComment = 315, + JSDocText = 316, + JSDocTypeLiteral = 317, + JSDocSignature = 318, + JSDocLink = 319, + JSDocLinkCode = 320, + JSDocLinkPlain = 321, + JSDocTag = 322, + JSDocAugmentsTag = 323, + JSDocImplementsTag = 324, + JSDocAuthorTag = 325, + JSDocDeprecatedTag = 326, + JSDocClassTag = 327, + JSDocPublicTag = 328, + JSDocPrivateTag = 329, + JSDocProtectedTag = 330, + JSDocReadonlyTag = 331, + JSDocOverrideTag = 332, + JSDocCallbackTag = 333, + JSDocEnumTag = 334, + JSDocParameterTag = 335, + JSDocReturnTag = 336, + JSDocThisTag = 337, + JSDocTypeTag = 338, + JSDocTemplateTag = 339, + JSDocTypedefTag = 340, + JSDocSeeTag = 341, + JSDocPropertyTag = 342, + SyntaxList = 343, + NotEmittedStatement = 344, + PartiallyEmittedExpression = 345, + CommaListExpression = 346, + MergeDeclarationMarker = 347, + EndOfDeclarationMarker = 348, + SyntheticReferenceExpression = 349, + Count = 350, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -481,13 +480,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 236, - LastStatement = 252, + FirstStatement = 235, + LastStatement = 251, FirstNode = 159, - FirstJSDocNode = 305, - LastJSDocNode = 343, - FirstJSDocTagNode = 323, - LastJSDocTagNode = 343, + FirstJSDocNode = 304, + LastJSDocNode = 342, + FirstJSDocTagNode = 322, + LastJSDocTagNode = 342, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -599,7 +598,6 @@ declare namespace ts { } export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; - export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; /** @deprecated Use `AssertsKeyword` instead. */ @@ -1264,12 +1262,6 @@ declare namespace ts { readonly expression: SuperExpression; } export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; - export interface PrivateIdentifierInInExpression extends Expression { - readonly kind: SyntaxKind.PrivateIdentifierInInExpression; - readonly name: PrivateIdentifier; - readonly inToken: Token; - readonly expression: Expression; - } export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -3436,8 +3428,6 @@ declare namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; - createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; - updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; createTemplateSpan(expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; createSemicolonClassElement(): SemicolonClassElement; @@ -4539,7 +4529,6 @@ declare namespace ts { function isSyntheticExpression(node: Node): node is SyntheticExpression; function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression; function isCommaListExpression(node: Node): node is CommaListExpression; - function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression; function isTemplateSpan(node: Node): node is TemplateSpan; function isSemicolonClassElement(node: Node): node is SemicolonClassElement; function isBlock(node: Node): node is Block; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 5b4f765e6b4d4..e81307d6ecb72 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -336,127 +336,126 @@ declare namespace ts { NonNullExpression = 228, MetaProperty = 229, SyntheticExpression = 230, - PrivateIdentifierInInExpression = 231, - TemplateSpan = 232, - SemicolonClassElement = 233, - Block = 234, - EmptyStatement = 235, - VariableStatement = 236, - ExpressionStatement = 237, - IfStatement = 238, - DoStatement = 239, - WhileStatement = 240, - ForStatement = 241, - ForInStatement = 242, - ForOfStatement = 243, - ContinueStatement = 244, - BreakStatement = 245, - ReturnStatement = 246, - WithStatement = 247, - SwitchStatement = 248, - LabeledStatement = 249, - ThrowStatement = 250, - TryStatement = 251, - DebuggerStatement = 252, - VariableDeclaration = 253, - VariableDeclarationList = 254, - FunctionDeclaration = 255, - ClassDeclaration = 256, - InterfaceDeclaration = 257, - TypeAliasDeclaration = 258, - EnumDeclaration = 259, - ModuleDeclaration = 260, - ModuleBlock = 261, - CaseBlock = 262, - NamespaceExportDeclaration = 263, - ImportEqualsDeclaration = 264, - ImportDeclaration = 265, - ImportClause = 266, - NamespaceImport = 267, - NamedImports = 268, - ImportSpecifier = 269, - ExportAssignment = 270, - ExportDeclaration = 271, - NamedExports = 272, - NamespaceExport = 273, - ExportSpecifier = 274, - MissingDeclaration = 275, - ExternalModuleReference = 276, - JsxElement = 277, - JsxSelfClosingElement = 278, - JsxOpeningElement = 279, - JsxClosingElement = 280, - JsxFragment = 281, - JsxOpeningFragment = 282, - JsxClosingFragment = 283, - JsxAttribute = 284, - JsxAttributes = 285, - JsxSpreadAttribute = 286, - JsxExpression = 287, - CaseClause = 288, - DefaultClause = 289, - HeritageClause = 290, - CatchClause = 291, - PropertyAssignment = 292, - ShorthandPropertyAssignment = 293, - SpreadAssignment = 294, - EnumMember = 295, - UnparsedPrologue = 296, - UnparsedPrepend = 297, - UnparsedText = 298, - UnparsedInternalText = 299, - UnparsedSyntheticReference = 300, - SourceFile = 301, - Bundle = 302, - UnparsedSource = 303, - InputFiles = 304, - JSDocTypeExpression = 305, - JSDocNameReference = 306, - JSDocMemberName = 307, - JSDocAllType = 308, - JSDocUnknownType = 309, - JSDocNullableType = 310, - JSDocNonNullableType = 311, - JSDocOptionalType = 312, - JSDocFunctionType = 313, - JSDocVariadicType = 314, - JSDocNamepathType = 315, - JSDocComment = 316, - JSDocText = 317, - JSDocTypeLiteral = 318, - JSDocSignature = 319, - JSDocLink = 320, - JSDocLinkCode = 321, - JSDocLinkPlain = 322, - JSDocTag = 323, - JSDocAugmentsTag = 324, - JSDocImplementsTag = 325, - JSDocAuthorTag = 326, - JSDocDeprecatedTag = 327, - JSDocClassTag = 328, - JSDocPublicTag = 329, - JSDocPrivateTag = 330, - JSDocProtectedTag = 331, - JSDocReadonlyTag = 332, - JSDocOverrideTag = 333, - JSDocCallbackTag = 334, - JSDocEnumTag = 335, - JSDocParameterTag = 336, - JSDocReturnTag = 337, - JSDocThisTag = 338, - JSDocTypeTag = 339, - JSDocTemplateTag = 340, - JSDocTypedefTag = 341, - JSDocSeeTag = 342, - JSDocPropertyTag = 343, - SyntaxList = 344, - NotEmittedStatement = 345, - PartiallyEmittedExpression = 346, - CommaListExpression = 347, - MergeDeclarationMarker = 348, - EndOfDeclarationMarker = 349, - SyntheticReferenceExpression = 350, - Count = 351, + TemplateSpan = 231, + SemicolonClassElement = 232, + Block = 233, + EmptyStatement = 234, + VariableStatement = 235, + ExpressionStatement = 236, + IfStatement = 237, + DoStatement = 238, + WhileStatement = 239, + ForStatement = 240, + ForInStatement = 241, + ForOfStatement = 242, + ContinueStatement = 243, + BreakStatement = 244, + ReturnStatement = 245, + WithStatement = 246, + SwitchStatement = 247, + LabeledStatement = 248, + ThrowStatement = 249, + TryStatement = 250, + DebuggerStatement = 251, + VariableDeclaration = 252, + VariableDeclarationList = 253, + FunctionDeclaration = 254, + ClassDeclaration = 255, + InterfaceDeclaration = 256, + TypeAliasDeclaration = 257, + EnumDeclaration = 258, + ModuleDeclaration = 259, + ModuleBlock = 260, + CaseBlock = 261, + NamespaceExportDeclaration = 262, + ImportEqualsDeclaration = 263, + ImportDeclaration = 264, + ImportClause = 265, + NamespaceImport = 266, + NamedImports = 267, + ImportSpecifier = 268, + ExportAssignment = 269, + ExportDeclaration = 270, + NamedExports = 271, + NamespaceExport = 272, + ExportSpecifier = 273, + MissingDeclaration = 274, + ExternalModuleReference = 275, + JsxElement = 276, + JsxSelfClosingElement = 277, + JsxOpeningElement = 278, + JsxClosingElement = 279, + JsxFragment = 280, + JsxOpeningFragment = 281, + JsxClosingFragment = 282, + JsxAttribute = 283, + JsxAttributes = 284, + JsxSpreadAttribute = 285, + JsxExpression = 286, + CaseClause = 287, + DefaultClause = 288, + HeritageClause = 289, + CatchClause = 290, + PropertyAssignment = 291, + ShorthandPropertyAssignment = 292, + SpreadAssignment = 293, + EnumMember = 294, + UnparsedPrologue = 295, + UnparsedPrepend = 296, + UnparsedText = 297, + UnparsedInternalText = 298, + UnparsedSyntheticReference = 299, + SourceFile = 300, + Bundle = 301, + UnparsedSource = 302, + InputFiles = 303, + JSDocTypeExpression = 304, + JSDocNameReference = 305, + JSDocMemberName = 306, + JSDocAllType = 307, + JSDocUnknownType = 308, + JSDocNullableType = 309, + JSDocNonNullableType = 310, + JSDocOptionalType = 311, + JSDocFunctionType = 312, + JSDocVariadicType = 313, + JSDocNamepathType = 314, + JSDocComment = 315, + JSDocText = 316, + JSDocTypeLiteral = 317, + JSDocSignature = 318, + JSDocLink = 319, + JSDocLinkCode = 320, + JSDocLinkPlain = 321, + JSDocTag = 322, + JSDocAugmentsTag = 323, + JSDocImplementsTag = 324, + JSDocAuthorTag = 325, + JSDocDeprecatedTag = 326, + JSDocClassTag = 327, + JSDocPublicTag = 328, + JSDocPrivateTag = 329, + JSDocProtectedTag = 330, + JSDocReadonlyTag = 331, + JSDocOverrideTag = 332, + JSDocCallbackTag = 333, + JSDocEnumTag = 334, + JSDocParameterTag = 335, + JSDocReturnTag = 336, + JSDocThisTag = 337, + JSDocTypeTag = 338, + JSDocTemplateTag = 339, + JSDocTypedefTag = 340, + JSDocSeeTag = 341, + JSDocPropertyTag = 342, + SyntaxList = 343, + NotEmittedStatement = 344, + PartiallyEmittedExpression = 345, + CommaListExpression = 346, + MergeDeclarationMarker = 347, + EndOfDeclarationMarker = 348, + SyntheticReferenceExpression = 349, + Count = 350, FirstAssignment = 63, LastAssignment = 78, FirstCompoundAssignment = 64, @@ -481,13 +480,13 @@ declare namespace ts { LastTemplateToken = 17, FirstBinaryOperator = 29, LastBinaryOperator = 78, - FirstStatement = 236, - LastStatement = 252, + FirstStatement = 235, + LastStatement = 251, FirstNode = 159, - FirstJSDocNode = 305, - LastJSDocNode = 343, - FirstJSDocTagNode = 323, - LastJSDocTagNode = 343, + FirstJSDocNode = 304, + LastJSDocNode = 342, + FirstJSDocTagNode = 322, + LastJSDocTagNode = 342, } export type TriviaSyntaxKind = SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia | SyntaxKind.NewLineTrivia | SyntaxKind.WhitespaceTrivia | SyntaxKind.ShebangTrivia | SyntaxKind.ConflictMarkerTrivia; export type LiteralSyntaxKind = SyntaxKind.NumericLiteral | SyntaxKind.BigIntLiteral | SyntaxKind.StringLiteral | SyntaxKind.JsxText | SyntaxKind.JsxTextAllWhiteSpaces | SyntaxKind.RegularExpressionLiteral | SyntaxKind.NoSubstitutionTemplateLiteral; @@ -599,7 +598,6 @@ declare namespace ts { } export type AssertsKeyword = KeywordToken; export type AwaitKeyword = KeywordToken; - export type InKeyword = KeywordToken; /** @deprecated Use `AwaitKeyword` instead. */ export type AwaitKeywordToken = AwaitKeyword; /** @deprecated Use `AssertsKeyword` instead. */ @@ -1264,12 +1262,6 @@ declare namespace ts { readonly expression: SuperExpression; } export type SuperProperty = SuperPropertyAccessExpression | SuperElementAccessExpression; - export interface PrivateIdentifierInInExpression extends Expression { - readonly kind: SyntaxKind.PrivateIdentifierInInExpression; - readonly name: PrivateIdentifier; - readonly inToken: Token; - readonly expression: Expression; - } export interface CallExpression extends LeftHandSideExpression, Declaration { readonly kind: SyntaxKind.CallExpression; readonly expression: LeftHandSideExpression; @@ -3436,8 +3428,6 @@ declare namespace ts { updateNonNullChain(node: NonNullChain, expression: Expression): NonNullChain; createMetaProperty(keywordToken: MetaProperty["keywordToken"], name: Identifier): MetaProperty; updateMetaProperty(node: MetaProperty, name: Identifier): MetaProperty; - createPrivateIdentifierInInExpression(name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; - updatePrivateIdentifierInInExpression(node: PrivateIdentifierInInExpression, name: PrivateIdentifier, inToken: Token, expression: Expression): PrivateIdentifierInInExpression; createTemplateSpan(expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; updateTemplateSpan(node: TemplateSpan, expression: Expression, literal: TemplateMiddle | TemplateTail): TemplateSpan; createSemicolonClassElement(): SemicolonClassElement; @@ -4539,7 +4529,6 @@ declare namespace ts { function isSyntheticExpression(node: Node): node is SyntheticExpression; function isPartiallyEmittedExpression(node: Node): node is PartiallyEmittedExpression; function isCommaListExpression(node: Node): node is CommaListExpression; - function isPrivateIdentifierInInExpression(node: Node): node is PrivateIdentifierInInExpression; function isTemplateSpan(node: Node): node is TemplateSpan; function isSemicolonClassElement(node: Node): node is SemicolonClassElement; function isBlock(node: Node): node is Block; diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index 000620cb6d561..fa8e0bf7db8d1 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -133,7 +133,7 @@ class Foo { for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any } whitespace(v) { - const a = v && /*0*/ #field/*1*/ + const a = v && /*0*/ #field /*1*/ /*2*/ in /*3*/ /*4*/ v; /*5*/ } From 320bc008cad34fbab3c96bc06922d35cb2ec424a Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 17 Sep 2021 14:50:45 +0100 Subject: [PATCH 12/41] [squash] PrivateIdentifier now extends PrimaryExpression Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 6 ++++-- src/compiler/parser.ts | 2 +- src/compiler/transformers/classFields.ts | 4 +++- src/compiler/types.ts | 3 ++- src/services/services.ts | 6 ++++++ 5 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bd5f974c7848e..4d2806426d8f8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23885,7 +23885,8 @@ namespace ts { return type; } - const privateId = expr.left as Node as PrivateIdentifier; + const privateId = expr.left; + Debug.assertNode(privateId, isPrivateIdentifier); const symbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); if (symbol === undefined) { return type; @@ -32324,7 +32325,8 @@ namespace ts { } function checkPrivateIdentifierInInExpression(node: BinaryExpression, checkMode?: CheckMode) { - const privateId = node.left as Node as PrivateIdentifier; + const privateId = node.left; + Debug.assertNode(privateId, isPrivateIdentifier); const exp = node.right; let rightType = checkExpression(exp, checkMode); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 2d15728953808..a8fbf92e2d7e7 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4614,7 +4614,7 @@ namespace ts { const inToken = parseTokenNode>(); const exp = parseBinaryExpressionOrHigher(OperatorPrecedence.Relational); - return finishNode(factory.createBinaryExpression(id as Node as Expression, inToken, exp), pos); + return finishNode(factory.createBinaryExpression(id, inToken, exp), pos); } function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 95fd15ed6b88b..f82d0535b5717 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -282,7 +282,9 @@ namespace ts { if (!shouldTransformPrivateElementsOrClassStaticBlocks) { return node; } - const info = accessPrivateIdentifier(node.left as Node as PrivateIdentifier); + const privId = node.left; + Debug.assertNode(privId, isPrivateIdentifier); + const info = accessPrivateIdentifier(privId); if (info) { const receiver = visitNode(node.right, visitor, isExpression); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f263c53a64961..a2d52971330dc 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1209,7 +1209,8 @@ namespace ts { readonly expression: Expression; } - export interface PrivateIdentifier extends Node { + // Typed as a PrimaryExpression due to its presence in BinaryExpressions (#field in expr) + export interface PrivateIdentifier extends PrimaryExpression { readonly kind: SyntaxKind.PrivateIdentifier; // escaping not strictly necessary // avoids gotchas in transforms and utils diff --git a/src/services/services.ts b/src/services/services.ts index 426fe7a52690d..557e4480e78a4 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -401,6 +401,12 @@ namespace ts { public kind!: SyntaxKind.PrivateIdentifier; public escapedText!: __String; public symbol!: Symbol; + _primaryExpressionBrand: any; + _memberExpressionBrand: any; + _leftHandSideExpressionBrand: any; + _updateExpressionBrand: any; + _unaryExpressionBrand: any; + _expressionBrand: any; constructor(_kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) { super(pos, end); } From cc80c7d2c0dd073d313ce3f3fd7c37996f812154 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 17 Sep 2021 15:44:23 +0100 Subject: [PATCH 13/41] [squash] accept public api baseline changes Signed-off-by: Ashley Claymore --- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 345ee175a1f7e..df6f7ad146965 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -666,7 +666,7 @@ declare namespace ts { readonly parent: Declaration; readonly expression: Expression; } - export interface PrivateIdentifier extends Node { + export interface PrivateIdentifier extends PrimaryExpression { readonly kind: SyntaxKind.PrivateIdentifier; readonly escapedText: __String; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index dc6b0f2787f76..defefcfb05200 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -666,7 +666,7 @@ declare namespace ts { readonly parent: Declaration; readonly expression: Expression; } - export interface PrivateIdentifier extends Node { + export interface PrivateIdentifier extends PrimaryExpression { readonly kind: SyntaxKind.PrivateIdentifier; readonly escapedText: __String; } From ebbb0639b55bcd979f22336bfd3ff39975bbb424 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Fri, 17 Sep 2021 16:02:18 +0100 Subject: [PATCH 14/41] [squash] classPrivateFieldInHelper now has documentation Signed-off-by: Ashley Claymore --- src/compiler/factory/emitHelpers.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index c83a60f4e6f9c..3eeb6ca674aaf 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -967,6 +967,18 @@ namespace ts { };` }; + /** + * Parameters: + * @param receiver — The object being checked if it has the private member. + * @param state — One of the following: + * - A WeakMap when the member is a private instance field. + * - A WeakSet when the member is a private instance method or accessor. + * - A function value that should be the undecorated class constructor when the member is a private static fields, method, or accessor. + * + * Usage: + * This helper is used to transform `#field in expression` to + * `__classPrivateFieldIn(expression, )` + */ export const classPrivateFieldInHelper: UnscopedEmitHelper = { name: "typescript:classPrivateFieldIn", importName: "__classPrivateFieldIn", From ea4fd4b374e667da8f11544fff59d76749ca72b8 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 20 Sep 2021 14:42:13 +0100 Subject: [PATCH 15/41] [squash] type-check now follows existing in-expression path Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 98 ++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4d2806426d8f8..c4f72e4bc15ab 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32045,14 +32045,48 @@ namespace ts { } function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { - if (leftType === silentNeverType || rightType === silentNeverType) { - return silentNeverType; + if (isPrivateIdentifier(left)) { + const privateId = left; + const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); + if (lexicallyScopedSymbol === undefined) { + if (!getContainingClass(privateId)) { + error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + } + else { + const suggestion = getSuggestedSymbolForNonexistentProperty(privateId, rightType); + if (suggestion) { + const suggestedName = symbolName(suggestion); + error(privateId, Diagnostics.Cannot_find_name_0_Did_you_mean_1, diagnosticName(privateId), suggestedName); + } + else { + error(privateId, Diagnostics.Cannot_find_name_0, diagnosticName(privateId)); + } + } + return anyType; + } + + markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); + getNodeLinks(privateId.parent).resolvedSymbol = lexicallyScopedSymbol; + if (rightType === silentNeverType) { + return silentNeverType; + } + rightType = checkNonNullType(rightType, right); + } + else { + if (leftType === silentNeverType || rightType === silentNeverType) { + return silentNeverType; + } + leftType = checkNonNullType(leftType, left); + rightType = checkNonNullType(rightType, right); + + // Require the left operand to be of type Any, the String primite type, or the Number primite type. + if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) || + isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { + error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); + } } - leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); // TypeScript 1.0 spec (April 2014): 4.15.5 - // The in operator requires the left operand to be of type Any, the String primitive type, or the Number primitive type, - // and the right operand to be + // The in operator requires right operand to be // // 1. assignable to the non-primitive type, // 2. an unconstrained type parameter, @@ -32070,15 +32104,6 @@ namespace ts { // unless *all* instantiations would result in an error. // // The result is always of the Boolean primitive type. - if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) || - isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { - error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); - } - checkInExpressionRHS(right, rightType); - return booleanType; - } - - function checkInExpressionRHS(right: Expression, rightType: Type) { const rightTypeConstraint = getConstraintOfType(rightType); if (!allTypesAssignableToKind(rightType, TypeFlags.NonPrimitive | TypeFlags.InstantiableNonPrimitive) || rightTypeConstraint && ( @@ -32088,6 +32113,7 @@ namespace ts { ) { error(right, Diagnostics.The_right_hand_side_of_an_in_expression_must_not_be_a_primitive); } + return booleanType; } function checkObjectLiteralAssignment(node: ObjectLiteralExpression, sourceType: Type, rightIsThis?: boolean): Type { @@ -32324,41 +32350,6 @@ namespace ts { return (target.flags & TypeFlags.Nullable) !== 0 || isTypeComparableTo(source, target); } - function checkPrivateIdentifierInInExpression(node: BinaryExpression, checkMode?: CheckMode) { - const privateId = node.left; - Debug.assertNode(privateId, isPrivateIdentifier); - const exp = node.right; - let rightType = checkExpression(exp, checkMode); - - const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); - if (lexicallyScopedSymbol === undefined) { - if (!getContainingClass(node)) { - error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); - } - else { - const suggestion = getSuggestedSymbolForNonexistentProperty(privateId, rightType); - if (suggestion) { - const suggestedName = symbolName(suggestion); - error(privateId, Diagnostics.Cannot_find_name_0_Did_you_mean_1, diagnosticName(privateId), suggestedName); - } - else { - error(privateId, Diagnostics.Cannot_find_name_0, diagnosticName(privateId)); - } - } - return anyType; - } - - markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); - getNodeLinks(node).resolvedSymbol = lexicallyScopedSymbol; - - if (rightType === silentNeverType) { - return silentNeverType; - } - rightType = checkNonNullType(rightType, exp); - checkInExpressionRHS(exp, rightType); - return booleanType; - } - function createCheckBinaryExpression() { interface WorkArea { readonly checkMode: CheckMode | undefined; @@ -32410,11 +32401,6 @@ namespace ts { setLastResult(state, checkDestructuringAssignment(node.left, checkExpression(node.right, checkMode), checkMode, node.right.kind === SyntaxKind.ThisKeyword)); return state; } - else if (node.operatorToken.kind === SyntaxKind.InKeyword && isPrivateIdentifier(node.left)) { - state.skip = true; - setLastResult(state, checkPrivateIdentifierInInExpression(node, checkMode)); - return state; - } return state; } @@ -40290,7 +40276,7 @@ namespace ts { if (links.resolvedSymbol) { return links.resolvedSymbol; } - checkPrivateIdentifierInInExpression(name.parent); + checkBinaryExpression(name.parent, CheckMode.Normal); return links.resolvedSymbol; } From 8b78f0107b5725121c19e44cd98a3d16020dc809 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Mon, 20 Sep 2021 17:40:39 +0100 Subject: [PATCH 16/41] [squash] parser now follows existing binaryExpression path Signed-off-by: Ashley Claymore --- src/compiler/parser.ts | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index a8fbf92e2d7e7..db1777dbe5d69 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4601,33 +4601,16 @@ namespace ts { ); } - function parsePrivateIdentifierInInExpression(pos: number): Expression { - // PrivateIdentifierInInExpression[in]: - // [+in] PrivateIdentifier in RelationalExpression[?in] - - Debug.assert(token() === SyntaxKind.PrivateIdentifier, "parsePrivateIdentifierInInExpression should only have been called if we had a privateIdentifier"); - Debug.assert(inDisallowInContext() === false, "parsePrivateIdentifierInInExpression should only have been called if 'in' is allowed"); - const id = parsePrivateIdentifier(); - if (token() !== SyntaxKind.InKeyword) { - return createMissingNode(SyntaxKind.InKeyword, /*reportAtCurrentPosition*/ true, Diagnostics._0_expected, tokenToString(SyntaxKind.InKeyword)); - } - - const inToken = parseTokenNode>(); - const exp = parseBinaryExpressionOrHigher(OperatorPrecedence.Relational); - return finishNode(factory.createBinaryExpression(id, inToken, exp), pos); - } - function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { // parse a BinaryExpression the LHS is either: - // 1) a PrivateIdentifierInInExpression when 'in' flag allowed and lookahead matches 'PrivateIdentifier in' + // 1) a PrivateIdentifier when 'in' flag allowed and lookahead matches 'PrivateIdentifier in' // 2) a UnaryExpression const pos = getNodePos(); - const tryPrivateIdentifierInIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext() && lookAhead(nextTokenIsInKeyword); - const leftOperand = - tryPrivateIdentifierInIn - ? parsePrivateIdentifierInInExpression(pos) : - parseUnaryExpressionOrHigher(); + const lookaheadMatchesPrivateIdentifierIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext() && lookAhead(nextTokenIsInKeyword); + const leftOperand = lookaheadMatchesPrivateIdentifierIn + ? parsePrivateIdentifier() + : parseUnaryExpressionOrHigher(); return parseBinaryExpressionRest(precedence, leftOperand, pos); } @@ -4664,9 +4647,11 @@ namespace ts { // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand // a ** b - c // ^token; leftOperand = b. Return b to the caller as a rightOperand - const consumeCurrentOperator = token() === SyntaxKind.AsteriskAsteriskToken ? - newPrecedence >= precedence : - newPrecedence > precedence; + const isPrivateIdentifierInInExpression = token() === SyntaxKind.InKeyword && isPrivateIdentifier(leftOperand); + const consumeCurrentOperator = isPrivateIdentifierInInExpression || + (token() === SyntaxKind.AsteriskAsteriskToken ? + newPrecedence >= precedence : + newPrecedence > precedence); if (!consumeCurrentOperator) { break; From 01c704271c8e6dad63519681d3d887670496797c Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 21 Sep 2021 16:20:49 +0100 Subject: [PATCH 17/41] [squash] correct typo in comment Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 2 +- src/compiler/factory/emitHelpers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c4f72e4bc15ab..8a30c19a76843 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32086,7 +32086,7 @@ namespace ts { } } // TypeScript 1.0 spec (April 2014): 4.15.5 - // The in operator requires right operand to be + // The in operator requires the right operand to be // // 1. assignable to the non-primitive type, // 2. an unconstrained type parameter, diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 3eeb6ca674aaf..54075c5dad4af 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -973,7 +973,7 @@ namespace ts { * @param state — One of the following: * - A WeakMap when the member is a private instance field. * - A WeakSet when the member is a private instance method or accessor. - * - A function value that should be the undecorated class constructor when the member is a private static fields, method, or accessor. + * - A function value that should be the undecorated class constructor when the member is a private static field, method, or accessor. * * Usage: * This helper is used to transform `#field in expression` to From fc2b262433e4a5b643614ccad7d2b37c45720f13 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 21 Sep 2021 21:50:21 +0100 Subject: [PATCH 18/41] [squash] no longer use esNext flag Signed-off-by: Ashley Claymore --- src/compiler/factory/nodeFactory.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 706ae174e1ff7..c697a04860b71 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -2801,9 +2801,6 @@ namespace ts { else if (isLogicalOrCoalescingAssignmentOperator(operatorKind)) { node.transformFlags |= TransformFlags.ContainsES2021; } - else if (operatorKind === SyntaxKind.InKeyword && isPrivateIdentifier(left)) { - node.transformFlags |= TransformFlags.ContainsESNext; - } return node; } From c007a12dd70b91958c1620ffe54a030e6260e779 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 21 Sep 2021 22:13:39 +0100 Subject: [PATCH 19/41] [squash] swap 'reciever, state' helper params Signed-off-by: Ashley Claymore --- src/compiler/factory/emitHelpers.ts | 10 ++++---- src/compiler/transformers/classFields.ts | 2 +- ...eInInExpressionTransform(target=es2020).js | 24 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index 54075c5dad4af..d64b0ff47ec6b 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -34,7 +34,7 @@ namespace ts { // Class Fields Helpers createClassPrivateFieldGetHelper(receiver: Expression, state: Identifier, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; createClassPrivateFieldSetHelper(receiver: Expression, state: Identifier, value: Expression, kind: PrivateIdentifierKind, f: Identifier | undefined): Expression; - createClassPrivateFieldInHelper(receiver: Expression, state: Identifier): Expression; + createClassPrivateFieldInHelper(state: Identifier, receiver: Expression): Expression; } export function createEmitHelperFactory(context: TransformationContext): EmitHelperFactory { @@ -397,9 +397,9 @@ namespace ts { return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldSet"), /*typeArguments*/ undefined, args); } - function createClassPrivateFieldInHelper(receiver: Expression, state: Identifier) { + function createClassPrivateFieldInHelper(state: Identifier, receiver: Expression) { context.requestEmitHelper(classPrivateFieldInHelper); - return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [receiver, state]); + return factory.createCallExpression(getUnscopedHelperName("__classPrivateFieldIn"), /* typeArguments*/ undefined, [state, receiver]); } } @@ -969,11 +969,11 @@ namespace ts { /** * Parameters: - * @param receiver — The object being checked if it has the private member. * @param state — One of the following: * - A WeakMap when the member is a private instance field. * - A WeakSet when the member is a private instance method or accessor. * - A function value that should be the undecorated class constructor when the member is a private static field, method, or accessor. + * @param receiver — The object being checked if it has the private member. * * Usage: * This helper is used to transform `#field in expression` to @@ -984,7 +984,7 @@ namespace ts { importName: "__classPrivateFieldIn", scoped: false, text: ` - var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, state) { + var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(state, receiver) { if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); return typeof state === "function" ? receiver === state : state.has(receiver); };` diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index f82d0535b5717..7102f0f5ed072 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -289,7 +289,7 @@ namespace ts { const receiver = visitNode(node.right, visitor, isExpression); return setOriginalNode( - context.getEmitHelperFactory().createClassPrivateFieldInHelper(receiver, info.brandCheckIdentifier), + context.getEmitHelperFactory().createClassPrivateFieldInHelper(info.brandCheckIdentifier, receiver), node ); } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js index cd7d3dbc61db2..37014cb7dd483 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js @@ -47,7 +47,7 @@ export { } //// [privateNameInInExpressionTransform.js] -var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(receiver, state) { +var __classPrivateFieldIn = (this && this.__classPrivateFieldIn) || function(state, receiver) { if (receiver === null || (typeof receiver !== "object" && typeof receiver !== "function")) throw new TypeError("Cannot use 'in' operator on non-object"); return typeof state === "function" ? receiver === state : state.has(receiver); }; @@ -58,25 +58,25 @@ class Foo { _Foo_field.set(this, 1); } check(v) { - __classPrivateFieldIn(v, _Foo_field); // expect Foo's 'field' WeakMap - __classPrivateFieldIn(v, _Foo_instances); // expect Foo's 'method' WeakSet - __classPrivateFieldIn(v, _a); // expect Foo's constructor - __classPrivateFieldIn(v, _a); // expect Foo's constructor + __classPrivateFieldIn(_Foo_field, v); // expect Foo's 'field' WeakMap + __classPrivateFieldIn(_Foo_instances, v); // expect Foo's 'method' WeakSet + __classPrivateFieldIn(_a, v); // expect Foo's constructor + __classPrivateFieldIn(_a, v); // expect Foo's constructor } precedence(v) { // '==' has lower precedence than 'in' // '<' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == __classPrivateFieldIn(v, _Foo_field) == v; // Good precedence: ((v == (#field in v)) == v) - v << __classPrivateFieldIn(v << v, _Foo_field); // Good precedence: (v << (#field in (v << v))) - v << __classPrivateFieldIn(v, _Foo_field) == v; // Good precedence: ((v << (#field in v)) == v) - v == __classPrivateFieldIn(v, _Foo_field) < v; // Good precedence: (v == ((#field in v) < v)) - __classPrivateFieldIn(v, _Foo_field) && __classPrivateFieldIn(v, _Foo_field); // Good precedence: ((#field in v) && (#field in v)) + v == __classPrivateFieldIn(_Foo_field, v) == v; // Good precedence: ((v == (#field in v)) == v) + v << __classPrivateFieldIn(_Foo_field, v << v); // Good precedence: (v << (#field in (v << v))) + v << __classPrivateFieldIn(_Foo_field, v) == v; // Good precedence: ((v << (#field in v)) == v) + v == __classPrivateFieldIn(_Foo_field, v) < v; // Good precedence: (v == ((#field in v) < v)) + __classPrivateFieldIn(_Foo_field, v) && __classPrivateFieldIn(_Foo_field, v); // Good precedence: ((#field in v) && (#field in v)) } invalidLHS(v) { 'prop' in v; 10; - __classPrivateFieldIn(v, _Foo_field); + __classPrivateFieldIn(_Foo_field, v); 10; } } @@ -87,7 +87,7 @@ class Bar { _Bar_field.set(this, 1); } check(v) { - __classPrivateFieldIn(v, _Bar_field); // expect Bar's 'field' WeakMap + __classPrivateFieldIn(_Bar_field, v); // expect Bar's 'field' WeakMap } } _Bar_field = new WeakMap(); From 40bd336da52a5d23a8dfdebace89db97a9fe2fbb Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 21 Sep 2021 22:19:28 +0100 Subject: [PATCH 20/41] [squash] remove change to parenthesizerRules Signed-off-by: Ashley Claymore --- src/compiler/factory/parenthesizerRules.ts | 5 ----- src/compiler/utilities.ts | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/compiler/factory/parenthesizerRules.ts b/src/compiler/factory/parenthesizerRules.ts index dd4f63881aa47..1e2cb936582fa 100644 --- a/src/compiler/factory/parenthesizerRules.ts +++ b/src/compiler/factory/parenthesizerRules.ts @@ -81,11 +81,6 @@ namespace ts { const binaryOperatorPrecedence = getOperatorPrecedence(SyntaxKind.BinaryExpression, binaryOperator); const binaryOperatorAssociativity = getOperatorAssociativity(SyntaxKind.BinaryExpression, binaryOperator); const emittedOperand = skipPartiallyEmittedExpressions(operand); - // If a privateIdentifier is on the left side, this is not a real expression and - // should not become parenthesized - if (isLeftSideOfBinary && isPrivateIdentifier(operand)) { - return false; - } if (!isLeftSideOfBinary && operand.kind === SyntaxKind.ArrowFunction && binaryOperatorPrecedence > OperatorPrecedence.Assignment) { // We need to parenthesize arrow functions on the right side to avoid it being // parsed as parenthesized expression: `a && (() => {})` diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8ac91f86f99dd..31fe24dc9d855 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3706,6 +3706,7 @@ namespace ts { case SyntaxKind.ThisKeyword: case SyntaxKind.SuperKeyword: case SyntaxKind.Identifier: + case SyntaxKind.PrivateIdentifier: case SyntaxKind.NullKeyword: case SyntaxKind.TrueKeyword: case SyntaxKind.FalseKeyword: From 7982164c198462d7a2826ba36649c340fb2ed8dd Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 21 Sep 2021 22:27:19 +0100 Subject: [PATCH 21/41] [squash] apply suggested changes to checker Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8a30c19a76843..14c8c6b018170 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23846,6 +23846,9 @@ namespace ts { case SyntaxKind.InstanceOfKeyword: return narrowTypeByInstanceof(type, expr, assumeTrue); case SyntaxKind.InKeyword: + if (isPrivateIdentifier(expr.left)) { + return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue); + } const target = getReferenceCandidate(expr.right); const leftType = getTypeOfNode(expr.left); if (leftType.flags & TypeFlags.StringLiteral) { @@ -23858,9 +23861,6 @@ namespace ts { return narrowByInKeyword(type, name, assumeTrue); } } - else if (isPrivateIdentifier(expr.left)) { - return narrowTypeByPrivateIdentifierInInExpression(type, expr, assumeTrue); - } break; case SyntaxKind.CommaToken: return narrowType(type, expr.right, assumeTrue); @@ -32046,45 +32046,43 @@ namespace ts { function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { if (isPrivateIdentifier(left)) { - const privateId = left; - const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); + const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(left.escapedText, left); if (lexicallyScopedSymbol === undefined) { - if (!getContainingClass(privateId)) { - error(privateId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + if (!getContainingClass(left)) { + error(left, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); } else { - const suggestion = getSuggestedSymbolForNonexistentProperty(privateId, rightType); + const suggestion = getSuggestedSymbolForNonexistentProperty(left, rightType); if (suggestion) { const suggestedName = symbolName(suggestion); - error(privateId, Diagnostics.Cannot_find_name_0_Did_you_mean_1, diagnosticName(privateId), suggestedName); + error(left, Diagnostics.Cannot_find_name_0_Did_you_mean_1, diagnosticName(left), suggestedName); } else { - error(privateId, Diagnostics.Cannot_find_name_0, diagnosticName(privateId)); + error(left, Diagnostics.Cannot_find_name_0, diagnosticName(left)); } } return anyType; } markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); - getNodeLinks(privateId.parent).resolvedSymbol = lexicallyScopedSymbol; + getNodeLinks(left.parent).resolvedSymbol = lexicallyScopedSymbol; if (rightType === silentNeverType) { return silentNeverType; } - rightType = checkNonNullType(rightType, right); } else { if (leftType === silentNeverType || rightType === silentNeverType) { return silentNeverType; } leftType = checkNonNullType(leftType, left); - rightType = checkNonNullType(rightType, right); - + // TypeScript 1.0 spec (April 2014): 4.15.5 // Require the left operand to be of type Any, the String primite type, or the Number primite type. if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) || isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); } } + rightType = checkNonNullType(rightType, right); // TypeScript 1.0 spec (April 2014): 4.15.5 // The in operator requires the right operand to be // From 1cd313f4fb0ea51b2ddbab4e4fa124f961fc5f4c Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 21 Sep 2021 22:30:39 +0100 Subject: [PATCH 22/41] [squash] remove need for assertion in fixSpelling Signed-off-by: Ashley Claymore --- src/services/codefixes/fixSpelling.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index e6ae6ef2faeb7..f4f8b31aca100 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -57,9 +57,8 @@ namespace ts.codefix { } suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType); } - else if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.InKeyword && isPrivateIdentifier(parent.left) && parent.left === node) { + else if (isBinaryExpression(parent) && parent.operatorToken.kind === SyntaxKind.InKeyword && parent.left === node && isPrivateIdentifier(node)) { const receiverType = checker.getTypeAtLocation(parent.right); - Debug.assert(isPrivateIdentifier(node), "Expected a privateIdentifier for spelling (in)"); suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, receiverType); } else if (isQualifiedName(parent) && parent.right === node) { From 97f7d30c0f00c1fa5b9b4e7aef7648b7d34cf836 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Tue, 21 Sep 2021 22:32:55 +0100 Subject: [PATCH 23/41] [squash] improve comment hint in test Signed-off-by: Ashley Claymore --- ...ivateNameInInExpressionTransform(target=es2020).errors.txt | 2 +- .../privateNameInInExpressionTransform(target=es2020).js | 4 ++-- .../privateNameInInExpressionTransform(target=es2020).symbols | 2 +- .../privateNameInInExpressionTransform(target=es2020).types | 2 +- ...ivateNameInInExpressionTransform(target=esnext).errors.txt | 2 +- .../privateNameInInExpressionTransform(target=esnext).js | 4 ++-- .../privateNameInInExpressionTransform(target=esnext).symbols | 2 +- .../privateNameInInExpressionTransform(target=esnext).types | 2 +- .../privateNames/privateNameInInExpressionTransform.ts | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt index 0368b9ef750bc..15d9b2410bfcd 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt @@ -15,7 +15,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr check(v: any) { #field in v; // expect Foo's 'field' WeakMap - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet #staticField in v; // expect Foo's constructor #staticMethod in v; // expect Foo's constructor } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js index 37014cb7dd483..be5174189832e 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js @@ -7,7 +7,7 @@ class Foo { check(v: any) { #field in v; // expect Foo's 'field' WeakMap - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet #staticField in v; // expect Foo's constructor #staticMethod in v; // expect Foo's constructor } @@ -59,7 +59,7 @@ class Foo { } check(v) { __classPrivateFieldIn(_Foo_field, v); // expect Foo's 'field' WeakMap - __classPrivateFieldIn(_Foo_instances, v); // expect Foo's 'method' WeakSet + __classPrivateFieldIn(_Foo_instances, v); // expect Foo's 'instances' WeakSet __classPrivateFieldIn(_a, v); // expect Foo's constructor __classPrivateFieldIn(_a, v); // expect Foo's constructor } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols index c0cc9938cd8d2..4772cc8fe207c 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols @@ -21,7 +21,7 @@ class Foo { #field in v; // expect Foo's 'field' WeakMap >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #staticField in v; // expect Foo's constructor diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types index 5c68549be8fd4..940e4bd1169f2 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types @@ -24,7 +24,7 @@ class Foo { >#field in v : boolean >v : any - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet >#method in v : boolean >v : any diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt index efcb361b3c4d7..cab9be3a96732 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt @@ -18,7 +18,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr check(v: any) { #field in v; // expect Foo's 'field' WeakMap - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet #staticField in v; // expect Foo's constructor #staticMethod in v; // expect Foo's constructor } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js index 8772d5e3933a3..d5fae618c56c1 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js @@ -7,7 +7,7 @@ class Foo { check(v: any) { #field in v; // expect Foo's 'field' WeakMap - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet #staticField in v; // expect Foo's constructor #staticMethod in v; // expect Foo's constructor } @@ -54,7 +54,7 @@ class Foo { static #staticMethod() { } check(v) { #field in v; // expect Foo's 'field' WeakMap - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet #staticField in v; // expect Foo's constructor #staticMethod in v; // expect Foo's constructor } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols index c0cc9938cd8d2..4772cc8fe207c 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols @@ -21,7 +21,7 @@ class Foo { #field in v; // expect Foo's 'field' WeakMap >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #staticField in v; // expect Foo's constructor diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types index 5c68549be8fd4..940e4bd1169f2 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types @@ -24,7 +24,7 @@ class Foo { >#field in v : boolean >v : any - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet >#method in v : boolean >v : any diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index 5c07e0ad87f67..e15d2a8b03ea1 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -8,7 +8,7 @@ class Foo { check(v: any) { #field in v; // expect Foo's 'field' WeakMap - #method in v; // expect Foo's 'method' WeakSet + #method in v; // expect Foo's 'instances' WeakSet #staticField in v; // expect Foo's constructor #staticMethod in v; // expect Foo's constructor } From e6729573208f86e59ec0dd106acbee75ca1b0e9d Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 12:07:27 +0100 Subject: [PATCH 24/41] [squash] fix comment typos Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 2 +- src/compiler/factory/emitHelpers.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 14c8c6b018170..57e745863e402 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32076,7 +32076,7 @@ namespace ts { } leftType = checkNonNullType(leftType, left); // TypeScript 1.0 spec (April 2014): 4.15.5 - // Require the left operand to be of type Any, the String primite type, or the Number primite type. + // Require the left operand to be of type Any, the String primitive type, or the Number primitive type. if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) || isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); diff --git a/src/compiler/factory/emitHelpers.ts b/src/compiler/factory/emitHelpers.ts index d64b0ff47ec6b..f99abb3e03dae 100644 --- a/src/compiler/factory/emitHelpers.ts +++ b/src/compiler/factory/emitHelpers.ts @@ -977,7 +977,7 @@ namespace ts { * * Usage: * This helper is used to transform `#field in expression` to - * `__classPrivateFieldIn(expression, )` + * `__classPrivateFieldIn(, expression)` */ export const classPrivateFieldInHelper: UnscopedEmitHelper = { name: "typescript:classPrivateFieldIn", From 2b7425dddebab05b958804ffd448adf6f994265b Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 12:13:17 +0100 Subject: [PATCH 25/41] [squash] add flow-test for Foo | FooSub | Bar Signed-off-by: Ashley Claymore --- .../privateNameInInExpression.errors.txt | 10 +++- .../reference/privateNameInInExpression.js | 16 +++++- .../privateNameInInExpression.symbols | 51 ++++++++++++------- .../reference/privateNameInInExpression.types | 17 ++++++- .../privateNames/privateNameInInExpression.ts | 8 ++- 5 files changed, 77 insertions(+), 25 deletions(-) diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 96f54bf1f3212..9a475c70c42e8 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -6,7 +6,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(29,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(43,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(108,12): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. ==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== @@ -59,7 +59,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t /*2*/in/*3*/ /*4*/v/*5*/ } - flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) { if (typeof u === 'object') { if (#field in n) { @@ -119,6 +119,12 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t fsb; // good fsb is Bar } + if (#field in fsfb) { + fsfb; // good fsfb is 'Foo | FooSub' + } else { + fsfb; // good fsfb is Bar + } + class Nested { m(v: any) { if (#field in v) { diff --git a/tests/baselines/reference/privateNameInInExpression.js b/tests/baselines/reference/privateNameInInExpression.js index fa8e0bf7db8d1..a57d640c8d664 100644 --- a/tests/baselines/reference/privateNameInInExpression.js +++ b/tests/baselines/reference/privateNameInInExpression.js @@ -34,7 +34,7 @@ class Foo { /*2*/in/*3*/ /*4*/v/*5*/ } - flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) { if (typeof u === 'object') { if (#field in n) { @@ -92,6 +92,12 @@ class Foo { fsb; // good fsb is Bar } + if (#field in fsfb) { + fsfb; // good fsfb is 'Foo | FooSub' + } else { + fsfb; // good fsfb is Bar + } + class Nested { m(v: any) { if (#field in v) { @@ -137,7 +143,7 @@ class Foo { /*2*/ in /*3*/ /*4*/ v; /*5*/ } - flow(u, n, fb, fs, b, fsb) { + flow(u, n, fb, fs, b, fsb, fsfb) { if (typeof u === 'object') { if (#field in n) { n; // good n is never @@ -190,6 +196,12 @@ class Foo { else { fsb; // good fsb is Bar } + if (#field in fsfb) { + fsfb; // good fsfb is 'Foo | FooSub' + } + else { + fsfb; // good fsfb is Bar + } class Nested { m(v) { if (#field in v) { diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 30a02dddacea9..56620d7021c93 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -78,20 +78,24 @@ class Foo { /*4*/v/*5*/ >v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15)) } - flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) { >flow : Symbol(Foo.flow, Decl(privateNameInInExpression.ts, 34, 5)) >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) >n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20)) >fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48)) >fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1)) >b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48)) >fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48)) +>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84)) +>Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48)) if (typeof u === 'object') { >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) @@ -195,18 +199,29 @@ class Foo { >fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) } + if (#field in fsfb) { +>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84)) + + fsfb; // good fsfb is 'Foo | FooSub' +>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84)) + + } else { + fsfb; // good fsfb is Bar +>fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84)) + } + class Nested { ->Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 91, 9)) +>Nested : Symbol(Nested, Decl(privateNameInInExpression.ts, 97, 9)) m(v: any) { ->m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 93, 22)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) +>m : Symbol(Nested.m, Decl(privateNameInInExpression.ts, 99, 22)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14)) if (#field in v) { ->v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14)) v; // good v is Foo ->v : Symbol(v, Decl(privateNameInInExpression.ts, 94, 14)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14)) } } } @@ -214,20 +229,20 @@ class Foo { } class FooSub extends Foo { subTypeOfFoo = true } ->FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 101, 1)) +>FooSub : Symbol(FooSub, Decl(privateNameInInExpression.ts, 107, 1)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) ->subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 103, 26)) +>subTypeOfFoo : Symbol(FooSub.subTypeOfFoo, Decl(privateNameInInExpression.ts, 109, 26)) class Bar { notFoo = true } ->Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 103, 48)) ->notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 104, 11)) +>Bar : Symbol(Bar, Decl(privateNameInInExpression.ts, 109, 48)) +>notFoo : Symbol(Bar.notFoo, Decl(privateNameInInExpression.ts, 110, 11)) function badSyntax(v: Foo) { ->badSyntax : Symbol(badSyntax, Decl(privateNameInInExpression.ts, 104, 27)) ->v : Symbol(v, Decl(privateNameInInExpression.ts, 106, 19)) +>badSyntax : Symbol(badSyntax, Decl(privateNameInInExpression.ts, 110, 27)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 112, 19)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) return #field in v; // Bad - outside of class ->v : Symbol(v, Decl(privateNameInInExpression.ts, 106, 19)) +>v : Symbol(v, Decl(privateNameInInExpression.ts, 112, 19)) } diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index f323b2a6a2dc3..684b2b6b11f4e 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -107,14 +107,15 @@ class Foo { /*4*/v/*5*/ >v : any } - flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { ->flow : (u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) => void + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) { +>flow : (u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) => void >u : unknown >n : never >fb : Foo | Bar >fs : FooSub >b : Bar >fsb : Bar | FooSub +>fsfb : Foo | Bar | FooSub if (typeof u === 'object') { >typeof u === 'object' : boolean @@ -233,6 +234,18 @@ class Foo { >fsb : Bar } + if (#field in fsfb) { +>#field in fsfb : boolean +>fsfb : Foo | Bar | FooSub + + fsfb; // good fsfb is 'Foo | FooSub' +>fsfb : Foo | FooSub + + } else { + fsfb; // good fsfb is Bar +>fsfb : Bar + } + class Nested { >Nested : Nested diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts index abea3ed544e64..e274378150990 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts @@ -37,7 +37,7 @@ class Foo { /*2*/in/*3*/ /*4*/v/*5*/ } - flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar) { + flow(u: unknown, n: never, fb: Foo | Bar, fs: FooSub, b: Bar, fsb: FooSub | Bar, fsfb: Foo | FooSub | Bar) { if (typeof u === 'object') { if (#field in n) { @@ -95,6 +95,12 @@ class Foo { fsb; // good fsb is Bar } + if (#field in fsfb) { + fsfb; // good fsfb is 'Foo | FooSub' + } else { + fsfb; // good fsfb is Bar + } + class Nested { m(v: any) { if (#field in v) { From 52b9f2ad2e47404f83f85160755e394dacb230af Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 13:08:29 +0100 Subject: [PATCH 26/41] [squash] add checkExternalEmitHelpers call and new test case Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 4 +++ src/compiler/types.ts | 3 +- tests/baselines/reference/importHelpersES6.js | 5 ++- .../reference/importHelpersES6.symbols | 18 ++++++++--- .../reference/importHelpersES6.types | 11 +++++++ ...elpersNoHelpersForPrivateFields.errors.txt | 23 +++++++++++++ .../importHelpersNoHelpersForPrivateFields.js | 32 +++++++++++++++++++ ...rtHelpersNoHelpersForPrivateFields.symbols | 25 +++++++++++++++ ...portHelpersNoHelpersForPrivateFields.types | 28 ++++++++++++++++ tests/cases/compiler/importHelpersES6.ts | 2 ++ .../importHelpersNoHelpersForPrivateFields.ts | 16 ++++++++++ 11 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/importHelpersNoHelpersForPrivateFields.errors.txt create mode 100644 tests/baselines/reference/importHelpersNoHelpersForPrivateFields.js create mode 100644 tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols create mode 100644 tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types create mode 100644 tests/cases/compiler/importHelpersNoHelpersForPrivateFields.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 57e745863e402..5527b258dbf19 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32046,6 +32046,9 @@ namespace ts { function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { if (isPrivateIdentifier(left)) { + if (languageVersion < ScriptTarget.ESNext) { + checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn); + } const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(left.escapedText, left); if (lexicallyScopedSymbol === undefined) { if (!getContainingClass(left)) { @@ -41664,6 +41667,7 @@ namespace ts { case ExternalEmitHelpers.MakeTemplateObject: return "__makeTemplateObject"; case ExternalEmitHelpers.ClassPrivateFieldGet: return "__classPrivateFieldGet"; case ExternalEmitHelpers.ClassPrivateFieldSet: return "__classPrivateFieldSet"; + case ExternalEmitHelpers.ClassPrivateFieldIn: return "__classPrivateFieldIn"; case ExternalEmitHelpers.CreateBinding: return "__createBinding"; default: return Debug.fail("Unrecognized helper"); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a2d52971330dc..6f99fef5d2964 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6831,7 +6831,8 @@ namespace ts { MakeTemplateObject = 1 << 18, // __makeTemplateObject (used for constructing template string array objects) ClassPrivateFieldGet = 1 << 19, // __classPrivateFieldGet (used by the class private field transformation) ClassPrivateFieldSet = 1 << 20, // __classPrivateFieldSet (used by the class private field transformation) - CreateBinding = 1 << 21, // __createBinding (use by the module transform for (re)exports and namespace imports) + ClassPrivateFieldIn = 1 << 21, // __classPrivateFieldIn (used by the class private field transformation) + CreateBinding = 1 << 22, // __createBinding (use by the module transform for (re)exports and namespace imports) FirstEmitHelper = Extends, LastEmitHelper = CreateBinding, diff --git a/tests/baselines/reference/importHelpersES6.js b/tests/baselines/reference/importHelpersES6.js index 9a7a6bc30c77c..ec2ca96a54b62 100644 --- a/tests/baselines/reference/importHelpersES6.js +++ b/tests/baselines/reference/importHelpersES6.js @@ -5,6 +5,7 @@ declare var dec: any; @dec export class A { #x: number = 1; async f() { this.#x = await this.#x; } + g(u) { return #x in u; } } const o = { a: 1 }; @@ -18,11 +19,12 @@ export declare function __metadata(metadataKey: any, metadataValue: any): Functi export declare function __awaiter(thisArg: any, _arguments: any, P: Function, generator: Function): any; export declare function __classPrivateFieldGet(a: any, b: any, c: any, d: any): any; export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e: any): any; +export declare function __classPrivateFieldIn(a: any, b: any): boolean; //// [a.js] var _A_x; -import { __awaiter, __classPrivateFieldGet, __classPrivateFieldSet, __decorate } from "tslib"; +import { __awaiter, __classPrivateFieldGet, __classPrivateFieldIn, __classPrivateFieldSet, __decorate } from "tslib"; let A = class A { constructor() { _A_x.set(this, 1); @@ -30,6 +32,7 @@ let A = class A { f() { return __awaiter(this, void 0, void 0, function* () { __classPrivateFieldSet(this, _A_x, yield __classPrivateFieldGet(this, _A_x, "f"), "f"); }); } + g(u) { return __classPrivateFieldIn(_A_x, u); } }; _A_x = new WeakMap(); A = __decorate([ diff --git a/tests/baselines/reference/importHelpersES6.symbols b/tests/baselines/reference/importHelpersES6.symbols index 38c044cbe709e..cce576b91581a 100644 --- a/tests/baselines/reference/importHelpersES6.symbols +++ b/tests/baselines/reference/importHelpersES6.symbols @@ -15,15 +15,20 @@ declare var dec: any; >this : Symbol(A, Decl(a.ts, 0, 21)) >this.#x : Symbol(A.#x, Decl(a.ts, 1, 21)) >this : Symbol(A, Decl(a.ts, 0, 21)) + + g(u) { return #x in u; } +>g : Symbol(A.g, Decl(a.ts, 3, 42)) +>u : Symbol(u, Decl(a.ts, 4, 6)) +>u : Symbol(u, Decl(a.ts, 4, 6)) } const o = { a: 1 }; ->o : Symbol(o, Decl(a.ts, 6, 5)) ->a : Symbol(a, Decl(a.ts, 6, 11)) +>o : Symbol(o, Decl(a.ts, 7, 5)) +>a : Symbol(a, Decl(a.ts, 7, 11)) const y = { ...o }; ->y : Symbol(y, Decl(a.ts, 7, 5)) ->o : Symbol(o, Decl(a.ts, 6, 5)) +>y : Symbol(y, Decl(a.ts, 8, 5)) +>o : Symbol(o, Decl(a.ts, 7, 5)) === tests/cases/compiler/tslib.d.ts === export declare function __extends(d: Function, b: Function): void; @@ -78,3 +83,8 @@ export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e >d : Symbol(d, Decl(tslib.d.ts, --, --)) >e : Symbol(e, Decl(tslib.d.ts, --, --)) +export declare function __classPrivateFieldIn(a: any, b: any): boolean; +>__classPrivateFieldIn : Symbol(__classPrivateFieldIn, Decl(tslib.d.ts, --, --)) +>a : Symbol(a, Decl(tslib.d.ts, --, --)) +>b : Symbol(b, Decl(tslib.d.ts, --, --)) + diff --git a/tests/baselines/reference/importHelpersES6.types b/tests/baselines/reference/importHelpersES6.types index bbb6f97969104..11ee2e21cf0a9 100644 --- a/tests/baselines/reference/importHelpersES6.types +++ b/tests/baselines/reference/importHelpersES6.types @@ -18,6 +18,12 @@ declare var dec: any; >await this.#x : number >this.#x : number >this : this + + g(u) { return #x in u; } +>g : (u: any) => boolean +>u : any +>#x in u : boolean +>u : any } const o = { a: 1 }; @@ -76,3 +82,8 @@ export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e >d : any >e : any +export declare function __classPrivateFieldIn(a: any, b: any): boolean; +>__classPrivateFieldIn : (a: any, b: any) => boolean +>a : any +>b : any + diff --git a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.errors.txt b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.errors.txt new file mode 100644 index 0000000000000..3e93768e8971c --- /dev/null +++ b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.errors.txt @@ -0,0 +1,23 @@ +tests/cases/compiler/main.ts(4,9): error TS2343: This syntax requires an imported helper named '__classPrivateFieldSet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. +tests/cases/compiler/main.ts(4,23): error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. +tests/cases/compiler/main.ts(5,9): error TS2343: This syntax requires an imported helper named '__classPrivateFieldIn' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + + +==== tests/cases/compiler/main.ts (3 errors) ==== + export class Foo { + #field = true; + f() { + this.#field = this.#field; + ~~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldSet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + ~~~~~~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldGet' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + #field in this; + ~~~~~~ +!!! error TS2343: This syntax requires an imported helper named '__classPrivateFieldIn' which does not exist in 'tslib'. Consider upgrading your version of 'tslib'. + } + } + +==== tests/cases/compiler/tslib.d.ts (0 errors) ==== + export {} + \ No newline at end of file diff --git a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.js b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.js new file mode 100644 index 0000000000000..d84cca941153b --- /dev/null +++ b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.js @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/importHelpersNoHelpersForPrivateFields.ts] //// + +//// [main.ts] +export class Foo { + #field = true; + f() { + this.#field = this.#field; + #field in this; + } +} + +//// [tslib.d.ts] +export {} + + +//// [main.js] +"use strict"; +var _Foo_field; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Foo = void 0; +const tslib_1 = require("tslib"); +class Foo { + constructor() { + _Foo_field.set(this, true); + } + f() { + (0, tslib_1.__classPrivateFieldSet)(this, _Foo_field, (0, tslib_1.__classPrivateFieldGet)(this, _Foo_field, "f"), "f"); + (0, tslib_1.__classPrivateFieldIn)(_Foo_field, this); + } +} +exports.Foo = Foo; +_Foo_field = new WeakMap(); diff --git a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols new file mode 100644 index 0000000000000..fe46c01d1fbbe --- /dev/null +++ b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols @@ -0,0 +1,25 @@ +=== tests/cases/compiler/main.ts === +export class Foo { +>Foo : Symbol(Foo, Decl(main.ts, 0, 0)) + + #field = true; +>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) + + f() { +>f : Symbol(Foo.f, Decl(main.ts, 1, 18)) + + this.#field = this.#field; +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) +>this.#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + + #field in this; +>this : Symbol(Foo, Decl(main.ts, 0, 0)) + } +} + +=== tests/cases/compiler/tslib.d.ts === +export {} +No type information for this code. +No type information for this code. \ No newline at end of file diff --git a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types new file mode 100644 index 0000000000000..46841900b2f70 --- /dev/null +++ b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types @@ -0,0 +1,28 @@ +=== tests/cases/compiler/main.ts === +export class Foo { +>Foo : Foo + + #field = true; +>#field : boolean +>true : true + + f() { +>f : () => void + + this.#field = this.#field; +>this.#field = this.#field : boolean +>this.#field : boolean +>this : this +>this.#field : boolean +>this : this + + #field in this; +>#field in this : boolean +>this : this + } +} + +=== tests/cases/compiler/tslib.d.ts === +export {} +No type information for this code. +No type information for this code. \ No newline at end of file diff --git a/tests/cases/compiler/importHelpersES6.ts b/tests/cases/compiler/importHelpersES6.ts index 394c01e147841..3655aa77420a6 100644 --- a/tests/cases/compiler/importHelpersES6.ts +++ b/tests/cases/compiler/importHelpersES6.ts @@ -6,6 +6,7 @@ declare var dec: any; @dec export class A { #x: number = 1; async f() { this.#x = await this.#x; } + g(u) { return #x in u; } } const o = { a: 1 }; @@ -19,3 +20,4 @@ export declare function __metadata(metadataKey: any, metadataValue: any): Functi export declare function __awaiter(thisArg: any, _arguments: any, P: Function, generator: Function): any; export declare function __classPrivateFieldGet(a: any, b: any, c: any, d: any): any; export declare function __classPrivateFieldSet(a: any, b: any, c: any, d: any, e: any): any; +export declare function __classPrivateFieldIn(a: any, b: any): boolean; diff --git a/tests/cases/compiler/importHelpersNoHelpersForPrivateFields.ts b/tests/cases/compiler/importHelpersNoHelpersForPrivateFields.ts new file mode 100644 index 0000000000000..e9687dfcd689b --- /dev/null +++ b/tests/cases/compiler/importHelpersNoHelpersForPrivateFields.ts @@ -0,0 +1,16 @@ +// @importHelpers: true +// @target: es2020 +// @module: commonjs +// @lib: esnext +// @moduleResolution: classic +// @filename: main.ts +export class Foo { + #field = true; + f() { + this.#field = this.#field; + #field in this; + } +} + +// @filename: tslib.d.ts +export {} From 476bf247fa462b701013f9670396e6d1b90f9bbb Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 16:34:05 +0100 Subject: [PATCH 27/41] [squash] simplify and correct parser Signed-off-by: Ashley Claymore --- src/compiler/parser.ts | 28 ++++++++----------- src/compiler/transformers/classFields.ts | 8 +++++- .../privateNameHashCharName.errors.txt | 5 +--- .../reference/privateNameHashCharName.types | 1 - .../privateNameInInExpression.errors.txt | 15 ++-------- .../reference/privateNameInInExpression.types | 2 -- ...ressionTransform(target=es2020).errors.txt | 25 ++++++++--------- ...eInInExpressionTransform(target=es2020).js | 28 +++++++++---------- ...ExpressionTransform(target=es2020).symbols | 14 +++++----- ...InExpressionTransform(target=es2020).types | 28 +++++++++---------- ...ressionTransform(target=esnext).errors.txt | 25 ++++++++--------- ...eInInExpressionTransform(target=esnext).js | 28 +++++++++---------- ...ExpressionTransform(target=esnext).symbols | 14 +++++----- ...InExpressionTransform(target=esnext).types | 28 +++++++++---------- .../privateNameInInExpressionTransform.ts | 14 +++++----- 15 files changed, 121 insertions(+), 142 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index db1777dbe5d69..fbde7975b5a04 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4602,15 +4602,8 @@ namespace ts { } function parseBinaryExpressionOrHigher(precedence: OperatorPrecedence): Expression { - // parse a BinaryExpression the LHS is either: - // 1) a PrivateIdentifier when 'in' flag allowed and lookahead matches 'PrivateIdentifier in' - // 2) a UnaryExpression - const pos = getNodePos(); - const lookaheadMatchesPrivateIdentifierIn = token() === SyntaxKind.PrivateIdentifier && !inDisallowInContext() && lookAhead(nextTokenIsInKeyword); - const leftOperand = lookaheadMatchesPrivateIdentifierIn - ? parsePrivateIdentifier() - : parseUnaryExpressionOrHigher(); + const leftOperand = parseUnaryExpressionOrHigher(); return parseBinaryExpressionRest(precedence, leftOperand, pos); } @@ -4647,11 +4640,9 @@ namespace ts { // ^^token; leftOperand = b. Return b ** c to the caller as a rightOperand // a ** b - c // ^token; leftOperand = b. Return b to the caller as a rightOperand - const isPrivateIdentifierInInExpression = token() === SyntaxKind.InKeyword && isPrivateIdentifier(leftOperand); - const consumeCurrentOperator = isPrivateIdentifierInInExpression || - (token() === SyntaxKind.AsteriskAsteriskToken ? + const consumeCurrentOperator = token() === SyntaxKind.AsteriskAsteriskToken ? newPrecedence >= precedence : - newPrecedence > precedence); + newPrecedence > precedence; if (!consumeCurrentOperator) { break; @@ -4746,6 +4737,14 @@ namespace ts { * */ function parseUnaryExpressionOrHigher(): UnaryExpression | BinaryExpression { + /** + * If we have a PrivateIdentifier, parse this unconditionally. + * A privateIdentifier is only valid on its own in the RelationalExpression: `#field in expr`. + * Subsequent steps will emit the error if this is not the case. + */ + if (token() === SyntaxKind.PrivateIdentifier) { + return parsePrivateIdentifier(); + } /** * ES7 UpdateExpression: * 1) LeftHandSideExpression[?Yield] @@ -6140,11 +6139,6 @@ namespace ts { return (tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.NumericLiteral || token() === SyntaxKind.BigIntLiteral || token() === SyntaxKind.StringLiteral) && !scanner.hasPrecedingLineBreak(); } - function nextTokenIsInKeyword() { - nextToken(); - return token() === SyntaxKind.InKeyword; - } - function isDeclaration(): boolean { while (true) { switch (token()) { diff --git a/src/compiler/transformers/classFields.ts b/src/compiler/transformers/classFields.ts index 7102f0f5ed072..ca13b6cef2839 100644 --- a/src/compiler/transformers/classFields.ts +++ b/src/compiler/transformers/classFields.ts @@ -266,12 +266,17 @@ namespace ts { /** * If we visit a private name, this means it is an undeclared private name. - * Replace it with an empty identifier to indicate a problem with the code. + * Replace it with an empty identifier to indicate a problem with the code, + * unless we are in a statement position - otherwise this will not trigger + * a SyntaxError. */ function visitPrivateIdentifier(node: PrivateIdentifier) { if (!shouldTransformPrivateElementsOrClassStaticBlocks) { return node; } + if (isStatement(node.parent)) { + return node; + } return setOriginalNode(factory.createIdentifier(""), node); } @@ -284,6 +289,7 @@ namespace ts { } const privId = node.left; Debug.assertNode(privId, isPrivateIdentifier); + Debug.assert(node.operatorToken.kind === SyntaxKind.InKeyword); const info = accessPrivateIdentifier(privId); if (info) { const receiver = visitNode(node.right, visitor, isExpression); diff --git a/tests/baselines/reference/privateNameHashCharName.errors.txt b/tests/baselines/reference/privateNameHashCharName.errors.txt index 211a62a47f97d..8fdc42b4348ac 100644 --- a/tests/baselines/reference/privateNameHashCharName.errors.txt +++ b/tests/baselines/reference/privateNameHashCharName.errors.txt @@ -1,15 +1,12 @@ tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS1127: Invalid character. -tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS2304: Cannot find name '#'. tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(4,5): error TS1127: Invalid character. tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(7,14): error TS1127: Invalid character. -==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (4 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (3 errors) ==== # ~ !!! error TS1127: Invalid character. - ~ -!!! error TS2304: Cannot find name '#'. class C { # diff --git a/tests/baselines/reference/privateNameHashCharName.types b/tests/baselines/reference/privateNameHashCharName.types index f321b7bcda4e7..520e51ee43495 100644 --- a/tests/baselines/reference/privateNameHashCharName.types +++ b/tests/baselines/reference/privateNameHashCharName.types @@ -1,6 +1,5 @@ === tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts === # -># : any class C { >C : C diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 9a475c70c42e8..9364c5f231169 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,15 +1,12 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2304: Cannot find name '#fiel'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS2304: Cannot find name '#field'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2304: Cannot find name '#field'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(29,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(43,27): error TS2531: Object is possibly 'null'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (6 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -39,16 +36,10 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t !!! error TS2304: Cannot find name '#fiel'. const c = (#field) in v; // Bad - privateID is not an expression on its own - ~~~~~~ -!!! error TS2304: Cannot find name '#field'. - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. for (#field in v) { /**/ } // Bad - 'in' not allowed ~~~~~~ -!!! error TS2304: Cannot find name '#field'. - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. +!!! error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any ~~~~~~~~~~~ diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 684b2b6b11f4e..1fe132a62c1b4 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -81,11 +81,9 @@ class Foo { >c : boolean >(#field) in v : boolean >(#field) : any ->#field : any >v : any for (#field in v) { /**/ } // Bad - 'in' not allowed ->#field : any >v : any for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt index 15d9b2410bfcd..27ed2ec7e2836 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt @@ -1,12 +1,11 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (6 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (5 errors) ==== class Foo { #field = 1; #method() {} @@ -20,25 +19,23 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr #staticMethod in v; // expect Foo's constructor } precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) - ~~~~~~~~~~~~~~~~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) - ~~~~~~~~~~~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + v == #field in v in v; // Good precedence: v == ((#field in v) in v) + ~~~~~~~~~~~ +!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } invalidLHS(v: any) { 'prop' in v = 10; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js index be5174189832e..2efa6bf485247 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js @@ -12,19 +12,19 @@ class Foo { #staticMethod in v; // expect Foo's constructor } precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + v == #field in v in v; // Good precedence: v == ((#field in v) in v) - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } invalidLHS(v: any) { 'prop' in v = 10; @@ -64,14 +64,14 @@ class Foo { __classPrivateFieldIn(_a, v); // expect Foo's constructor } precedence(v) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == __classPrivateFieldIn(_Foo_field, v) == v; // Good precedence: ((v == (#field in v)) == v) - v << __classPrivateFieldIn(_Foo_field, v << v); // Good precedence: (v << (#field in (v << v))) - v << __classPrivateFieldIn(_Foo_field, v) == v; // Good precedence: ((v << (#field in v)) == v) - v == __classPrivateFieldIn(_Foo_field, v) < v; // Good precedence: (v == ((#field in v) < v)) - __classPrivateFieldIn(_Foo_field, v) && __classPrivateFieldIn(_Foo_field, v); // Good precedence: ((#field in v) && (#field in v)) + v == __classPrivateFieldIn(_Foo_field, v) || v; // Good precedence: (v == (#field in v)) || v + v << in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) + v << in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v + v == __classPrivateFieldIn(_Foo_field, v) in v; // Good precedence: v == ((#field in v) in v) + __classPrivateFieldIn(_Foo_field, v) && __classPrivateFieldIn(_Foo_field, v); // Good precedence: (#field in v) && (#field in v) } invalidLHS(v) { 'prop' in v; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols index 4772cc8fe207c..67d39b4f789ab 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols @@ -34,31 +34,31 @@ class Foo { >precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 11, 5)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + v == #field in v in v; // Good precedence: v == ((#field in v) in v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types index 940e4bd1169f2..60457910597a8 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types @@ -40,43 +40,43 @@ class Foo { >precedence : (v: any) => void >v : any - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) ->v == #field in v == v : boolean + v == #field in v || v; // Good precedence: (v == (#field in v)) || v +>v == #field in v || v : any >v == #field in v : boolean >v : any >#field in v : boolean >v : any >v : any - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) ->v << #field in v << v : number + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) +>v << #field in v << v : boolean +>v << #field : number >v : any ->#field in v << v : boolean >v << v : number >v : any >v : any - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v >v << #field in v == v : boolean ->v << #field in v : number +>v << #field in v : boolean +>v << #field : number >v : any ->#field in v : boolean >v : any >v : any - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) ->v == #field in v < v : boolean + v == #field in v in v; // Good precedence: v == ((#field in v) in v) +>v == #field in v in v : boolean >v : any ->#field in v < v : boolean +>#field in v in v : boolean >#field in v : boolean >v : any >v : any - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) >#field in v && #field in v : boolean >#field in v : boolean >v : any diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt index cab9be3a96732..ad670906d9809 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt @@ -1,13 +1,12 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(4,26): error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (7 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (6 errors) ==== class Foo { #field = 1; #method() {} @@ -23,25 +22,23 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr #staticMethod in v; // expect Foo's constructor } precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) - ~~~~~~~~~~~~~~~~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) - ~~~~~~~~~~~ -!!! error TS2363: The right-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + v == #field in v in v; // Good precedence: v == ((#field in v) in v) + ~~~~~~~~~~~ +!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } invalidLHS(v: any) { 'prop' in v = 10; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js index d5fae618c56c1..fc2506e2fc124 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js @@ -12,19 +12,19 @@ class Foo { #staticMethod in v; // expect Foo's constructor } precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + v == #field in v in v; // Good precedence: v == ((#field in v) in v) - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } invalidLHS(v: any) { 'prop' in v = 10; @@ -59,14 +59,14 @@ class Foo { #staticMethod in v; // expect Foo's constructor } precedence(v) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v + v == #field in v in v; // Good precedence: v == ((#field in v) in v) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } invalidLHS(v) { 'prop' in v; diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols index 4772cc8fe207c..67d39b4f789ab 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols @@ -34,31 +34,31 @@ class Foo { >precedence : Symbol(Foo.precedence, Decl(privateNameInInExpressionTransform.ts, 11, 5)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + v == #field in v in v; // Good precedence: v == ((#field in v) in v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types index 940e4bd1169f2..60457910597a8 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types @@ -40,43 +40,43 @@ class Foo { >precedence : (v: any) => void >v : any - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) ->v == #field in v == v : boolean + v == #field in v || v; // Good precedence: (v == (#field in v)) || v +>v == #field in v || v : any >v == #field in v : boolean >v : any >#field in v : boolean >v : any >v : any - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) ->v << #field in v << v : number + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) +>v << #field in v << v : boolean +>v << #field : number >v : any ->#field in v << v : boolean >v << v : number >v : any >v : any - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v >v << #field in v == v : boolean ->v << #field in v : number +>v << #field in v : boolean +>v << #field : number >v : any ->#field in v : boolean >v : any >v : any - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) ->v == #field in v < v : boolean + v == #field in v in v; // Good precedence: v == ((#field in v) in v) +>v == #field in v in v : boolean >v : any ->#field in v < v : boolean +>#field in v in v : boolean >#field in v : boolean >v : any >v : any - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) >#field in v && #field in v : boolean >#field in v : boolean >v : any diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index e15d2a8b03ea1..bf09f9b4b14b1 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -13,19 +13,19 @@ class Foo { #staticMethod in v; // expect Foo's constructor } precedence(v: any) { - // '==' has lower precedence than 'in' - // '<' has same precedence than 'in' + // '==' and '||' have lower precedence than 'in' + // 'in' has same precedence than 'in' // '<<' has higher precedence than 'in' - v == #field in v == v; // Good precedence: ((v == (#field in v)) == v) + v == #field in v || v; // Good precedence: (v == (#field in v)) || v - v << #field in v << v; // Good precedence: (v << (#field in (v << v))) + v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) - v << #field in v == v; // Good precedence: ((v << (#field in v)) == v) + v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v - v == #field in v < v; // Good precedence: (v == ((#field in v) < v)) + v == #field in v in v; // Good precedence: v == ((#field in v) in v) - #field in v && #field in v; // Good precedence: ((#field in v) && (#field in v)) + #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } invalidLHS(v: any) { 'prop' in v = 10; From be26d0af49a8efe342f50c6cac943f0bf0bc3948 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 18:40:58 +0100 Subject: [PATCH 28/41] [squash] move most of the added checker logic to expression level Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 64 +++++++++++-------- .../fixForgottenThisPropertyAccess.ts | 2 +- .../privateNameHashCharName.errors.txt | 5 +- .../privateNameInInExpression.errors.txt | 15 ++++- .../reference/privateNameInInExpression.types | 8 +-- ...ressionTransform(target=es2020).errors.txt | 11 +++- ...InExpressionTransform(target=es2020).types | 4 +- ...ressionTransform(target=esnext).errors.txt | 11 +++- ...InExpressionTransform(target=esnext).types | 4 +- 9 files changed, 84 insertions(+), 40 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5527b258dbf19..62ca3e1cbffd1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23887,7 +23887,7 @@ namespace ts { const privateId = expr.left; Debug.assertNode(privateId, isPrivateIdentifier); - const symbol = lookupSymbolForPrivateIdentifierDeclaration(privateId.escapedText, privateId); + const symbol = getNodeLinks(privateId.parent).resolvedSymbol; if (symbol === undefined) { return type; } @@ -27737,6 +27737,30 @@ namespace ts { } } + function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type { + // The only valid position for a PrivateIdentifier to appear as an expression is on the left side of + // the `#field in expr` BinaryExpression + const isPrivateFieldInInExpression = isBinaryExpression(privId.parent) + && privId.parent.left === privId + && privId.parent.operatorToken.kind === SyntaxKind.InKeyword; + if (isPrivateFieldInInExpression) { + const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); + if (lexicallyScopedSymbol === undefined) { + if (!getContainingClass(privId)) { + error(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + } + } + else { + markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); + getNodeLinks(privId.parent).resolvedSymbol = lexicallyScopedSymbol; + } + } + else { + error(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + } + return anyType; + } + function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined { return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName); } @@ -32045,35 +32069,23 @@ namespace ts { } function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { - if (isPrivateIdentifier(left)) { + const privIdOnLeft = isPrivateIdentifier(left); + if (privIdOnLeft) { if (languageVersion < ScriptTarget.ESNext) { checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn); } - const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(left.escapedText, left); - if (lexicallyScopedSymbol === undefined) { - if (!getContainingClass(left)) { - error(left, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); - } - else { - const suggestion = getSuggestedSymbolForNonexistentProperty(left, rightType); - if (suggestion) { - const suggestedName = symbolName(suggestion); - error(left, Diagnostics.Cannot_find_name_0_Did_you_mean_1, diagnosticName(left), suggestedName); - } - else { - error(left, Diagnostics.Cannot_find_name_0, diagnosticName(left)); - } - } - return anyType; - } - - markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); - getNodeLinks(left.parent).resolvedSymbol = lexicallyScopedSymbol; - if (rightType === silentNeverType) { - return silentNeverType; + // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type + // which provides us with the oppotunity to emit more detailed errors + const symbol = getNodeLinks(left.parent).resolvedSymbol; + if (!symbol) { + const isUncheckedJS = isUncheckedJSSuggestion(left, rightType.symbol, /*excludeClasses*/ true); + reportNonexistentProperty(left, rightType, isUncheckedJS); } } - else { + if (leftType === silentNeverType || rightType === silentNeverType) { + return silentNeverType; + } + if (!privIdOnLeft) { if (leftType === silentNeverType || rightType === silentNeverType) { return silentNeverType; } @@ -33486,6 +33498,8 @@ namespace ts { switch (kind) { case SyntaxKind.Identifier: return checkIdentifier(node as Identifier, checkMode); + case SyntaxKind.PrivateIdentifier: + return checkPrivateIdentifierExpression(node as PrivateIdentifier); case SyntaxKind.ThisKeyword: return checkThisExpression(node); case SyntaxKind.SuperKeyword: diff --git a/src/services/codefixes/fixForgottenThisPropertyAccess.ts b/src/services/codefixes/fixForgottenThisPropertyAccess.ts index 067ce4b0b9e26..9e4a386e7f315 100644 --- a/src/services/codefixes/fixForgottenThisPropertyAccess.ts +++ b/src/services/codefixes/fixForgottenThisPropertyAccess.ts @@ -32,7 +32,7 @@ namespace ts.codefix { function getInfo(sourceFile: SourceFile, pos: number, diagCode: number): Info | undefined { const node = getTokenAtPosition(sourceFile, pos); - if (isIdentifier(node)) { + if (isIdentifier(node) || isPrivateIdentifier(node)) { return { node, className: diagCode === didYouMeanStaticMemberCode ? getContainingClass(node)!.name!.text : undefined }; } } diff --git a/tests/baselines/reference/privateNameHashCharName.errors.txt b/tests/baselines/reference/privateNameHashCharName.errors.txt index 8fdc42b4348ac..4e47406d104dc 100644 --- a/tests/baselines/reference/privateNameHashCharName.errors.txt +++ b/tests/baselines/reference/privateNameHashCharName.errors.txt @@ -1,12 +1,15 @@ tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS1127: Invalid character. +tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(4,5): error TS1127: Invalid character. tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(7,14): error TS1127: Invalid character. -==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (3 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (4 errors) ==== # ~ !!! error TS1127: Invalid character. + ~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. class C { # diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 9364c5f231169..4604c5bc55565 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,12 +1,15 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2304: Cannot find name '#fiel'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(29,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(43,27): error TS2531: Object is possibly 'null'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS2339: Property '#field' does not exist on type 'Foo'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (6 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -33,13 +36,17 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #fiel in v; // Bad - typo in privateID ~~~~~ -!!! error TS2304: Cannot find name '#fiel'. +!!! error TS2339: Property '#fiel' does not exist on type 'any'. const c = (#field) in v; // Bad - privateID is not an expression on its own + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. for (#field in v) { /**/ } // Bad - 'in' not allowed ~~~~~~ !!! error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any ~~~~~~~~~~~ @@ -132,6 +139,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t function badSyntax(v: Foo) { return #field in v; // Bad - outside of class ~~~~~~ +!!! error TS2339: Property '#field' does not exist on type 'Foo'. + ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 1fe132a62c1b4..51244c05c7607 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -73,8 +73,8 @@ class Foo { >v : any const b = #fiel in v; // Bad - typo in privateID ->b : any ->#fiel in v : any +>b : boolean +>#fiel in v : boolean >v : any const c = (#field) in v; // Bad - privateID is not an expression on its own @@ -275,11 +275,11 @@ class Bar { notFoo = true } >true : true function badSyntax(v: Foo) { ->badSyntax : (v: Foo) => any +>badSyntax : (v: Foo) => boolean >v : Foo return #field in v; // Bad - outside of class ->#field in v : any +>#field in v : boolean >v : Foo } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt index 27ed2ec7e2836..ffa5b573db7a0 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt @@ -1,11 +1,14 @@ +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS2339: Property '#field' does not exist on type 'Foo'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (5 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (8 errors) ==== class Foo { #field = 1; #method() {} @@ -26,10 +29,14 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr v == #field in v || v; // Good precedence: (v == (#field in v)) || v v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ @@ -57,6 +64,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr function syntaxError(v: Foo) { return #field in v; // expect `return in v` so runtime will have a syntax error ~~~~~~ +!!! error TS2339: Property '#field' does not exist on type 'Foo'. + ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types index 60457910597a8..940d2a3589e11 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types @@ -118,11 +118,11 @@ class Bar { } function syntaxError(v: Foo) { ->syntaxError : (v: Foo) => any +>syntaxError : (v: Foo) => boolean >v : Foo return #field in v; // expect `return in v` so runtime will have a syntax error ->#field in v : any +>#field in v : boolean >v : Foo } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt index ad670906d9809..fc5752c007444 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt @@ -1,12 +1,15 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(4,26): error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS2339: Property '#field' does not exist on type 'Foo'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (6 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (9 errors) ==== class Foo { #field = 1; #method() {} @@ -29,10 +32,14 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr v == #field in v || v; // Good precedence: (v == (#field in v)) || v v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v + ~~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ @@ -60,6 +67,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr function syntaxError(v: Foo) { return #field in v; // expect `return in v` so runtime will have a syntax error ~~~~~~ +!!! error TS2339: Property '#field' does not exist on type 'Foo'. + ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types index 60457910597a8..940d2a3589e11 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types @@ -118,11 +118,11 @@ class Bar { } function syntaxError(v: Foo) { ->syntaxError : (v: Foo) => any +>syntaxError : (v: Foo) => boolean >v : Foo return #field in v; // expect `return in v` so runtime will have a syntax error ->#field in v : any +>#field in v : boolean >v : Foo } From f7ddce591273821f4235d09e189f7081592f8466 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 19:03:33 +0100 Subject: [PATCH 29/41] [squash] always error when privateId could not be resolved Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 11 ++++------- .../reference/privateNameInInExpression.errors.txt | 5 ++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 62ca3e1cbffd1..4f5a8d1ea5fdd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -27743,19 +27743,16 @@ namespace ts { const isPrivateFieldInInExpression = isBinaryExpression(privId.parent) && privId.parent.left === privId && privId.parent.operatorToken.kind === SyntaxKind.InKeyword; + let symbolResolved = false; if (isPrivateFieldInInExpression) { const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); - if (lexicallyScopedSymbol === undefined) { - if (!getContainingClass(privId)) { - error(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); - } - } - else { + if (lexicallyScopedSymbol) { + symbolResolved = true; markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); getNodeLinks(privId.parent).resolvedSymbol = lexicallyScopedSymbol; } } - else { + if (!symbolResolved) { error(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); } return anyType; diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 4604c5bc55565..f11b91e924d16 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,5 +1,6 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS18016: Private identifiers are not allowed outside class bodies. @@ -9,7 +10,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (10 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -37,6 +38,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #fiel in v; // Bad - typo in privateID ~~~~~ !!! error TS2339: Property '#fiel' does not exist on type 'any'. + ~~~~~ +!!! error TS18016: Private identifiers are not allowed outside class bodies. const c = (#field) in v; // Bad - privateID is not an expression on its own ~~~~~~ From 01e0e60df029d3a7b81965e9074fb3107b2f7f27 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 19:37:34 +0100 Subject: [PATCH 30/41] [squash] reword comment Signed-off-by: Ashley Claymore --- src/compiler/parser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index fbde7975b5a04..f564baf65a68c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4641,8 +4641,8 @@ namespace ts { // a ** b - c // ^token; leftOperand = b. Return b to the caller as a rightOperand const consumeCurrentOperator = token() === SyntaxKind.AsteriskAsteriskToken ? - newPrecedence >= precedence : - newPrecedence > precedence; + newPrecedence >= precedence : + newPrecedence > precedence; if (!consumeCurrentOperator) { break; @@ -4740,7 +4740,7 @@ namespace ts { /** * If we have a PrivateIdentifier, parse this unconditionally. * A privateIdentifier is only valid on its own in the RelationalExpression: `#field in expr`. - * Subsequent steps will emit the error if this is not the case. + * The checker will emit an error if this is not the case. */ if (token() === SyntaxKind.PrivateIdentifier) { return parsePrivateIdentifier(); From 913d044cf7931e58d0500d82c13f3486c275bee8 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 19:44:23 +0100 Subject: [PATCH 31/41] [squash] fix codeFixSpelling test Signed-off-by: Ashley Claymore --- tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts index daba84dde2012..e6b023dd1c00f 100644 --- a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts +++ b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts @@ -9,6 +9,7 @@ verify.codeFixAvailable([ { description: "Change spelling to '#foo'" }, + { description: "Add 'this.' to unresolved variable" }, { description: "Remove unused declaration for: '#foo'" }, ]); @@ -17,3 +18,9 @@ verify.codeFix({ description: "Change spelling to '#foo'", newRangeContent: "return #foo in v;" }); + +verify.codeFix({ + index: 1, + description: "Add 'this.' to unresolved variable", + newRangeContent: "return this.#fo in v;" +}); From 7c49552cd516da620b903edccc61af54d1872c3b Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Wed, 22 Sep 2021 19:57:50 +0100 Subject: [PATCH 32/41] [squash] do less work Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4f5a8d1ea5fdd..17f649c5601a7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32083,9 +32083,6 @@ namespace ts { return silentNeverType; } if (!privIdOnLeft) { - if (leftType === silentNeverType || rightType === silentNeverType) { - return silentNeverType; - } leftType = checkNonNullType(leftType, left); // TypeScript 1.0 spec (April 2014): 4.15.5 // Require the left operand to be of type Any, the String primitive type, or the Number primitive type. @@ -40288,7 +40285,7 @@ namespace ts { if (links.resolvedSymbol) { return links.resolvedSymbol; } - checkBinaryExpression(name.parent, CheckMode.Normal); + checkPrivateIdentifierExpression(name); return links.resolvedSymbol; } From c6b039fa078cf478ac93f91e966469b0ef54727e Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 10:42:09 +0100 Subject: [PATCH 33/41] store symbol by priateId not binaryExpression Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 35 ++++++++----------- src/compiler/utilities.ts | 2 ++ src/compiler/utilitiesPublic.ts | 1 + .../privateNameInInExpression.symbols | 21 +++++++++++ .../reference/privateNameInInExpression.types | 23 ++++++++++++ ...ExpressionTransform(target=es2020).symbols | 10 ++++++ ...InExpressionTransform(target=es2020).types | 11 ++++++ ...ExpressionTransform(target=esnext).symbols | 10 ++++++ ...InExpressionTransform(target=esnext).types | 11 ++++++ .../privateNameInInExpressionUnused.symbols | 1 + .../privateNameInInExpressionUnused.types | 1 + 11 files changed, 105 insertions(+), 21 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 17f649c5601a7..59f3b82681c2a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23887,7 +23887,7 @@ namespace ts { const privateId = expr.left; Debug.assertNode(privateId, isPrivateIdentifier); - const symbol = getNodeLinks(privateId.parent).resolvedSymbol; + const symbol = getNodeLinks(privateId).resolvedSymbol; if (symbol === undefined) { return type; } @@ -27738,18 +27738,13 @@ namespace ts { } function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type { - // The only valid position for a PrivateIdentifier to appear as an expression is on the left side of - // the `#field in expr` BinaryExpression - const isPrivateFieldInInExpression = isBinaryExpression(privId.parent) - && privId.parent.left === privId - && privId.parent.operatorToken.kind === SyntaxKind.InKeyword; let symbolResolved = false; - if (isPrivateFieldInInExpression) { - const lexicallyScopedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); + if (isExpressionNode(privId)) { + const lexicallyScopedSymbol = getNodeLinks(privId).resolvedSymbol || lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); if (lexicallyScopedSymbol) { symbolResolved = true; markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); - getNodeLinks(privId.parent).resolvedSymbol = lexicallyScopedSymbol; + getNodeLinks(privId).resolvedSymbol = lexicallyScopedSymbol; } } if (!symbolResolved) { @@ -32072,9 +32067,8 @@ namespace ts { checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn); } // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type - // which provides us with the oppotunity to emit more detailed errors - const symbol = getNodeLinks(left.parent).resolvedSymbol; - if (!symbol) { + // which provides us with the opportunity to emit more detailed errors + if (!getNodeLinks(left).resolvedSymbol) { const isUncheckedJS = isUncheckedJSSuggestion(left, rightType.symbol, /*excludeClasses*/ true); reportNonexistentProperty(left, rightType, isUncheckedJS); } @@ -40251,6 +40245,14 @@ namespace ts { } return result; } + else if (isPrivateIdentifier(name)) { + const links = getNodeLinks(name); + if (links.resolvedSymbol) { + return links.resolvedSymbol; + } + checkPrivateIdentifierExpression(name); + return links.resolvedSymbol; + } else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) { const links = getNodeLinks(name); if (links.resolvedSymbol) { @@ -40280,15 +40282,6 @@ namespace ts { return resolveEntityName(name as Identifier, /*meaning*/ SymbolFlags.FunctionScopedVariable); } - if (isPrivateIdentifier(name) && isBinaryExpression(name.parent)) { - const links = getNodeLinks(name.parent); - if (links.resolvedSymbol) { - return links.resolvedSymbol; - } - checkPrivateIdentifierExpression(name); - return links.resolvedSymbol; - } - return undefined; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 31fe24dc9d855..86363d8296077 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1949,6 +1949,8 @@ namespace ts { node = node.parent; } return node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node); + case SyntaxKind.PrivateIdentifier: + return isBinaryExpression(node.parent) && node.parent.left === node && node.parent.operatorToken.kind === SyntaxKind.InKeyword; case SyntaxKind.Identifier: if (node.parent.kind === SyntaxKind.TypeQuery || isJSDocLinkLike(node.parent) || isJSDocNameReference(node.parent) || isJSDocMemberName(node.parent) || isJSXTagName(node)) { return true; diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 91b0a1cc9a9cc..fc58d1d0000f4 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1512,6 +1512,7 @@ namespace ts { case SyntaxKind.ClassExpression: case SyntaxKind.FunctionExpression: case SyntaxKind.Identifier: + case SyntaxKind.PrivateIdentifier: // technically this is only an Expression if it's in a `#field in expr` BinaryExpression case SyntaxKind.RegularExpressionLiteral: case SyntaxKind.NumericLiteral: case SyntaxKind.BigIntLiteral: diff --git a/tests/baselines/reference/privateNameInInExpression.symbols b/tests/baselines/reference/privateNameInInExpression.symbols index 56620d7021c93..2827e42d11150 100644 --- a/tests/baselines/reference/privateNameInInExpression.symbols +++ b/tests/baselines/reference/privateNameInInExpression.symbols @@ -20,27 +20,33 @@ class Foo { const a = #field in v; >a : Symbol(a, Decl(privateNameInInExpression.ts, 7, 13)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) const b = #field in v.p1.p2; >b : Symbol(b, Decl(privateNameInInExpression.ts, 9, 13)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) const c = #field in (v as {}); >c : Symbol(c, Decl(privateNameInInExpression.ts, 11, 13)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) const d = #field in (v as Foo); >d : Symbol(d, Decl(privateNameInInExpression.ts, 13, 13)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) >Foo : Symbol(Foo, Decl(privateNameInInExpression.ts, 0, 0)) const e = #field in (v as never); >e : Symbol(e, Decl(privateNameInInExpression.ts, 15, 13)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) for (let f in #field in v as any) { /**/ } // unlikely but valid >f : Symbol(f, Decl(privateNameInInExpression.ts, 17, 16)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 6, 12)) } badRhs(v: any) { @@ -49,6 +55,7 @@ class Foo { const a = #field in (v as unknown); // Bad - RHS of in must be object type or any >a : Symbol(a, Decl(privateNameInInExpression.ts, 20, 13)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) const b = #fiel in v; // Bad - typo in privateID @@ -64,6 +71,7 @@ class Foo { for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any >d : Symbol(d, Decl(privateNameInInExpression.ts, 28, 16)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 19, 11)) } whitespace(v: any) { @@ -73,6 +81,7 @@ class Foo { const a = v && /*0*/#field/*1*/ >a : Symbol(a, Decl(privateNameInInExpression.ts, 31, 13)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 30, 15)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) /*2*/in/*3*/ /*4*/v/*5*/ @@ -101,6 +110,7 @@ class Foo { >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) if (#field in n) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >n : Symbol(n, Decl(privateNameInInExpression.ts, 35, 20)) n; // good n is never @@ -108,6 +118,7 @@ class Foo { } if (#field in u) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is Foo @@ -122,6 +133,7 @@ class Foo { >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) if (#field in u) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is Foo @@ -133,6 +145,7 @@ class Foo { } if (#method in u) { +>#method : Symbol(Foo.#method, Decl(privateNameInInExpression.ts, 2, 28)) >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is Foo @@ -140,6 +153,7 @@ class Foo { } if (#staticField in u) { +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpression.ts, 1, 15)) >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is typeof Foo @@ -147,6 +161,7 @@ class Foo { } if (#staticMethod in u) { +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpression.ts, 3, 16)) >u : Symbol(u, Decl(privateNameInInExpression.ts, 35, 9)) u; // good u is typeof Foo @@ -156,6 +171,7 @@ class Foo { } if (#field in fb) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >fb : Symbol(fb, Decl(privateNameInInExpression.ts, 35, 30)) fb; // good fb is Foo @@ -167,6 +183,7 @@ class Foo { } if (#field in fs) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >fs : Symbol(fs, Decl(privateNameInInExpression.ts, 35, 45)) fs; // good fs is FooSub @@ -178,6 +195,7 @@ class Foo { } if (#field in b) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >b : Symbol(b, Decl(privateNameInInExpression.ts, 35, 57)) b; // good b is 'Bar & Foo' @@ -189,6 +207,7 @@ class Foo { } if (#field in fsb) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >fsb : Symbol(fsb, Decl(privateNameInInExpression.ts, 35, 65)) fsb; // good fsb is FooSub @@ -200,6 +219,7 @@ class Foo { } if (#field in fsfb) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >fsfb : Symbol(fsfb, Decl(privateNameInInExpression.ts, 35, 84)) fsfb; // good fsfb is 'Foo | FooSub' @@ -218,6 +238,7 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14)) if (#field in v) { +>#field : Symbol(Foo.#field, Decl(privateNameInInExpression.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpression.ts, 100, 14)) v; // good v is Foo diff --git a/tests/baselines/reference/privateNameInInExpression.types b/tests/baselines/reference/privateNameInInExpression.types index 51244c05c7607..af1ce8a754f1c 100644 --- a/tests/baselines/reference/privateNameInInExpression.types +++ b/tests/baselines/reference/privateNameInInExpression.types @@ -23,11 +23,13 @@ class Foo { const a = #field in v; >a : boolean >#field in v : boolean +>#field : any >v : any const b = #field in v.p1.p2; >b : boolean >#field in v.p1.p2 : boolean +>#field : any >v.p1.p2 : any >v.p1 : any >v : any @@ -37,6 +39,7 @@ class Foo { const c = #field in (v as {}); >c : boolean >#field in (v as {}) : boolean +>#field : any >(v as {}) : {} >v as {} : {} >v : any @@ -44,6 +47,7 @@ class Foo { const d = #field in (v as Foo); >d : boolean >#field in (v as Foo) : boolean +>#field : any >(v as Foo) : Foo >v as Foo : Foo >v : any @@ -51,6 +55,7 @@ class Foo { const e = #field in (v as never); >e : boolean >#field in (v as never) : boolean +>#field : any >(v as never) : never >v as never : never >v : any @@ -59,6 +64,7 @@ class Foo { >f : string >#field in v as any : any >#field in v : boolean +>#field : any >v : any } badRhs(v: any) { @@ -68,6 +74,7 @@ class Foo { const a = #field in (v as unknown); // Bad - RHS of in must be object type or any >a : boolean >#field in (v as unknown) : boolean +>#field : any >(v as unknown) : unknown >v as unknown : unknown >v : any @@ -75,6 +82,7 @@ class Foo { const b = #fiel in v; // Bad - typo in privateID >b : boolean >#fiel in v : boolean +>#fiel : any >v : any const c = (#field) in v; // Bad - privateID is not an expression on its own @@ -89,6 +97,7 @@ class Foo { for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any >d : string >#field in v : boolean +>#field : any >v : any } whitespace(v: any) { @@ -100,6 +109,7 @@ class Foo { >v && /*0*/#field/*1*/ /*2*/in/*3*/ /*4*/v : any >v : any >#field/*1*/ /*2*/in/*3*/ /*4*/v : boolean +>#field : any /*2*/in/*3*/ /*4*/v/*5*/ @@ -123,6 +133,7 @@ class Foo { if (#field in n) { >#field in n : boolean +>#field : any >n : never n; // good n is never @@ -131,6 +142,7 @@ class Foo { if (#field in u) { >#field in u : boolean +>#field : any >u : object | null u; // good u is Foo @@ -148,6 +160,7 @@ class Foo { if (#field in u) { >#field in u : boolean +>#field : any >u : object u; // good u is Foo @@ -160,6 +173,7 @@ class Foo { if (#method in u) { >#method in u : boolean +>#method : any >u : object u; // good u is Foo @@ -168,6 +182,7 @@ class Foo { if (#staticField in u) { >#staticField in u : boolean +>#staticField : any >u : object u; // good u is typeof Foo @@ -176,6 +191,7 @@ class Foo { if (#staticMethod in u) { >#staticMethod in u : boolean +>#staticMethod : any >u : object u; // good u is typeof Foo @@ -186,6 +202,7 @@ class Foo { if (#field in fb) { >#field in fb : boolean +>#field : any >fb : Foo | Bar fb; // good fb is Foo @@ -198,6 +215,7 @@ class Foo { if (#field in fs) { >#field in fs : boolean +>#field : any >fs : FooSub fs; // good fs is FooSub @@ -210,6 +228,7 @@ class Foo { if (#field in b) { >#field in b : boolean +>#field : any >b : Bar b; // good b is 'Bar & Foo' @@ -222,6 +241,7 @@ class Foo { if (#field in fsb) { >#field in fsb : boolean +>#field : any >fsb : Bar | FooSub fsb; // good fsb is FooSub @@ -234,6 +254,7 @@ class Foo { if (#field in fsfb) { >#field in fsfb : boolean +>#field : any >fsfb : Foo | Bar | FooSub fsfb; // good fsfb is 'Foo | FooSub' @@ -253,6 +274,7 @@ class Foo { if (#field in v) { >#field in v : boolean +>#field : any >v : any v; // good v is Foo @@ -280,6 +302,7 @@ function badSyntax(v: Foo) { return #field in v; // Bad - outside of class >#field in v : boolean +>#field : any >v : Foo } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols index 67d39b4f789ab..d156de810aa25 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols @@ -19,15 +19,19 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #field in v; // expect Foo's 'field' WeakMap +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #method in v; // expect Foo's 'instances' WeakSet +>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #staticField in v; // expect Foo's constructor +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #staticMethod in v; // expect Foo's constructor +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) } precedence(v: any) { @@ -40,6 +44,7 @@ class Foo { v == #field in v || v; // Good precedence: (v == (#field in v)) || v >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) @@ -55,11 +60,14 @@ class Foo { v == #field in v in v; // Good precedence: v == ((#field in v) in v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) } invalidLHS(v: any) { @@ -70,6 +78,7 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) #field in v = 10; +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) } } @@ -85,6 +94,7 @@ class Bar { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) #field in v; // expect Bar's 'field' WeakMap +>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) } } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types index 940d2a3589e11..6dfc66fbc3151 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types @@ -22,18 +22,22 @@ class Foo { #field in v; // expect Foo's 'field' WeakMap >#field in v : boolean +>#field : any >v : any #method in v; // expect Foo's 'instances' WeakSet >#method in v : boolean +>#method : any >v : any #staticField in v; // expect Foo's constructor >#staticField in v : boolean +>#staticField : any >v : any #staticMethod in v; // expect Foo's constructor >#staticMethod in v : boolean +>#staticMethod : any >v : any } precedence(v: any) { @@ -49,6 +53,7 @@ class Foo { >v == #field in v : boolean >v : any >#field in v : boolean +>#field : any >v : any >v : any @@ -73,14 +78,17 @@ class Foo { >v : any >#field in v in v : boolean >#field in v : boolean +>#field : any >v : any >v : any #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) >#field in v && #field in v : boolean >#field in v : boolean +>#field : any >v : any >#field in v : boolean +>#field : any >v : Foo } invalidLHS(v: any) { @@ -95,6 +103,7 @@ class Foo { #field in v = 10; >#field in v : boolean +>#field : any >v : any >10 : 10 } @@ -113,6 +122,7 @@ class Bar { #field in v; // expect Bar's 'field' WeakMap >#field in v : boolean +>#field : any >v : any } } @@ -123,6 +133,7 @@ function syntaxError(v: Foo) { return #field in v; // expect `return in v` so runtime will have a syntax error >#field in v : boolean +>#field : any >v : Foo } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols index 67d39b4f789ab..d156de810aa25 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols @@ -19,15 +19,19 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #field in v; // expect Foo's 'field' WeakMap +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #method in v; // expect Foo's 'instances' WeakSet +>#method : Symbol(Foo.#method, Decl(privateNameInInExpressionTransform.ts, 1, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #staticField in v; // expect Foo's constructor +>#staticField : Symbol(Foo.#staticField, Decl(privateNameInInExpressionTransform.ts, 2, 16)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) #staticMethod in v; // expect Foo's constructor +>#staticMethod : Symbol(Foo.#staticMethod, Decl(privateNameInInExpressionTransform.ts, 3, 27)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 6, 10)) } precedence(v: any) { @@ -40,6 +44,7 @@ class Foo { v == #field in v || v; // Good precedence: (v == (#field in v)) || v >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) @@ -55,11 +60,14 @@ class Foo { v == #field in v in v; // Good precedence: v == ((#field in v) in v) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) } invalidLHS(v: any) { @@ -70,6 +78,7 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) #field in v = 10; +>#field : Symbol(Foo.#field, Decl(privateNameInInExpressionTransform.ts, 0, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 27, 15)) } } @@ -85,6 +94,7 @@ class Bar { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) #field in v; // expect Bar's 'field' WeakMap +>#field : Symbol(Bar.#field, Decl(privateNameInInExpressionTransform.ts, 33, 11)) >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 35, 10)) } } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types index 940d2a3589e11..6dfc66fbc3151 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types @@ -22,18 +22,22 @@ class Foo { #field in v; // expect Foo's 'field' WeakMap >#field in v : boolean +>#field : any >v : any #method in v; // expect Foo's 'instances' WeakSet >#method in v : boolean +>#method : any >v : any #staticField in v; // expect Foo's constructor >#staticField in v : boolean +>#staticField : any >v : any #staticMethod in v; // expect Foo's constructor >#staticMethod in v : boolean +>#staticMethod : any >v : any } precedence(v: any) { @@ -49,6 +53,7 @@ class Foo { >v == #field in v : boolean >v : any >#field in v : boolean +>#field : any >v : any >v : any @@ -73,14 +78,17 @@ class Foo { >v : any >#field in v in v : boolean >#field in v : boolean +>#field : any >v : any >v : any #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) >#field in v && #field in v : boolean >#field in v : boolean +>#field : any >v : any >#field in v : boolean +>#field : any >v : Foo } invalidLHS(v: any) { @@ -95,6 +103,7 @@ class Foo { #field in v = 10; >#field in v : boolean +>#field : any >v : any >10 : 10 } @@ -113,6 +122,7 @@ class Bar { #field in v; // expect Bar's 'field' WeakMap >#field in v : boolean +>#field : any >v : any } } @@ -123,6 +133,7 @@ function syntaxError(v: Foo) { return #field in v; // expect `return in v` so runtime will have a syntax error >#field in v : boolean +>#field : any >v : Foo } diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.symbols b/tests/baselines/reference/privateNameInInExpressionUnused.symbols index 03c516ae887f9..027c62bc16385 100644 --- a/tests/baselines/reference/privateNameInInExpressionUnused.symbols +++ b/tests/baselines/reference/privateNameInInExpressionUnused.symbols @@ -16,6 +16,7 @@ class Foo { // This should count as using/reading '#brand' return #brand in v; +>#brand : Symbol(Foo.#brand, Decl(privateNameInInExpressionUnused.ts, 1, 23)) >v : Symbol(v, Decl(privateNameInInExpressionUnused.ts, 4, 10)) } } diff --git a/tests/baselines/reference/privateNameInInExpressionUnused.types b/tests/baselines/reference/privateNameInInExpressionUnused.types index 74eea43e013b8..95d8c12e0530d 100644 --- a/tests/baselines/reference/privateNameInInExpressionUnused.types +++ b/tests/baselines/reference/privateNameInInExpressionUnused.types @@ -15,6 +15,7 @@ class Foo { // This should count as using/reading '#brand' return #brand in v; >#brand in v : boolean +>#brand : any >v : any } } From 6f56c6afdb18c34d6c36ac39b2cd39e3b872bfb3 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 11:03:14 +0100 Subject: [PATCH 34/41] moved parsePrivateIdentifier into parsePrimaryExpression Signed-off-by: Ashley Claymore --- src/compiler/parser.ts | 17 +++++++++-------- ...portHelpersNoHelpersForPrivateFields.symbols | 1 + ...importHelpersNoHelpersForPrivateFields.types | 1 + 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f564baf65a68c..cbc9b026f0817 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -4737,14 +4737,6 @@ namespace ts { * */ function parseUnaryExpressionOrHigher(): UnaryExpression | BinaryExpression { - /** - * If we have a PrivateIdentifier, parse this unconditionally. - * A privateIdentifier is only valid on its own in the RelationalExpression: `#field in expr`. - * The checker will emit an error if this is not the case. - */ - if (token() === SyntaxKind.PrivateIdentifier) { - return parsePrivateIdentifier(); - } /** * ES7 UpdateExpression: * 1) LeftHandSideExpression[?Yield] @@ -5611,6 +5603,15 @@ namespace ts { break; case SyntaxKind.TemplateHead: return parseTemplateExpression(/* isTaggedTemplate */ false); + case SyntaxKind.PrivateIdentifier: + /** + * If we have a PrivateIdentifier, parse this unconditionally. Even though a privateIdentifier is + * only syntactically valid here when it in in the BinaryExpression: `#field in expr`. + * The checker will emit an error for invalid cases. + */ + if (token() === SyntaxKind.PrivateIdentifier) { + return parsePrivateIdentifier(); + } } return parseIdentifier(Diagnostics.Expression_expected); diff --git a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols index fe46c01d1fbbe..2fbafd889213f 100644 --- a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols +++ b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.symbols @@ -15,6 +15,7 @@ export class Foo { >this : Symbol(Foo, Decl(main.ts, 0, 0)) #field in this; +>#field : Symbol(Foo.#field, Decl(main.ts, 0, 18)) >this : Symbol(Foo, Decl(main.ts, 0, 0)) } } diff --git a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types index 46841900b2f70..fcb8f6214dd4f 100644 --- a/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types +++ b/tests/baselines/reference/importHelpersNoHelpersForPrivateFields.types @@ -18,6 +18,7 @@ export class Foo { #field in this; >#field in this : boolean +>#field : any >this : this } } From c3b6c2a85773e7ce319be81ccc1e090084ee4a85 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 11:09:38 +0100 Subject: [PATCH 35/41] [squash] checkInExpressionn bails out early on silentNeverType Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 59f3b82681c2a..0c189224e4c74 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32061,8 +32061,10 @@ namespace ts { } function checkInExpression(left: Expression, right: Expression, leftType: Type, rightType: Type): Type { - const privIdOnLeft = isPrivateIdentifier(left); - if (privIdOnLeft) { + if (leftType === silentNeverType || rightType === silentNeverType) { + return silentNeverType; + } + if (isPrivateIdentifier(left)) { if (languageVersion < ScriptTarget.ESNext) { checkExternalEmitHelpers(left, ExternalEmitHelpers.ClassPrivateFieldIn); } @@ -32073,10 +32075,7 @@ namespace ts { reportNonexistentProperty(left, rightType, isUncheckedJS); } } - if (leftType === silentNeverType || rightType === silentNeverType) { - return silentNeverType; - } - if (!privIdOnLeft) { + else { leftType = checkNonNullType(leftType, left); // TypeScript 1.0 spec (April 2014): 4.15.5 // Require the left operand to be of type Any, the String primitive type, or the Number primitive type. From 5fbb2da940d4dcdf70d737b5d1f8109f909f1869 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 14:19:44 +0100 Subject: [PATCH 36/41] [squash] more detailed error messages Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 37 ++++++++++++------- src/compiler/diagnosticMessages.json | 4 ++ .../fixForgottenThisPropertyAccess.ts | 2 +- .../privateNameHashCharName.errors.txt | 5 +-- .../privateNameInInExpression.errors.txt | 21 +++++------ ...ressionTransform(target=es2020).errors.txt | 16 +------- ...eInInExpressionTransform(target=es2020).js | 4 +- ...ExpressionTransform(target=es2020).symbols | 2 +- ...InExpressionTransform(target=es2020).types | 2 +- ...ressionTransform(target=esnext).errors.txt | 16 +------- ...eInInExpressionTransform(target=esnext).js | 4 +- ...ExpressionTransform(target=esnext).symbols | 2 +- ...InExpressionTransform(target=esnext).types | 2 +- .../privateNameInInExpressionTransform.ts | 2 +- .../codeFixSpellingPrivateNameInIn.ts | 7 ---- 15 files changed, 51 insertions(+), 75 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0c189224e4c74..e1d927e4a7968 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23887,12 +23887,15 @@ namespace ts { const privateId = expr.left; Debug.assertNode(privateId, isPrivateIdentifier); - const symbol = getNodeLinks(privateId).resolvedSymbol; - if (symbol === undefined) { + const links = getNodeLinks(privateId); + if (links.resolvedSymbol === undefined) { + checkPrivateIdentifierExpression(privateId); + } + if (links.resolvedSymbol === undefined) { return type; } - const classSymbol = symbol.parent!; - const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration")) + const classSymbol = links.resolvedSymbol.parent!; + const targetType = hasStaticModifier(Debug.checkDefined(links.resolvedSymbol.valueDeclaration, "should always have a declaration")) ? getTypeOfSymbol(classSymbol) as InterfaceType : getDeclaredTypeOfSymbol(classSymbol); return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); @@ -27738,17 +27741,23 @@ namespace ts { } function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type { - let symbolResolved = false; - if (isExpressionNode(privId)) { - const lexicallyScopedSymbol = getNodeLinks(privId).resolvedSymbol || lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); - if (lexicallyScopedSymbol) { - symbolResolved = true; - markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); - getNodeLinks(privId).resolvedSymbol = lexicallyScopedSymbol; + if (getContainingClass(privId)) { + if (isExpressionNode(privId)) { + const lexicallyScopedSymbol = getNodeLinks(privId).resolvedSymbol || lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); + if (lexicallyScopedSymbol) { + markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); + getNodeLinks(privId).resolvedSymbol = lexicallyScopedSymbol; + } + else { + grammarErrorOnNode(privId, Diagnostics.Cannot_find_name_0, idText(privId)); + } + } + else { + grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression); } } - if (!symbolResolved) { - error(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + else { + grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); } return anyType; } @@ -32070,7 +32079,7 @@ namespace ts { } // Unlike in 'checkPrivateIdentifierExpression' we now have access to the RHS type // which provides us with the opportunity to emit more detailed errors - if (!getNodeLinks(left).resolvedSymbol) { + if (!getNodeLinks(left).resolvedSymbol && getContainingClass(left)) { const isUncheckedJS = isUncheckedJSSuggestion(left, rightType.symbol, /*excludeClasses*/ true); reportNonexistentProperty(left, rightType, isUncheckedJS); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index b68a1b6ba23a1..8b26e8c053a82 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1388,6 +1388,10 @@ "category": "Message", "code": 1449 }, + "Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression": { + "category": "Error", + "code": 1450 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", diff --git a/src/services/codefixes/fixForgottenThisPropertyAccess.ts b/src/services/codefixes/fixForgottenThisPropertyAccess.ts index 9e4a386e7f315..8e275724f995a 100644 --- a/src/services/codefixes/fixForgottenThisPropertyAccess.ts +++ b/src/services/codefixes/fixForgottenThisPropertyAccess.ts @@ -4,7 +4,7 @@ namespace ts.codefix { const didYouMeanStaticMemberCode = Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code; const errorCodes = [ Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0.code, - Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies.code, + Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression.code, didYouMeanStaticMemberCode, ]; registerCodeFix({ diff --git a/tests/baselines/reference/privateNameHashCharName.errors.txt b/tests/baselines/reference/privateNameHashCharName.errors.txt index 4e47406d104dc..8fdc42b4348ac 100644 --- a/tests/baselines/reference/privateNameHashCharName.errors.txt +++ b/tests/baselines/reference/privateNameHashCharName.errors.txt @@ -1,15 +1,12 @@ tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS1127: Invalid character. -tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(1,1): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(4,5): error TS1127: Invalid character. tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts(7,14): error TS1127: Invalid character. -==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (4 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameHashCharName.ts (3 errors) ==== # ~ !!! error TS1127: Invalid character. - ~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. class C { # diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index f11b91e924d16..738481039ae46 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,16 +1,15 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2304: Cannot find name '#fiel'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS18016: Private identifiers are not allowed outside class bodies. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS18016: Private identifiers are not allowed outside class bodies. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(29,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(43,27): error TS2531: Object is possibly 'null'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS2339: Property '#field' does not exist on type 'Foo'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(114,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (10 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts (9 errors) ==== class Foo { #field = 1; static #staticField = 2; @@ -37,19 +36,19 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const b = #fiel in v; // Bad - typo in privateID ~~~~~ -!!! error TS2339: Property '#fiel' does not exist on type 'any'. +!!! error TS2304: Cannot find name '#fiel'. ~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. +!!! error TS2339: Property '#fiel' does not exist on type 'any'. const c = (#field) in v; // Bad - privateID is not an expression on its own ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. +!!! error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression for (#field in v) { /**/ } // Bad - 'in' not allowed ~~~~~~ -!!! error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. +!!! error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. +!!! error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. for (let d in #field in v) { /**/ } // Bad - rhs of in should be a object/any ~~~~~~~~~~~ @@ -142,8 +141,6 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t function badSyntax(v: Foo) { return #field in v; // Bad - outside of class ~~~~~~ -!!! error TS2339: Property '#field' does not exist on type 'Foo'. - ~~~~~~ !!! error TS18016: Private identifiers are not allowed outside class bodies. } \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt index ffa5b573db7a0..c1bfcabc72d04 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt @@ -1,14 +1,10 @@ -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS2339: Property '#field' does not exist on type 'Foo'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (8 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (4 errors) ==== class Foo { #field = 1; #method() {} @@ -23,20 +19,16 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr } precedence(v: any) { // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ @@ -63,10 +55,6 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr function syntaxError(v: Foo) { return #field in v; // expect `return in v` so runtime will have a syntax error - ~~~~~~ -!!! error TS2339: Property '#field' does not exist on type 'Foo'. - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. } export { } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js index 2efa6bf485247..f8ad510a09802 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).js @@ -13,7 +13,7 @@ class Foo { } precedence(v: any) { // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v @@ -65,7 +65,7 @@ class Foo { } precedence(v) { // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == __classPrivateFieldIn(_Foo_field, v) || v; // Good precedence: (v == (#field in v)) || v v << in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols index d156de810aa25..572beaf65eee3 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).symbols @@ -39,7 +39,7 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types index 6dfc66fbc3151..135b6495e4eeb 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).types @@ -45,7 +45,7 @@ class Foo { >v : any // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt index fc5752c007444..b6eabeaca3499 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt @@ -1,15 +1,11 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(4,26): error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(22,14): error TS18016: Private identifiers are not allowed outside class bodies. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS2339: Property '#field' does not exist on type 'Foo'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(42,12): error TS18016: Private identifiers are not allowed outside class bodies. -==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (9 errors) ==== +==== tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts (5 errors) ==== class Foo { #field = 1; #method() {} @@ -26,20 +22,16 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr } precedence(v: any) { // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. ~~~~~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. v << #field in v == v; // Good precedence (SyntaxError): ((v << #field) in v) == v - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ @@ -66,10 +58,6 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr function syntaxError(v: Foo) { return #field in v; // expect `return in v` so runtime will have a syntax error - ~~~~~~ -!!! error TS2339: Property '#field' does not exist on type 'Foo'. - ~~~~~~ -!!! error TS18016: Private identifiers are not allowed outside class bodies. } export { } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js index fc2506e2fc124..50a91a840e5b2 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).js @@ -13,7 +13,7 @@ class Foo { } precedence(v: any) { // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v @@ -60,7 +60,7 @@ class Foo { } precedence(v) { // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v v << #field in v << v; // Good precedence (SyntaxError): (v << #field) in (v << v) diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols index d156de810aa25..572beaf65eee3 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).symbols @@ -39,7 +39,7 @@ class Foo { >v : Symbol(v, Decl(privateNameInInExpressionTransform.ts, 12, 15)) // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types index 6dfc66fbc3151..135b6495e4eeb 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).types @@ -45,7 +45,7 @@ class Foo { >v : any // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v diff --git a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts index bf09f9b4b14b1..f7be176373a5e 100644 --- a/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts +++ b/tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts @@ -14,7 +14,7 @@ class Foo { } precedence(v: any) { // '==' and '||' have lower precedence than 'in' - // 'in' has same precedence than 'in' + // 'in' naturally has same precedence as 'in' // '<<' has higher precedence than 'in' v == #field in v || v; // Good precedence: (v == (#field in v)) || v diff --git a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts index e6b023dd1c00f..daba84dde2012 100644 --- a/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts +++ b/tests/cases/fourslash/codeFixSpellingPrivateNameInIn.ts @@ -9,7 +9,6 @@ verify.codeFixAvailable([ { description: "Change spelling to '#foo'" }, - { description: "Add 'this.' to unresolved variable" }, { description: "Remove unused declaration for: '#foo'" }, ]); @@ -18,9 +17,3 @@ verify.codeFix({ description: "Change spelling to '#foo'", newRangeContent: "return #foo in v;" }); - -verify.codeFix({ - index: 1, - description: "Add 'this.' to unresolved variable", - newRangeContent: "return this.#fo in v;" -}); From c924a517b343ec285c4734d57d293d21a27255b3 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 15:19:14 +0100 Subject: [PATCH 37/41] [squash] resolves conflict in diagnosticMessages.json Signed-off-by: Ashley Claymore --- src/compiler/diagnosticMessages.json | 6 +++++- .../reference/privateNameInInExpression.errors.txt | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 8b26e8c053a82..24c1ed24c4fe3 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1388,9 +1388,13 @@ "category": "Message", "code": 1449 }, + "Dynamic imports can only accept a module specifier and an optional assertion as arguments": { + "category": "Message", + "code": 1450 + }, "Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression": { "category": "Error", - "code": 1450 + "code": 1451 }, "The types of '{0}' are incompatible between these types.": { diff --git a/tests/baselines/reference/privateNameInInExpression.errors.txt b/tests/baselines/reference/privateNameInInExpression.errors.txt index 738481039ae46..d4ba5f81e9fdc 100644 --- a/tests/baselines/reference/privateNameInInExpression.errors.txt +++ b/tests/baselines/reference/privateNameInInExpression.errors.txt @@ -1,8 +1,8 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(21,29): error TS2571: Object is of type 'unknown'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2304: Cannot find name '#fiel'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(23,19): error TS2339: Property '#fiel' does not exist on type 'any'. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression -tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(25,20): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression +tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(27,14): error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(29,23): error TS2407: The right-hand side of a 'for...in' statement must be of type 'any', an object type or a type parameter, but here has type 'boolean'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.ts(43,27): error TS2531: Object is possibly 'null'. @@ -42,11 +42,11 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpression.t const c = (#field) in v; // Bad - privateID is not an expression on its own ~~~~~~ -!!! error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression +!!! error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression for (#field in v) { /**/ } // Bad - 'in' not allowed ~~~~~~ -!!! error TS1450: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression +!!! error TS1451: Private identifiers are only allowed in class bodies and may only be used as part of a class member declaration, property access, or on the left-hand-side of an 'in' expression ~~~~~~ !!! error TS2406: The left-hand side of a 'for...in' statement must be a variable or a property access. From 33c3b55ebc91b69030bd957f4c02259526601d1f Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 15:49:56 +0100 Subject: [PATCH 38/41] [squash] update baseline for importHelpersES6 Signed-off-by: Ashley Claymore --- tests/baselines/reference/importHelpersES6.symbols | 1 + tests/baselines/reference/importHelpersES6.types | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/baselines/reference/importHelpersES6.symbols b/tests/baselines/reference/importHelpersES6.symbols index cce576b91581a..1015f19b7457c 100644 --- a/tests/baselines/reference/importHelpersES6.symbols +++ b/tests/baselines/reference/importHelpersES6.symbols @@ -19,6 +19,7 @@ declare var dec: any; g(u) { return #x in u; } >g : Symbol(A.g, Decl(a.ts, 3, 42)) >u : Symbol(u, Decl(a.ts, 4, 6)) +>#x : Symbol(A.#x, Decl(a.ts, 1, 21)) >u : Symbol(u, Decl(a.ts, 4, 6)) } diff --git a/tests/baselines/reference/importHelpersES6.types b/tests/baselines/reference/importHelpersES6.types index 11ee2e21cf0a9..70f8e09c9d9c6 100644 --- a/tests/baselines/reference/importHelpersES6.types +++ b/tests/baselines/reference/importHelpersES6.types @@ -23,6 +23,7 @@ declare var dec: any; >g : (u: any) => boolean >u : any >#x in u : boolean +>#x : any >u : any } From e3c25c998759fa8c0f02a651c984d9349ca529c7 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 17:59:57 +0100 Subject: [PATCH 39/41] [squash] remove redundent if and comment from parser Signed-off-by: Ashley Claymore --- src/compiler/parser.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 65bf30be6692e..67359ff05de9d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -5618,14 +5618,7 @@ namespace ts { case SyntaxKind.TemplateHead: return parseTemplateExpression(/* isTaggedTemplate */ false); case SyntaxKind.PrivateIdentifier: - /** - * If we have a PrivateIdentifier, parse this unconditionally. Even though a privateIdentifier is - * only syntactically valid here when it in in the BinaryExpression: `#field in expr`. - * The checker will emit an error for invalid cases. - */ - if (token() === SyntaxKind.PrivateIdentifier) { - return parsePrivateIdentifier(); - } + return parsePrivateIdentifier(); } return parseIdentifier(Diagnostics.Expression_expected); From 74e56a6c442674605b2be75802f093a6e749f3f3 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 18:39:23 +0100 Subject: [PATCH 40/41] [squash] split up grammar/check/symbolLookup Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 67 +++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b4c96abe7a2ae..976287576c906 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -23895,17 +23895,13 @@ namespace ts { return type; } - const privateId = expr.left; - Debug.assertNode(privateId, isPrivateIdentifier); - const links = getNodeLinks(privateId); - if (links.resolvedSymbol === undefined) { - checkPrivateIdentifierExpression(privateId); - } - if (links.resolvedSymbol === undefined) { + Debug.assertNode(expr.left, isPrivateIdentifier); + const symbol = getSymbolForPrivateIdentifierExpression(expr.left); + if (symbol === undefined) { return type; } - const classSymbol = links.resolvedSymbol.parent!; - const targetType = hasStaticModifier(Debug.checkDefined(links.resolvedSymbol.valueDeclaration, "should always have a declaration")) + const classSymbol = symbol.parent!; + const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration")) ? getTypeOfSymbol(classSymbol) as InterfaceType : getDeclaredTypeOfSymbol(classSymbol); return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom); @@ -27755,28 +27751,40 @@ namespace ts { } } - function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type { - if (getContainingClass(privId)) { - if (isExpressionNode(privId)) { - const lexicallyScopedSymbol = getNodeLinks(privId).resolvedSymbol || lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); - if (lexicallyScopedSymbol) { - markPropertyAsReferenced(lexicallyScopedSymbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); - getNodeLinks(privId).resolvedSymbol = lexicallyScopedSymbol; - } - else { - grammarErrorOnNode(privId, Diagnostics.Cannot_find_name_0, idText(privId)); - } - } - else { - grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression); - } + function checkGrammarPrivateIdentifierExpression(privId: PrivateIdentifier): boolean { + if (!getContainingClass(privId)) { + return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); } - else { - grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_not_allowed_outside_class_bodies); + if (!isExpressionNode(privId)) { + return grammarErrorOnNode(privId, Diagnostics.Private_identifiers_are_only_allowed_in_class_bodies_and_may_only_be_used_as_part_of_a_class_member_declaration_property_access_or_on_the_left_hand_side_of_an_in_expression); + } + if (!getSymbolForPrivateIdentifierExpression(privId)) { + return grammarErrorOnNode(privId, Diagnostics.Cannot_find_name_0, idText(privId)); + } + return false; + } + + function checkPrivateIdentifierExpression(privId: PrivateIdentifier): Type { + checkGrammarPrivateIdentifierExpression(privId); + const symbol = getSymbolForPrivateIdentifierExpression(privId); + if (symbol) { + markPropertyAsReferenced(symbol, /* nodeForCheckWriteOnly: */ undefined, /* isThisAccess: */ false); } return anyType; } + function getSymbolForPrivateIdentifierExpression(privId: PrivateIdentifier): Symbol | undefined { + if (!isExpressionNode(privId)) { + return undefined; + } + + const links = getNodeLinks(privId); + if (links.resolvedSymbol === undefined) { + links.resolvedSymbol = lookupSymbolForPrivateIdentifierDeclaration(privId.escapedText, privId); + } + return links.resolvedSymbol; + } + function getPrivateIdentifierPropertyOfType(leftType: Type, lexicallyScopedIdentifier: Symbol): Symbol | undefined { return getPropertyOfType(leftType, lexicallyScopedIdentifier.escapedName); } @@ -40300,12 +40308,7 @@ namespace ts { return result; } else if (isPrivateIdentifier(name)) { - const links = getNodeLinks(name); - if (links.resolvedSymbol) { - return links.resolvedSymbol; - } - checkPrivateIdentifierExpression(name); - return links.resolvedSymbol; + return getSymbolForPrivateIdentifierExpression(name); } else if (name.kind === SyntaxKind.PropertyAccessExpression || name.kind === SyntaxKind.QualifiedName) { const links = getNodeLinks(name); From 844793413566680009095b4fc7f991071d515e15 Mon Sep 17 00:00:00 2001 From: Ashley Claymore Date: Thu, 23 Sep 2021 20:00:36 +0100 Subject: [PATCH 41/41] [squash] reword message for existing left side of in-expression error Signed-off-by: Ashley Claymore --- src/compiler/checker.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- .../inOperatorWithInvalidOperands.errors.txt | 32 +++++++++---------- ...ressionTransform(target=es2020).errors.txt | 4 +-- ...ressionTransform(target=esnext).errors.txt | 4 +-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 976287576c906..f69eb33e8f986 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32122,7 +32122,7 @@ namespace ts { // Require the left operand to be of type Any, the String primitive type, or the Number primitive type. if (!(allTypesAssignableToKind(leftType, TypeFlags.StringLike | TypeFlags.NumberLike | TypeFlags.ESSymbolLike) || isTypeAssignableToKind(leftType, TypeFlags.Index | TypeFlags.TemplateLiteral | TypeFlags.StringMapping | TypeFlags.TypeParameter))) { - error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_of_type_any_string_number_or_symbol); + error(left, Diagnostics.The_left_hand_side_of_an_in_expression_must_be_a_private_identifier_or_of_type_any_string_number_or_symbol); } } rightType = checkNonNullType(rightType, right); diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1999aac58c337..ea7a259a6c075 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1658,7 +1658,7 @@ "category": "Error", "code": 2359 }, - "The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'.": { + "The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'.": { "category": "Error", "code": 2360 }, diff --git a/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt b/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt index f624cb1ff0f52..7984645b6d648 100644 --- a/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt +++ b/tests/baselines/reference/inOperatorWithInvalidOperands.errors.txt @@ -1,12 +1,12 @@ -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(15,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(16,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(17,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(19,11): error TS2531: Object is possibly 'null'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(20,11): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(22,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(23,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(24,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(25,12): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(35,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(36,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(37,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. @@ -17,7 +17,7 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(42,16): error TS2361: The right-hand side of an 'in' expression must not be a primitive. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(43,16): error TS2531: Object is possibly 'null'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(44,17): error TS2532: Object is possibly 'undefined'. -tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,11): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInvalidOperands.ts(47,17): error TS2361: The right-hand side of an 'in' expression must not be a primitive. @@ -38,13 +38,13 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv var ra1 = a1 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. var ra2 = a2 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. var ra3 = a3 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. var ra4 = a4 in x; var ra5 = null in x; ~~~~ @@ -55,16 +55,16 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv var ra7 = E.a in x; var ra8 = false in x; ~~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. var ra9 = {} in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. var ra10 = a5 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. var ra11 = a6 in x; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. // invalid right operands // the right operand is required to be of type Any, an object type, or a type parameter type @@ -108,6 +108,6 @@ tests/cases/conformance/expressions/binaryOperators/inOperator/inOperatorWithInv // both operands are invalid var rc1 = {} in ''; ~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. ~~ !!! error TS2361: The right-hand side of an 'in' expression must not be a primitive. \ No newline at end of file diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt index c1bfcabc72d04..1d00c96fac064 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=es2020).errors.txt @@ -1,5 +1,5 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. @@ -32,7 +32,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) } diff --git a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt index b6eabeaca3499..b48e2ac7889ef 100644 --- a/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt +++ b/tests/baselines/reference/privateNameInInExpressionTransform(target=esnext).errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(4,26): error TS2805: Static fields with private names can't have initializers when the '--useDefineForClassFields' flag is not specified with a '--target' of 'esnext'. Consider adding the '--useDefineForClassFields' flag. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(20,24): error TS2361: The right-hand side of an 'in' expression must not be a primitive. -tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(24,14): error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(29,21): error TS1005: ';' expected. tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTransform.ts(30,21): error TS1005: ';' expected. @@ -35,7 +35,7 @@ tests/cases/conformance/classes/members/privateNames/privateNameInInExpressionTr v == #field in v in v; // Good precedence: v == ((#field in v) in v) ~~~~~~~~~~~ -!!! error TS2360: The left-hand side of an 'in' expression must be of type 'any', 'string', 'number', or 'symbol'. +!!! error TS2360: The left-hand side of an 'in' expression must be a private identifier or of type 'any', 'string', 'number', or 'symbol'. #field in v && #field in v; // Good precedence: (#field in v) && (#field in v) }