diff --git a/packages/pyright-internal/src/analyzer/binder.ts b/packages/pyright-internal/src/analyzer/binder.ts index 2c13b65cf..270f4ff04 100644 --- a/packages/pyright-internal/src/analyzer/binder.ts +++ b/packages/pyright-internal/src/analyzer/binder.ts @@ -3924,6 +3924,12 @@ export class Binder extends ParseTreeWalker { if (this._isTypingAnnotation(typeAnnotation, 'Final')) { isFinal = true; + } else if ( + typeAnnotation.nodeType === ParseNodeType.Index && + typeAnnotation.items.length > 0 && + this._isTypingAnnotation(typeAnnotation.baseExpression, 'Annotated') + ) { + return this._isAnnotationFinal(typeAnnotation.items[0].valueExpression); } else if (typeAnnotation.nodeType === ParseNodeType.Index && typeAnnotation.items.length === 1) { // Recursively call to see if the base expression is "Final". const finalInfo = this._isAnnotationFinal(typeAnnotation.baseExpression); diff --git a/packages/pyright-internal/src/tests/samples/final3.py b/packages/pyright-internal/src/tests/samples/final3.py index 3d618c592..dfd277522 100644 --- a/packages/pyright-internal/src/tests/samples/final3.py +++ b/packages/pyright-internal/src/tests/samples/final3.py @@ -2,37 +2,37 @@ # introduced in Python 3.8. import typing -from typing import Any, Final, Protocol, TypeVar +from typing import Annotated, Any, Final, Protocol, TypeVar T = TypeVar("T") -foo1: typing.Final = 3 +v1: typing.Final = 3 -must_be_int: int = foo1 +must_be_int: int = v1 # This should generate an error because # reassignment of a Final variable should # not be allowed. -foo1 = 4 +v1 = 4 # This should generate an error because there # is a previous Final declaration. -foo1: Final[int] +v1: Final[int] # This should generate an error because # the type doesn't match. -foo2: Final[str] = 3 +v2: Final[str] = 3 # This should generate an error because # we expect only one type argument for Final. -foo3: Final[str, int] = "hello" +v3: Final[str, int] = "hello" -foo4: Final = 5 -reveal_type(foo4, expected_text="Literal[5]") +v4: Final = 5 +reveal_type(v4, expected_text="Literal[5]") -class Foo: +class ClassA: member1: Final = 4 # This should generate an error because only @@ -85,11 +85,11 @@ class Foo: self.member7: Final = 6 -reveal_type(Foo.member1, expected_text="Literal[4]") -reveal_type(Foo(True).member1, expected_text="Literal[4]") +reveal_type(ClassA.member1, expected_text="Literal[4]") +reveal_type(ClassA(True).member1, expected_text="Literal[4]") -class Bar(Foo): +class ClassB(ClassA): # This should generate an error because we are overriding # a member that is marked Final in the parent class. member1 = 5 @@ -118,7 +118,7 @@ def func1(a: Final[int]): b: list[Final[int]] = [] -class ClassA: +class ClassC: member1: Final = 3 member2: Final member4: Final @@ -133,10 +133,10 @@ class ClassA: self.member3: Final = "hi" # This should generate an error. - ClassA.member4 = "hi" + ClassC.member4 = "hi" # This should generate an error. - ClassA.member5 = 3 + ClassC.member5 = 3 def other(self): # This should generate an error. @@ -149,7 +149,7 @@ class ClassA: self.member3 = "hi" -a = ClassA() +a = ClassC() # This should generate an error. a.member1 = 4 @@ -185,7 +185,7 @@ def func2(): (a, x) = (1, 2) -class ClassB: +class ClassD: def __init__(self): self.x: Final = 1 @@ -194,7 +194,7 @@ class ClassB: self.x += 1 -class ClassC(Protocol): +class ClassE(Protocol): x: Final[int] @@ -206,11 +206,11 @@ def func3(x: type[T]) -> T: func3(Final[int]) -foo5: Final = lambda: None +v5: Final = lambda: None # This should generate an error because foo5 is declared as Final. -def foo5() -> None: +def v5() -> None: pass @@ -218,3 +218,19 @@ def foo5() -> None: from typing import ClassVar ClassVar: Final = 3 + + +v6: Annotated[Final[int], "meta"] = 1 + +# This should generate an error +v6 = 2 + +v7: Annotated[Annotated[Final[int], "meta"], "meta"] = 1 + +# This should generate an error +v7 = 2 + +v8: Annotated[Final, "meta"] = 1 + +# This should generate an error +v8 = 2 diff --git a/packages/pyright-internal/src/tests/typeEvaluator4.test.ts b/packages/pyright-internal/src/tests/typeEvaluator4.test.ts index 654292f10..b7a3fe6d6 100644 --- a/packages/pyright-internal/src/tests/typeEvaluator4.test.ts +++ b/packages/pyright-internal/src/tests/typeEvaluator4.test.ts @@ -32,7 +32,7 @@ test('Final2', () => { test('Final3', () => { const analysisResults = TestUtils.typeAnalyzeSampleFiles(['final3.py']); - TestUtils.validateResults(analysisResults, 35); + TestUtils.validateResults(analysisResults, 38); }); test('Final4', () => {