mirror of
https://github.com/microsoft/pyright.git
synced 2024-09-11 16:06:39 +03:00
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:
parent
21a6a1b2a9
commit
05c46fc09a
@ -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 {
|
||||
|
10
packages/pyright-internal/src/tests/samples/list2.py
Normal file
10
packages/pyright-internal/src/tests/samples/list2.py
Normal 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)])
|
@ -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('.');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user