QA: scan the Functors package extension - #331
Merged
ChrisRackauckas merged 2 commits intoAug 1, 2026
Merged
Conversation
ExplicitImports discovers extension modules via `Base.get_extension`, which returns `nothing` until the trigger weakdep is loaded, so the QA lane was scanning only `DiffEqCallbacks` itself and never `DiffEqCallbacksFunctorsExt`. Adding Functors to the QA environment and loading it in `qa.jl` brings the extension into the checked module set. The newly-surfaced findings are all the extension importing the parent package's internal recursive-container generics, which is what the extension exists to implement; they are ignored with a comment rather than promoted to public API. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
ExplicitImports silently skips an extension whose module cannot be resolved: `Base.get_extension` returns `nothing` and every check reports a clean pass. A later break in the extension's precompilation would therefore turn QA green while extension coverage silently dropped back to zero, with the 22 `all_explicit_imports_are_public` ignores still riding on a module nothing was checking. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
ChrisRackauckas
marked this pull request as ready for review
August 1, 2026 08:40
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR should be ignored until reviewed by @ChrisRackauckas.
Problem
run_qaruns ExplicitImports' checks over the package module and its extensions — ExplicitImports reads the[extensions]table fromProject.tomland adds each one — but only when the extension module actually exists:An extension module only exists once its trigger weakdep is loaded, and the QA environment loaded none. So
DiffEqCallbacksFunctorsExtwas never checked by QA.Note the failure mode is silent: an unresolvable extension is skipped and the checks still report a clean pass.
Change
test/qa/Project.toml: addFunctors(same UUID as the root[weakdeps]entry) withcompat = "0.5", mirroring the root[compat].test/qa/qa.jl:using Functorsbeforerun_qa, with a comment explaining why.test/qa/qa.jl: a load guard asserting the extension module really exists, so a future break in the extension's precompilation fails QA instead of silently reverting coverage to zero:That guard matters more than usual here, since the 22 ignore entries below are all justified by the extension being scanned.
Proof the extension is actually scanned now
A passing QA summary is not proof — each ExplicitImports check folds all submodules into a single
@test, so a clean extension yields an identical summary either way. Run directly against the QA environment:versus the same script without
Functors(i.e. whatmasterdoes today):The first QA run after adding the weakdep also errored with 22 new
all_explicit_imports_are_publicfindings fromext/DiffEqCallbacksFunctorsExt.jl, which is independent confirmation that the extension entered the checked set.Coverage
DiffEqCallbacksFunctorsExt(trigger:Functors)That is the package's only extension, so coverage is complete — nothing is left out for GPU-hardware, external-system-library, or resolution reasons.
Ignore entries added
One block, under
all_explicit_imports_are_public, covering 22 symbols:allocate_vjp,allocate_vjp_internal,allocate_zeros,internal_add!,internal_adjoint,internal_allocate_zeros,internal_axpy!,internal_copy,internal_copyto!,internal_neg!,internal_scalar_mul!,internal_sub!,internal_zero!,recursive_add!,recursive_adjoint,recursive_axpy!,recursive_copy,recursive_copyto!,recursive_neg!,recursive_scalar_mul!,recursive_sub!,recursive_zero!Justification: every one of these is a
DiffEqCallbacksinternal generic that the extension imports from its own parent package in order to addFunctors-traversable methods to it — that is the entire purpose of the extension. There is no public spelling, and promoting 22 internal recursive-container helpers to public API (which under the SciML rules would also require docstrings and rendered docs entries, plus a SemVer commitment) would be the wrong fix. Qualifying instead of importing (DiffEqCallbacks.recursive_copyto!(y, x) = ...) just moves the same finding toall_qualified_accesses_are_public.ExplicitImports'
allow_internal_imports = trueis meant to exempt exactly this kind of same-package import, but it testsBase.moduleroot(mod) == Base.moduleroot(importing_from), and an extension module is its ownmodulerootrather than the parent package's — so parent-package imports from an extension are not recognised as internal. Arguably worth an upstream issue on ExplicitImports.jl; ignoring here in the meantime.No check was disabled, no
@test_broken/@test_skipwas added.Extension source changes
None. The findings were all the unavoidable parent-package-internals class above; there were no
no_implicit_importsor*_via_ownersfindings to fix.Local result
Julia 1.12. The
QA/qa.jlcount went20 passed + 1 errored(weakdep added, before the ignore block) →21(ignores added) →22(load guard added), the last increment being the newExtensions loadedassertion.🤖 Generated with Claude Code
https://claude.ai/code/session_01Yb5kCpT5SRzTrhppKSh1n7