Skip to content

fix invalid-argument "Expected first argument to super" false positives #3065#3069

Open
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:3065
Open

fix invalid-argument "Expected first argument to super" false positives #3065#3069
asukaminato0721 wants to merge 1 commit intofacebook:mainfrom
asukaminato0721:3065

Conversation

@asukaminato0721
Copy link
Copy Markdown
Contributor

@asukaminato0721 asukaminato0721 commented Apr 8, 2026

Summary

Fixes #3065

relaxing the explicit super(cls, obj) first-argument check.

now accepts not just literal class defs, but also class-object values typed as type[C] and type[Self], which covers patterns like self.__class__, type(self), and cls in __new__.

Test Plan

add test

@meta-cla meta-cla bot added the cla signed label Apr 8, 2026
@github-actions github-actions bot added the size/m label Apr 8, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

Diff from mypy_primer, showing the effect of this PR on open source code:

setuptools (https://github.com/pypa/setuptools)
- ERROR setuptools/_distutils/dir_util.py:28:9-33: Expected first argument to `super` to be a class object, got `type[Self@SkipRepeatAbsolutePaths]` [invalid-argument]

comtypes (https://github.com/enthought/comtypes)
- ERROR comtypes/safearray.py:393:13-51: Expected first argument to `super` to be a class object, got `type[_Pointer[_Pointer[Unknown]]]` [invalid-argument]
+ ERROR comtypes/safearray.py:393:13-51: Illegal `super(type[_Pointer[_Pointer[Unknown]]], _make_safearray_type.__)` call: `_make_safearray_type.__` is not an instance or subclass of `type[_Pointer[_Pointer[Unknown]]]` [invalid-super-call]

dd-trace-py (https://github.com/DataDog/dd-trace-py)
- ERROR ddtrace/internal/settings/_config.py:825:20-47: Expected first argument to `super` to be a class object, got `type[Self@Config]` [invalid-argument]
- ERROR ddtrace/internal/settings/_config.py:830:20-47: Expected first argument to `super` to be a class object, got `type[Self@Config]` [invalid-argument]

zope.interface (https://github.com/zopefoundation/zope.interface)
- ERROR src/zope/interface/ro.py:209:16-38: Expected first argument to `super` to be a class object, got `type[Self@_NamedBool]` [invalid-argument]
+ ERROR src/zope/interface/ro.py:210:9-22: Object of class `_NamedBool` has no attribute `__name__` [missing-attribute]

prefect (https://github.com/PrefectHQ/prefect)
- ERROR src/integrations/prefect-dbt/prefect_dbt/cli/commands.py:422:16-48: Expected first argument to `super` to be a class object, got `type[Self@DbtCoreOperation]` [invalid-argument]
- ERROR src/integrations/prefect-dbt/prefect_dbt/cli/commands.py:437:16-48: Expected first argument to `super` to be a class object, got `type[Self@DbtCoreOperation]` [invalid-argument]

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 8, 2026

Primer Diff Classification

✅ 5 improvement(s) | 5 project(s) total | +2, -7 errors

5 improvement(s) across setuptools, comtypes, dd-trace-py, zope.interface, prefect.

Project Verdict Changes Error Kinds Root Cause
setuptools ✅ Improvement -1 invalid-argument class_object()
comtypes ✅ Improvement +1, -1 invalid-argument, invalid-super-call pyrefly/lib/alt/solve.rs
dd-trace-py ✅ Improvement -2 invalid-argument pyrefly/lib/alt/solve.rs
zope.interface ✅ Improvement +1, -1 invalid-argument, missing-attribute pyrefly/lib/alt/solve.rs
prefect ✅ Improvement -2 invalid-argument pyrefly/lib/alt/solve.rs
Detailed analysis

✅ Improvement (5)

setuptools (-1)

This is a clear false positive removal. The code super(cls, cls.instance).clear() in a @classmethod is valid Python — cls is a class object. Pyrefly's old code failed to recognize type[Self] as a valid class object for super()'s first argument. The PR correctly adds pattern matching for Type::Type(box Type::ClassType(...) | box Type::SelfType(...)), fixing the false positive.
Attribution: The change in pyrefly/lib/alt/solve.rs in the SuperStyle::ExplicitArgs handling now matches Type::Type(box Type::ClassType(cls) | box Type::SelfType(cls)) for the first argument to super(), extracting the class object via cls.[class_object()](https://github.com/facebook/pyrefly/blob/main/pyrefly/lib/alt/solve.rs). Previously, only Type::ClassDef was accepted, so type[Self@SkipRepeatAbsolutePaths] (which is Type::Type(box Type::SelfType(...))) was rejected with an invalid-argument error.

comtypes (+1, -1)

This is a net improvement in error quality. The OLD error was a clear false positive: it said type[_Pointer[_Pointer[Unknown]]] is not a class object, but it obviously IS a class object (it's type[X]). The NEW error is more nuanced and arguably correct from a static analysis perspective: the @Patch decorator dynamically patches the class __ onto POINTER(POINTER(sa_type)), but statically the type checker cannot verify that self (typed as an instance of the local class __) is actually an instance of POINTER(POINTER(sa_type)). Pyright also flags this same issue. While the code works at runtime due to the monkey-patching, the static type relationship genuinely cannot be verified. The error message is now accurate and specific rather than misleading. The transition from a wrong error (false claim about first arg not being a class) to a correct error (valid concern about instance relationship) is an improvement in diagnostic quality.
Attribution: The change in pyrefly/lib/alt/solve.rs in the SuperStyle::ExplicitArgs handling refactored how the first argument to super() is matched. Previously, only Type::ClassDef(cls) was accepted as a valid class for the first argument. The new code adds Type::Type(box Type::ClassType(cls) | box Type::SelfType(cls)) as valid patterns, which correctly handles cases where the first argument is a type[X] expression (like POINTER(POINTER(sa_type))). This fixes the false positive. However, the code then proceeds to check whether self is an instance/subclass of that class, and since the @Patch decorator dynamically reassigns the class body, the type checker can't verify the relationship, producing the new invalid-super-call error.

dd-trace-py (-2)

The removed errors were false positives. self.__class__ returns the runtime class of the instance, which is a perfectly valid first argument to super(). Pyrefly was failing to recognize that type[Self@Config] (internally Type::Type(Type::SelfType(...))) represents a class object. The PR correctly adds handling for Type::Type(box Type::ClassType(cls) | box Type::SelfType(cls)) to extract the underlying class, fixing this false positive.
Attribution: The change in pyrefly/lib/alt/solve.rs in the SuperStyle::ExplicitArgs handling added a new match arm Type::Type(box Type::ClassType(cls) | box Type::SelfType(cls)) that correctly extracts the class object from type[X] types. Previously, only bare Type::ClassDef was accepted as a valid first argument to super(), causing self.__class__ (which has type type[Self@Config], represented as Type::Type(Type::SelfType(...))) to be incorrectly rejected. The new test case test_super_explicit_dynamic_first_arg confirms this fix with patterns like super(self.__class__, self) and super(type(self), self).

zope.interface (+1, -1)

The removed error was a clear false positive — cls in __new__ is a valid class object, and pyrefly was incorrectly rejecting type[Self@_NamedBool]. The PR correctly fixes this.

The new missing-attribute error on inst.__name__ is a borderline case. Looking at the code, inst = super(cls, _NamedBool).__new__(cls, val) — the type checker infers inst as an int (or _NamedBool) based on the return type of int.__new__. The int type does not declare a __name__ attribute. However, _NamedBool inherits from int, and Python subclasses of int (unlike int itself) DO get a __dict__ at runtime, allowing arbitrary attribute assignment like inst.__name__ = name. Static type checkers flag this because __name__ is not declared in the class definition of _NamedBool or int. Pyright also flags this (confirmed by the [pyright: yes] annotation). The code works perfectly at runtime, so this is a false positive from the runtime correctness perspective, but it's a known limitation shared with pyright — the proper fix would be to declare __name__ as a class attribute in _NamedBool.

Net assessment: removing 1 clear false positive and adding 1 debatable error (co-reported by pyright) is a net improvement. The core fix (handling Type[Self@X] in super's first argument) is correct and important.

Attribution: The change in pyrefly/lib/alt/solve.rs in the SuperStyle::ExplicitArgs handling added a new match arm Type::Type(box Type::ClassType(cls) | box Type::SelfType(cls)) that extracts the class from wrapped type objects. Previously, type[Self@_NamedBool] fell through to the error case. Now it's correctly recognized as a class object, removing the false positive. The new missing-attribute error on line 210 is a pre-existing issue that was previously masked — when the super() call errored out, pyrefly likely didn't continue analyzing the subsequent line. Now that the super() call succeeds, pyrefly proceeds to check inst.__name__ = name and flags it because _NamedBool (subclass of int) doesn't declare __name__ as a known attribute.

prefect (-2)

Both removed errors were false positives. type(self) returns a type[Self] which is a class object — a perfectly valid first argument to super(). The PR correctly extends the match in pyrefly/lib/alt/solve.rs to handle Type::Type(box Type::ClassType | box Type::SelfType) as valid class objects for super()'s first argument. Lines 422 and 437 both use super(type(self), modified_self), which is a standard Python pattern for calling parent methods with a different instance.
Attribution: The change in pyrefly/lib/alt/solve.rs in the SuperStyle::ExplicitArgs handling now matches Type::Type(box Type::ClassType(cls) | box Type::SelfType(cls)) for the first argument to super(), extracting the underlying class object. Previously, only Type::ClassDef(cls) was matched, so type[Self@DbtCoreOperation] (which is Type::Type(box Type::SelfType(...))) fell through to the error case. The new test test_super_explicit_dynamic_first_arg confirms this pattern with super(type(self), self) and super(cls, ...) calls.


Was this helpful? React with 👍 or 👎

Classification by primer-classifier (5 LLM)

@asukaminato0721 asukaminato0721 marked this pull request as ready for review April 8, 2026 01:55
Copilot AI review requested due to automatic review settings April 8, 2026 01:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes false-positive invalid-argument diagnostics for super(cls, obj) when the first argument is a dynamically-computed class object (e.g. self.__class__, type(self), or cls in __new__), aligning behavior with other type checkers and addressing #3065.

Changes:

  • Extend super explicit-args handling to accept type[ClassType] / type[SelfType] as a valid first argument to super.
  • Add a regression test covering multiple dynamic-first-arg super(...) patterns from the reported repros.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
pyrefly/lib/alt/solve.rs Accepts additional type shapes for super’s first argument to avoid invalid-argument false positives.
pyrefly/lib/test/class_super.rs Adds a regression testcase ensuring these dynamic-first-arg super(...) calls do not error.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2758 to +2764
let cls_type = self.get_idx(*cls_binding).ty().clone();
let cls = match &cls_type {
Type::Any(style) => return style.propagate(),
Type::ClassDef(cls) => cls,
Type::Type(box Type::ClassType(cls) | box Type::SelfType(cls)) => {
cls.class_object()
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

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

cls_type is cloned unconditionally (let cls_type = ... .clone()), even though it’s only needed for display in the error path. Consider keeping a &Type (e.g. let cls_type = self.get_idx(*cls_binding).ty();) and cloning only when formatting the error message to avoid extra work on every super(...) solve.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

invalid-argument "Expected first argument to super" false positives

2 participants