mirror of
https://github.com/microsoft/pyright.git
synced 2024-10-06 12:57:14 +03:00
Fixed bug that results in a false positive error when using a TypeVar with an upper bound of type
as a base class in a class
statement. This addresses #8313. (#8321)
This commit is contained in:
parent
3ddf0ad705
commit
1c85d650ae
@ -1381,28 +1381,44 @@ export function createTypeEvaluator(
|
||||
return;
|
||||
}
|
||||
|
||||
if (flags & EvalFlags.NoTypeVarTuple) {
|
||||
if ((flags & EvalFlags.NoTypeVarTuple) !== 0) {
|
||||
if (isVariadicTypeVar(typeResult.type) && !typeResult.type.isVariadicInUnion) {
|
||||
addError(LocMessage.typeVarTupleContext(), node);
|
||||
typeResult.type = UnknownType.create();
|
||||
}
|
||||
}
|
||||
|
||||
if (!isEffectivelyInstantiable(typeResult.type)) {
|
||||
const isEmptyVariadic =
|
||||
isClassInstance(typeResult.type) &&
|
||||
ClassType.isTupleClass(typeResult.type) &&
|
||||
typeResult.type.tupleTypeArguments?.length === 0;
|
||||
|
||||
const isEllipsis =
|
||||
isClassInstance(typeResult.type) && ClassType.isBuiltIn(typeResult.type, ['EllipsisType', 'ellipsis']);
|
||||
|
||||
if (!isEmptyVariadic && !isEllipsis) {
|
||||
addExpectedClassDiagnostic(typeResult.type, node);
|
||||
typeResult.type = UnknownType.create();
|
||||
typeResult.typeErrors = true;
|
||||
}
|
||||
if (isEffectivelyInstantiable(typeResult.type, { honorTypeVarBounds: true })) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exempt ellipses.
|
||||
if (isClassInstance(typeResult.type) && ClassType.isBuiltIn(typeResult.type, ['EllipsisType', 'ellipsis'])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Exempt empty tuples, which can be used for specializing a TypeVarTuple.
|
||||
if (isClassInstance(typeResult.type) && typeResult.type.tupleTypeArguments?.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const diag = new DiagnosticAddendum();
|
||||
if (isUnion(typeResult.type)) {
|
||||
doForEachSubtype(typeResult.type, (subtype) => {
|
||||
if (!isEffectivelyInstantiable(subtype, { honorTypeVarBounds: true })) {
|
||||
diag.addMessage(LocAddendum.typeNotClass().format({ type: printType(subtype) }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addDiagnostic(
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
LocMessage.typeExpectedClass().format({ type: printType(typeResult.type) }) + diag.getString(),
|
||||
node
|
||||
);
|
||||
|
||||
typeResult.type = UnknownType.create();
|
||||
typeResult.typeErrors = true;
|
||||
}
|
||||
|
||||
function getTypeOfAwaitOperator(node: AwaitNode, flags: EvalFlags, inferenceContext?: InferenceContext) {
|
||||
@ -3287,23 +3303,6 @@ export function createTypeEvaluator(
|
||||
return diagnostic;
|
||||
}
|
||||
|
||||
function addExpectedClassDiagnostic(type: Type, node: ParseNode) {
|
||||
const diag = new DiagnosticAddendum();
|
||||
if (isUnion(type)) {
|
||||
doForEachSubtype(type, (subtype) => {
|
||||
if (!isEffectivelyInstantiable(subtype)) {
|
||||
diag.addMessage(LocAddendum.typeNotClass().format({ type: printType(subtype) }));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addDiagnostic(
|
||||
DiagnosticRule.reportGeneralTypeIssues,
|
||||
LocMessage.typeExpectedClass().format({ type: printType(type) }) + diag.getString(),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
function assignTypeToNameNode(
|
||||
nameNode: NameNode,
|
||||
typeResult: TypeResult,
|
||||
@ -14912,9 +14911,6 @@ export function createTypeEvaluator(
|
||||
let typeArg0Type = typeArgs[0].type;
|
||||
if (!validateTypeArg(typeArgs[0])) {
|
||||
typeArg0Type = UnknownType.create();
|
||||
} else if (!isEffectivelyInstantiable(typeArg0Type)) {
|
||||
addExpectedClassDiagnostic(typeArg0Type, typeArgs[0].node);
|
||||
typeArg0Type = UnknownType.create();
|
||||
}
|
||||
|
||||
let optionalType = combineTypes([typeArg0Type, noneTypeClass ?? UnknownType.create()]);
|
||||
@ -15656,9 +15652,6 @@ export function createTypeEvaluator(
|
||||
})
|
||||
) {
|
||||
typeArgType = UnknownType.create();
|
||||
} else if (!isEffectivelyInstantiable(typeArgType)) {
|
||||
addExpectedClassDiagnostic(typeArgType, typeArg.node);
|
||||
typeArgType = UnknownType.create();
|
||||
}
|
||||
|
||||
// If this is an unpacked tuple, explode out the individual items.
|
||||
|
@ -277,6 +277,10 @@ export interface RequiresSpecializationOptions {
|
||||
ignoreImplicitTypeArgs?: boolean;
|
||||
}
|
||||
|
||||
export interface IsInstantiableOptions {
|
||||
honorTypeVarBounds?: boolean;
|
||||
}
|
||||
|
||||
// Tracks whether a function signature has been seen before within
|
||||
// an expression. For example, in the expression "foo(foo, foo)", the
|
||||
// signature for "foo" will be seen three times at three different
|
||||
@ -2352,11 +2356,23 @@ export function isMetaclassInstance(type: Type): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
export function isEffectivelyInstantiable(type: Type): boolean {
|
||||
export function isEffectivelyInstantiable(type: Type, options?: IsInstantiableOptions, recursionCount = 0): boolean {
|
||||
if (recursionCount > maxTypeRecursionCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
recursionCount++;
|
||||
|
||||
if (TypeBase.isInstantiable(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (options?.honorTypeVarBounds && isTypeVar(type) && type.details.boundType) {
|
||||
if (isEffectivelyInstantiable(type.details.boundType, options, recursionCount)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the special case of 'type' (or subclasses thereof),
|
||||
// which are instantiable.
|
||||
if (isMetaclassInstance(type)) {
|
||||
@ -2364,7 +2380,7 @@ export function isEffectivelyInstantiable(type: Type): boolean {
|
||||
}
|
||||
|
||||
if (isUnion(type)) {
|
||||
return type.subtypes.every((subtype) => isEffectivelyInstantiable(subtype));
|
||||
return type.subtypes.every((subtype) => isEffectivelyInstantiable(subtype, options, recursionCount));
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -503,7 +503,7 @@
|
||||
"typedDictSecondArgDict": "Expected dict or keyword parameter as second parameter",
|
||||
"typedDictSecondArgDictEntry": "Expected simple dictionary entry",
|
||||
"typedDictSet": "Could not assign item in TypedDict",
|
||||
"typeExpectedClass": "Expected type expression but received \"{type}\"",
|
||||
"typeExpectedClass": "Expected class but received \"{type}\"",
|
||||
"typeGuardArgCount": "Expected a single type argument after \"TypeGuard\" or \"TypeIs\"",
|
||||
"typeGuardParamCount": "User-defined type guard functions and methods must have at least one input parameter",
|
||||
"typeIsReturnType": "Return type of TypeIs (\"{returnType}\") is not consistent with value parameter type (\"{type}\")",
|
||||
|
@ -6,6 +6,7 @@ from typing import Any, TypeVar
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
T2 = TypeVar("T2", bound=type[Any])
|
||||
|
||||
|
||||
class A:
|
||||
@ -77,3 +78,10 @@ class L(type[T]):
|
||||
def func2(cls: type[T]):
|
||||
class M(cls):
|
||||
pass
|
||||
|
||||
|
||||
def func3(cls: T2) -> T2:
|
||||
class M(cls):
|
||||
pass
|
||||
|
||||
return M
|
||||
|
Loading…
Reference in New Issue
Block a user