Skip to content

Commit

Permalink
Fixed bug that resulted in a false negative when assigning a speciali…
Browse files Browse the repository at this point in the history
…zed recursive generic type alias to itself when variance of its type parameters are not covariant. This addresses #9081. (#9103)
  • Loading branch information
erictraut authored Sep 28, 2024
1 parent 61767ff commit 7309394
Show file tree
Hide file tree
Showing 14 changed files with 211 additions and 114 deletions.
4 changes: 2 additions & 2 deletions packages/pyright-internal/src/analyzer/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2333,7 +2333,7 @@ export class Checker extends ParseTreeWalker {
// the TypeVar multiple times.
const baseType = this._evaluator.getType(baseExpression);
const aliasInfo = baseType?.props?.typeAliasInfo;
if (aliasInfo?.typeParams && subscriptIndex < aliasInfo.typeParams.length) {
if (aliasInfo?.shared.typeParams && subscriptIndex < aliasInfo.shared.typeParams.length) {
isExempt = true;
}
}
Expand Down Expand Up @@ -4238,7 +4238,7 @@ export class Checker extends ParseTreeWalker {
if (deprecatedForm) {
if (
(isInstantiableClass(type) && type.shared.fullName === deprecatedForm.fullName) ||
type.props?.typeAliasInfo?.fullName === deprecatedForm.fullName
type.props?.typeAliasInfo?.shared.fullName === deprecatedForm.fullName
) {
if (
PythonVersion.isGreaterOrEqualTo(
Expand Down
7 changes: 5 additions & 2 deletions packages/pyright-internal/src/analyzer/constructors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,12 @@ export function validateConstructorArgs(
// If this is an unspecialized generic type alias, specialize it now
// using default type argument values.
const aliasInfo = type.props?.typeAliasInfo;
if (aliasInfo?.typeParams && !aliasInfo.typeArgs) {
if (aliasInfo?.shared.typeParams && !aliasInfo.typeArgs) {
type = applySolvedTypeVars(type, new ConstraintSolution(), {
replaceUnsolved: { scopeIds: [aliasInfo.typeVarScopeId], tupleClassType: evaluator.getTupleClassType() },
replaceUnsolved: {
scopeIds: [aliasInfo.shared.typeVarScopeId],
tupleClassType: evaluator.getTupleClassType(),
},
}) as ClassType;
}

Expand Down
14 changes: 10 additions & 4 deletions packages/pyright-internal/src/analyzer/packageTypeVerifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -765,15 +765,17 @@ export class PackageTypeVerifier {
if (isUnknown(typeArg)) {
this._addSymbolError(
symbolInfo,
`Type argument ${index + 1} for type alias "${aliasInfo!.name}" has unknown type`,
`Type argument ${index + 1} for type alias "${aliasInfo!.shared.name}" has unknown type`,
declRange,
declFileUri
);
knownStatus = TypeKnownStatus.Unknown;
} else if (isPartlyUnknown(typeArg)) {
this._addSymbolError(
symbolInfo,
`Type argument ${index + 1} for type alias "${aliasInfo!.name}" has partially unknown type`,
`Type argument ${index + 1} for type alias "${
aliasInfo!.shared.name
}" has partially unknown type`,
declRange,
declFileUri
);
Expand Down Expand Up @@ -1308,11 +1310,15 @@ export class PackageTypeVerifier {
if (aliasInfo?.typeArgs) {
aliasInfo.typeArgs.forEach((typeArg, index) => {
if (isUnknown(typeArg)) {
diag.addMessage(`Type argument ${index + 1} for type alias "${aliasInfo!.name}" has unknown type`);
diag.addMessage(
`Type argument ${index + 1} for type alias "${aliasInfo!.shared.name}" has unknown type`
);
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.Unknown);
} else if (isPartlyUnknown(typeArg)) {
diag.addMessage(
`Type argument ${index + 1} for type alias "${aliasInfo!.name}" has partially unknown type`
`Type argument ${index + 1} for type alias "${
aliasInfo!.shared.name
}" has partially unknown type`
);
knownStatus = this._updateKnownStatusIfWorse(knownStatus, TypeKnownStatus.PartiallyUnknown);
}
Expand Down
8 changes: 5 additions & 3 deletions packages/pyright-internal/src/analyzer/sourceMapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,13 +605,15 @@ export class SourceMapper {
useTypeAlias = false
) {
const fileUri =
useTypeAlias && type.props?.typeAliasInfo ? type.props.typeAliasInfo.fileUri : type.shared.fileUri;
useTypeAlias && type.props?.typeAliasInfo ? type.props.typeAliasInfo.shared.fileUri : type.shared.fileUri;
const sourceFiles = this._getSourceFiles(fileUri, /* stubToShadow */ undefined, originated);

const fullName =
useTypeAlias && type.props?.typeAliasInfo ? type.props.typeAliasInfo.fullName : type.shared.fullName;
useTypeAlias && type.props?.typeAliasInfo ? type.props.typeAliasInfo.shared.fullName : type.shared.fullName;
const moduleName =
useTypeAlias && type.props?.typeAliasInfo ? type.props.typeAliasInfo.moduleName : type.shared.moduleName;
useTypeAlias && type.props?.typeAliasInfo
? type.props.typeAliasInfo.shared.moduleName
: type.shared.moduleName;
const fullClassName = fullName.substring(moduleName.length + 1 /* +1 for trailing dot */);

for (const sourceFile of sourceFiles) {
Expand Down
10 changes: 5 additions & 5 deletions packages/pyright-internal/src/analyzer/tracePrinter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function createTracePrinter(roots: Uri[]): TracePrinter {
if (type) {
switch (type.category) {
case TypeCategory.Any:
return `Any ${wrap(type.props?.typeAliasInfo?.fullName)}`;
return `Any ${wrap(type.props?.typeAliasInfo?.shared.fullName)}`;

case TypeCategory.Class:
if (TypeBase.isInstantiable(type)) {
Expand All @@ -75,24 +75,24 @@ export function createTracePrinter(roots: Uri[]): TracePrinter {
return `Module '${type.priv.moduleName}' (${type.priv.moduleName})`;

case TypeCategory.Never:
return `Never ${wrap(type.props?.typeAliasInfo?.fullName)}`;
return `Never ${wrap(type.props?.typeAliasInfo?.shared.fullName)}`;

case TypeCategory.Overloaded:
return `Overloaded [${OverloadedType.getOverloads(type)
.map((o) => wrap(printType(o), '"'))
.join(',')}]`;

case TypeCategory.TypeVar:
return `TypeVar '${type.shared.name}' ${wrap(type.props?.typeAliasInfo?.fullName)}`;
return `TypeVar '${type.shared.name}' ${wrap(type.props?.typeAliasInfo?.shared.fullName)}`;

case TypeCategory.Unbound:
return `Unbound ${wrap(type.props?.typeAliasInfo?.fullName)}`;
return `Unbound ${wrap(type.props?.typeAliasInfo?.shared.fullName)}`;

case TypeCategory.Union:
return `Union [${type.priv.subtypes.map((o) => wrap(printType(o), '"')).join(',')}]`;

case TypeCategory.Unknown:
return `Unknown ${wrap(type.props?.typeAliasInfo?.fullName)}`;
return `Unknown ${wrap(type.props?.typeAliasInfo?.shared.fullName)}`;

default:
assertNever(type);
Expand Down
Loading

0 comments on commit 7309394

Please sign in to comment.