From 53a2b22784a10330df5bf862260bf4af1857f3b0 Mon Sep 17 00:00:00 2001 From: Eric Traut Date: Wed, 1 Nov 2023 19:32:42 -0700 Subject: [PATCH] Additional simplifications for member accesses. (#6294) --- .../pyright-internal/src/analyzer/checker.ts | 24 +- .../src/analyzer/codeFlowEngine.ts | 10 +- .../src/analyzer/constructorTransform.ts | 8 +- .../src/analyzer/constructors.ts | 86 +++--- .../pyright-internal/src/analyzer/enums.ts | 4 +- .../src/analyzer/functionTransform.ts | 4 +- .../src/analyzer/protocols.ts | 6 +- .../src/analyzer/typeDocStringUtils.ts | 10 +- .../src/analyzer/typeEvaluator.ts | 253 ++++++++---------- .../src/analyzer/typeEvaluatorTypes.ts | 55 +--- .../src/analyzer/typeGuards.ts | 10 +- .../src/analyzer/typeUtils.ts | 48 ++-- .../languageService/callHierarchyProvider.ts | 12 +- .../src/languageService/completionProvider.ts | 4 +- .../src/languageService/tooltipUtils.ts | 6 +- 15 files changed, 217 insertions(+), 323 deletions(-) diff --git a/packages/pyright-internal/src/analyzer/checker.ts b/packages/pyright-internal/src/analyzer/checker.ts index f50b778eb442..8ff0e361d43e 100644 --- a/packages/pyright-internal/src/analyzer/checker.ts +++ b/packages/pyright-internal/src/analyzer/checker.ts @@ -123,7 +123,7 @@ import { import { AssignTypeFlags, ClassMember, - ClassMemberLookupFlags, + MemberAccessFlags, applySolvedTypeVars, buildTypeVarContextFromSpecializedClass, convertToInstance, @@ -2137,7 +2137,7 @@ export class Checker extends ParseTreeWalker { // Does the class have an operator overload for eq? const metaclass = leftType.details.effectiveMetaclass; if (metaclass && isClass(metaclass)) { - if (lookUpClassMember(metaclass, '__eq__', ClassMemberLookupFlags.SkipObjectBaseClass)) { + if (lookUpClassMember(metaclass, '__eq__', MemberAccessFlags.SkipObjectBaseClass)) { return true; } } @@ -2176,7 +2176,7 @@ export class Checker extends ParseTreeWalker { const eqMethod = lookUpClassMember( ClassType.cloneAsInstantiable(leftType), '__eq__', - ClassMemberLookupFlags.SkipObjectBaseClass + MemberAccessFlags.SkipObjectBaseClass ); if (eqMethod) { @@ -4519,7 +4519,7 @@ export class Checker extends ParseTreeWalker { // as Final in parent classes. private _validateFinalMemberOverrides(classType: ClassType) { classType.details.fields.forEach((localSymbol, name) => { - const parentSymbol = lookUpClassMember(classType, name, ClassMemberLookupFlags.SkipOriginalClass); + const parentSymbol = lookUpClassMember(classType, name, MemberAccessFlags.SkipOriginalClass); if ( parentSymbol && isInstantiableClass(parentSymbol.classType) && @@ -4656,7 +4656,7 @@ export class Checker extends ParseTreeWalker { const postInitMember = lookUpClassMember( classType, '__post_init__', - ClassMemberLookupFlags.SkipBaseClasses | ClassMemberLookupFlags.DeclaredTypesOnly + MemberAccessFlags.SkipBaseClasses | MemberAccessFlags.DeclaredTypesOnly ); // If there's no __post_init__ method, there's nothing to check. @@ -4897,7 +4897,7 @@ export class Checker extends ParseTreeWalker { // If the symbol is declared by its parent, we can assume it // is initialized there. - const parentSymbol = lookUpClassMember(classType, name, ClassMemberLookupFlags.SkipOriginalClass); + const parentSymbol = lookUpClassMember(classType, name, MemberAccessFlags.SkipOriginalClass); if (parentSymbol) { return; } @@ -5097,12 +5097,12 @@ export class Checker extends ParseTreeWalker { const initMember = lookUpClassMember( classType, '__init__', - ClassMemberLookupFlags.SkipObjectBaseClass | ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipObjectBaseClass | MemberAccessFlags.SkipInstanceMembers ); const newMember = lookUpClassMember( classType, '__new__', - ClassMemberLookupFlags.SkipObjectBaseClass | ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipObjectBaseClass | MemberAccessFlags.SkipInstanceMembers ); if (!initMember || !newMember || !isClass(initMember.classType) || !isClass(newMember.classType)) { @@ -5125,7 +5125,7 @@ export class Checker extends ParseTreeWalker { const callMethod = lookUpClassMember( metaclass, '__call__', - ClassMemberLookupFlags.SkipTypeBaseClass | ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipTypeBaseClass | MemberAccessFlags.SkipInstanceMembers ); if (callMethod) { return; @@ -5628,7 +5628,7 @@ export class Checker extends ParseTreeWalker { } assert(isClass(mroBaseClass)); - const baseClassAndSymbol = lookUpClassMember(mroBaseClass, name, ClassMemberLookupFlags.Default); + const baseClassAndSymbol = lookUpClassMember(mroBaseClass, name, MemberAccessFlags.Default); if (!baseClassAndSymbol) { continue; } @@ -6286,9 +6286,9 @@ export class Checker extends ParseTreeWalker { // it could be combined with other classes in a multi-inheritance // situation that effectively adds new superclasses that we don't know // about statically. - let effectiveFlags = ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipOriginalClass; + let effectiveFlags = MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipOriginalClass; if (ClassType.isFinal(classType)) { - effectiveFlags |= ClassMemberLookupFlags.SkipObjectBaseClass; + effectiveFlags |= MemberAccessFlags.SkipObjectBaseClass; } const methodMember = lookUpClassMember(classType, methodType.details.name, effectiveFlags); diff --git a/packages/pyright-internal/src/analyzer/codeFlowEngine.ts b/packages/pyright-internal/src/analyzer/codeFlowEngine.ts index af518b23994b..462f3c6de427 100644 --- a/packages/pyright-internal/src/analyzer/codeFlowEngine.ts +++ b/packages/pyright-internal/src/analyzer/codeFlowEngine.ts @@ -63,7 +63,6 @@ import { UnknownType, } from './types'; import { - ClassMemberLookupFlags, cleanIncompleteUnknown, derivesFromStdlibClass, doForEachSubtype, @@ -71,6 +70,7 @@ import { isTypeAliasPlaceholder, lookUpClassMember, mapSubtypes, + MemberAccessFlags, } from './typeUtils'; export interface FlowNodeTypeResult { @@ -1525,7 +1525,7 @@ export function getCodeFlowEngine( const metaclassCallMember = lookUpClassMember( callSubtype.details.effectiveMetaclass, '__call__', - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipObjectBaseClass ); if (metaclassCallMember) { return; @@ -1535,14 +1535,14 @@ export function getCodeFlowEngine( let constructorMember = lookUpClassMember( callSubtype, '__init__', - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipObjectBaseClass ); if (constructorMember === undefined) { constructorMember = lookUpClassMember( callSubtype, '__new__', - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipObjectBaseClass ); } @@ -1564,7 +1564,7 @@ export function getCodeFlowEngine( const callMember = lookUpClassMember( callSubtype, '__call__', - ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipInstanceMembers ); if (callMember) { const callMemberType = evaluator.getTypeOfMember(callMember); diff --git a/packages/pyright-internal/src/analyzer/constructorTransform.ts b/packages/pyright-internal/src/analyzer/constructorTransform.ts index 0685e88e4084..73d7c657f73a 100644 --- a/packages/pyright-internal/src/analyzer/constructorTransform.ts +++ b/packages/pyright-internal/src/analyzer/constructorTransform.ts @@ -36,11 +36,11 @@ import { } from './types'; import { applySolvedTypeVars, - ClassMemberLookupFlags, convertToInstance, getTypeVarScopeId, lookUpObjectMember, makeInferenceContext, + MemberAccessFlags, } from './typeUtils'; import { TypeVarContext } from './typeVarContext'; @@ -79,11 +79,7 @@ function applyPartialTransform( return result; } - const callMemberResult = lookUpObjectMember( - result.returnType, - '__call__', - ClassMemberLookupFlags.SkipInstanceVariables - ); + const callMemberResult = lookUpObjectMember(result.returnType, '__call__', MemberAccessFlags.SkipInstanceMembers); if (!callMemberResult || !isTypeSame(convertToInstance(callMemberResult.classType), result.returnType)) { return result; } diff --git a/packages/pyright-internal/src/analyzer/constructors.ts b/packages/pyright-internal/src/analyzer/constructors.ts index 174800371ce9..f2ac455f11b6 100644 --- a/packages/pyright-internal/src/analyzer/constructors.ts +++ b/packages/pyright-internal/src/analyzer/constructors.ts @@ -20,17 +20,10 @@ import { getFileInfo } from './analyzerNodeInfo'; import { populateTypeVarContextBasedOnExpectedType } from './constraintSolver'; import { applyConstructorTransform, hasConstructorTransform } from './constructorTransform'; import { getTypeVarScopesForNode } from './parseTreeUtils'; +import { CallResult, FunctionArgument, TypeEvaluator, TypeResult } from './typeEvaluatorTypes'; import { - CallResult, - ClassMemberLookup, - FunctionArgument, - MemberAccessFlags, - TypeEvaluator, - TypeResult, -} from './typeEvaluatorTypes'; -import { - ClassMemberLookupFlags, InferenceContext, + MemberAccessFlags, applySolvedTypeVars, buildTypeVarContextFromSpecializedClass, convertToInstance, @@ -57,7 +50,6 @@ import { isAnyOrUnknown, isClassInstance, isFunction, - isInstantiableClass, isNever, isOverloadedFunction, isTypeVar, @@ -97,13 +89,13 @@ export function validateConstructorArguments( } // Determine whether the class overrides the object.__new__ method. - const newMethodTypeResult = evaluator.getTypeOfClassMemberName( + const newMethodTypeResult = evaluator.getTypeOfObjectMember( errorNode, type, '__new__', { method: 'get' }, /* diag */ undefined, - MemberAccessFlags.AccessClassMembersOnly | + MemberAccessFlags.SkipClassMembers | MemberAccessFlags.SkipObjectBaseClass | MemberAccessFlags.SkipAttributeAccessOverride | MemberAccessFlags.TreatConstructorAsClassMethod @@ -200,7 +192,7 @@ function validateNewAndInitMethods( type: ClassType, skipUnknownArgCheck: boolean, inferenceContext: InferenceContext | undefined, - newMethodTypeResult: ClassMemberLookup | undefined + newMethodTypeResult: TypeResult | undefined ): CallResult { let returnType: Type | undefined; let validatedArgExpressions = false; @@ -270,13 +262,13 @@ function validateNewAndInitMethods( } // Determine whether the class overrides the object.__init__ method. - initMethodTypeResult = evaluator.getTypeOfClassMemberName( + initMethodTypeResult = evaluator.getTypeOfObjectMember( errorNode, initMethodBindToType, '__init__', { method: 'get' }, /* diag */ undefined, - MemberAccessFlags.AccessClassMembersOnly | + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipObjectBaseClass | MemberAccessFlags.SkipAttributeAccessOverride ); @@ -655,46 +647,36 @@ function validateMetaclassCall( skipUnknownArgCheck: boolean, inferenceContext: InferenceContext | undefined ): CallResult | undefined { - const metaclass = type.details.effectiveMetaclass; + const metaclassCallMethodInfo = evaluator.getTypeOfObjectMember( + errorNode, + type, + '__call__', + { method: 'get' }, + /* diag */ undefined, + MemberAccessFlags.SkipInstanceMembers | + MemberAccessFlags.SkipTypeBaseClass | + MemberAccessFlags.SkipAttributeAccessOverride + ); - if (metaclass && isInstantiableClass(metaclass) && !ClassType.isSameGenericClass(metaclass, type)) { - const metaclassCallMethodInfo = evaluator.getTypeOfClassMemberName( + if (metaclassCallMethodInfo) { + const callResult = evaluator.validateCallArguments( errorNode, - ClassType.cloneAsInstance(metaclass), - '__call__', - { method: 'get' }, - /* diag */ undefined, - MemberAccessFlags.AccessClassMembersOnly | - MemberAccessFlags.SkipTypeBaseClass | - MemberAccessFlags.SkipAttributeAccessOverride, - type + argList, + metaclassCallMethodInfo, + /* typeVarContext */ undefined, + skipUnknownArgCheck, + inferenceContext ); - if (metaclassCallMethodInfo) { - const callResult = evaluator.validateCallArguments( - errorNode, - argList, - metaclassCallMethodInfo, - /* typeVarContext */ undefined, - skipUnknownArgCheck, - inferenceContext - ); - - if (!callResult.returnType || isUnknown(callResult.returnType)) { - // The return result isn't known. We'll assume in this case that - // the metaclass __call__ method allocated a new instance of the - // requested class. - const typeVarContext = new TypeVarContext(getTypeVarScopeId(type)); - callResult.returnType = applyExpectedTypeForConstructor( - evaluator, - type, - inferenceContext, - typeVarContext - ); - } - - return callResult; + if (!callResult.returnType || isUnknown(callResult.returnType)) { + // The return result isn't known. We'll assume in this case that + // the metaclass __call__ method allocated a new instance of the + // requested class. + const typeVarContext = new TypeVarContext(getTypeVarScopeId(type)); + callResult.returnType = applyExpectedTypeForConstructor(evaluator, type, inferenceContext, typeVarContext); } + + return callResult; } return undefined; @@ -789,7 +771,7 @@ export function createFunctionFromConstructor( const initInfo = lookUpClassMember( classType, '__init__', - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipObjectBaseClass ); if (initInfo) { @@ -853,7 +835,7 @@ export function createFunctionFromConstructor( const newInfo = lookUpClassMember( classType, '__new__', - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipObjectBaseClass + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipObjectBaseClass ); if (newInfo) { diff --git a/packages/pyright-internal/src/analyzer/enums.ts b/packages/pyright-internal/src/analyzer/enums.ts index 87e60c93cc3c..35607fb9f4e2 100644 --- a/packages/pyright-internal/src/analyzer/enums.ts +++ b/packages/pyright-internal/src/analyzer/enums.ts @@ -29,7 +29,7 @@ import { Symbol, SymbolFlags } from './symbol'; import { isSingleDunderName } from './symbolNameUtils'; import { FunctionArgument, TypeEvaluator, TypeResult } from './typeEvaluatorTypes'; import { enumerateLiteralsForType } from './typeGuards'; -import { ClassMemberLookupFlags, computeMroLinearization, lookUpClassMember } from './typeUtils'; +import { MemberAccessFlags, computeMroLinearization, lookUpClassMember } from './typeUtils'; import { AnyType, ClassType, @@ -332,7 +332,7 @@ export function transformTypeForPossibleEnumClass( // the value of each enum element is simply the value assigned to it. // The __new__ method can transform the value in ways that we cannot // determine statically. - const newMember = lookUpClassMember(enumClassInfo.classType, '__new__', ClassMemberLookupFlags.SkipBaseClasses); + const newMember = lookUpClassMember(enumClassInfo.classType, '__new__', MemberAccessFlags.SkipBaseClasses); if (newMember) { // We may want to change this to UnknownType in the future, but // for now, we'll leave it as Any which is consistent with the diff --git a/packages/pyright-internal/src/analyzer/functionTransform.ts b/packages/pyright-internal/src/analyzer/functionTransform.ts index 56a983c967ba..bd20fc5ceff9 100644 --- a/packages/pyright-internal/src/analyzer/functionTransform.ts +++ b/packages/pyright-internal/src/analyzer/functionTransform.ts @@ -24,7 +24,7 @@ import { OverloadedFunctionType, Type, } from './types'; -import { ClassMember, ClassMemberLookupFlags, lookUpObjectMember, synthesizeTypeVarForSelfCls } from './typeUtils'; +import { ClassMember, lookUpObjectMember, MemberAccessFlags, synthesizeTypeVarForSelfCls } from './typeUtils'; export function applyFunctionTransform( evaluator: TypeEvaluator, @@ -65,7 +65,7 @@ function applyTotalOrderingTransform( // Verify that the class has at least one of the required functions. let firstMemberFound: ClassMember | undefined; const missingMethods = orderingMethods.filter((methodName) => { - const memberInfo = lookUpObjectMember(instanceType, methodName, ClassMemberLookupFlags.SkipInstanceVariables); + const memberInfo = lookUpObjectMember(instanceType, methodName, MemberAccessFlags.SkipInstanceMembers); if (memberInfo && !firstMemberFound) { firstMemberFound = memberInfo; } diff --git a/packages/pyright-internal/src/analyzer/protocols.ts b/packages/pyright-internal/src/analyzer/protocols.ts index 1bbbd3575b77..9f5ebb93217d 100644 --- a/packages/pyright-internal/src/analyzer/protocols.ts +++ b/packages/pyright-internal/src/analyzer/protocols.ts @@ -36,10 +36,10 @@ import { applySolvedTypeVars, AssignTypeFlags, ClassMember, - ClassMemberLookupFlags, containsLiteralType, getTypeVarScopeId, lookUpClassMember, + MemberAccessFlags, partiallySpecializeType, populateTypeVarContextForSelfType, removeParamSpecVariadicsFromSignature, @@ -489,9 +489,7 @@ function assignClassToProtocolInternal( if (isSrcReadOnly) { // The source attribute is read-only. Make sure the setter // is not defined in the dest property. - if ( - lookUpClassMember(destMemberType, '__set__', ClassMemberLookupFlags.SkipInstanceVariables) - ) { + if (lookUpClassMember(destMemberType, '__set__', MemberAccessFlags.SkipInstanceMembers)) { if (subDiag) { subDiag.addMessage( Localizer.DiagnosticAddendum.memberIsWritableInProtocol().format({ name }) diff --git a/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts b/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts index 19c344f5fb53..7ea90d937735 100644 --- a/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts +++ b/packages/pyright-internal/src/analyzer/typeDocStringUtils.ts @@ -37,17 +37,17 @@ import { ModuleNode, ParseNodeType } from '../parser/parseNodes'; import { TypeEvaluator } from './typeEvaluatorTypes'; import { ClassIteratorFlags, - ClassMemberLookupFlags, getClassIterator, getClassMemberIterator, isMaybeDescriptorInstance, + MemberAccessFlags, } from './typeUtils'; const DefaultClassIteratorFlagsForFunctions = - ClassMemberLookupFlags.SkipObjectBaseClass | - ClassMemberLookupFlags.SkipInstanceVariables | - ClassMemberLookupFlags.SkipOriginalClass | - ClassMemberLookupFlags.DeclaredTypesOnly; + MemberAccessFlags.SkipObjectBaseClass | + MemberAccessFlags.SkipInstanceMembers | + MemberAccessFlags.SkipOriginalClass | + MemberAccessFlags.DeclaredTypesOnly; function isInheritedFromBuiltin(type: FunctionType | OverloadedFunctionType, classType?: ClassType): boolean { if (type.category === TypeCategory.OverloadedFunction) { diff --git a/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright-internal/src/analyzer/typeEvaluator.ts index b72032f6f779..5c4225851784 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -171,7 +171,6 @@ import { FunctionArgument, FunctionTypeResult, MemberAccessDeprecationInfo, - MemberAccessFlags, PrintTypeOptions, ResolveAliasOptions, TypeEvaluator, @@ -185,8 +184,8 @@ import * as TypePrinter from './typePrinter'; import { AssignTypeFlags, ClassMember, - ClassMemberLookupFlags, InferenceContext, + MemberAccessFlags, UniqueSignatureTracker, addConditionToType, addTypeVarsToListIfUnique, @@ -1995,7 +1994,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions memberName: string, usage: EvaluatorUsage = { method: 'get' }, diag: DiagnosticAddendum | undefined = undefined, - flags = MemberAccessFlags.None, + flags = MemberAccessFlags.Default, selfType?: ClassType | TypeVarType ): TypeResult | undefined { if (ClassType.isPartiallyEvaluated(objectType)) { @@ -2019,7 +2018,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions // If the object type is an instantiable (i.e. it derives from "type") and // we've been asked not to consider instance members, don't look in the class. // Consider only the metaclass class variables in this case. - let skipObjectTypeLookup = objectTypeIsInstantiable && (flags & MemberAccessFlags.AccessClassMembersOnly) !== 0; + let skipObjectTypeLookup = objectTypeIsInstantiable && (flags & MemberAccessFlags.SkipInstanceMembers) !== 0; // Look up the attribute in the metaclass first. If the member is a descriptor // (an object with a __get__ and __set__ method) and the access is a 'get', @@ -2057,8 +2056,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions let effectiveFlags = flags; if (objectTypeIsInstantiable) { - effectiveFlags |= - MemberAccessFlags.AccessClassMembersOnly | MemberAccessFlags.SkipAttributeAccessOverride; + effectiveFlags |= MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipAttributeAccessOverride; + effectiveFlags &= ~MemberAccessFlags.SkipClassMembers; } else { effectiveFlags |= MemberAccessFlags.DisallowClassVarWrites; } @@ -2083,9 +2082,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions // an instance of a class. Limit access to metaclass instance members // in this case. if (!objectTypeIsInstantiable) { - effectiveFlags |= - MemberAccessFlags.AccessInstanceMembersOnly | MemberAccessFlags.SkipAttributeAccessOverride; - effectiveFlags &= ~MemberAccessFlags.AccessClassMembersOnly; + effectiveFlags |= MemberAccessFlags.SkipClassMembers | MemberAccessFlags.SkipAttributeAccessOverride; + effectiveFlags &= ~MemberAccessFlags.SkipInstanceMembers; } memberInfo = getTypeOfClassMemberName( @@ -2099,7 +2097,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions ); } - if (memberInfo && !memberInfo.isSetTypeError) { + if (memberInfo && !memberInfo.isDescriptorError) { return { type: memberInfo.type, classType: memberInfo.classType, @@ -2117,32 +2115,32 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions memberName: string, recursionCount = 0 ): FunctionType | OverloadedFunctionType | undefined { - const memberInfo = lookUpClassMember(classType, memberName, ClassMemberLookupFlags.SkipInstanceVariables); + const memberInfo = lookUpClassMember(classType, memberName, MemberAccessFlags.SkipInstanceMembers); - if (memberInfo) { - const unboundMethodType = getTypeOfMember(memberInfo); - if (isFunction(unboundMethodType) || isOverloadedFunction(unboundMethodType)) { - const boundMethod = bindFunctionToClassOrObject( - ClassType.cloneAsInstance(classType), - unboundMethodType, - /* memberClass */ undefined, - /* treatConstructorAsClassMember */ true, - /* selfType */ undefined, - /* diag */ undefined, - recursionCount - ); + if (!memberInfo) { + return undefined; + } - if (boundMethod) { - return boundMethod; - } - } else if (isAnyOrUnknown(unboundMethodType)) { - const unknownFunction = FunctionType.createSynthesizedInstance( - '', - FunctionTypeFlags.SkipArgsKwargsCompatibilityCheck - ); - FunctionType.addDefaultParameters(unknownFunction); - return unknownFunction; - } + const unboundMethodType = getTypeOfMember(memberInfo); + if (isFunction(unboundMethodType) || isOverloadedFunction(unboundMethodType)) { + return bindFunctionToClassOrObject( + ClassType.cloneAsInstance(classType), + unboundMethodType, + /* memberClass */ undefined, + /* treatConstructorAsClassMember */ true, + /* selfType */ undefined, + /* diag */ undefined, + recursionCount + ); + } + + if (isAnyOrUnknown(unboundMethodType)) { + const unknownFunction = FunctionType.createSynthesizedInstance( + '', + FunctionTypeFlags.SkipArgsKwargsCompatibilityCheck + ); + FunctionType.addDefaultParameters(unknownFunction); + return unknownFunction; } return undefined; @@ -2345,8 +2343,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const classMemberInfo = lookUpClassMember( classTypeInfo.classType, expression.value, - ClassMemberLookupFlags.SkipInstanceVariables | - ClassMemberLookupFlags.DeclaredTypesOnly + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.DeclaredTypesOnly ); if (classMemberInfo) { symbol = classMemberInfo.symbol; @@ -2372,7 +2369,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions classMemberInfo = lookUpObjectMember( baseType, expression.memberName.value, - ClassMemberLookupFlags.DeclaredTypesOnly + MemberAccessFlags.DeclaredTypesOnly ); classOrObjectBase = baseType; memberAccessClass = classMemberInfo?.classType; @@ -2388,7 +2385,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions classMemberInfo = lookUpClassMember( baseType, expression.memberName.value, - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.DeclaredTypesOnly + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.DeclaredTypesOnly ); classOrObjectBase = baseType; memberAccessClass = classMemberInfo?.classType; @@ -2751,11 +2748,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (subtype.details.isInstanceHashable !== undefined) { isObjectHashable = subtype.details.isInstanceHashable; } else { - const hashMember = lookUpObjectMember( - subtype, - '__hash__', - ClassMemberLookupFlags.SkipObjectBaseClass - ); + const hashMember = lookUpObjectMember(subtype, '__hash__', MemberAccessFlags.SkipObjectBaseClass); if (hashMember && hashMember.isTypeDeclared) { const decls = hashMember.symbol.getTypedDeclarations(); @@ -3110,7 +3103,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const memberInfo = lookUpClassMember( classType.classType, nameNode.value, - ClassMemberLookupFlags.SkipOriginalClass + MemberAccessFlags.SkipOriginalClass ); if (memberInfo?.isTypeDeclared) { declaredType = getTypeOfMember(memberInfo); @@ -3307,7 +3300,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions let memberInfo = lookUpClassMember( classTypeInfo.classType, memberName, - isInstanceMember ? ClassMemberLookupFlags.Default : ClassMemberLookupFlags.SkipInstanceVariables + isInstanceMember ? MemberAccessFlags.Default : MemberAccessFlags.SkipInstanceMembers ); const memberFields = classTypeInfo.classType.details.fields; @@ -3333,7 +3326,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const classMemberDetails = lookUpClassMember( memberClass, memberName, - ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipInstanceMembers ); let isPotentiallyDescriptor = false; @@ -3400,11 +3393,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions } // Look up the member info again, now that we've potentially updated it. - memberInfo = lookUpClassMember( - classTypeInfo.classType, - memberName, - ClassMemberLookupFlags.DeclaredTypesOnly - ); + memberInfo = lookUpClassMember(classTypeInfo.classType, memberName, MemberAccessFlags.DeclaredTypesOnly); if (!memberInfo && srcExprNode && !isTypeIncomplete) { reportPossibleUnknownAssignment( @@ -4997,13 +4986,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions classMemberInfo = lookUpClassMember( baseType, node.memberName.value, - ClassMemberLookupFlags.SkipOriginalClass + MemberAccessFlags.SkipOriginalClass ); } else if (isClassInstance(baseType)) { classMemberInfo = lookUpObjectMember( baseType, node.memberName.value, - ClassMemberLookupFlags.SkipOriginalClass + MemberAccessFlags.SkipOriginalClass ); } @@ -5339,11 +5328,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions case TypeCategory.Function: case TypeCategory.OverloadedFunction: { - if (memberName === '__defaults__') { - // The "__defaults__" member is not currently defined in the "function" - // class, so we'll special-case it here. - type = AnyType.create(); - } else if (memberName === '__self__') { + if (memberName === '__self__') { // The "__self__" member is not currently defined in the "function" // class, so we'll special-case it here. const functionType = isFunction(baseType) ? baseType : baseType.overloads[0]; @@ -5354,11 +5339,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions type = functionType.boundToType; } } else { - if (!functionObj) { - type = AnyType.create(); - } else { - type = getTypeOfMemberAccessWithBaseType(node, { type: functionObj }, usage, flags).type; - } + type = getTypeOfMemberAccessWithBaseType( + node, + { type: functionObj ?? AnyType.create() }, + usage, + flags + ).type; } break; } @@ -5457,39 +5443,15 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions flags: MemberAccessFlags, selfType?: ClassType | TypeVarType ): ClassMemberLookup | undefined { - let classLookupFlags = ClassMemberLookupFlags.Default; - if (flags & MemberAccessFlags.AccessClassMembersOnly) { - classLookupFlags |= ClassMemberLookupFlags.SkipInstanceVariables; - } - if (flags & MemberAccessFlags.AccessInstanceMembersOnly) { - classLookupFlags |= ClassMemberLookupFlags.SkipClassVariables; - } - if (flags & MemberAccessFlags.SkipBaseClasses) { - classLookupFlags |= ClassMemberLookupFlags.SkipBaseClasses; - } - if (flags & MemberAccessFlags.SkipObjectBaseClass) { - classLookupFlags |= ClassMemberLookupFlags.SkipObjectBaseClass; - } - if (flags & MemberAccessFlags.SkipTypeBaseClass) { - classLookupFlags |= ClassMemberLookupFlags.SkipTypeBaseClass; - } - if (flags & MemberAccessFlags.SkipOriginalClass) { - classLookupFlags |= ClassMemberLookupFlags.SkipOriginalClass; - } - const isAccessedThroughObject = TypeBase.isInstance(classType); // Always look for a member with a declared type first. - let memberInfo = lookUpClassMember( - classType, - memberName, - classLookupFlags | ClassMemberLookupFlags.DeclaredTypesOnly - ); + let memberInfo = lookUpClassMember(classType, memberName, flags | MemberAccessFlags.DeclaredTypesOnly); // If we couldn't find a symbol with a declared type, use // a symbol with an inferred type. if (!memberInfo) { - memberInfo = lookUpClassMember(classType, memberName, classLookupFlags); + memberInfo = lookUpClassMember(classType, memberName, flags); } if (memberInfo) { @@ -5612,38 +5574,40 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions diag ); - if (!descriptorResult) { - return undefined; - } - type = descriptorResult.type; - let isSetTypeError = false; + let isDescriptorError = true; + + if (descriptorResult) { + isDescriptorError = false; + + type = descriptorResult.type; + + if (usage.method === 'set' && usage.setType) { + // Verify that the assigned type is compatible. + if (!assignType(type, usage.setType.type, diag?.createAddendum())) { + if (!usage.setType.isIncomplete) { + diag?.addMessage( + Localizer.DiagnosticAddendum.memberAssignment().format({ + type: printType(usage.setType.type), + name: memberName, + classType: printObjectTypeForClass(classType), + }) + ); + } + isDescriptorError = true; + } - if (usage.method === 'set' && usage.setType) { - // Verify that the assigned type is compatible. - if (!assignType(type, usage.setType.type, diag?.createAddendum())) { - if (!usage.setType.isIncomplete) { + if ( + isInstantiableClass(memberInfo.classType) && + ClassType.isFrozenDataClass(memberInfo.classType) && + isAccessedThroughObject + ) { diag?.addMessage( - Localizer.DiagnosticAddendum.memberAssignment().format({ - type: printType(usage.setType.type), - name: memberName, - classType: printObjectTypeForClass(classType), + Localizer.DiagnosticAddendum.dataClassFrozen().format({ + name: printType(ClassType.cloneAsInstance(memberInfo.classType)), }) ); + isDescriptorError = true; } - isSetTypeError = true; - } - - if ( - isInstantiableClass(memberInfo.classType) && - ClassType.isFrozenDataClass(memberInfo.classType) && - isAccessedThroughObject - ) { - diag?.addMessage( - Localizer.DiagnosticAddendum.dataClassFrozen().format({ - name: printType(ClassType.cloneAsInstance(memberInfo.classType)), - }) - ); - isSetTypeError = true; } } @@ -5651,11 +5615,11 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions symbol: memberInfo.symbol, type, isTypeIncomplete, - isSetTypeError, + isDescriptorError, isClassMember: !memberInfo.isInstanceMember, isClassVar: memberInfo.isClassVar, classType: memberInfo.classType, - isAsymmetricAccessor: descriptorResult.isAsymmetricAccessor, + isAsymmetricAccessor: descriptorResult?.isAsymmetricAccessor ?? false, memberAccessDeprecationInfo: descriptorResult?.memberAccessDeprecationInfo, }; } @@ -5663,17 +5627,14 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions // No attribute of that name was found. If this is a member access // through an object, see if there's an attribute access override // method ("__getattr__", etc.). - if ( - (flags & (MemberAccessFlags.AccessClassMembersOnly | MemberAccessFlags.SkipAttributeAccessOverride)) === - 0 - ) { + if ((flags & (MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipAttributeAccessOverride)) === 0) { const generalAttrType = applyAttributeAccessOverride(classType, errorNode, usage, memberName, selfType); if (generalAttrType) { return { symbol: undefined, type: generalAttrType.type, isTypeIncomplete: false, - isSetTypeError: false, + isDescriptorError: false, isClassMember: false, isClassVar: false, isAsymmetricAccessor: generalAttrType.isAsymmetricAccessor, @@ -5748,7 +5709,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const accessMethod = lookUpClassMember( lookupClass, accessMethodName, - ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipInstanceMembers ); // Handle properties specially. @@ -5834,7 +5795,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions // interpreted as a read-only attribute rather than a protocol, so accessing // it directly from the class has an ambiguous meaning. if ( - (flags & MemberAccessFlags.AccessClassMembersOnly) !== 0 && + (flags & MemberAccessFlags.SkipInstanceMembers) !== 0 && ClassType.isProtocolClass(classType) ) { diag?.addMessage(Localizer.DiagnosticAddendum.propertyAccessFromProtocolClass()); @@ -6104,8 +6065,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions let isAsymmetric = false; - const getterSymbolResult = lookUpClassMember(classType, '__get__', ClassMemberLookupFlags.SkipBaseClasses); - const setterSymbolResult = lookUpClassMember(classType, '__set__', ClassMemberLookupFlags.SkipBaseClasses); + const getterSymbolResult = lookUpClassMember(classType, '__get__', MemberAccessFlags.SkipBaseClasses); + const setterSymbolResult = lookUpClassMember(classType, '__set__', MemberAccessFlags.SkipBaseClasses); if (!getterSymbolResult || !setterSymbolResult) { isAsymmetric = false; @@ -6141,8 +6102,8 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions let isAsymmetric = false; - const getterSymbolResult = lookUpClassMember(classType, '__getattr__', ClassMemberLookupFlags.SkipBaseClasses); - const setterSymbolResult = lookUpClassMember(classType, '__setattr__', ClassMemberLookupFlags.SkipBaseClasses); + const getterSymbolResult = lookUpClassMember(classType, '__getattr__', MemberAccessFlags.SkipBaseClasses); + const setterSymbolResult = lookUpClassMember(classType, '__setattr__', MemberAccessFlags.SkipBaseClasses); if (!getterSymbolResult || !setterSymbolResult) { isAsymmetric = false; @@ -6187,7 +6148,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions name, { method: 'get' }, /* diag */ undefined, - MemberAccessFlags.AccessClassMembersOnly | + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipObjectBaseClass | MemberAccessFlags.SkipAttributeAccessOverride, selfType @@ -8288,7 +8249,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const effectiveTargetClass = isClass(targetClassType) ? targetClassType : undefined; const lookupResults = bindToType - ? lookUpClassMember(bindToType, memberName, ClassMemberLookupFlags.Default, effectiveTargetClass) + ? lookUpClassMember(bindToType, memberName, MemberAccessFlags.Default, effectiveTargetClass) : undefined; if (lookupResults && isInstantiableClass(lookupResults.classType)) { return { @@ -9592,7 +9553,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions '__call__', /* usage */ undefined, /* diag */ undefined, - MemberAccessFlags.SkipAttributeAccessOverride | MemberAccessFlags.AccessClassMembersOnly + MemberAccessFlags.SkipAttributeAccessOverride | MemberAccessFlags.SkipInstanceMembers )?.type; if (!memberType) { @@ -11729,11 +11690,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions } if (isClassInstance(argType)) { - const callMember = lookUpObjectMember( - argType, - '__call__', - ClassMemberLookupFlags.SkipInstanceVariables - ); + const callMember = lookUpObjectMember(argType, '__call__', MemberAccessFlags.SkipInstanceMembers); if (callMember) { const memberType = getTypeOfMember(callMember); if (isOverloadedFunction(memberType)) { @@ -12609,7 +12566,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions methodName, /* usage */ undefined, /* diag */ undefined, - MemberAccessFlags.AccessClassMembersOnly | MemberAccessFlags.SkipAttributeAccessOverride + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipAttributeAccessOverride )?.type; } @@ -16251,14 +16208,14 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions // See if there's already a non-synthesized __init__ method. // We shouldn't override it. if (!skipSynthesizedInit) { - const initSymbol = lookUpClassMember(classType, '__init__', ClassMemberLookupFlags.SkipBaseClasses); + const initSymbol = lookUpClassMember(classType, '__init__', MemberAccessFlags.SkipBaseClasses); if (initSymbol) { hasExistingInitMethod = true; } } let skipSynthesizeHash = false; - const hashSymbol = lookUpClassMember(classType, '__hash__', ClassMemberLookupFlags.SkipBaseClasses); + const hashSymbol = lookUpClassMember(classType, '__hash__', MemberAccessFlags.SkipBaseClasses); // If there is a hash symbol defined in the class (i.e. one that we didn't // synthesize above), then we shouldn't synthesize a new one for the dataclass. @@ -16656,15 +16613,16 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions }); const errorNode = argList.length > 0 ? argList[0].node!.name! : node.name; - const initSubclassMethodInfo = getTypeOfClassMemberName( + const initSubclassMethodInfo = getTypeOfObjectMember( errorNode, classType, '__init_subclass__', { method: 'get' }, /* diag */ undefined, - MemberAccessFlags.AccessClassMembersOnly | + MemberAccessFlags.SkipClassMembers | MemberAccessFlags.SkipObjectBaseClass | - MemberAccessFlags.SkipOriginalClass + MemberAccessFlags.SkipOriginalClass | + MemberAccessFlags.SkipAttributeAccessOverride ); if (initSubclassMethodInfo) { @@ -16685,7 +16643,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const newMethodMember = lookUpClassMember( classType.details.effectiveMetaclass, '__new__', - ClassMemberLookupFlags.SkipTypeBaseClass + MemberAccessFlags.SkipTypeBaseClass ); if (newMethodMember) { @@ -17384,7 +17342,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions const baseClassMemberInfo = lookUpClassMember( containingClassType, methodName, - ClassMemberLookupFlags.SkipOriginalClass + MemberAccessFlags.SkipOriginalClass ); if (baseClassMemberInfo) { @@ -19893,7 +19851,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions if (isInstantiableClass(subtype)) { // Try to find a member that has a declared type. If so, that // overrides any inferred types. - let member = lookUpClassMember(subtype, memberName, ClassMemberLookupFlags.DeclaredTypesOnly); + let member = lookUpClassMember(subtype, memberName, MemberAccessFlags.DeclaredTypesOnly); if (!member) { member = lookUpClassMember(subtype, memberName); } @@ -19911,7 +19869,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions } else if (isClassInstance(subtype)) { // Try to find a member that has a declared type. If so, that // overrides any inferred types. - let member = lookUpObjectMember(subtype, memberName, ClassMemberLookupFlags.DeclaredTypesOnly); + let member = lookUpObjectMember(subtype, memberName, MemberAccessFlags.DeclaredTypesOnly); if (!member) { member = lookUpObjectMember(subtype, memberName); } @@ -25971,7 +25929,6 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions getBuiltInType, getTypeOfMember, getTypeOfObjectMember, - getTypeOfClassMemberName, getBoundMethod, getTypeOfMagicMethodCall, bindFunctionToClassOrObject, diff --git a/packages/pyright-internal/src/analyzer/typeEvaluatorTypes.ts b/packages/pyright-internal/src/analyzer/typeEvaluatorTypes.ts index d87b1d721619..8c6263d8961e 100644 --- a/packages/pyright-internal/src/analyzer/typeEvaluatorTypes.ts +++ b/packages/pyright-internal/src/analyzer/typeEvaluatorTypes.ts @@ -48,7 +48,7 @@ import { TypeVarType, UnknownType, } from './types'; -import { AssignTypeFlags, ClassMember, InferenceContext } from './typeUtils'; +import { AssignTypeFlags, ClassMember, InferenceContext, MemberAccessFlags } from './typeUtils'; import { TypeVarContext } from './typeVarContext'; // Maximum number of unioned subtypes for an inferred type (e.g. @@ -387,8 +387,8 @@ export interface ClassMemberLookup { type: Type; isTypeIncomplete: boolean; - // True if access violates the type (used only for 'set' usage). - isSetTypeError: boolean; + // True if binding or descriptor access failed. + isDescriptorError: boolean; // True if class member, false otherwise. isClassMember: boolean; @@ -428,46 +428,6 @@ export interface ResolveAliasOptions { skipFileNeededCheck?: boolean; } -export const enum MemberAccessFlags { - None = 0, - - // By default, member accesses are assumed to access the attributes - // of a class instance. By setting this flag, only attributes of - // the class are considered. - AccessClassMembersOnly = 1 << 0, - - // Consider only instance members, not members that could be - // class members. - AccessInstanceMembersOnly = 1 << 1, - - // By default, members of base classes are also searched. - // Set this flag to consider only the specified class' members. - SkipBaseClasses = 1 << 2, - - // Do not include the "object" base class in the search. - SkipObjectBaseClass = 1 << 3, - - // Consider writes to symbols flagged as ClassVars as an error. - DisallowClassVarWrites = 1 << 4, - - // Normally __new__ is treated as a static method, but when - // it is invoked implicitly through a constructor call, it - // acts like a class method instead. - TreatConstructorAsClassMethod = 1 << 5, - - // If an attribute cannot be found when looking for instance - // members, normally an attribute access override method - // (__getattr__, etc.) may provide the missing attribute type. - // This disables this check. - SkipAttributeAccessOverride = 1 << 7, - - // Do not include the class itself, only base classes. - SkipOriginalClass = 1 << 8, - - // Do not include the "type" base class in the search. - SkipTypeBaseClass = 1 << 9, -} - export interface ValidateTypeArgsOptions { allowEmptyTuple?: boolean; allowVariadicTypeVar?: boolean; @@ -582,15 +542,6 @@ export interface TypeEvaluator { flags?: MemberAccessFlags, selfType?: ClassType | TypeVarType ): TypeResult | undefined; - getTypeOfClassMemberName: ( - errorNode: ExpressionNode, - classType: ClassType, - memberName: string, - usage: EvaluatorUsage, - diag: DiagnosticAddendum | undefined, - flags: MemberAccessFlags, - selfType?: ClassType | TypeVarType - ) => ClassMemberLookup | undefined; getBoundMethod: ( classType: ClassType, memberName: string, diff --git a/packages/pyright-internal/src/analyzer/typeGuards.ts b/packages/pyright-internal/src/analyzer/typeGuards.ts index 72d3d02032eb..6ddd80452e98 100644 --- a/packages/pyright-internal/src/analyzer/typeGuards.ts +++ b/packages/pyright-internal/src/analyzer/typeGuards.ts @@ -65,7 +65,6 @@ import { applySolvedTypeVars, AssignTypeFlags, ClassMember, - ClassMemberLookupFlags, computeMroLinearization, containsAnyOrUnknown, convertToInstance, @@ -87,6 +86,7 @@ import { lookUpClassMember, lookUpObjectMember, mapSubtypes, + MemberAccessFlags, specializeTupleClass, specializeWithUnknown, transformPossibleRecursiveTypeAlias, @@ -1563,7 +1563,7 @@ function narrowTypeForIsInstance( isCallable = !!lookUpClassMember( concreteVarType, '__call__', - ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipInstanceMembers ); } } @@ -2499,11 +2499,7 @@ function narrowTypeForCallable( } // See if the object is callable. - const callMemberType = lookUpClassMember( - subtype, - '__call__', - ClassMemberLookupFlags.SkipInstanceVariables - ); + const callMemberType = lookUpClassMember(subtype, '__call__', MemberAccessFlags.SkipInstanceMembers); if (!callMemberType) { if (!isPositiveTest) { diff --git a/packages/pyright-internal/src/analyzer/typeUtils.ts b/packages/pyright-internal/src/analyzer/typeUtils.ts index 94ba81412ef1..847c608c603f 100644 --- a/packages/pyright-internal/src/analyzer/typeUtils.ts +++ b/packages/pyright-internal/src/analyzer/typeUtils.ts @@ -90,7 +90,7 @@ export interface ClassMember { skippedUndeclaredType: boolean; } -export const enum ClassMemberLookupFlags { +export const enum MemberAccessFlags { Default = 0, // By default, the original (derived) class is searched along @@ -106,21 +106,35 @@ export const enum ClassMemberLookupFlags { // Skip the 'object' base class in particular. SkipObjectBaseClass = 1 << 2, + // Skip the 'type' base class in particular. + SkipTypeBaseClass = 1 << 3, + // By default, both class and instance variables are searched. // If this flag is set, the instance variables are skipped. - SkipInstanceVariables = 1 << 3, + SkipInstanceMembers = 1 << 4, // By default, both class and instance variables are searched. // If this flag is set, the class variables are skipped. - SkipClassVariables = 1 << 4, + SkipClassMembers = 1 << 5, // By default, the first symbol is returned even if it has only // an inferred type associated with it. If this flag is set, // the search looks only for symbols with declared types. - DeclaredTypesOnly = 1 << 5, + DeclaredTypesOnly = 1 << 6, - // Skip the 'type' base class in particular. - SkipTypeBaseClass = 1 << 6, + // Consider writes to symbols flagged as ClassVars as an error. + DisallowClassVarWrites = 1 << 7, + + // Normally __new__ is treated as a static method, but when + // it is invoked implicitly through a constructor call, it + // acts like a class method instead. + TreatConstructorAsClassMethod = 1 << 8, + + // If an attribute cannot be found when looking for instance + // members, normally an attribute access override method + // (__getattr__, etc.) may provide the missing attribute type. + // This disables this check. + SkipAttributeAccessOverride = 1 << 9, } export const enum ClassIteratorFlags { @@ -1135,7 +1149,7 @@ export function isCallableType(type: Type): boolean { return true; } - const callMember = lookUpObjectMember(type, '__call__', ClassMemberLookupFlags.SkipInstanceVariables); + const callMember = lookUpObjectMember(type, '__call__', MemberAccessFlags.SkipInstanceMembers); return !!callMember; } @@ -1489,7 +1503,7 @@ export function getContainerDepth(type: Type, recursionCount = 0) { export function lookUpObjectMember( objectType: ClassType, memberName: string, - flags = ClassMemberLookupFlags.Default, + flags = MemberAccessFlags.Default, skipMroClass?: ClassType | undefined ): ClassMember | undefined { if (isClassInstance(objectType)) { @@ -1504,7 +1518,7 @@ export function lookUpObjectMember( export function lookUpClassMember( classType: ClassType, memberName: string, - flags = ClassMemberLookupFlags.Default, + flags = MemberAccessFlags.Default, skipMroClass?: ClassType | undefined ): ClassMember | undefined { const memberItr = getClassMemberIterator(classType, memberName, flags, skipMroClass); @@ -1525,26 +1539,26 @@ export function lookUpClassMember( export function* getClassMemberIterator( classType: ClassType | AnyType | UnknownType, memberName: string, - flags = ClassMemberLookupFlags.Default, + flags = MemberAccessFlags.Default, skipMroClass?: ClassType | undefined ) { - const declaredTypesOnly = (flags & ClassMemberLookupFlags.DeclaredTypesOnly) !== 0; + const declaredTypesOnly = (flags & MemberAccessFlags.DeclaredTypesOnly) !== 0; let skippedUndeclaredType = false; if (isClass(classType)) { let classFlags = ClassIteratorFlags.Default; - if (flags & ClassMemberLookupFlags.SkipOriginalClass) { + if (flags & MemberAccessFlags.SkipOriginalClass) { if (isClass(classType)) { skipMroClass = classType; } } - if (flags & ClassMemberLookupFlags.SkipBaseClasses) { + if (flags & MemberAccessFlags.SkipBaseClasses) { classFlags = classFlags | ClassIteratorFlags.SkipBaseClasses; } - if (flags & ClassMemberLookupFlags.SkipObjectBaseClass) { + if (flags & MemberAccessFlags.SkipObjectBaseClass) { classFlags = classFlags | ClassIteratorFlags.SkipObjectBaseClass; } - if (flags & ClassMemberLookupFlags.SkipTypeBaseClass) { + if (flags & MemberAccessFlags.SkipTypeBaseClass) { classFlags = classFlags | ClassIteratorFlags.SkipTypeBaseClass; } @@ -1576,7 +1590,7 @@ export function* getClassMemberIterator( const memberFields = specializedMroClass.details.fields; // Look at instance members first if requested. - if ((flags & ClassMemberLookupFlags.SkipInstanceVariables) === 0) { + if ((flags & MemberAccessFlags.SkipInstanceMembers) === 0) { const symbol = memberFields.get(memberName); if (symbol && symbol.isInstanceMember()) { const hasDeclaredType = symbol.hasTypedDeclarations(); @@ -1598,7 +1612,7 @@ export function* getClassMemberIterator( } // Next look at class members. - if ((flags & ClassMemberLookupFlags.SkipClassVariables) === 0) { + if ((flags & MemberAccessFlags.SkipClassMembers) === 0) { const symbol = memberFields.get(memberName); if (symbol && symbol.isClassMember()) { const hasDeclaredType = symbol.hasTypedDeclarations(); diff --git a/packages/pyright-internal/src/languageService/callHierarchyProvider.ts b/packages/pyright-internal/src/languageService/callHierarchyProvider.ts index 2e60e8290786..13298cb793b2 100644 --- a/packages/pyright-internal/src/languageService/callHierarchyProvider.ts +++ b/packages/pyright-internal/src/languageService/callHierarchyProvider.ts @@ -22,22 +22,22 @@ import * as ParseTreeUtils from '../analyzer/parseTreeUtils'; import { ParseTreeWalker } from '../analyzer/parseTreeWalker'; import { isUserCode } from '../analyzer/sourceFileInfoUtils'; import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes'; -import { ClassMemberLookupFlags, doForEachSubtype, lookUpClassMember, lookUpObjectMember } from '../analyzer/typeUtils'; +import { MemberAccessFlags, doForEachSubtype, lookUpClassMember, lookUpObjectMember } from '../analyzer/typeUtils'; import { ClassType, isClassInstance, isFunction, isInstantiableClass } from '../analyzer/types'; import { throwIfCancellationRequested } from '../common/cancellationUtils'; import { appendArray } from '../common/collectionUtils'; +import { isDefined } from '../common/core'; import { ProgramView, ReferenceUseCase, SymbolUsageProvider } from '../common/extensibility'; import { getSymbolKind } from '../common/lspUtils'; import { convertPathToUri, getFileName } from '../common/pathUtils'; import { convertOffsetsToRange } from '../common/positionUtils'; +import { ServiceKeys } from '../common/serviceProviderExtensions'; import { Position, rangesAreEqual } from '../common/textRange'; import { ReferencesProvider, ReferencesResult } from '../languageService/referencesProvider'; import { CallNode, MemberAccessNode, NameNode, ParseNode, ParseNodeType } from '../parser/parseNodes'; import { ParseResults } from '../parser/parser'; import { DocumentSymbolCollector } from './documentSymbolCollector'; import { canNavigateToFile } from './navigationUtils'; -import { ServiceKeys } from '../common/serviceProviderExtensions'; -import { isDefined } from '../common/core'; export class CallHierarchyProvider { private readonly _parseResults: ParseResults | undefined; @@ -180,9 +180,9 @@ export class CallHierarchyProvider { const initMethodMember = lookUpClassMember( classType, '__init__', - ClassMemberLookupFlags.SkipInstanceVariables | - ClassMemberLookupFlags.SkipObjectBaseClass | - ClassMemberLookupFlags.SkipBaseClasses + MemberAccessFlags.SkipInstanceMembers | + MemberAccessFlags.SkipObjectBaseClass | + MemberAccessFlags.SkipBaseClasses ); if (initMethodMember) { const initMethodType = this._evaluator.getTypeOfMember(initMethodMember); diff --git a/packages/pyright-internal/src/languageService/completionProvider.ts b/packages/pyright-internal/src/languageService/completionProvider.ts index b1be52523bdc..79f2a2bb963e 100644 --- a/packages/pyright-internal/src/languageService/completionProvider.ts +++ b/packages/pyright-internal/src/languageService/completionProvider.ts @@ -60,7 +60,6 @@ import { TypeCategory, } from '../analyzer/types'; import { - ClassMemberLookupFlags, containsLiteralType, doForEachSignature, doForEachSubtype, @@ -70,6 +69,7 @@ import { isMaybeDescriptorInstance, isNoneInstance, lookUpClassMember, + MemberAccessFlags, } from '../analyzer/typeUtils'; import { throwIfCancellationRequested } from '../common/cancellationUtils'; import { appendArray } from '../common/collectionUtils'; @@ -1633,7 +1633,7 @@ export class CompletionProvider { const classMember = lookUpClassMember( classResults.classType, classVariableName, - ClassMemberLookupFlags.SkipInstanceVariables | ClassMemberLookupFlags.SkipOriginalClass + MemberAccessFlags.SkipInstanceMembers | MemberAccessFlags.SkipOriginalClass ); // First, see whether we can use semantic info to get variable type. diff --git a/packages/pyright-internal/src/languageService/tooltipUtils.ts b/packages/pyright-internal/src/languageService/tooltipUtils.ts index c9ed286ca53b..89d464587999 100644 --- a/packages/pyright-internal/src/languageService/tooltipUtils.ts +++ b/packages/pyright-internal/src/languageService/tooltipUtils.ts @@ -22,7 +22,7 @@ import { getVariableDocString, } from '../analyzer/typeDocStringUtils'; import { TypeEvaluator } from '../analyzer/typeEvaluatorTypes'; -import { ClassMemberLookupFlags, lookUpClassMember } from '../analyzer/typeUtils'; +import { MemberAccessFlags, lookUpClassMember } from '../analyzer/typeUtils'; import { ClassType, FunctionType, @@ -400,7 +400,7 @@ export function getClassAndConstructorTypes(node: NameNode, evaluator: TypeEvalu // Try to get the `__init__` method first because it typically has more type information than `__new__`. // Don't exclude `object.__init__` since in the plain case we want to show Foo(). - const initMember = lookUpClassMember(classType, '__init__', ClassMemberLookupFlags.SkipInstanceVariables); + const initMember = lookUpClassMember(classType, '__init__', MemberAccessFlags.SkipInstanceMembers); if (initMember) { const functionType = evaluator.getTypeOfMember(initMember); @@ -421,7 +421,7 @@ export function getClassAndConstructorTypes(node: NameNode, evaluator: TypeEvalu const newMember = lookUpClassMember( classType, '__new__', - ClassMemberLookupFlags.SkipObjectBaseClass | ClassMemberLookupFlags.SkipInstanceVariables + MemberAccessFlags.SkipObjectBaseClass | MemberAccessFlags.SkipInstanceMembers ); if (newMember) {