Skip to content

Commit df7da3c

Browse files
committed
Drop the dead incomplete_attributes computation in pyrefly coverage
1 parent 4944790 commit df7da3c

2 files changed

Lines changed: 24 additions & 129 deletions

File tree

pyrefly/lib/commands/coverage/collect.rs

Lines changed: 24 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,24 +1009,6 @@ fn return_annotation_range(bindings: &Bindings, return_idx: Idx<Key>) -> Option<
10091009
}
10101010
}
10111011

1012-
/// Only the first parameter (`self`/`cls`) is allowed to be unannotated.
1013-
fn is_function_completely_annotated(
1014-
bindings: &Bindings,
1015-
answers: &Answers,
1016-
undecorated_idx: Idx<KeyUndecoratedFunction>,
1017-
) -> bool {
1018-
let fun = bindings.get(undecorated_idx);
1019-
let return_idx = bindings.key_to_idx(&Key::ReturnType(ShortIdentifier::new(&fun.def.name)));
1020-
if return_annotation_range(bindings, return_idx).is_none() {
1021-
return false;
1022-
}
1023-
1024-
let implicit_receiver = has_implicit_receiver(fun, answers, undecorated_idx);
1025-
params_with_keys(&fun.def.parameters, implicit_receiver)
1026-
.iter()
1027-
.all(|(key, param)| key.is_none() || param.annotation.is_some())
1028-
}
1029-
10301012
/// Only a bare `Any` counts as unknown; container types like `list[Any]` are known.
10311013
fn is_type_known(ty: &Type) -> bool {
10321014
!ty.is_any()
@@ -1224,36 +1206,16 @@ fn parse_classes(
12241206
module: &Module,
12251207
bindings: &Bindings,
12261208
answers: &Answers,
1227-
transaction: &Transaction,
1228-
handle: &Handle,
12291209
tco_classes: &SmallSet<Idx<KeyClass>>,
12301210
) -> Vec<ReportClass> {
12311211
let mut classes = Vec::new();
12321212

1233-
// group method definitions by class
1234-
let mut methods_by_class: HashMap<Idx<KeyClass>, Vec<Idx<KeyUndecoratedFunction>>> =
1235-
HashMap::new();
1236-
for idx in bindings.keys::<Key>() {
1237-
if let Key::Definition(_) = bindings.idx_to_key(idx)
1238-
&& let Binding::Function(x, _pred, _class_meta) = bindings.get(idx)
1239-
{
1240-
let undecorated_idx = bindings.get(*x).undecorated_idx;
1241-
if let Some(class_key) = bindings.get(undecorated_idx).class_key {
1242-
methods_by_class
1243-
.entry(class_key)
1244-
.or_default()
1245-
.push(undecorated_idx);
1246-
}
1247-
}
1248-
}
1249-
12501213
for class_idx in bindings.keys::<KeyClass>() {
12511214
// Skip @type_check_only classes.
12521215
if tco_classes.contains(&class_idx) {
12531216
continue;
12541217
}
1255-
let binding_class = bindings.get(class_idx);
1256-
let cls_binding = match binding_class {
1218+
let cls_binding = match bindings.get(class_idx) {
12571219
BindingClass::ClassDef(cls) => cls,
12581220
BindingClass::FunctionalClassDef(..) => continue,
12591221
};
@@ -1266,65 +1228,13 @@ fn parse_classes(
12661228
if is_deleted_class(module, bindings, cls_binding) {
12671229
continue;
12681230
}
1269-
let class_type = match answers.get_idx(class_idx) {
1270-
Some(result) => match &result.0 {
1271-
Some(cls) => cls.clone(),
1272-
None => continue,
1273-
},
1274-
None => continue,
1275-
};
1276-
let class_name = class_fqn(module, parent, name);
1277-
let mro = class_mro(bindings, answers, &class_type);
1278-
// Check methods defined directly on this class
1279-
let mut incomplete_attributes = Vec::new();
1280-
for &undecorated_idx in methods_by_class.get(&class_idx).into_iter().flatten() {
1281-
if !is_function_completely_annotated(bindings, answers, undecorated_idx) {
1282-
incomplete_attributes.push(IncompleteAttribute {
1283-
name: bindings.get(undecorated_idx).def.name.to_string(),
1284-
declared_in: class_name.clone(),
1285-
});
1286-
}
1287-
}
1288-
// Check inherited methods
1289-
for ancestor_class_type in mro.ancestors_no_object() {
1290-
let ancestor_class = ancestor_class_type.class_object();
1291-
// Skip methods inherited from builtins
1292-
if ancestor_class.module_name().as_str() == "builtins" {
1293-
continue;
1294-
}
1295-
let ancestor_name = class_fqn(
1296-
ancestor_class.module(),
1297-
ancestor_class.qname().parent(),
1298-
ancestor_class.name(),
1299-
);
1300-
let Some(ancestor_class_fields) = transaction.get_class_fields(handle, ancestor_class)
1301-
else {
1302-
continue;
1303-
};
1304-
for field_name in ancestor_class_fields.names() {
1305-
let field_name_str = field_name.to_string();
1306-
// Skip if we already have this attribute listed (it has been overridden
1307-
// by the current class or another class in the MRO)
1308-
if incomplete_attributes
1309-
.iter()
1310-
.any(|a| a.name == field_name_str)
1311-
{
1312-
continue;
1313-
}
1314-
if !ancestor_class_fields.is_field_annotated(field_name) {
1315-
incomplete_attributes.push(IncompleteAttribute {
1316-
name: field_name_str,
1317-
declared_in: ancestor_name.clone(),
1318-
});
1319-
}
1320-
}
1231+
// Skip classes that fail to resolve.
1232+
if answers.get_idx(class_idx).is_none_or(|c| c.0.is_none()) {
1233+
continue;
13211234
}
1322-
let location = range_to_location(module, cls_binding.def.range);
1323-
incomplete_attributes.sort();
13241235
classes.push(ReportClass {
1325-
name: class_name,
1326-
incomplete_attributes,
1327-
location,
1236+
name: class_fqn(module, parent, name),
1237+
location: range_to_location(module, cls_binding.def.range),
13281238
});
13291239
}
13301240
classes.sort();
@@ -1442,14 +1352,7 @@ impl ModuleSymbols {
14421352
&tco_classes,
14431353
);
14441354
merge_overloads(&mut functions);
1445-
let classes = parse_classes(
1446-
&module,
1447-
&bindings,
1448-
&answers,
1449-
transaction,
1450-
handle,
1451-
&tco_classes,
1452-
);
1355+
let classes = parse_classes(&module, &bindings, &answers, &tco_classes);
14531356
let mut variables = parse_variables(
14541357
&module,
14551358
&bindings,
@@ -3127,20 +3030,19 @@ def g(x: int) -> int:
31273030
(env, base, derived)
31283031
}
31293032

3130-
/// Collect `derived` from a run that retains all bindings/answers, as ground truth.
3131-
fn base_derived_reference() -> ModuleSymbols {
3033+
/// Collect `derived`'s class members from a run that retains all bindings/answers,
3034+
/// as ground truth. Inherited members are looked up in the base module via
3035+
/// `get_class_fields`, so they exercise the cross-module path.
3036+
fn base_derived_reference() -> SmallSet<String> {
31323037
let (env, _, derived) = base_derived_env();
31333038
let (state, _) = env.to_state();
3134-
let symbols = ModuleSymbols::collect(&state.transaction(), &derived, false).unwrap();
3039+
let symbols = ModuleSymbols::collect(&state.transaction(), &derived, true).unwrap();
3040+
let members = symbols.stub_merge.unwrap().class_members;
31353041
assert!(
3136-
symbols
3137-
.classes
3138-
.iter()
3139-
.any(|c| !c.incomplete_attributes.is_empty()),
3140-
"Derived should report an inherited incomplete attribute from Base: {:?}",
3141-
symbols.classes
3042+
members.contains("derived.Derived.x"),
3043+
"Derived should report the member `x` inherited from Base: {members:?}"
31423044
);
3143-
symbols
3045+
members
31443046
}
31453047

31463048
/// Inherited members survive base-bindings eviction via retained `Solutions` metadata.
@@ -3159,11 +3061,11 @@ def g(x: int) -> int:
31593061
);
31603062

31613063
transaction.run(&[derived.dupe()], Require::Everything, None);
3162-
let symbols = ModuleSymbols::collect(&transaction, &derived, false).unwrap();
3064+
let symbols = ModuleSymbols::collect(&transaction, &derived, true).unwrap();
31633065
assert_eq!(
3164-
symbols.classes,
3165-
base_derived_reference().classes,
3166-
"evicting the base module's bindings must not change the derived class report"
3066+
symbols.stub_merge.unwrap().class_members,
3067+
base_derived_reference(),
3068+
"evicting the base module's bindings must not change the derived class members"
31673069
);
31683070
}
31693071

@@ -3176,7 +3078,7 @@ def g(x: int) -> int:
31763078
let mut transaction = state.new_transaction(Require::Exports, None);
31773079
transaction.set_memory(env.get_memory());
31783080
transaction.set_solutions_hook(Some(Box::new(|handle, transaction| {
3179-
if let Some(symbols) = ModuleSymbols::collect(transaction, handle, false) {
3081+
if let Some(symbols) = ModuleSymbols::collect(transaction, handle, true) {
31803082
collected.lock().insert(handle.dupe(), symbols);
31813083
}
31823084
})));
@@ -3189,9 +3091,9 @@ def g(x: int) -> int:
31893091

31903092
let symbols = collected.lock().remove(&derived).unwrap();
31913093
assert_eq!(
3192-
symbols.classes,
3193-
base_derived_reference().classes,
3194-
"hook-collected symbols must match symbols collected with bindings retained"
3094+
symbols.stub_merge.unwrap().class_members,
3095+
base_derived_reference(),
3096+
"hook-collected members must match members collected with bindings retained"
31953097
);
31963098
}
31973099
}

pyrefly/lib/commands/coverage/types.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,9 @@ pub struct Function {
189189
pub range: TextRange,
190190
}
191191

192-
#[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)]
193-
pub struct IncompleteAttribute {
194-
pub name: String,
195-
pub declared_in: String,
196-
}
197-
198192
#[derive(Debug, Serialize, PartialEq, Eq, PartialOrd, Ord)]
199193
pub struct ReportClass {
200194
pub name: String,
201-
pub incomplete_attributes: Vec<IncompleteAttribute>,
202195
pub location: Location,
203196
}
204197

0 commit comments

Comments
 (0)