Skip to content

Commit b9d8669

Browse files
yangdanny97meta-codesync[bot]
authored andcommitted
only give abstract instantiation error on bare class names
Summary: fixes #1379 Reviewed By: ndmitchell Differential Revision: D85569244 fbshipit-source-id: 43fd615177a57e1625217439daf74755a2449c6b
1 parent eedf0c8 commit b9d8669

File tree

3 files changed

+45
-11
lines changed

3 files changed

+45
-11
lines changed

pyrefly/lib/alt/call.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ pub enum CallStyle<'a> {
6060
FreeForm,
6161
}
6262

63+
#[derive(Debug, Clone, PartialEq, Eq)]
64+
pub enum ConstructorKind {
65+
// `MyClass`
66+
BareClassName,
67+
// `type[MyClass]` or `type[Self]`
68+
TypeOfClass,
69+
}
70+
6371
/// A thing that can be called (see as_call_target and call_infer).
6472
/// Note that a single "call" may invoke multiple functions under the hood,
6573
/// e.g., `__new__` followed by `__init__` for Class.
@@ -72,7 +80,7 @@ pub enum CallTarget {
7280
/// Method of a class. The `Type` is the self/cls argument.
7381
BoundMethod(Type, TargetWithTParams<Function>),
7482
/// A class object.
75-
Class(ClassType),
83+
Class(ClassType, ConstructorKind),
7684
/// A TypedDict.
7785
TypedDict(TypedDict),
7886
/// An overloaded function.
@@ -140,15 +148,21 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
140148
_ => None,
141149
}
142150
}
143-
Type::ClassDef(cls) => {
144-
self.as_call_target_impl(Type::type_form(self.instantiate(&cls)), quantified)
145-
}
151+
Type::ClassDef(cls) => match self.instantiate(&cls) {
152+
// `instantiate` can only return `ClassType` or `TypedDict`
153+
Type::ClassType(cls) => {
154+
Some(CallTarget::Class(cls, ConstructorKind::BareClassName))
155+
}
156+
Type::TypedDict(typed_dict) => Some(CallTarget::TypedDict(typed_dict)),
157+
_ => unreachable!(),
158+
},
146159
Type::Type(box Type::ClassType(cls)) | Type::Type(box Type::SelfType(cls)) => {
147-
Some(CallTarget::Class(cls))
148-
}
149-
Type::Type(box Type::Tuple(tuple)) => {
150-
Some(CallTarget::Class(self.erase_tuple_type(tuple)))
160+
Some(CallTarget::Class(cls, ConstructorKind::TypeOfClass))
151161
}
162+
Type::Type(box Type::Tuple(tuple)) => Some(CallTarget::Class(
163+
self.erase_tuple_type(tuple),
164+
ConstructorKind::TypeOfClass,
165+
)),
152166
Type::Type(box Type::Quantified(quantified)) => {
153167
Some(CallTarget::Callable(TargetWithTParams(
154168
None,
@@ -609,7 +623,7 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
609623
}
610624
};
611625
let res = match call_target {
612-
CallTarget::Class(cls) => {
626+
CallTarget::Class(cls, constructor_kind) => {
613627
if cls.has_qname("typing", "Any") {
614628
return self.error(
615629
errors,
@@ -633,7 +647,9 @@ impl<'a, Ans: LookupAnswer> AnswersSolver<'a, Ans> {
633647
let abstract_members = self.get_abstract_members_for_class(cls.class_object());
634648
let unimplemented_abstract_methods =
635649
abstract_members.unimplemented_abstract_methods();
636-
if !unimplemented_abstract_methods.is_empty() {
650+
if constructor_kind == ConstructorKind::BareClassName
651+
&& !unimplemented_abstract_methods.is_empty()
652+
{
637653
self.error(
638654
errors,
639655
range,

pyrefly/lib/report/pysa/call_graph.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,7 @@ impl<'a> CallGraphVisitor<'a> {
10391039
new_targets: vec![],
10401040
})
10411041
}
1042-
Some(crate::alt::call::CallTarget::Class(class_type)) => {
1042+
Some(crate::alt::call::CallTarget::Class(class_type, _)) => {
10431043
// Constructing a class instance.
10441044
self.module_context
10451045
.transaction

pyrefly/lib/test/abstract_methods.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,24 @@ x = Child() # E: Cannot instantiate `Child`
233233
"#,
234234
);
235235

236+
testcase!(
237+
test_no_error_for_type_of_class,
238+
r#"
239+
from abc import ABC, abstractmethod
240+
241+
class A(ABC):
242+
@abstractmethod
243+
def m(self) -> None: ...
244+
245+
@classmethod
246+
def classm(cls) -> None:
247+
cls() # should not error
248+
249+
def test(cls: type[A]):
250+
cls() # should not error
251+
"#,
252+
);
253+
236254
testcase!(
237255
test_abstract_async_iterator,
238256
TestEnv::new().enable_implicit_abstract_class_error(),

0 commit comments

Comments
 (0)