Skip to content

Commit

Permalink
Add a note on variable annotation within unchecked function (#13851)
Browse files Browse the repository at this point in the history
Closes #3948

This however gives a note instead of an error, so that type checking
result will be success. I also added an error code for the note so that
people can opt-out if they want to.
  • Loading branch information
ilevkivskyi authored Oct 9, 2022
1 parent 56258a6 commit fe1e571
Show file tree
Hide file tree
Showing 10 changed files with 35 additions and 8 deletions.
3 changes: 3 additions & 0 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2643,6 +2643,9 @@ def visit_assignment_stmt(self, s: AssignmentStmt) -> None:
):
self.fail(message_registry.DEPENDENT_FINAL_IN_CLASS_BODY, s)

if s.unanalyzed_type and not self.in_checked_function():
self.msg.annotation_in_unchecked_function(context=s)

def check_type_alias_rvalue(self, s: AssignmentStmt) -> None:
if not (self.is_stub and isinstance(s.rvalue, OpExpr) and s.rvalue.op == "|"):
# We do this mostly for compatibility with old semantic analyzer.
Expand Down
3 changes: 3 additions & 0 deletions mypy/errorcodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@ def __str__(self) -> str:
UNREACHABLE: Final = ErrorCode(
"unreachable", "Warn about unreachable statements or expressions", "General"
)
ANNOTATION_UNCHECKED = ErrorCode(
"annotation-unchecked", "Notify about type annotations in unchecked functions", "General"
)
PARTIALLY_DEFINED: Final[ErrorCode] = ErrorCode(
"partially-defined",
"Warn about variables that are defined only in some execution paths",
Expand Down
8 changes: 8 additions & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -2148,6 +2148,14 @@ def add_fixture_note(self, fullname: str, ctx: Context) -> None:
ctx,
)

def annotation_in_unchecked_function(self, context: Context) -> None:
self.note(
"By default the bodies of untyped functions are not checked,"
" consider using --check-untyped-defs",
context,
code=codes.ANNOTATION_UNCHECKED,
)


def quote_type_string(type_string: str) -> str:
"""Quotes a type representation for use in messages."""
Expand Down
4 changes: 2 additions & 2 deletions test-data/unit/check-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -1189,7 +1189,7 @@ reveal_type(Foo().Meta.name) # N: Revealed type is "builtins.str"

class A:
def __init__(self):
self.x = None # type: int
self.x = None # type: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs
a = None # type: A
a.x = 1
a.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
Expand All @@ -1201,7 +1201,7 @@ a.x = 1
a.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int")
class A:
def __init__(self):
self.x = None # type: int
self.x = None # type: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs


-- Special cases
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -1641,7 +1641,7 @@ A(a=func).a = func # E: Property "a" defined in "A" is read-only
# flags: --python-version 3.7
from dataclasses import dataclass

def foo():
def foo() -> None:
@dataclass
class Foo:
foo: int
Expand Down
5 changes: 5 additions & 0 deletions test-data/unit/check-errorcodes.test
Original file line number Diff line number Diff line change
Expand Up @@ -964,3 +964,8 @@ class C(abc.ABC):
T = TypeVar("T")
def test(tp: Type[T]) -> T: ...
test(C) # E: Only concrete class can be given where "Type[C]" is expected [type-abstract]

[case testUncheckedAnnotationSuppressed]
# flags: --disable-error-code=annotation-unchecked
def f():
x: int = "no" # No warning here
5 changes: 3 additions & 2 deletions test-data/unit/check-incremental.test
Original file line number Diff line number Diff line change
Expand Up @@ -3544,11 +3544,11 @@ class Bar(Baz): pass

[file c.py]
class Baz:
def __init__(self):
def __init__(self) -> None:
self.x = 12 # type: int
[file c.py.2]
class Baz:
def __init__(self):
def __init__(self) -> None:
self.x = 'lol' # type: str
[out]
[out2]
Expand Down Expand Up @@ -5730,6 +5730,7 @@ class C:
tmp/a.py:2: error: "object" has no attribute "xyz"

[case testIncrementalInvalidNamedTupleInUnannotatedFunction]
# flags: --disable-error-code=annotation-unchecked
import a

[file a.py]
Expand Down
2 changes: 1 addition & 1 deletion test-data/unit/check-inference-context.test
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,7 @@ if int():
from typing import List
class A:
def __init__(self):
self.x = [] # type: List[int]
self.x = [] # type: List[int] # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs
a = A()
a.x = []
a.x = [1]
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-newsemanal.test
Original file line number Diff line number Diff line change
Expand Up @@ -2963,6 +2963,7 @@ def g() -> None:
reveal_type(y) # N: Revealed type is "__main__.G[Any]"

[case testNewAnalyzerRedefinedNonlocal]
# flags: --disable-error-code=annotation-unchecked
import typing

def f():
Expand All @@ -2977,7 +2978,7 @@ def g() -> None:

def foo() -> None:
nonlocal bar
bar = [] # type: typing.List[int] # E: Name "bar" already defined on line 11
bar = [] # type: typing.List[int] # E: Name "bar" already defined on line 12
[builtins fixtures/list.pyi]

[case testNewAnalyzerMoreInvalidTypeVarArgumentsDeferred]
Expand Down
8 changes: 7 additions & 1 deletion test-data/unit/check-statements.test
Original file line number Diff line number Diff line change
Expand Up @@ -2186,7 +2186,7 @@ N = TypedDict('N', {'x': int})
[out]

[case testGlobalWithoutInitialization]

# flags: --disable-error-code=annotation-unchecked
from typing import List

def foo() -> None:
Expand All @@ -2200,3 +2200,9 @@ def foo2():
bar2 = [] # type: List[str]
bar2
[builtins fixtures/list.pyi]

[case testNoteUncheckedAnnotation]
def foo():
x: int = "no" # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs
y = "no" # type: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs
z: int # N: By default the bodies of untyped functions are not checked, consider using --check-untyped-defs

0 comments on commit fe1e571

Please sign in to comment.