Changed the hard-coded type of the __class__ symbol to be Self@T rather than T (where T is the enclosing class). This addresses #8305. (#8307)

This commit is contained in:
Eric Traut 2024-07-03 14:45:58 -07:00 committed by GitHub
parent d7c5d0691a
commit 8ed6c10486
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 26 additions and 20 deletions

View File

@ -532,7 +532,7 @@ export class Binder extends ParseTreeWalker {
const enclosingClass = ParseTreeUtils.getEnclosingClass(node);
if (enclosingClass) {
// Add the implicit "__class__" symbol described in PEP 3135.
this._addImplicitSymbolToCurrentScope('__class__', node, 'class');
this._addImplicitSymbolToCurrentScope('__class__', node, 'type[self]');
}
this._deferBinding(() => {

View File

@ -46,7 +46,7 @@ export const enum DeclarationType {
Alias,
}
export type IntrinsicType = 'Any' | 'str' | 'str | None' | 'int' | 'Iterable[str]' | 'class' | 'Dict[str, Any]';
export type IntrinsicType = 'Any' | 'str' | 'str | None' | 'int' | 'Iterable[str]' | 'type[self]' | 'Dict[str, Any]';
export interface DeclarationBase {
// Category of this symbol (function, variable, etc.).

View File

@ -8396,7 +8396,6 @@ export function createTypeEvaluator(
if ((functionInfo?.flags & FunctionTypeFlags.StaticMethod) !== 0) {
addError(LocMessage.superCallZeroArgFormStaticMethod(), node.leftExpression);
targetClassType = UnknownType.create();
}
}
} else {
@ -8405,6 +8404,8 @@ export function createTypeEvaluator(
}
}
const concreteTargetClassType = makeTopLevelTypeVarsConcrete(targetClassType);
// Determine whether to further narrow the type.
let bindToType: ClassType | undefined;
if (node.arguments.length > 1) {
@ -8417,11 +8418,11 @@ export function createTypeEvaluator(
if (isAnyOrUnknown(secondArgType)) {
// Ignore unknown or any types.
} else if (isClassInstance(secondArgType)) {
if (isInstantiableClass(targetClassType)) {
if (isInstantiableClass(concreteTargetClassType)) {
if (
!derivesFromClassRecursive(
ClassType.cloneAsInstantiable(secondArgType),
targetClassType,
concreteTargetClassType,
/* ignoreUnknown */ true
)
) {
@ -8430,10 +8431,10 @@ export function createTypeEvaluator(
}
bindToType = secondArgType;
} else if (isInstantiableClass(secondArgType)) {
if (isInstantiableClass(targetClassType)) {
if (isInstantiableClass(concreteTargetClassType)) {
if (
!ClassType.isBuiltIn(targetClassType, 'type') &&
!derivesFromClassRecursive(secondArgType, targetClassType, /* ignoreUnknown */ true)
!ClassType.isBuiltIn(concreteTargetClassType, 'type') &&
!derivesFromClassRecursive(secondArgType, concreteTargetClassType, /* ignoreUnknown */ true)
) {
reportError = true;
}
@ -8514,7 +8515,7 @@ export function createTypeEvaluator(
const parentNode = node.parent;
if (parentNode?.nodeType === ParseNodeType.MemberAccess) {
const memberName = parentNode.memberName.value;
let effectiveTargetClass = isClass(targetClassType) ? targetClassType : undefined;
let effectiveTargetClass = isClass(concreteTargetClassType) ? concreteTargetClassType : undefined;
// If the bind-to type is a protocol, don't use the effective target class.
// This pattern is used for mixins, where the mixin type is a protocol class
@ -8568,7 +8569,7 @@ export function createTypeEvaluator(
}
// Handle the super() call when used outside of a member access expression.
if (isInstantiableClass(targetClassType)) {
if (isInstantiableClass(concreteTargetClassType)) {
// We don't know which member is going to be accessed, so we cannot
// deterministically determine the correct type in this case. We'll
// use a heuristic that produces the "correct" (desired) behavior in
@ -8578,14 +8579,15 @@ export function createTypeEvaluator(
if (bindToType) {
let nextBaseClassType: Type | undefined;
if (ClassType.isSameGenericClass(bindToType, targetClassType)) {
if (ClassType.isSameGenericClass(bindToType, concreteTargetClassType)) {
if (bindToType.details.baseClasses.length > 0) {
nextBaseClassType = bindToType.details.baseClasses[0];
}
} else {
const baseClassIndex = bindToType.details.baseClasses.findIndex(
(baseClass) =>
isClass(baseClass) && ClassType.isSameGenericClass(baseClass, targetClassType as ClassType)
isClass(baseClass) &&
ClassType.isSameGenericClass(baseClass, concreteTargetClassType as ClassType)
);
if (baseClassIndex >= 0 && baseClassIndex < bindToType.details.baseClasses.length - 1) {
@ -8609,11 +8611,11 @@ export function createTypeEvaluator(
} else {
// If the class derives from one or more unknown classes,
// return unknown here to prevent spurious errors.
if (targetClassType.details.mro.some((mroBase) => isAnyOrUnknown(mroBase))) {
if (concreteTargetClassType.details.mro.some((mroBase) => isAnyOrUnknown(mroBase))) {
return { type: UnknownType.create() };
}
const baseClasses = targetClassType.details.baseClasses;
const baseClasses = concreteTargetClassType.details.baseClasses;
if (baseClasses.length > 0) {
const baseClassType = baseClasses[0];
if (isInstantiableClass(baseClassType)) {
@ -20977,10 +20979,14 @@ export function createTypeEvaluator(
return { type: AnyType.create() };
}
if (declaration.intrinsicType === 'class') {
if (declaration.intrinsicType === 'type[self]') {
const classNode = ParseTreeUtils.getEnclosingClass(declaration.node) as ClassNode;
const classTypeInfo = getTypeOfClass(classNode);
return { type: classTypeInfo?.classType };
return {
type: classTypeInfo
? synthesizeTypeVarForSelfCls(classTypeInfo.classType, /* isClsParam */ true)
: UnknownType.create(),
};
}
const strType = getBuiltInObject(declaration.node, 'str');

View File

@ -29,6 +29,7 @@ import {
VariableDeclaration,
} from '../analyzer/declaration';
import { isDefinedInFile } from '../analyzer/declarationUtils';
import { transformTypeForEnumMember } from '../analyzer/enums';
import { ImportedModuleDescriptor, ImportResolver } from '../analyzer/importResolver';
import { ImportResult } from '../analyzer/importResult';
import { getParameterListDetails, ParameterKind } from '../analyzer/parameterUtils';
@ -80,6 +81,7 @@ import { ProgramView } from '../common/extensibility';
import { fromLSPAny, toLSPAny } from '../common/lspUtils';
import { convertOffsetToPosition, convertPositionToOffset } from '../common/positionUtils';
import { PythonVersion, pythonVersion3_10, pythonVersion3_5 } from '../common/pythonVersion';
import '../common/serviceProviderExtensions';
import * as StringUtils from '../common/stringUtils';
import { comparePositions, Position, TextRange } from '../common/textRange';
import { TextRangeCollection } from '../common/textRangeCollection';
@ -127,8 +129,6 @@ import {
} from './completionProviderUtils';
import { DocumentSymbolCollector } from './documentSymbolCollector';
import { getAutoImportText, getDocumentationPartsForTypeAndDecl } from './tooltipUtils';
import '../common/serviceProviderExtensions';
import { transformTypeForEnumMember } from '../analyzer/enums';
namespace Keywords {
const base: string[] = [
@ -3033,7 +3033,7 @@ export class CompletionProvider {
switch (resolvedDeclaration.type) {
case DeclarationType.Intrinsic:
return resolvedDeclaration.intrinsicType === 'class'
return resolvedDeclaration.intrinsicType === 'type[self]'
? CompletionItemKind.Class
: CompletionItemKind.Variable;

View File

@ -38,7 +38,7 @@ class F(E):
class G(E, metaclass=type):
def my_method(self):
print(__class__)
reveal_type(__class__, expected_text="type[Self@G]")
# This should generate an error because only one metaclass is supported.