From 4e19c09044e4c636171fdff77f29e064578da1f6 Mon Sep 17 00:00:00 2001
From: Ned Batchelder <ned@nedbatchelder.com>
Date: Sat, 13 May 2023 11:54:14 -0400
Subject: [PATCH] wip

---
 coverage/inorout.py | 11 +++++++----
 coverage/plugin.py  |  6 ++++--
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/coverage/inorout.py b/coverage/inorout.py
index d2dbdcdf7..d716d5b83 100644
--- a/coverage/inorout.py
+++ b/coverage/inorout.py
@@ -357,21 +357,24 @@ def nope(disp: TFileDisposition, reason: str) -> TFileDisposition:
             if not plugin._coverage_enabled:
                 continue
 
+            plugin_name = plugin._coverage_plugin_name
             try:
                 file_tracer = plugin.file_tracer(canonical)
                 if file_tracer is not None:
                     file_tracer._coverage_plugin = plugin
-                    disp.trace = True
                     disp.file_tracer = file_tracer
                     if file_tracer.has_dynamic_source_filename():
                         disp.has_dynamic_filename = True
                     else:
                         disp.source_filename = canonical_filename(
-                            file_tracer.source_filename()
+                            file_tracer.source_filename() or
+                            canonical
                         )
+                    if not file_tracer.trace:
+                        return nope(disp, f"plugin {plugin_name!r} claimed it as untraced")
+                    disp.trace = True
                     break
             except Exception:
-                plugin_name = plugin._coverage_plugin_name
                 tb = traceback.format_exc()
                 self.warn(f"Disabling plug-in {plugin_name!r} due to an exception:\n{tb}")
                 plugin._coverage_enabled = False
@@ -388,7 +391,7 @@ def nope(disp: TFileDisposition, reason: str) -> TFileDisposition:
                 )
             reason = self.check_include_omit_etc(disp.source_filename, frame)
             if reason:
-                nope(disp, reason)
+                return nope(disp, reason)
 
         return disp
 
diff --git a/coverage/plugin.py b/coverage/plugin.py
index 2c1ffada4..007e140ed 100644
--- a/coverage/plugin.py
+++ b/coverage/plugin.py
@@ -274,7 +274,9 @@ class FileTracer(CoveragePluginBase):
 
     """
 
-    def source_filename(self) -> str:       # type: ignore[return]
+    trace = True
+
+    def source_filename(self) -> Optional[str]:
         """The source file name for this file.
 
         This may be any file name you like.  A key responsibility of a plug-in
@@ -287,7 +289,7 @@ def source_filename(self) -> str:       # type: ignore[return]
         Returns the file name to credit with this execution.
 
         """
-        _needs_to_implement(self, "source_filename")
+        return None
 
     def has_dynamic_source_filename(self) -> bool:
         """Does this FileTracer have dynamic source file names?