From d951b8d5c3e586d842a5f3b176f1c5cd4f8f62eb Mon Sep 17 00:00:00 2001 From: jorenham Date: Thu, 2 Jul 2026 18:39:08 +0200 Subject: [PATCH] Don't treat missing exports as origins in `pyrefly coverage` --- pyrefly/lib/commands/coverage/collect.rs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pyrefly/lib/commands/coverage/collect.rs b/pyrefly/lib/commands/coverage/collect.rs index d958b2f83b..b9af297e00 100644 --- a/pyrefly/lib/commands/coverage/collect.rs +++ b/pyrefly/lib/commands/coverage/collect.rs @@ -336,9 +336,10 @@ fn trace_export_origin( } match transaction.get_exports(&cur_handle).get(&cur_name) { - Some(ExportLocation::ThisModule(_)) | None => { + Some(ExportLocation::ThisModule(_)) => { return Some(format!("{module_name}.{cur_name}")); } + None => return None, Some(ExportLocation::OtherModule(other_module, alias)) => { if let Some(alias) = alias { cur_name = alias.clone(); @@ -2877,6 +2878,22 @@ def g(x: int) -> int: assert!(fqns.contains("pkg._internal.bar")); assert!(!fqns.contains("pkg._internal.foo")); assert!(!fqns.contains("pkg.baz")); + + // A re-export of a name its origin module doesn't define keeps the + // local FQN but traces to no origin (github.com/facebook/pyrefly/issues/4025) + let fqns = compute( + &[ + ( + "pkg", + "pkg/__init__.py", + "from pkg._internal import ghost\n__all__ = [\"ghost\"]\n", + ), + ("pkg._internal", "pkg/_internal.py", "x: int = 1\n"), + ], + &["pkg", "pkg._internal"], + ); + assert!(fqns.contains("pkg.ghost")); + assert!(!fqns.contains("pkg._internal.ghost")); } /// Dataclass and NamedTuple fields are IMPLICIT; methods on those classes count normally.