Repository: python/mypy

Issue:
reveal_type() should state when surrounding function is not type checked
*Problem*

When using `reveal_type(x)` in a function that isn't type checked, it will report that `x` is of type `Any` even though the type may be know in other contexts. It should notify the user that the code isn't being type checked at all, so the `Any` is meaningless.

*Example and why it's confusing*

I was trying to use MyPy for the first time (count me as a noob, assume I'm a developer in a context where I don't have the ability to go learn MyPy in extreme detail). Suppose I have this code snippet as I'm trying to learn how to correctly type instance variables in a class:

```
class Person(object):
    def __init__(self, name):
        # type: (str) -> None
        self.name = name

def test():
    person = Person('Jane Doe')
    reveal_type(person.name)
    person.name = 5
```

Running type checking results in:

```
test.py:9: error: Revealed type is 'Any'
```

As a user, I had previously iterated on a bunch of other options and not understanding why the checker wasn't working. Once I got the point of using `reveal_type`, I took the error to confirm that "this code is being type checked, so clearly the problem isn't that" (I was worried that e.g. the entire file was being skipped).

The actual problem here is that the function `test` itself is just not being typed checked at all (from what I understand after having talked to someone more knowledgable than me on this), and merely adding a type signature to `test` causes the type checker to be enabled and result in expected output. Given:

```
class Person(object):
    def __init__(self, name):
        # type: (str) -> None
        self.name = name

def test():
    # type: () -> None
    person = Person('Jane Doe')
    reveal_type(person.name)
    person.name = 5
```

Output is now:

```
test.py:10: error: Revealed type is 'builtins.str'
test.py:11: error: Incompatible types in assignment (expression has type "int", variable has type "str")
```

It would be great if `reveal_type` could give me the contextually highly-likely-to-be-relevant information that type checking is just not happening for the code where I'm using it.


Discussion:
Thanks for reporting the issue!

Your proposal sounds reasonable and shouldn't be too hard to implement. 

If `reveal_type` is in an unchecked function, output something like 
```
error: reveal_type in an unchecked function will always output `Any`
``` 

Are you interested in taking a stab at it? It shouldn't be too hard. 

Resolving patch:
diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py
--- a/mypy/checkexpr.py
+++ b/mypy/checkexpr.py
@@ -1713,6 +1713,8 @@ def visit_reveal_type_expr(self, expr: RevealTypeExpr) -> Type:
         revealed_type = self.accept(expr.expr, type_context=self.type_context[-1])
         if not self.chk.current_node_deferred:
             self.msg.reveal_type(revealed_type, expr)
+            if not self.chk.in_checked_function():
+                self.msg.note("'reveal_type' always outputs 'Any' in unchecked functions", expr)
         return revealed_type
 
     def visit_type_application(self, tapp: TypeApplication) -> Type:


Test patch:
diff --git a/test-data/unit/check-expressions.test b/test-data/unit/check-expressions.test
--- a/test-data/unit/check-expressions.test
+++ b/test-data/unit/check-expressions.test
@@ -1680,6 +1680,29 @@ x = 1 + 1
 [out]
 main:2: error: Revealed type is 'builtins.int'
 
+[case testRevealUncheckedFunction]
+def f():
+    x = 42
+    reveal_type(x)
+[out]
+main:3: error: Revealed type is 'Any'
+main:3: note: 'reveal_type' always outputs 'Any' in unchecked functions
+
+[case testRevealCheckUntypedDefs]
+# flags: --check-untyped-defs
+def f():
+    x = 42
+    reveal_type(x)
+[out]
+main:4: error: Revealed type is 'builtins.int'
+
+[case testRevealTypedDef]
+def f() -> None:
+    x = 42
+    reveal_type(x)
+[out]
+main:3: error: Revealed type is 'builtins.int'
+
 [case testEqNone]
 None == None
 [builtins fixtures/ops.pyi]