From 3f3b05e67e35c7e97ca3c6bb76be7006aaef72f5 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Mon, 13 Mar 2017 13:15:09 -0700 Subject: [PATCH] Properly track method/property distinction for intersection members --- src/compiler/checker.ts | 38 +++++++++++++++++-------------- src/compiler/types.ts | 16 +++++++------ src/services/findAllReferences.ts | 2 +- src/services/symbolDisplay.ts | 2 +- 4 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 00498191f28..b581ca315b4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5129,7 +5129,8 @@ namespace ts { const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0; // Flags we want to propagate to the result if they exist in all source symbols let commonFlags = isUnion ? SymbolFlags.None : SymbolFlags.Optional; - let checkFlags = CheckFlags.SyntheticProperty; + let syntheticFlag = CheckFlags.SyntheticMethod; + let checkFlags = 0; for (const current of types) { const type = getApparentType(current); if (type !== unknownType) { @@ -5148,6 +5149,9 @@ namespace ts { (modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) | (modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) | (modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0); + if (!isMethodLike(prop)) { + syntheticFlag = CheckFlags.SyntheticProperty; + } } else if (isUnion) { checkFlags |= CheckFlags.Partial; @@ -5177,7 +5181,7 @@ namespace ts { propTypes.push(type); } const result = createSymbol(SymbolFlags.Property | commonFlags, name); - result.checkFlags = checkFlags; + result.checkFlags = syntheticFlag | checkFlags; result.containingType = containingType; result.declarations = declarations; result.type = isUnion ? getUnionType(propTypes) : getIntersectionType(propTypes); @@ -8630,7 +8634,7 @@ namespace ts { // Invoke the callback for each underlying property symbol of the given symbol and return the first // value that isn't undefined. function forEachProperty(prop: Symbol, callback: (p: Symbol) => T): T { - if (getCheckFlags(prop) & CheckFlags.SyntheticProperty) { + if (getCheckFlags(prop) & CheckFlags.Synthetic) { for (const t of (prop).containingType.types) { const p = getPropertyOfType(t, prop.name); const result = p && forEachProperty(p, callback); @@ -13112,7 +13116,7 @@ namespace ts { const flags = getCombinedModifierFlags(s.valueDeclaration); return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; } - if (getCheckFlags(s) & CheckFlags.SyntheticProperty) { + if (getCheckFlags(s) & CheckFlags.Synthetic) { const checkFlags = (s).checkFlags; const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : @@ -13130,6 +13134,10 @@ namespace ts { return s.valueDeclaration ? getCombinedNodeFlags(s.valueDeclaration) : 0; } + function isMethodLike(symbol: Symbol) { + return !!(symbol.flags & SymbolFlags.Method || getCheckFlags(symbol) & CheckFlags.SyntheticMethod); + } + /** * Check whether the requested property access is valid. * Returns true if node is a valid property access, and false otherwise. @@ -13159,11 +13167,11 @@ namespace ts { // where this references the constructor function object of a derived class, // a super property access is permitted and must specify a public static member function of the base class. if (languageVersion < ScriptTarget.ES2015) { - const propKind = getDeclarationKindFromSymbol(prop); - if (propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature) { - // `prop` refers to a *property* declared in the super class - // rather than a *method*, so it does not satisfy the above criteria. - + const hasNonMethodDeclaration = forEachProperty(prop, p => { + const propKind = getDeclarationKindFromSymbol(p); + return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature; + }); + if (hasNonMethodDeclaration) { error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword); return false; } @@ -19697,7 +19705,7 @@ namespace ts { else { // derived overrides base. const derivedDeclarationFlags = getDeclarationModifierFlagsFromSymbol(derived); - if ((baseDeclarationFlags & ModifierFlags.Private) || (derivedDeclarationFlags & ModifierFlags.Private)) { + if (baseDeclarationFlags & ModifierFlags.Private || derivedDeclarationFlags & ModifierFlags.Private) { // either base or derived property is private - not override, skip it continue; } @@ -19707,28 +19715,24 @@ namespace ts { continue; } - if ((base.flags & derived.flags & SymbolFlags.Method) || ((base.flags & SymbolFlags.PropertyOrAccessor) && (derived.flags & SymbolFlags.PropertyOrAccessor))) { + if (isMethodLike(base) && isMethodLike(derived) || base.flags & SymbolFlags.PropertyOrAccessor && derived.flags & SymbolFlags.PropertyOrAccessor) { // method is overridden with method or property/accessor is overridden with property/accessor - correct case continue; } let errorMessage: DiagnosticMessage; - if (base.flags & SymbolFlags.Method) { + if (isMethodLike(base)) { if (derived.flags & SymbolFlags.Accessor) { errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_accessor; } else { - Debug.assert((derived.flags & SymbolFlags.Property) !== 0); errorMessage = Diagnostics.Class_0_defines_instance_member_function_1_but_extended_class_2_defines_it_as_instance_member_property; } } else if (base.flags & SymbolFlags.Property) { - Debug.assert((derived.flags & SymbolFlags.Method) !== 0); errorMessage = Diagnostics.Class_0_defines_instance_member_property_1_but_extended_class_2_defines_it_as_instance_member_function; } else { - Debug.assert((base.flags & SymbolFlags.Accessor) !== 0); - Debug.assert((derived.flags & SymbolFlags.Method) !== 0); errorMessage = Diagnostics.Class_0_defines_instance_member_accessor_1_but_extended_class_2_defines_it_as_instance_member_function; } @@ -21387,7 +21391,7 @@ namespace ts { } function getRootSymbols(symbol: Symbol): Symbol[] { - if (getCheckFlags(symbol) & CheckFlags.SyntheticProperty) { + if (getCheckFlags(symbol) & CheckFlags.Synthetic) { const symbols: Symbol[] = []; const name = symbol.name; forEach(getSymbolLinks(symbol).containingType.types, t => { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 97b12aeb712..e36731ff363 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2811,13 +2811,15 @@ namespace ts { export const enum CheckFlags { Instantiated = 1 << 0, // Instantiated symbol SyntheticProperty = 1 << 1, // Property in union or intersection type - Readonly = 1 << 2, // Readonly transient symbol - Partial = 1 << 3, // Synthetic property present in some but not all constituents - HasNonUniformType = 1 << 4, // Synthetic property with non-uniform type in constituents - ContainsPublic = 1 << 5, // Synthetic property with public constituent(s) - ContainsProtected = 1 << 6, // Synthetic property with protected constituent(s) - ContainsPrivate = 1 << 7, // Synthetic property with private constituent(s) - ContainsStatic = 1 << 8, // Synthetic property with static constituent(s) + SyntheticMethod = 1 << 2, // Method in union or intersection type + Readonly = 1 << 3, // Readonly transient symbol + Partial = 1 << 4, // Synthetic property present in some but not all constituents + HasNonUniformType = 1 << 5, // Synthetic property with non-uniform type in constituents + ContainsPublic = 1 << 6, // Synthetic property with public constituent(s) + ContainsProtected = 1 << 7, // Synthetic property with protected constituent(s) + ContainsPrivate = 1 << 8, // Synthetic property with private constituent(s) + ContainsStatic = 1 << 9, // Synthetic property with static constituent(s) + Synthetic = SyntheticProperty | SyntheticMethod } /* @internal */ diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index bc1e50391c8..2c999ba9fa0 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -281,7 +281,7 @@ namespace ts.FindAllReferences { // if this symbol is visible from its parent container, e.g. exported, then bail out // if symbol correspond to the union property - bail out - if (symbol.parent || (symbol.flags & SymbolFlags.Transient && (symbol).checkFlags & CheckFlags.SyntheticProperty)) { + if (symbol.parent || (symbol.flags & SymbolFlags.Transient && (symbol).checkFlags & CheckFlags.Synthetic)) { return undefined; } diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index b7b739b4244..b457165b67f 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -52,7 +52,7 @@ namespace ts.SymbolDisplay { if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement; if (flags & SymbolFlags.Property) { - if (flags & SymbolFlags.Transient && (symbol).checkFlags & CheckFlags.SyntheticProperty) { + if (flags & SymbolFlags.Transient && (symbol).checkFlags & CheckFlags.Synthetic) { // If union property is result of union of non method (property/accessors/variables), it is labeled as property const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => { const rootSymbolFlags = rootSymbol.getFlags();