Skip to content

Operations on an explicit Any produce Unknown, so unknown-variable-type / unknown-argument-type fire on explicit Any #4848

Description

@phiresky

Describe the bug

Attribute access, method calls, binary operators and subscripts on a value typed as explicit Any are inferred as Unknown (implicit Any). Pyright and mypy keep the result as explicit Any.

This is an issue when migrating from pyright, in our code base it causes hundreds of additional "useless" warnings against semi-typed operations in numpy and similar - once a value is explicit Any (like the result on NDArray[Any] operation), every expression built on it becomes unknown rather than staying Any as they do in pyright and mypy.

from typing import Any, reveal_type

def f(a: Any) -> None:
    reveal_type(a.foo)    # attribute access
    reveal_type(a.foo())  # method call
    reveal_type(a + 1)    # binary operator
    reveal_type(a[0])     # subscript
$ pyrefly check t.py
 INFO t.py:4:16-23: revealed type: Unknown [reveal-type]
 INFO t.py:5:16-25: revealed type: Unknown [reveal-type]
 INFO t.py:6:16-23: revealed type: Unknown [reveal-type]
 INFO t.py:7:16-22: revealed type: Unknown [reveal-type]

$ pyright t.py
  t.py:4:17 - information: Type of "a.foo" is "Any"
  t.py:5:17 - information: Type of "a.foo()" is "Any"
  t.py:6:17 - information: Type of "a + 1" is "Any"
  t.py:7:17 - information: Type of "a[0]" is "Any"

$ mypy t.py
t.py:4: note: Revealed type is "Any"
t.py:5: note: Revealed type is "Any"
t.py:6: note: Revealed type is "Any"
t.py:7: note: Revealed type is "Any"

Reproduces on pyrefly 1.2.0 and 1.3.0-dev.4. Compared against basedpyright 1.39.9 and mypy (latest).

AI-written detail:

The unknown-* error kinds are documented as implicit-Any-only. The error-kinds reference describes unknown-argument-type as "a call argument whose type is an implicit Any (unknown)" and says it "mirrors pyright's reportUnknownArgumentType", and the pyright diagnostics mapping rates reportUnknownVariableTypeunknown-variable-type and reportUnknownArgumentTypeunknown-argument-type as "Direct" fidelity.

This also matches the design described in #3661 (2026-07-28), where pyrefly's Anys are grouped into three categories, (1) implicitly inferred for un-annotated values, (2) explicitly annotated, (3) generated to stop error cascades, and the usage-site unknown-* errors were shipped for category 1 only, with reportAny named as the intended usage-site check for category 2.

Because of the inference above, an operation on a category-2 Any yields a category-1 Any, so the unknown-* kinds fire on explicit Any one step removed from its source. Pyright's rules deliberately exempt that case (basedpyright has reportAny / pyrefly has explicit-any for that policy):

from typing import Any

def f(a: Any) -> None:
    b = a.foo     # unknown-variable-type: The type of `b` is unknown; it is inferred as an implicit `Any`
    print(a + 1)  # unknown-argument-type: The type of this argument is unknown

On a real codebase this makes the checks unusable with numpy and torch. torch.nn.Module.__call__ is typed Callable[..., Any], so every q = self.proj(x) is explicit Any and the following q.reshape(...).transpose(1, 2) is flagged. numpy ufuncs return NDArray[Any], and np.isfinite(x) & np.isfinite(y) & np.isfinite(z) is flagged from the second & on. On a ~146k-line monorepo, unknown-variable-type / unknown-argument-type report 231 / 402 diagnostics where pyright's reportUnknownVariableType / reportUnknownArgumentType report 49 / 26 on the same tree. The difference is almost entirely this pattern.

Expected

An operation whose only source of Any is an explicit Any should yield explicit Any, and the unknown-* kinds should not fire on it.

Related

Sandbox Link

https://pyrefly.org/sandbox/?project=v2.pZRLCsIwFEW38iCDtgiNTrsK59ZBUmMNlCTkI7h7Xz6CrbUgTtOTR5N7c35V5kI2qa816-LHZdhvaM3aq9ZNXCXAvLeSBy8gn-sLXTfIk_KaUHrTtALCDg5lLMeE7AO0yTpbgU_7c2IRdoG7wUrjP1MkcMwLUC4E_YXxjwHHYqxtrwgKUsDNe-M6Sl_btR2pUPSiB0dnOyjEwrI7k8m983H4v9nSvfqnPlVuT7VVnip2B4nNPjwB

(Only applicable for extension issues) IDE Information

No response

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions