Repository: quora/pyanalyze
Pull request #636 (2023-05-27 02:57:04)

Add new reveal_type error code


Patch:
diff --git a/README.md b/README.md
index 366b0e0..ec2c2dc 100644
--- a/README.md
+++ b/README.md
@@ -100,13 +100,13 @@ def get_known_argspecs(arg_spec_cache):
 
 ### Displaying and checking the type of an expression
 
-You can use `pyanalyze.extensions.reveal_type(expr)` to display the type pyanalyze infers for an expression. This can be
+You can use `typing_extensions.reveal_type(expr)` to display the type pyanalyze infers for an expression. This can be
 useful to understand errors or to debug why pyanalyze does not catch a particular issue. For example:
 
 ```python
-from pyanalyze.extensions import reveal_type
+from typing_extensions import reveal_type
 
-reveal_type(1)  # Revealed type is 'Literal[1]' (code: inference_failure)
+reveal_type(1)  # Revealed type is 'Literal[1]' (code: reveal_type)
 ```
 
 This function is also considered a builtin while type checking, so you can use `reveal_type()` in code that is type checked but not run.
@@ -114,9 +114,9 @@ This function is also considered a builtin while type checking, so you can use `
 For callable objects, `reveal_type()` will also display the signature inferred by pyanalyze:
 
 ```python
-from pyanalyze.extensions import reveal_type
+from typing_extensions import reveal_type
 
-reveal_type(reveal_type)  # Revealed type is 'Literal[<function reveal_type at 0x104bf55e0>]', signature is (value, /) -> None (code: inference_failure)
+reveal_type(reveal_type)  # Revealed type is 'Literal[<function reveal_type at 0x104bf55e0>]', signature is (value, /) -> None (code: reveal_type)
 ```
 
 A similar function, `pyanalyze.dump_value`, can be used to get lower-level details of the `Value` object pyanalyze infers for an expression.
diff --git a/docs/changelog.md b/docs/changelog.md
index 2d367f0..1931259 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -2,6 +2,9 @@
 
 ## Unreleased
 
+- Add new error code `reveal_type` for `reveal_type()`
+  and similar functions, which previously emitted
+  `inference_failure` (#636)
 - Take into account additional base classes declared in stub
   files (fixing some false positives around `typing.IO`) (#635)
 - Fix crash on stubs that contain dict or set literals (#634)
diff --git a/pyanalyze/error_code.py b/pyanalyze/error_code.py
index 61285ea..6cf574d 100644
--- a/pyanalyze/error_code.py
+++ b/pyanalyze/error_code.py
@@ -104,6 +104,7 @@ class ErrorCode(enum.Enum):
     deprecated = 84
     invalid_override_decorator = 85
     override_does_not_override = 86
+    reveal_type = 87
 
 
 # Allow testing unannotated functions without too much fuss
@@ -165,7 +166,8 @@ ERROR_DESCRIPTION = {
     ),
     ErrorCode.bad_global: "Bad global declaration.",
     ErrorCode.condition_always_true: "Condition is always true.",
-    ErrorCode.inference_failure: "Internal error in value inference.",
+    ErrorCode.inference_failure: "Incorrectly inferred type.",
+    ErrorCode.reveal_type: "Revealed type.",
     ErrorCode.bad_format_string: "Incorrect arguments to a %-formatted string.",
     ErrorCode.yield_without_value: "yield without a value in an async function",
     ErrorCode.invalid_method_return_type: (
diff --git a/pyanalyze/implementation.py b/pyanalyze/implementation.py
index 9ff65b9..c744ba5 100644
--- a/pyanalyze/implementation.py
+++ b/pyanalyze/implementation.py
@@ -1144,7 +1144,7 @@ def _reveal_type_impl(ctx: CallContext) -> Value:
     value = ctx.vars["value"]
     if ctx.visitor._is_checking():
         message = f"Revealed type is {ctx.visitor.display_value(value)}"
-        ctx.show_error(message, ErrorCode.inference_failure, arg="value")
+        ctx.show_error(message, ErrorCode.reveal_type, arg="value")
     return value
 
 
@@ -1160,7 +1160,7 @@ def _reveal_locals_impl(ctx: CallContext) -> Value:
             details.append(CanAssignError(f"{varname}: {val}"))
         ctx.show_error(
             "Revealed local types are:",
-            ErrorCode.inference_failure,
+            ErrorCode.reveal_type,
             detail=str(CanAssignError(children=details)),
         )
     return KnownValue(None)
@@ -1174,7 +1174,7 @@ def _dump_value_impl(ctx: CallContext) -> Value:
             sig = ctx.visitor.arg_spec_cache.get_argspec(value.val)
             if sig is not None:
                 message += f", signature is {sig!r}"
-        ctx.show_error(message, ErrorCode.inference_failure, arg="value")
+        ctx.show_error(message, ErrorCode.reveal_type, arg="value")
     return value
 
 


Test patch:
diff --git a/pyanalyze/test_inference_helpers.py b/pyanalyze/test_inference_helpers.py
index 14f10f3..9dac312 100644
--- a/pyanalyze/test_inference_helpers.py
+++ b/pyanalyze/test_inference_helpers.py
@@ -11,8 +11,8 @@ class TestInferenceHelpers(TestNameCheckVisitorBase):
         from pyanalyze.value import Value
 
         def capybara(val: Value) -> None:
-            reveal_type(dump_value)  # E: inference_failure
-            dump_value(reveal_type)  # E: inference_failure
+            reveal_type(dump_value)  # E: reveal_type
+            dump_value(reveal_type)  # E: reveal_type
             assert_is_value(1, KnownValue(1))
             assert_is_value(1, KnownValue(2))  # E: inference_failure
             assert_is_value(1, val)  # E: inference_failure
@@ -22,8 +22,8 @@ class TestInferenceHelpers(TestNameCheckVisitorBase):
         from pyanalyze import assert_is_value, dump_value
 
         def capybara():
-            x = dump_value(1)  # E: inference_failure
-            y = reveal_type(1)  # E: inference_failure
+            x = dump_value(1)  # E: reveal_type
+            y = reveal_type(1)  # E: reveal_type
             assert_is_value(x, KnownValue(1))
             assert_is_value(y, KnownValue(1))
 
@@ -64,5 +64,5 @@ class TestRevealLocals(TestNameCheckVisitorBase):
         def capybara(a: object, b: str) -> None:
             c = 3
             if b == "x":
-                reveal_locals()  # E: inference_failure
+                reveal_locals()  # E: reveal_type
             print(a, b, c)