Changed tuple expression inference behavior to not preserve literal entry types if the tuple expression is embedded within another tuple, set, list, or dictionary expression. This addresses #7159. (#7970)

This commit is contained in:
Eric Traut 2024-05-22 16:10:05 -07:00 committed by GitHub
parent 0618acc535
commit 50d4f44735
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 102 additions and 37 deletions

View File

@ -261,11 +261,11 @@ var1 = [4]
When inferring the type of a tuple expression (in the absence of bidirectional inference hints), Pyright assumes that the tuple has a fixed length, and each tuple element is typed as specifically as possible. When inferring the type of a tuple expression (in the absence of bidirectional inference hints), Pyright assumes that the tuple has a fixed length, and each tuple element is typed as specifically as possible.
```python ```python
# The inferred type is tuple[Literal[1], Literal["a"], Literal[True]] # The inferred type is tuple[Literal[1], Literal["a"], Literal[True]].
var1 = (1, "a", True) var1 = (1, "a", True)
def func1(a: int): def func1(a: int):
# The inferred type is tuple[int, int] # The inferred type is tuple[int, int].
var2 = (a, a) var2 = (a, a)
# If you want the type to be tuple[int, ...] # If you want the type to be tuple[int, ...]
@ -274,6 +274,13 @@ def func1(a: int):
var3: tuple[int, ...] = (a, a) var3: tuple[int, ...] = (a, a)
``` ```
Because tuples are typed as specifically as possible, literal types are normally retained. However, as an exception to this inference rule, if the tuple expression is nested within another tuple, set, list or dictionary expression, literal types are not retained. This is done to avoid the inference of complex types (e.g. unions with many subtypes) when evaluating tuple statements with many entries.
```python
# The inferred type is list[tuple[int, str, bool]].
var4 = [(1, "a", True), (2, "b", False), (3, "c", False)]
```
#### List Expressions #### List Expressions
When inferring the type of a list expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics: When inferring the type of a list expression (in the absence of bidirectional inference hints), Pyright uses the following heuristics:

View File

@ -1175,7 +1175,7 @@ export function createTypeEvaluator(
} }
case ParseNodeType.ListComprehension: { case ParseNodeType.ListComprehension: {
typeResult = getTypeOfListComprehension(node, inferenceContext); typeResult = getTypeOfListComprehension(node, flags, inferenceContext);
break; break;
} }
@ -7826,6 +7826,12 @@ export function createTypeEvaluator(
return { type: makeTupleObject([]), isEmptyTupleShorthand: true }; return { type: makeTupleObject([]), isEmptyTupleShorthand: true };
} }
flags &= ~(
EvaluatorFlags.ExpectingTypeAnnotation |
EvaluatorFlags.EvaluateStringLiteralAsType |
EvaluatorFlags.ExpectingInstantiableType
);
// If the expected type is a union, recursively call for each of the subtypes // If the expected type is a union, recursively call for each of the subtypes
// to find one that matches. // to find one that matches.
let effectiveExpectedType = inferenceContext?.expectedType; let effectiveExpectedType = inferenceContext?.expectedType;
@ -7845,6 +7851,7 @@ export function createTypeEvaluator(
const subtypeResult = useSpeculativeMode(node, () => { const subtypeResult = useSpeculativeMode(node, () => {
return getTypeOfTupleWithContext( return getTypeOfTupleWithContext(
node, node,
flags,
makeInferenceContext(subtype), makeInferenceContext(subtype),
/* signatureTracker */ undefined /* signatureTracker */ undefined
); );
@ -7865,6 +7872,7 @@ export function createTypeEvaluator(
if (effectiveExpectedType) { if (effectiveExpectedType) {
const result = getTypeOfTupleWithContext( const result = getTypeOfTupleWithContext(
node, node,
flags,
makeInferenceContext(effectiveExpectedType), makeInferenceContext(effectiveExpectedType),
signatureTracker signatureTracker
); );
@ -7876,7 +7884,7 @@ export function createTypeEvaluator(
expectedTypeDiagAddendum = result?.expectedTypeDiagAddendum; expectedTypeDiagAddendum = result?.expectedTypeDiagAddendum;
} }
const typeResult = getTypeOfTupleInferred(node); const typeResult = getTypeOfTupleInferred(node, flags);
// If there was an expected type of Any, replace the resulting type // If there was an expected type of Any, replace the resulting type
// with Any rather than return a type with unknowns. // with Any rather than return a type with unknowns.
@ -7889,6 +7897,7 @@ export function createTypeEvaluator(
function getTypeOfTupleWithContext( function getTypeOfTupleWithContext(
node: TupleNode, node: TupleNode,
flags: EvaluatorFlags,
inferenceContext: InferenceContext, inferenceContext: InferenceContext,
signatureTracker: UniqueSignatureTracker | undefined signatureTracker: UniqueSignatureTracker | undefined
): TypeResult | undefined { ): TypeResult | undefined {
@ -7947,7 +7956,7 @@ export function createTypeEvaluator(
const entryTypeResults = node.expressions.map((expr, index) => const entryTypeResults = node.expressions.map((expr, index) =>
getTypeOfExpression( getTypeOfExpression(
expr, expr,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
makeInferenceContext( makeInferenceContext(
index < expectedTypes.length ? expectedTypes[index] : undefined, index < expectedTypes.length ? expectedTypes[index] : undefined,
inferenceContext.isTypeIncomplete inferenceContext.isTypeIncomplete
@ -7956,7 +7965,7 @@ export function createTypeEvaluator(
) )
); );
const isIncomplete = entryTypeResults.some((result) => result.isIncomplete); const isIncomplete = entryTypeResults.some((result) => result.isIncomplete);
const type = makeTupleObject(buildTupleTypesList(entryTypeResults)); const type = makeTupleObject(buildTupleTypesList(entryTypeResults, /* stripLiterals */ false));
// Copy any expected type diag addenda for precision error reporting. // Copy any expected type diag addenda for precision error reporting.
let expectedTypeDiagAddendum: DiagnosticAddendum | undefined; let expectedTypeDiagAddendum: DiagnosticAddendum | undefined;
@ -7972,11 +7981,15 @@ export function createTypeEvaluator(
return { type, expectedTypeDiagAddendum, isIncomplete }; return { type, expectedTypeDiagAddendum, isIncomplete };
} }
function getTypeOfTupleInferred(node: TupleNode): TypeResult { function getTypeOfTupleInferred(node: TupleNode, flags: EvaluatorFlags): TypeResult {
const entryTypeResults = node.expressions.map((expr) => getTypeOfExpression(expr)); const entryTypeResults = node.expressions.map((expr) =>
getTypeOfExpression(expr, flags | EvaluatorFlags.StripLiteralTypeForTuple)
);
const isIncomplete = entryTypeResults.some((result) => result.isIncomplete); const isIncomplete = entryTypeResults.some((result) => result.isIncomplete);
const type = makeTupleObject(buildTupleTypesList(entryTypeResults)); const type = makeTupleObject(
buildTupleTypesList(entryTypeResults, (flags & EvaluatorFlags.StripLiteralTypeForTuple) !== 0)
);
if (isIncomplete) { if (isIncomplete) {
if (getContainerDepth(type) > maxInferredContainerDepth) { if (getContainerDepth(type) > maxInferredContainerDepth) {
@ -7987,7 +8000,7 @@ export function createTypeEvaluator(
return { type, isIncomplete }; return { type, isIncomplete };
} }
function buildTupleTypesList(entryTypeResults: TypeResult[]): TupleTypeArgument[] { function buildTupleTypesList(entryTypeResults: TypeResult[], stripLiterals: boolean): TupleTypeArgument[] {
const entryTypes: TupleTypeArgument[] = []; const entryTypes: TupleTypeArgument[] = [];
for (const typeResult of entryTypeResults) { for (const typeResult of entryTypeResults) {
@ -8017,7 +8030,8 @@ export function createTypeEvaluator(
} else if (isNever(typeResult.type) && typeResult.isIncomplete && !typeResult.unpackedType) { } else if (isNever(typeResult.type) && typeResult.isIncomplete && !typeResult.unpackedType) {
entryTypes.push({ type: UnknownType.create(/* isIncomplete */ true), isUnbounded: false }); entryTypes.push({ type: UnknownType.create(/* isIncomplete */ true), isUnbounded: false });
} else { } else {
entryTypes.push({ type: typeResult.type, isUnbounded: !!typeResult.unpackedType }); const entryType = stripLiterals ? stripLiteralValue(typeResult.type) : typeResult.type;
entryTypes.push({ type: entryType, isUnbounded: !!typeResult.unpackedType });
} }
} }
@ -13318,7 +13332,7 @@ export function createTypeEvaluator(
} }
const subtypeResult = useSpeculativeMode(node, () => { const subtypeResult = useSpeculativeMode(node, () => {
return getTypeOfDictionaryWithContext(node, makeInferenceContext(subtype)); return getTypeOfDictionaryWithContext(node, flags, makeInferenceContext(subtype));
}); });
if (subtypeResult && assignType(subtype, subtypeResult.type)) { if (subtypeResult && assignType(subtype, subtypeResult.type)) {
@ -13341,6 +13355,7 @@ export function createTypeEvaluator(
expectedTypeDiagAddendum = new DiagnosticAddendum(); expectedTypeDiagAddendum = new DiagnosticAddendum();
const result = getTypeOfDictionaryWithContext( const result = getTypeOfDictionaryWithContext(
node, node,
flags,
makeInferenceContext(effectiveExpectedType), makeInferenceContext(effectiveExpectedType),
expectedTypeDiagAddendum expectedTypeDiagAddendum
); );
@ -13349,12 +13364,13 @@ export function createTypeEvaluator(
} }
} }
const result = getTypeOfDictionaryInferred(node, /* hasExpectedType */ !!inferenceContext); const result = getTypeOfDictionaryInferred(node, flags, /* hasExpectedType */ !!inferenceContext);
return { ...result, expectedTypeDiagAddendum }; return { ...result, expectedTypeDiagAddendum };
} }
function getTypeOfDictionaryWithContext( function getTypeOfDictionaryWithContext(
node: DictionaryNode, node: DictionaryNode,
flags: EvaluatorFlags,
inferenceContext: InferenceContext, inferenceContext: InferenceContext,
expectedDiagAddendum?: DiagnosticAddendum expectedDiagAddendum?: DiagnosticAddendum
): TypeResult | undefined { ): TypeResult | undefined {
@ -13381,6 +13397,7 @@ export function createTypeEvaluator(
// Infer the key and value types if possible. // Infer the key and value types if possible.
const keyValueTypeResult = getKeyAndValueTypesFromDictionary( const keyValueTypeResult = getKeyAndValueTypesFromDictionary(
node, node,
flags,
keyTypes, keyTypes,
valueTypes, valueTypes,
/* forceStrictInference */ true, /* forceStrictInference */ true,
@ -13472,6 +13489,7 @@ export function createTypeEvaluator(
// Infer the key and value types if possible. // Infer the key and value types if possible.
const keyValueResult = getKeyAndValueTypesFromDictionary( const keyValueResult = getKeyAndValueTypesFromDictionary(
node, node,
flags,
keyTypes, keyTypes,
valueTypes, valueTypes,
/* forceStrictInference */ true, /* forceStrictInference */ true,
@ -13510,7 +13528,11 @@ export function createTypeEvaluator(
// Attempts to infer the type of a dictionary statement. If hasExpectedType // Attempts to infer the type of a dictionary statement. If hasExpectedType
// is true, strict inference is used for the subexpressions. // is true, strict inference is used for the subexpressions.
function getTypeOfDictionaryInferred(node: DictionaryNode, hasExpectedType: boolean): TypeResult { function getTypeOfDictionaryInferred(
node: DictionaryNode,
flags: EvaluatorFlags,
hasExpectedType: boolean
): TypeResult {
const fallbackType = hasExpectedType ? AnyType.create() : UnknownType.create(); const fallbackType = hasExpectedType ? AnyType.create() : UnknownType.create();
let keyType: Type = fallbackType; let keyType: Type = fallbackType;
let valueType: Type = fallbackType; let valueType: Type = fallbackType;
@ -13525,6 +13547,7 @@ export function createTypeEvaluator(
// Infer the key and value types if possible. // Infer the key and value types if possible.
const keyValueResult = getKeyAndValueTypesFromDictionary( const keyValueResult = getKeyAndValueTypesFromDictionary(
node, node,
flags,
keyTypeResults, keyTypeResults,
valueTypeResults, valueTypeResults,
/* forceStrictInference */ hasExpectedType, /* forceStrictInference */ hasExpectedType,
@ -13586,6 +13609,7 @@ export function createTypeEvaluator(
function getKeyAndValueTypesFromDictionary( function getKeyAndValueTypesFromDictionary(
node: DictionaryNode, node: DictionaryNode,
flags: EvaluatorFlags,
keyTypes: TypeResultWithNode[], keyTypes: TypeResultWithNode[],
valueTypes: TypeResultWithNode[], valueTypes: TypeResultWithNode[],
forceStrictInference: boolean, forceStrictInference: boolean,
@ -13598,6 +13622,16 @@ export function createTypeEvaluator(
let isIncomplete = false; let isIncomplete = false;
let typeErrors = false; let typeErrors = false;
// Mask out some of the flags that are not applicable for a dictionary key
// even if it appears within an inlined TypedDict annotation.
const keyFlags =
flags &
~(
EvaluatorFlags.ExpectingTypeAnnotation |
EvaluatorFlags.EvaluateStringLiteralAsType |
EvaluatorFlags.ExpectingInstantiableType
);
// Infer the key and value types if possible. // Infer the key and value types if possible.
node.entries.forEach((entryNode, index) => { node.entries.forEach((entryNode, index) => {
let addUnknown = true; let addUnknown = true;
@ -13605,7 +13639,7 @@ export function createTypeEvaluator(
if (entryNode.nodeType === ParseNodeType.DictionaryKeyEntry) { if (entryNode.nodeType === ParseNodeType.DictionaryKeyEntry) {
const keyTypeResult = getTypeOfExpression( const keyTypeResult = getTypeOfExpression(
entryNode.keyExpression, entryNode.keyExpression,
/* flags */ undefined, keyFlags | EvaluatorFlags.StripLiteralTypeForTuple,
makeInferenceContext( makeInferenceContext(
expectedKeyType ?? (forceStrictInference ? NeverType.createNever() : undefined) expectedKeyType ?? (forceStrictInference ? NeverType.createNever() : undefined)
) )
@ -13645,7 +13679,7 @@ export function createTypeEvaluator(
entryInferenceContext = makeInferenceContext(effectiveValueType); entryInferenceContext = makeInferenceContext(effectiveValueType);
valueTypeResult = getTypeOfExpression( valueTypeResult = getTypeOfExpression(
entryNode.valueExpression, entryNode.valueExpression,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
entryInferenceContext entryInferenceContext
); );
} else { } else {
@ -13654,7 +13688,7 @@ export function createTypeEvaluator(
entryInferenceContext = makeInferenceContext(effectiveValueType); entryInferenceContext = makeInferenceContext(effectiveValueType);
valueTypeResult = getTypeOfExpression( valueTypeResult = getTypeOfExpression(
entryNode.valueExpression, entryNode.valueExpression,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
entryInferenceContext entryInferenceContext
); );
} }
@ -13717,7 +13751,7 @@ export function createTypeEvaluator(
const entryInferenceContext = makeInferenceContext(expectedType); const entryInferenceContext = makeInferenceContext(expectedType);
let unexpandedTypeResult = getTypeOfExpression( let unexpandedTypeResult = getTypeOfExpression(
entryNode.expandExpression, entryNode.expandExpression,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
entryInferenceContext entryInferenceContext
); );
@ -13817,6 +13851,7 @@ export function createTypeEvaluator(
} else if (entryNode.nodeType === ParseNodeType.ListComprehension) { } else if (entryNode.nodeType === ParseNodeType.ListComprehension) {
const dictEntryTypeResult = getElementTypeFromListComprehension( const dictEntryTypeResult = getElementTypeFromListComprehension(
entryNode, entryNode,
flags,
expectedValueType, expectedValueType,
expectedKeyType expectedKeyType
); );
@ -13868,6 +13903,12 @@ export function createTypeEvaluator(
addDiagnostic(DiagnosticRule.reportInvalidTypeForm, LocMessage.listInAnnotation() + diag.getString(), node); addDiagnostic(DiagnosticRule.reportInvalidTypeForm, LocMessage.listInAnnotation() + diag.getString(), node);
} }
flags &= ~(
EvaluatorFlags.ExpectingTypeAnnotation |
EvaluatorFlags.EvaluateStringLiteralAsType |
EvaluatorFlags.ExpectingInstantiableType
);
// If the expected type is a union, recursively call for each of the subtypes // If the expected type is a union, recursively call for each of the subtypes
// to find one that matches. // to find one that matches.
let effectiveExpectedType = inferenceContext?.expectedType; let effectiveExpectedType = inferenceContext?.expectedType;
@ -13885,7 +13926,7 @@ export function createTypeEvaluator(
} }
const subtypeResult = useSpeculativeMode(node, () => { const subtypeResult = useSpeculativeMode(node, () => {
return getTypeOfListOrSetWithContext(node, makeInferenceContext(subtype)); return getTypeOfListOrSetWithContext(node, flags, makeInferenceContext(subtype));
}); });
if (subtypeResult && assignType(subtype, subtypeResult.type)) { if (subtypeResult && assignType(subtype, subtypeResult.type)) {
@ -13905,7 +13946,7 @@ export function createTypeEvaluator(
let expectedTypeDiagAddendum: DiagnosticAddendum | undefined; let expectedTypeDiagAddendum: DiagnosticAddendum | undefined;
if (effectiveExpectedType) { if (effectiveExpectedType) {
const result = getTypeOfListOrSetWithContext(node, makeInferenceContext(effectiveExpectedType)); const result = getTypeOfListOrSetWithContext(node, flags, makeInferenceContext(effectiveExpectedType));
if (result && !result.typeErrors) { if (result && !result.typeErrors) {
return result; return result;
} }
@ -13913,7 +13954,11 @@ export function createTypeEvaluator(
expectedTypeDiagAddendum = result?.expectedTypeDiagAddendum; expectedTypeDiagAddendum = result?.expectedTypeDiagAddendum;
} }
const typeResult = getTypeOfListOrSetInferred(node, /* hasExpectedType */ inferenceContext !== undefined); const typeResult = getTypeOfListOrSetInferred(
node,
flags,
/* hasExpectedType */ inferenceContext !== undefined
);
return { ...typeResult, expectedTypeDiagAddendum }; return { ...typeResult, expectedTypeDiagAddendum };
} }
@ -13921,6 +13966,7 @@ export function createTypeEvaluator(
// Returns undefined if that type cannot be honored. // Returns undefined if that type cannot be honored.
function getTypeOfListOrSetWithContext( function getTypeOfListOrSetWithContext(
node: ListNode | SetNode, node: ListNode | SetNode,
flags: EvaluatorFlags,
inferenceContext: InferenceContext inferenceContext: InferenceContext
): TypeResult | undefined { ): TypeResult | undefined {
const builtInClassName = node.nodeType === ParseNodeType.List ? 'list' : 'set'; const builtInClassName = node.nodeType === ParseNodeType.List ? 'list' : 'set';
@ -13945,11 +13991,11 @@ export function createTypeEvaluator(
let entryTypeResult: TypeResult; let entryTypeResult: TypeResult;
if (entry.nodeType === ParseNodeType.ListComprehension) { if (entry.nodeType === ParseNodeType.ListComprehension) {
entryTypeResult = getElementTypeFromListComprehension(entry, expectedEntryType); entryTypeResult = getElementTypeFromListComprehension(entry, flags, expectedEntryType);
} else { } else {
entryTypeResult = getTypeOfExpression( entryTypeResult = getTypeOfExpression(
entry, entry,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
makeInferenceContext(expectedEntryType) makeInferenceContext(expectedEntryType)
); );
} }
@ -14044,7 +14090,11 @@ export function createTypeEvaluator(
} }
// Attempts to infer the type of a list or set statement with no "expected type". // Attempts to infer the type of a list or set statement with no "expected type".
function getTypeOfListOrSetInferred(node: ListNode | SetNode, hasExpectedType: boolean): TypeResult { function getTypeOfListOrSetInferred(
node: ListNode | SetNode,
flags: EvaluatorFlags,
hasExpectedType: boolean
): TypeResult {
const builtInClassName = node.nodeType === ParseNodeType.List ? 'list' : 'set'; const builtInClassName = node.nodeType === ParseNodeType.List ? 'list' : 'set';
const verifyHashable = node.nodeType === ParseNodeType.Set; const verifyHashable = node.nodeType === ParseNodeType.Set;
let isEmptyContainer = false; let isEmptyContainer = false;
@ -14056,9 +14106,9 @@ export function createTypeEvaluator(
let entryTypeResult: TypeResult; let entryTypeResult: TypeResult;
if (entry.nodeType === ParseNodeType.ListComprehension && !entry.isGenerator) { if (entry.nodeType === ParseNodeType.ListComprehension && !entry.isGenerator) {
entryTypeResult = getElementTypeFromListComprehension(entry); entryTypeResult = getElementTypeFromListComprehension(entry, flags);
} else { } else {
entryTypeResult = getTypeOfExpression(entry); entryTypeResult = getTypeOfExpression(entry, flags | EvaluatorFlags.StripLiteralTypeForTuple);
} }
if (entryTypeResult.isIncomplete) { if (entryTypeResult.isIncomplete) {
@ -14479,7 +14529,11 @@ export function createTypeEvaluator(
return { type: functionType, isIncomplete, typeErrors }; return { type: functionType, isIncomplete, typeErrors };
} }
function getTypeOfListComprehension(node: ListComprehensionNode, inferenceContext?: InferenceContext): TypeResult { function getTypeOfListComprehension(
node: ListComprehensionNode,
flags: EvaluatorFlags,
inferenceContext?: InferenceContext
): TypeResult {
let isIncomplete = false; let isIncomplete = false;
let typeErrors = false; let typeErrors = false;
@ -14501,7 +14555,7 @@ export function createTypeEvaluator(
const builtInIteratorType = getTypingType(node, isAsync ? 'AsyncGenerator' : 'Generator'); const builtInIteratorType = getTypingType(node, isAsync ? 'AsyncGenerator' : 'Generator');
const expectedEntryType = getExpectedEntryTypeForIterable(node, builtInIteratorType, inferenceContext); const expectedEntryType = getExpectedEntryTypeForIterable(node, builtInIteratorType, inferenceContext);
const elementTypeResult = getElementTypeFromListComprehension(node, expectedEntryType); const elementTypeResult = getElementTypeFromListComprehension(node, flags, expectedEntryType);
if (elementTypeResult.isIncomplete) { if (elementTypeResult.isIncomplete) {
isIncomplete = true; isIncomplete = true;
} }
@ -14610,6 +14664,7 @@ export function createTypeEvaluator(
// as opposed to the entire list. // as opposed to the entire list.
function getElementTypeFromListComprehension( function getElementTypeFromListComprehension(
node: ListComprehensionNode, node: ListComprehensionNode,
flags: EvaluatorFlags,
expectedValueOrElementType?: Type, expectedValueOrElementType?: Type,
expectedKeyType?: Type expectedKeyType?: Type
): TypeResult { ): TypeResult {
@ -14628,7 +14683,7 @@ export function createTypeEvaluator(
// Create a tuple with the key/value types. // Create a tuple with the key/value types.
const keyTypeResult = getTypeOfExpression( const keyTypeResult = getTypeOfExpression(
node.expression.keyExpression, node.expression.keyExpression,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
makeInferenceContext(expectedKeyType) makeInferenceContext(expectedKeyType)
); );
if (keyTypeResult.isIncomplete) { if (keyTypeResult.isIncomplete) {
@ -14644,7 +14699,7 @@ export function createTypeEvaluator(
const valueTypeResult = getTypeOfExpression( const valueTypeResult = getTypeOfExpression(
node.expression.valueExpression, node.expression.valueExpression,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
makeInferenceContext(expectedValueOrElementType) makeInferenceContext(expectedValueOrElementType)
); );
if (valueTypeResult.isIncomplete) { if (valueTypeResult.isIncomplete) {
@ -14666,13 +14721,13 @@ export function createTypeEvaluator(
// The parser should have reported an error in this case because it's not allowed. // The parser should have reported an error in this case because it's not allowed.
getTypeOfExpression( getTypeOfExpression(
node.expression.expandExpression, node.expression.expandExpression,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
makeInferenceContext(expectedValueOrElementType) makeInferenceContext(expectedValueOrElementType)
); );
} else if (isExpressionNode(node)) { } else if (isExpressionNode(node)) {
const exprTypeResult = getTypeOfExpression( const exprTypeResult = getTypeOfExpression(
node.expression as ExpressionNode, node.expression as ExpressionNode,
/* flags */ undefined, flags | EvaluatorFlags.StripLiteralTypeForTuple,
makeInferenceContext(expectedValueOrElementType) makeInferenceContext(expectedValueOrElementType)
); );
if (exprTypeResult.isIncomplete) { if (exprTypeResult.isIncomplete) {

View File

@ -154,6 +154,10 @@ export const enum EvaluatorFlags {
// Allow use of the Concatenate special form. // Allow use of the Concatenate special form.
AllowConcatenate = 1 << 27, AllowConcatenate = 1 << 27,
// Do not infer literal types within a tuple (used for tuples nested within
// other container classes).
StripLiteralTypeForTuple = 1 << 28,
// Defaults used for evaluating the LHS of a call expression. // Defaults used for evaluating the LHS of a call expression.
CallBaseDefaults = DoNotSpecialize, CallBaseDefaults = DoNotSpecialize,

View File

@ -15,4 +15,4 @@ times = [
for meridian in ("am", "pm") for meridian in ("am", "pm")
) )
] ]
reveal_type(times, expected_text="list[tuple[int, int, Literal['am', 'pm']]]") reveal_type(times, expected_text="list[tuple[int, int, str]]")

View File

@ -1,8 +1,7 @@
# This sample tests the inferred type of async and sync generators. # This sample tests the inferred type of async and sync generators.
async def foo() -> int: async def foo() -> int: ...
...
async def main() -> None: async def main() -> None:
@ -19,7 +18,7 @@ async def main() -> None:
reveal_type(v4, expected_text="AsyncGenerator[int, None]") reveal_type(v4, expected_text="AsyncGenerator[int, None]")
v5 = ((0, await foo()) for _ in [1, 2]) v5 = ((0, await foo()) for _ in [1, 2])
reveal_type(v5, expected_text="AsyncGenerator[tuple[Literal[0], int], None]") reveal_type(v5, expected_text="AsyncGenerator[tuple[int, int], None]")
v6 = (x for x in [1, 2] if (x, await foo())) v6 = (x for x in [1, 2] if (x, await foo()))
reveal_type(v6, expected_text="AsyncGenerator[int, None]") reveal_type(v6, expected_text="AsyncGenerator[int, None]")

View File

@ -742,7 +742,7 @@ test('TypedDictInline1', () => {
configOptions.diagnosticRuleSet.enableExperimentalFeatures = true; configOptions.diagnosticRuleSet.enableExperimentalFeatures = true;
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typedDictInline1.py'], configOptions); const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typedDictInline1.py'], configOptions);
TestUtils.validateResults(analysisResults, 8); TestUtils.validateResults(analysisResults, 9);
}); });
test('ClassVar1', () => { test('ClassVar1', () => {