Skip to content

Conversation

sharkdp
Copy link
Contributor

@sharkdp sharkdp commented Jul 22, 2025

Summary

It was faster to implement this then to write the ticket: Disallow ClassVar annotations almost everywhere outside of class body scopes.

Test Plan

New Markdown tests

@sharkdp sharkdp requested a review from carljm as a code owner July 22, 2025 11:42
@sharkdp sharkdp added the ty Multi-file analysis & type inference label Jul 22, 2025
Copy link
Contributor

github-actions bot commented Jul 22, 2025

mypy_primer results

No ecosystem changes detected ✅
No memory usage changes detected ✅

Comment on lines +103 to +105
def __init__(self) -> None:
# error: [invalid-type-form] "`ClassVar` annotations are not allowed for non-name targets"
self.x: ClassVar[int] = 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the spec explicitly forbid something like this?

class C:
    @classmethod
    def f(cls):
        cls.x: ClassVar[int] = 1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the spec explicitly forbid something like this?

It does not explicitly forbid it. It does say "ClassVar may be used in one of several forms:" and then proceeds to show two examples where ClassVar is used at the class-body level.

Mypy, pyright and pyrefly all reject your example for various reasons (including: "ClassVar" is not allowed in this context).

I also didn't find any references of code like this in the ecosystem.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be reasonable to support that, but I also don't think it's high priority at all, especially if no other type checker does.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By "support that", you mean: "tolerate that"? Because I'm not sure what the intention here would be? When would a cls.x: … = 1 attribute not be a (pure) class variable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess if it were declared with x: int = 42 in the class body, then that would make it an instance attribute with a class-level default, and cls.x = 56 in a class method would be mutating the default value of the instance attribute? That's a bit suspect, though; I'd consider it at least a code smell.

And I suppose we'd want to emit an error if it were declared as an instance variable in an instance method or the class body, but declared as a class variable in a classmethod

@sharkdp sharkdp merged commit 6d4687c into main Jul 22, 2025
37 checks passed
@sharkdp sharkdp deleted the david/illegal-classvar-use branch July 22, 2025 12:21
dcreager added a commit that referenced this pull request Jul 22, 2025
* main: (76 commits)
  Move fix suggestion to subdiagnostic (#19464)
  [ty] Implement non-stdlib stub mapping for classes and functions (#19471)
  [ty] Disallow illegal uses of `ClassVar` (#19483)
  [ty] Disallow `Final` in function parameter/return-type annotations (#19480)
  [ty] Extend `Final` test suite (#19476)
  [ty] Minor change to diagnostic message for invalid Literal uses (#19482)
  [ty] Detect illegal non-enum attribute accesses in Literal annotation (#19477)
  [ty] Reduce size of `TypeInference` (#19435)
  Run MD tests for Markdown-only changes (#19479)
  Revert "[ty] Detect illegal non-enum attribute accesses in Literal annotation"
  [ty] Detect illegal non-enum attribute accesses in Literal annotation
  [ty] Added semantic token support for more identifiers (#19473)
  [ty] Make tuple subclass constructors sound (#19469)
  [ty] Pass down specialization to generic dataclass bases (#19472)
  [ty] Garbage-collect reachability constraints (#19414)
  [ty] Implicit instance attributes declared `Final` (#19462)
  [ty] Expansion of enums into unions of literals (#19382)
  [ty] Avoid rechecking the entire project when changing the opened files (#19463)
  [ty] Add warning for unknown `TY_MEMORY_REPORT` value (#19465)
  [ty] Sync vendored typeshed stubs (#19461)
  ...
dcreager added a commit that referenced this pull request Jul 22, 2025
* main:
  [ty] Use `ThinVec` for sub segments in `PlaceExpr` (#19470)
  [ty] Splat variadic arguments into parameter list (#18996)
  [`flake8-pyi`] Skip fix if all `Union` members are `None` (`PYI016`)  (#19416)
  Skip notebook with errors in ecosystem check (#19491)
  [ty] Consistent use of American english (in rules) (#19488)
  [ty] Support iterating over enums (#19486)
  Fix panic for illegal `Literal[…]` annotations with inner subscript expressions (#19489)
  Move fix suggestion to subdiagnostic (#19464)
  [ty] Implement non-stdlib stub mapping for classes and functions (#19471)
  [ty] Disallow illegal uses of `ClassVar` (#19483)
  [ty] Disallow `Final` in function parameter/return-type annotations (#19480)
  [ty] Extend `Final` test suite (#19476)
  [ty] Minor change to diagnostic message for invalid Literal uses (#19482)
  [ty] Detect illegal non-enum attribute accesses in Literal annotation (#19477)
  [ty] Reduce size of `TypeInference` (#19435)
  Run MD tests for Markdown-only changes (#19479)
  Revert "[ty] Detect illegal non-enum attribute accesses in Literal annotation"
  [ty] Detect illegal non-enum attribute accesses in Literal annotation
  [ty] Added semantic token support for more identifiers (#19473)
  [ty] Make tuple subclass constructors sound (#19469)
UnboundVariable pushed a commit to UnboundVariable/ruff that referenced this pull request Jul 23, 2025
* main: (28 commits)
  [ty] highlight the argument in `static_assert` error messages (astral-sh#19426)
  [ty] Infer single-valuedness for enums based on `int`/`str` (astral-sh#19510)
  [ty] Restructure submodule query around `File` dependency
  [ty] Make `Module` a Salsa ingredient
  [ty] Reachability analysis for `isinstance(…)` branches (astral-sh#19503)
  [ty] Normalize single-member enums to their instance type (astral-sh#19502)
  [ty] Invert `ty_ide` and `ty_project` dependency (astral-sh#19501)
  [ty] Implement mock language server for testing (astral-sh#19391)
  [ty] Detect enums if metaclass is a subtype of EnumType/EnumMeta (astral-sh#19481)
  [ty] perform type narrowing for places marked `global` too (astral-sh#19381)
  [ty] Use `ThinVec` for sub segments in `PlaceExpr` (astral-sh#19470)
  [ty] Splat variadic arguments into parameter list (astral-sh#18996)
  [`flake8-pyi`] Skip fix if all `Union` members are `None` (`PYI016`)  (astral-sh#19416)
  Skip notebook with errors in ecosystem check (astral-sh#19491)
  [ty] Consistent use of American english (in rules) (astral-sh#19488)
  [ty] Support iterating over enums (astral-sh#19486)
  Fix panic for illegal `Literal[…]` annotations with inner subscript expressions (astral-sh#19489)
  Move fix suggestion to subdiagnostic (astral-sh#19464)
  [ty] Implement non-stdlib stub mapping for classes and functions (astral-sh#19471)
  [ty] Disallow illegal uses of `ClassVar` (astral-sh#19483)
  ...

# Conflicts:
#	crates/ty_ide/src/goto.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ty Multi-file analysis & type inference
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants