fix(ts): avoid normalizing runtime dynamic imports - #3219
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.
Formal verification. No changes could be formally verified in this run.
Graphify review — findings
Reworks the TypeScript import(...) type-argument fix (#3154) to run AST-first inside _extract_generic: instead of always regex-rewriting TS source before parsing, it now parses first and only rewrites when tree.root_node.has_error, using _recover_3154_ts_import_types to locate import(...) calls nested in the malformed relational-expression parse and replace them with same-length placeholders before reparsing. Removes the old unconditional _normalize_ts_import_types pass from extract_js and extract_vue, so valid comparison expressions in semicolon-less TS (e.g. a < b followed by a runtime await import(...)) are no longer corrupted (#3210). Also tightens the _rescue_js_dynamic_imports bail-out and match regexes to recognize import followed by whitespace or line-continuations before the paren.
Worth a look
- find_candidates walks parent chain but only handles expression_statement, dropping lexical_declaration cases —
graphify/extractors/engine.py:2900· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- TS import-type recovery skips generic calls outside expression statements —
graphify/extractors/engine.py:2916· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 2014 functions depend on the 448 functions this change touches.
Health — this change adds coupling hotspots:
- new:
extract()— 520 callers, 43 callees - new:
_rebuild_code()— 113 callers, 50 callees - new:
_extract_generic()— 18 callers, 26 callees - new:
extract_xaml()— 19 callers, 17 callees - new:
extract_js()— 85 callers, 3 callees - new:
dispatch_command()— 2 callers, 123 callees - new:
extract_objc()— 27 callers, 9 callees - new:
_get_extractor()— 26 callers, 6 callees - …and 33 more — each is listed as a finding
Verification — 2014 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1849 function(s) in the blast radius were not formally verified this run
Formal verification
Could not verify: Could not verify extract\_js.
The verifier did not have enough to check extract\_js, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify extract\_vue.
The verifier did not have enough to check extract\_vue, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_rescue\_js\_dynamic\_imports.
The verifier did not have enough to check \_rescue\_js\_dynamic\_imports, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
Could not verify: Could not verify \_extract\_generic.
The verifier did not have enough to check \_extract\_generic, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: parameter `path` is annotated `Path` — outside the synthesizable primitive/collection set
· 2 grounded finding(s) anchored inline below; 39 more finding(s) on lines outside this diff (see the check run).
| @@ -1345,21 +1301,13 @@ def extract_python(path: Path) -> dict: | |||
| def extract_js(path: Path) -> dict: | |||
There was a problem hiding this comment.
extract_js()
85 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
| return bytes(rewritten) | ||
|
|
||
|
|
||
| def _extract_generic( |
There was a problem hiding this comment.
_extract_generic()
fans out to 26 callees (efferent coupling); 18 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
What does this PR do?
Fixes a regression introduced by the TypeScript import-type normalization from #3154 that could incorrectly rewrite valid runtime
import()expressions in semicolon-less TypeScript.The previous regex-based normalization ran before parsing and could mistake relational expressions such as:
for a generic call containing an import type. This caused the runtime import to be removed before tree-sitter parsed the file.
This PR replaces the regex-first approach with an AST-first recovery mechanism.
Changes
import(...)type used as an explicit type argument is a syntax error, dropping symbols after it #3154 parser ambiguity.import(...)ranges using byte-length- and newline-preserving placeholders.import(...)type used as an explicit type argument is a syntax error, dropping symbols after it #3154 case is detected.importand(.import(...)type used as an explicit type argument is a syntax error, dropping symbols after it #3154 recovery behavior for generic call type arguments.Regression coverage
Added coverage for:
import(...),import (...), and multiline whitespace variants.typeof import(...)andimport(...).Fooinside generic call type arguments.Verification
git diff --check: cleanThe recovery path only performs a second parse when the initial TypeScript parse has an error and the specific #3154 AST pattern is detected, so clean TypeScript and legitimate runtime dynamic imports retain the normal single-parse path.