Skip to content

Class-object types are rendered inconsistently in cls diagnostics and quoted-annotation hovers #4703

Description

@h3har

Describe the Bug

Pyrefly renders class-object types inconsistently in both the invalid-annotation diagnostic for a cls receiver and the hover for a quoted class name inside that receiver annotation.

Version: pyrefly 1.2.0

Reproduction

Sandbox link

class Base:
    @classmethod
    def method(cls: type['Child']) -> None:
        pass

class Child(Base):
    pass

Running pyrefly check test.py emits:

ERROR `method` method cls type `type[Child]` is not a superclass of class `Base` [invalid-annotation]
 --> test.py:3:9
  |
3 |     def method(cls: type['Child']) -> None:
  |         ^^^^^^
  |
 INFO 1 diagnostic

This issue is about how the types are presented, not whether the narrower receiver annotation should be accepted in this non-overloaded method.

The diagnostic mixes two levels of abstraction. type[Child] is the type of the class object received by cls, but the superclass relationship is between the classes Child and Base. A class-based rendering would therefore say:

`Child` is not a superclass of class `Base`

Alternatively, a type-based rendering could consistently compare the two class-object types and use type terminology, for example:

`type[Child]` is not a supertype of `type[Base]`

The current wording instead compares type[Child] with class Base and calls the parameterized type a "superclass".

Hover

With the cursor over Child inside type['Child'], Pyrefly's language server shows:

type[type[Child]]

The referenced name is Child; its hover should not acquire both the class-object wrapper and another wrapper from the surrounding type[...]. Depending on Pyrefly's hover convention, this could be displayed as the class symbol (as it is for Child in an unquoted type[Child] annotation) or as type[Child], but not type[type[Child]].

The doubled hover type is not specific to receiver annotations. It also occurs for Child in an ordinary annotation such as:

class Holder:
    value: type['Child']

This suggests that the hover symptom is associated with a quoted class name nested in type[...], while the diagnostic symptom is associated with the rendering of rejected cls receiver types.

Related issue

#4701 reports the separate behavioral issue that constructor overloads reject subclass receiver annotations accepted on ordinary overloads. Its output contains this diagnostic wording, but it does not cover the wording or quoted-annotation hover behavior reported here.

Activity

  1. Divyansh151005 commented on Aug 29, 2026

    @Divyansh151005
    Contributor

    I'd like to take this.

    Understanding:

    • The invalid-annotation message for rejected cls receivers mixes levels (type[Child] vs class Base) and calls a class-object type a "superclass".
    • Hover on a quoted name inside type['Child'] incorrectly shows type[type[Child]].

    Plan:

    • Align the diagnostic wording so the compared sides and terminology are consistent (prefer class-based: `Child` is not a superclass of class `Base`).
    • Fix hover so a quoted class nested in type[...] does not double-wrap.
    • Add regression tests for both.

    #claim

  2. github-actions commented on Aug 29, 2026

    @github-actions

    @Divyansh151005 you've claimed this issue — it's now assigned to you. Thanks for picking it up! 🎉

  3. added theissue type on Aug 31, 2026
  4. added and removed
    language-serverIssues specific to our IDE integration rather than type checking
    on Aug 31, 2026
  5. github-actions commented on Sep 15, 2026

    @github-actions

    This issue has someone assigned, but has not had recent activity for more than 2 weeks.

    If you are still working on this issue, please add a comment so everyone knows. Otherwise, please unassign yourself and allow someone else to take over.

    Thank you for your contributions!

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

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions