Repository navigation
JS --related keeps a dynamic import() edge when another test imports the symbol statically - #316
Open
calvin-archastro wants to merge 1 commit into
Conversation
Contributor
Author
|
ArchDev is watching this pull request. State: watching (cycle 2 of 2 for head c3c5703).
Paused by the |
… imports the symbol statically
calvin-archastro
force-pushed
the
factory/tsk_01QxoVonabcFdNiQFUjDE6-c8e079ef-60af-4f85-8acf-
branch
from
November 1, 2026 16:50
9ce9c8b to
c3c5703
Compare
This branch has not been deployed
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.
Review on ArchCode
Epic: Aster bug bash fixes
Task: JS --related keeps a dynamic import() edge when another test imports the symbol statically
Problem and author intent
--relateddropped a JavaScript/TypeScript test that reaches a changed export only throughimport()(orrequire()) whenever another file already named that export statically.Reported layout (aster 0.30.1, ArchAstro/aster#239):
src/str.jsexportsup;src/dyn.jsdefineslazy = () => import('./str.js');test/dyn.test.jscalls(await lazy()).up(...);test/static.test.jsimportsupstatically. EditingtoUpperCase()totoLowerCase()planned onlytest/static.test.js. Deleting the static importer madetest/dyn.test.jsappear (lazy <- up). Both tests should be selected, and removing the static importer should still select only the dynamic test. A module nobody loads dynamically should keep name-based narrowing.Before this change, the JS walker records an
import()/require()specifier on the enclosing definition’sloadedset. In the definition loop, unit users are marked only for an unnamed definition (!referenced && !is_test), plus callbacks/macros. A static importer setsreferenced, soRun::usersnever runs. The dynamic loader uses the file but does not name the symbol, and the dynamic test names the symbol but does not use the source file, soRun::symbolskips it too.Intent: mark definitions that load the changed file through
import()/require()even when something already names the symbol, without widening selection for ordinary static imports.What changed
Indexgainsdynamic_users: Vec<Vec<DefId>>, one list per unit. While the link-timeresolveclosure is in scope, each definition with a non-emptyloadedset has those specifiers resolved and re-export-expanded; the file’s own unit is removed; each remaining unit records that definition as a dynamic user. Comments state that static imports are not this index: they name what they use, and file-toploadedalso holds static import specifiers and top-level mocks, which must not be treated as dynamic loaders or every importer would be selected.On each changed definition,
Run::dynamic_usersmarks those loaders withWhy::Viaeven if another file already named the symbol. The loader is skipped when it is the definition itself, and a test definition still does not mark a non-test loader.open_loadsand the unnamedusersshortcut are unchanged (this diff only adds lines).tests/related.rsaddsjs_dynamic_import_edge_survives_a_static_importer, which builds the issue workspace for.jsand.ts, with and without the static importer, plus an unrelatedplainmodule used only by staticshout/whispertests.Scope
src/related/engine.rstests/related.rsDiff stats match the projected revision: 7 hunks, +163/−0, no omitted hunks or paths. Six engine hunks and one test hunk account for the line count. No other paths are in the revision.
Out of scope, and not present in the diff:
open_loadsfull-run behavior, and Python/Go import handling.Risk assessment
Medium. The mark site is on the shared related-engine path, but the new edges come only from existing
loadedfacts that the JS walker fills for dynamicimport()/require(). Selection can widen for JS/TS projects that dynamically load a changed module; it should not drop tests that were already selected. A mistaken index of file-top static imports or top-level mocks would select every importer and defeat name-based narrowing. The new test is written to catch that for one static-only module, but the indexer loop shown does not itself skip a file-top definition.User impact
Affected-test plans with
--relatedshould include tests that reach a changed member through a dynamic loader, including when a static importer already names that member. Plan notes andgraph --sourceare expected to cite the loader (for examplelazyandsrc/dyn.*). Workspaces with only the dynamic importer should still select that test. Modules with no dynamic loader should keep per-name selection (editingshoutshould not pull inwhisper).Testing
The regression is
tests/related.rs::js_dynamic_import_edge_survives_a_static_importer. Forjsandtsit editssrc/str.*, planstest, and asserts the npm test command contains bothtest/dyn.test.*andtest/static.test.*, excludesshout.testandwhisper.test, and that notes and a successfulgraph --source --commit HEADmentionlazyand the dynamic test. A second workspace without the static importer expects onlynpm test -- test/dyn.test.{ext} --coverage.enabled=false. A further edit ofsrc/plain.jsexpects onlytest/shout.test.js.That function is evidence of intended coverage, not proof it passed. Patch excerpts do not include test results.
Upstream checks all report
succeededwith exit code 0:format,lint,test,fix,reformat,verify-lint,verify-test, andcommit(“No remediation files to commit”; fix reports no files edited). Those outcomes are advisory. The payload is truncated: check excerpts are cut off and do not namejs_dynamic_import_edge_survives_a_static_importer,tests/related.rs, orcargo test --test related. Visible test lines are from other suites. Do not treat this as proof the canonical test ran in CI.Author-reported verification (not shown in the patch or in untruncated CI logs):
cargo test --test related js_dynamic_import_edge_survives_a_static_importer;cargo test --test related(64 passed);cargo test --lib related::(57 passed); clippy with-D warnings;cargo fmt --all --check. The upstream lint/test commands cited in the fix note are workspace-wide locked runs, but their excerpts do not confirm those related filters.Follow-ups and known issues
import()only, notrequire(), even though the index comments treat both as dynamic loads.loaded(static imports and top-level mocks) is not indexed. The shown loop records everyfile.defsentry with non-emptyloadedand has no explicit file-top guard. Confirm file-top is not infile.defs, or add a guard, before relying on name-based narrowing.cargo test --test related js_dynamic_import_edge_survives_a_static_importeris the direct check.open_loadsand non-JS import handling were left unchanged on purpose.