Fixed a bug that caused a false positive error when using a list comprehension within a method that is overloaded to accept either a LiteralString or a str. The incorrect overload was chosen in some cases, picking the LiteralString variant rather than the str variant even though LiteralString variant generated type errors.

This commit is contained in:
Eric Traut 2022-08-03 06:57:24 -07:00
parent 21a6a1b2a9
commit 05c46fc09a
3 changed files with 49 additions and 5 deletions

View File

@ -12347,6 +12347,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const builtInClassName = node.nodeType === ParseNodeType.List ? 'list' : 'set';
expectedType = transformPossibleRecursiveTypeAlias(expectedType);
let isIncomplete = false;
let typeErrors = false;
if (!isClassInstance(expectedType)) {
return undefined;
@ -12392,6 +12393,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (entryTypeResult.isIncomplete) {
isIncomplete = true;
}
if (entryTypeResult.typeErrors) {
typeErrors = true;
}
});
const isExpectedTypeListOrSet =
@ -12406,7 +12410,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
}
const type = getBuiltInObject(node, builtInClassName, [specializedEntryType]);
return { type, isIncomplete };
return { type, isIncomplete, typeErrors };
}
// Attempts to infer the type of a list or set statement with no "expected type".
@ -12414,6 +12418,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const builtInClassName = node.nodeType === ParseNodeType.List ? 'list' : 'set';
let isEmptyContainer = false;
let isIncomplete = false;
let typeErrors = false;
let entryTypes: Type[] = [];
node.entries.forEach((entry, index) => {
@ -12432,6 +12437,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (entryTypeResult.isIncomplete) {
isIncomplete = true;
}
if (entryTypeResult.typeErrors) {
typeErrors = true;
}
if (index < maxEntriesToUseForInference) {
entryTypes.push(entryTypeResult.type);
@ -12475,7 +12483,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
)
: UnknownType.create();
return { type, isIncomplete };
return { type, isIncomplete, typeErrors };
}
function inferTypeArgFromExpectedType(
@ -12549,6 +12557,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const typesToCombine: Type[] = [];
let isIncomplete = false;
let typeErrors = false;
if (isNodeReachable(node.ifExpression)) {
const ifType = getTypeOfExpression(node.ifExpression, flags, expectedType);
@ -12556,6 +12565,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (ifType.isIncomplete) {
isIncomplete = true;
}
if (ifType.typeErrors) {
typeErrors = true;
}
}
if (isNodeReachable(node.elseExpression)) {
@ -12564,9 +12576,12 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (elseType.isIncomplete) {
isIncomplete = true;
}
if (elseType.typeErrors) {
typeErrors = true;
}
}
return { type: combineTypes(typesToCombine), isIncomplete };
return { type: combineTypes(typesToCombine), isIncomplete, typeErrors };
}
function getTypeOfYield(node: YieldNode): TypeResult {
@ -12790,11 +12805,15 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
function getTypeOfListComprehension(node: ListComprehensionNode, expectedType?: Type): TypeResult {
let isIncomplete = false;
let typeErrors = false;
const elementTypeResult = getElementTypeFromListComprehension(node);
if (elementTypeResult.isIncomplete) {
isIncomplete = true;
}
if (elementTypeResult.typeErrors) {
typeErrors = true;
}
const elementType = elementTypeResult.type;
let isAsync = node.forIfNodes.some((comp) => {
@ -12834,7 +12853,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
);
}
return { type, isIncomplete };
return { type, isIncomplete, typeErrors };
}
function reportPossibleUnknownAssignment(
@ -12917,6 +12936,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
expectedKeyType?: Type
): TypeResult {
let isIncomplete = false;
let typeErrors = false;
// "Execute" the list comprehensions from start to finish.
for (const forIfNode of node.forIfNodes) {
@ -12936,6 +12956,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (keyTypeResult.isIncomplete) {
isIncomplete = true;
}
if (keyTypeResult.typeErrors) {
typeErrors = true;
}
let keyType = keyTypeResult.type;
if (!expectedKeyType || !containsLiteralType(expectedKeyType)) {
keyType = stripLiteralValue(keyType);
@ -12949,6 +12972,9 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (valueTypeResult.isIncomplete) {
isIncomplete = true;
}
if (valueTypeResult.typeErrors) {
typeErrors = true;
}
let valueType = valueTypeResult.type;
if (!expectedValueOrElementType || !containsLiteralType(expectedValueOrElementType)) {
valueType = stripLiteralValue(valueType);
@ -12967,10 +12993,13 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
if (exprTypeResult.isIncomplete) {
isIncomplete = true;
}
if (exprTypeResult.typeErrors) {
typeErrors = true;
}
type = exprTypeResult.type;
}
return { type, isIncomplete };
return { type, isIncomplete, typeErrors };
}
function getTypeOfSlice(node: SliceNode): TypeResult {

View File

@ -0,0 +1,10 @@
# This sample tests that list and list comprehension type errors are
# reported up for correct overload selection.
import random
from typing_extensions import LiteralString
# The join method is overloaded with both LiteralString and str variants.
# We need to use the str overload here.
def func(x: LiteralString):
"".join([random.choice(x) for _ in range(8)])

View File

@ -993,6 +993,11 @@ test('List1', () => {
TestUtils.validateResults(analysisResults, 1);
});
test('List2', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['list2.py']);
TestUtils.validateResults(analysisResults, 0);
});
test('Comparison1', () => {
const configOptions = new ConfigOptions('.');