Rewrote containsAnyOrUnknown so it support recursive detection.

This commit is contained in:
Eric Traut 2023-06-03 16:38:09 -06:00
parent 5d083ac45a
commit 771b7e66bb
2 changed files with 46 additions and 28 deletions

View File

@ -198,7 +198,6 @@ import {
computeMroLinearization,
containsAnyOrUnknown,
containsLiteralType,
containsUnknown,
convertParamSpecValueToType,
convertToInstance,
convertToInstantiable,
@ -8218,16 +8217,21 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
for (let dedupedIndex = 0; dedupedIndex < dedupedMatchResults.length; dedupedIndex++) {
if (assignType(dedupedMatchResults[dedupedIndex], result.returnType)) {
if (!containsAnyOrUnknown(dedupedMatchResults[dedupedIndex])) {
const anyOrUnknown = containsAnyOrUnknown(
dedupedMatchResults[dedupedIndex],
/* recurse */ false
);
if (!anyOrUnknown) {
isSubtypeSubsumed = true;
} else if (!containsUnknown(dedupedMatchResults[dedupedIndex])) {
} else if (isAny(anyOrUnknown)) {
dedupedResultsIncludeAny = true;
}
break;
} else if (assignType(result.returnType, dedupedMatchResults[dedupedIndex])) {
if (!containsAnyOrUnknown(result.returnType)) {
const anyOrUnknown = containsAnyOrUnknown(result.returnType, /* recurse */ false);
if (!anyOrUnknown) {
dedupedMatchResults[dedupedIndex] = NeverType.createNever();
} else if (!containsUnknown(dedupedMatchResults[dedupedIndex])) {
} else if (isAny(anyOrUnknown)) {
dedupedResultsIncludeAny = true;
}
break;
@ -15433,7 +15437,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const decorator = node.decorators[i];
const newDecoratedType = applyClassDecorator(decoratedType, classType, decorator);
if (containsUnknown(newDecoratedType)) {
const unknownOrAny = containsAnyOrUnknown(newDecoratedType, /* recurse */ false);
if (unknownOrAny && isUnknown(unknownOrAny)) {
// Report this error only on the first unknown type.
if (!foundUnknown) {
addDiagnostic(
@ -16547,7 +16553,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const decorator = node.decorators[i];
const newDecoratedType = applyFunctionDecorator(decoratedType, functionType, decorator, node);
if (containsUnknown(newDecoratedType)) {
const unknownOrAny = containsAnyOrUnknown(newDecoratedType, /* recurse */ false);
if (unknownOrAny && isUnknown(unknownOrAny)) {
// Report this error only on the first unknown type.
if (!foundUnknown) {
addDiagnostic(

View File

@ -2103,32 +2103,42 @@ export function getMembersForModule(moduleType: ModuleType, symbolTable: SymbolT
});
}
// Determines if the type is an Any or Unknown or a union that
// contains one of these. It does not look at type arguments.
export function containsAnyOrUnknown(type: Type) {
let foundAnyOrUnknown = false;
// Determines if the type contains an Any or Unknown type. If so,
// it returns the Any or Unknown type. Unknowns are preferred over
// Any if both are present. If recurse is true, it will recurse
// through type arguments and parameters.
export function containsAnyOrUnknown(type: Type, recurse: boolean): AnyType | UnknownType | undefined {
class AnyOrUnknownWalker extends TypeWalker {
anyOrUnknownType: AnyType | UnknownType | undefined;
doForEachSubtype(type, (subtype) => {
if (isAnyOrUnknown(subtype)) {
foundAnyOrUnknown = true;
constructor(private _recurse: boolean) {
super();
}
});
return foundAnyOrUnknown;
}
// Determines if the type is an Unknown or a union that contains an Unknown.
// It does not look at type arguments.
export function containsUnknown(type: Type) {
let foundUnknown = false;
doForEachSubtype(type, (subtype) => {
if (isUnknown(subtype)) {
foundUnknown = true;
override visitUnknown(type: UnknownType) {
this.anyOrUnknownType = this.anyOrUnknownType ? preserveUnknown(this.anyOrUnknownType, type) : type;
}
});
return foundUnknown;
override visitAny(type: AnyType) {
this.anyOrUnknownType = this.anyOrUnknownType ? preserveUnknown(this.anyOrUnknownType, type) : type;
}
override visitClass(type: ClassType) {
if (this._recurse) {
super.visitClass(type);
}
}
override visitFunction(type: FunctionType) {
if (this._recurse) {
super.visitFunction(type);
}
}
}
const walker = new AnyOrUnknownWalker(recurse);
walker.walk(type);
return walker.anyOrUnknownType;
}
// Determines if any part of the type contains "Unknown", including any type arguments.