diff --git a/packages/pyright/.gitrepo b/packages/pyright/.gitrepo index 4a475fde52e1..e37124199393 100644 --- a/packages/pyright/.gitrepo +++ b/packages/pyright/.gitrepo @@ -6,7 +6,7 @@ [subrepo] remote = https://github.com/microsoft/pyright.git branch = master - commit = f5916b7965413f98e2f3ee5fab737112a630c845 - parent = 5a713a5aeeba0a6d7581135a1bc7bf13f094e756 + commit = 177714b1a6852d9848ee659a82f450b8230303a0 + parent = ab6d9f50e697bf169ac53e5ae8b1f82346359607 method = merge cmdver = 0.4.1 diff --git a/packages/pyright/packages/pyright-internal/src/analyzer/typeEvaluator.ts b/packages/pyright/packages/pyright-internal/src/analyzer/typeEvaluator.ts index ddeee2a1a6c6..aa4ecf51092d 100644 --- a/packages/pyright/packages/pyright-internal/src/analyzer/typeEvaluator.ts +++ b/packages/pyright/packages/pyright-internal/src/analyzer/typeEvaluator.ts @@ -359,6 +359,11 @@ export const enum EvaluatorFlags { // normally not allowed if ExpectingType is set. GenericClassTypeAllowed = 1 << 9, + // A type annotation restricts the types of expressions that are + // allowed. If this flag is set, illegal type expressions are + // flagged as errors. + ExpectingTypeAnnotation = 1 << 10, + // TypeVars within this expression must not refer to type vars // used in an outer scope that. DisallowTypeVarsWithScopeId = 1 << 11, @@ -922,7 +927,17 @@ export function createTypeEvaluator( } case ParseNodeType.Call: { - typeResult = getTypeFromCall(node, expectedTypeAlt); + if ((flags & EvaluatorFlags.ExpectingTypeAnnotation) !== 0) { + addDiagnostic( + getFileInfo(node).diagnosticRuleSet.reportGeneralTypeIssues, + DiagnosticRule.reportGeneralTypeIssues, + Localizer.Diagnostic.typeAnnotationCall(), + node + ); + typeResult = { node, type: UnknownType.create() }; + } else { + typeResult = getTypeFromCall(node, expectedTypeAlt); + } break; } @@ -1114,7 +1129,21 @@ export function createTypeEvaluator( } case ParseNodeType.Unpack: { - const iterType = getTypeOfExpression(node.expression, expectedTypeAlt, flags).type; + let iterExpectedType: Type | undefined; + if (expectedTypeAlt) { + const iterableType = getBuiltInType(node, 'Iterable'); + if (iterableType && isClass(iterableType)) { + iterExpectedType = ObjectType.create( + ClassType.cloneForSpecialization( + iterableType, + [expectedTypeAlt], + /* isTypeArgumentExplicit */ true + ) + ); + } + } + + const iterType = getTypeOfExpression(node.expression, iterExpectedType, flags).type; if ( (flags & EvaluatorFlags.TypeVarTupleDisallowed) === 0 && isVariadicTypeVar(iterType) && @@ -1135,7 +1164,8 @@ export function createTypeEvaluator( EvaluatorFlags.EvaluateStringLiteralAsType | EvaluatorFlags.ParamSpecDisallowed | EvaluatorFlags.TypeVarTupleDisallowed | - EvaluatorFlags.ExpectingType + EvaluatorFlags.ExpectingType | + EvaluatorFlags.ExpectingTypeAnnotation ); break; } @@ -1217,6 +1247,7 @@ export function createTypeEvaluator( let evaluatorFlags = EvaluatorFlags.ExpectingType | + EvaluatorFlags.ExpectingTypeAnnotation | EvaluatorFlags.ConvertEllipsisToAny | EvaluatorFlags.EvaluateStringLiteralAsType | EvaluatorFlags.ParamSpecDisallowed; @@ -3048,7 +3079,7 @@ export function createTypeEvaluator( EvaluatorFlags.DoNotSpecialize ); - const indexTypeResult = getTypeFromIndexWithBaseType( + getTypeFromIndexWithBaseType( target, baseTypeResult.type, { @@ -3060,7 +3091,7 @@ export function createTypeEvaluator( EvaluatorFlags.None ); - writeTypeCache(target, indexTypeResult.type); + writeTypeCache(target, type); break; } @@ -3751,6 +3782,7 @@ export function createTypeEvaluator( EvaluatorFlags.DoNotSpecialize | (flags & (EvaluatorFlags.ExpectingType | + EvaluatorFlags.ExpectingTypeAnnotation | EvaluatorFlags.AllowForwardReferences | EvaluatorFlags.AssociateTypeVarsWithCurrentScope)); const baseTypeResult = getTypeOfExpression(node.leftExpression, undefined, baseTypeFlags); @@ -5229,6 +5261,7 @@ export function createTypeEvaluator( let adjustedFlags = flags | EvaluatorFlags.ExpectingType | + EvaluatorFlags.ExpectingTypeAnnotation | EvaluatorFlags.ConvertEllipsisToAny | EvaluatorFlags.EvaluateStringLiteralAsType | EvaluatorFlags.FinalDisallowed; @@ -5372,7 +5405,9 @@ export function createTypeEvaluator( } function buildTupleTypesList(entryTypeResults: TypeResult[]): Type[] { - let tupleTypes: Type[] = []; + const entryTypes: Type[] = []; + let isOpenEnded = false; + for (const typeResult of entryTypeResults) { if (typeResult.unpackedType) { // Is this an unpacked tuple? If so, we can append the individual @@ -5384,24 +5419,26 @@ export function createTypeEvaluator( // If the Tuple wasn't specialized or has a "..." type parameter, we can't // make any determination about its contents. - if (!typeArgs || typeArgs.some((t) => isEllipsisType(t))) { - tupleTypes = [AnyType.create(/* isEllipsis */ false), AnyType.create(/* isEllipsis */ true)]; - break; - } - - for (const typeArg of typeArgs) { - tupleTypes.push(typeArg); + if (!typeArgs || isOpenEndedTupleClass(typeResult.unpackedType.classType)) { + entryTypes.push(typeResult.type); + isOpenEnded = true; + } else { + entryTypes.push(...typeArgs); } } else { - tupleTypes = [AnyType.create(/* isEllipsis */ false), AnyType.create(/* isEllipsis */ true)]; - break; + entryTypes.push(typeResult.type); + isOpenEnded = true; } } else { - tupleTypes.push(typeResult.type); + entryTypes.push(typeResult.type); } } - return tupleTypes; + if (isOpenEnded) { + return [combineTypes(entryTypes), AnyType.create(/* isEllipsis */ true)]; + } + + return entryTypes; } function updateNamedTupleBaseClass(classType: ClassType, typeArgs: Type[], isTypeArgumentExplicit: boolean) { @@ -13916,7 +13953,9 @@ export function createTypeEvaluator( if (nodeToEvaluate.nodeType === ParseNodeType.TypeAnnotation) { evaluateTypeAnnotationExpression(nodeToEvaluate); } else { - getTypeOfExpression(nodeToEvaluate); + const fileInfo = getFileInfo(nodeToEvaluate); + const flags = fileInfo.isStubFile ? EvaluatorFlags.AllowForwardReferences : EvaluatorFlags.None; + getTypeOfExpression(nodeToEvaluate, /* expectedType */ undefined, flags); } } @@ -16210,7 +16249,8 @@ export function createTypeEvaluator( } } } else { - let allowForwardReferences = false; + const fileInfo = getFileInfo(node); + let allowForwardReferences = fileInfo.isStubFile; // Determine if this node is within a quoted type annotation. if (ParseTreeUtils.isWithinTypeAnnotation(node, !isAnnotationEvaluationPostponed(getFileInfo(node)))) { @@ -19365,6 +19405,9 @@ export function createTypeEvaluator( if (result) { assignedSubtype = ObjectType.create(result); } + } else if (isAnyOrUnknown(assignedSubtype)) { + // Any or Unknown do not narrow because they're assignable to all types. + return declaredType; } return assignedSubtype; diff --git a/packages/pyright/packages/pyright-internal/src/localization/localize.ts b/packages/pyright/packages/pyright-internal/src/localization/localize.ts index 2da3655b883d..4453d87c7d5c 100644 --- a/packages/pyright/packages/pyright-internal/src/localization/localize.ts +++ b/packages/pyright/packages/pyright-internal/src/localization/localize.ts @@ -585,6 +585,7 @@ export namespace Localizer { export const typeAliasNotInModule = () => getRawString('Diagnostic.typeAliasNotInModule'); export const typeAliasRedeclared = () => new ParameterizedString<{ name: string }>(getRawString('Diagnostic.typeAliasRedeclared')); + export const typeAnnotationCall = () => getRawString('Diagnostic.typeAnnotationCall'); export const typeArgListNotAllowed = () => getRawString('Diagnostic.typeArgListNotAllowed'); export const typeArgsExpectingNone = () => getRawString('Diagnostic.typeArgsExpectingNone'); export const typeArgsMismatchOne = () => diff --git a/packages/pyright/packages/pyright-internal/src/localization/package.nls.en-us.json b/packages/pyright/packages/pyright-internal/src/localization/package.nls.en-us.json index bd9dbf7dc792..61d62cdf8c10 100644 --- a/packages/pyright/packages/pyright-internal/src/localization/package.nls.en-us.json +++ b/packages/pyright/packages/pyright-internal/src/localization/package.nls.en-us.json @@ -299,6 +299,7 @@ "typeAliasNotInModule": "A TypeAlias can be defined only within a module scope", "typeAliasRedeclared": "\"{name}\" is declared as a TypeAlias and can be assigned only once", "typeArgListNotAllowed": "List expression not allowed for this type argument", + "typeAnnotationCall": "Illegal type annotation: call expression not allowed", "typeArgsExpectingNone": "Expected no type arguments", "typeArgsMismatchOne": "Expected one type argument but received {received}", "typeArgsMissingForAlias": "Expected type arguments for generic type alias \"{name}\"", diff --git a/packages/pyright/packages/pyright-internal/src/tests/samples/annotations1.py b/packages/pyright/packages/pyright-internal/src/tests/samples/annotations1.py index b847be8c66c9..04e8ec792ed1 100644 --- a/packages/pyright/packages/pyright-internal/src/tests/samples/annotations1.py +++ b/packages/pyright/packages/pyright-internal/src/tests/samples/annotations1.py @@ -34,3 +34,12 @@ def func5(self) -> "Optional"[int]: class ClassC: pass + + +def func10(): + pass + +# This should generate an error because function calls +# are not allowed within a type annotation. +x: func10() + diff --git a/packages/pyright/packages/pyright-internal/src/tests/samples/tuples12.py b/packages/pyright/packages/pyright-internal/src/tests/samples/tuples12.py new file mode 100644 index 000000000000..2d89b9498c94 --- /dev/null +++ b/packages/pyright/packages/pyright-internal/src/tests/samples/tuples12.py @@ -0,0 +1,26 @@ +# This sample tests type inference for tuples that contain unpack +# operators. + +from typing import Literal + + +def func1(a: int, *args: int): + v1 = (a, *args) + t1: Literal["tuple[int, ...]"] = reveal_type(v1) + + +def func2(a: int, *args: str): + v1 = (a, *args) + t1: Literal["tuple[int | str, ...]"] = reveal_type(v1) + + +def func3(a: int, b: str, *args: str): + v1 = (*args, a, *args, b, *(a, b, a)) + t1: Literal["tuple[str | int, ...]"] = reveal_type(v1) + + +def func4(): + a = 3.4 + b = [1, 2, 3] + v1 = (a, *b) + t1: Literal["tuple[float | int, ...]"] = reveal_type(v1) diff --git a/packages/pyright/packages/pyright-internal/src/tests/samples/typeNarrowing21.py b/packages/pyright/packages/pyright-internal/src/tests/samples/typeNarrowing21.py index 074766cd5b45..9271823db46d 100644 --- a/packages/pyright/packages/pyright-internal/src/tests/samples/typeNarrowing21.py +++ b/packages/pyright/packages/pyright-internal/src/tests/samples/typeNarrowing21.py @@ -17,8 +17,12 @@ def func1(v1: List[Optional[complex]]): t_v1_0_updated1: Literal["None"] = reveal_type(v1[0]) t_v1_1_updated1: Literal["None"] = reveal_type(v1[1]) + v1[0], v1[1] = 1, 2 + t_v1_0_updated2: Literal["Literal[1]"] = reveal_type(v1[0]) + t_v1_1_updated2: Literal["Literal[2]"] = reveal_type(v1[1]) + v1 = [] - t_v1_0_updated2: Literal["complex | None"] = reveal_type(v1[0]) + t_v1_0_updated3: Literal["complex | None"] = reveal_type(v1[0]) i = 1 if v1[i]: diff --git a/packages/pyright/packages/pyright-internal/src/tests/typeEvaluator1.test.ts b/packages/pyright/packages/pyright-internal/src/tests/typeEvaluator1.test.ts index b52008424610..4e4dd0ccab88 100644 --- a/packages/pyright/packages/pyright-internal/src/tests/typeEvaluator1.test.ts +++ b/packages/pyright/packages/pyright-internal/src/tests/typeEvaluator1.test.ts @@ -557,7 +557,7 @@ test('Function14', () => { test('Annotations1', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['annotations1.py']); - TestUtils.validateResults(analysisResults, 3); + TestUtils.validateResults(analysisResults, 4); }); test('Annotations2', () => { @@ -834,6 +834,12 @@ test('Tuples11', () => { TestUtils.validateResults(analysisResults, 1); }); +test('Tuples12', () => { + const analysisResults = TestUtils.typeAnalyzeSampleFiles(['tuples12.py']); + + TestUtils.validateResults(analysisResults, 0); +}); + test('NamedTuples1', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['namedTuples1.py']);