-
-
Notifications
You must be signed in to change notification settings - Fork 159
fix(hir): ambient declare function require must not shadow the require intrinsic
#8452
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| fix(hir): an ambient `declare function require(name: string): any` no longer shadows the require intrinsic (#8447). The body-less declaration was registered as an external FFI function; since #8343's `require_is_shadowed_by_local` that made `require("node:fs")` lower to a call to a `require` symbol no archive defines, failing every consumer at link (`Undefined symbols: "_require"` — compile-smoke's `test_issue_8002_8003_thread_realm_caches`, one of parity's 27 dark-debt compile_fails). A `function require(...)` WITH a body (the CJS wrap's synthetic require, #8343) still shadows. Regression pair in `perry-hir::lower::tests`; the ambient test fails without the fix. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -990,3 +990,55 @@ fn method_local_shadowing_the_class_name_still_wins_in_new() { | |||||||||||||||||
| "a method-scope local named after the class must still win for `new`: {body}" | ||||||||||||||||||
| ); | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// #8447: the ambient-typing idiom `declare function require(name: string): any` | ||||||||||||||||||
| /// names the global require intrinsic — it must NOT be registered as an | ||||||||||||||||||
| /// external FFI function. That registration made every require-shadowing guard | ||||||||||||||||||
| /// (`require_is_shadowed_by_local`, `try_require_literal`) treat the global as | ||||||||||||||||||
| /// shadowed since #8343, so `require("node:fs")` lowered to a call to a | ||||||||||||||||||
| /// `require` symbol no archive defines, and every consumer failed at link | ||||||||||||||||||
| /// (`Undefined symbols: "_require"`). | ||||||||||||||||||
| #[test] | ||||||||||||||||||
| fn test_ambient_require_declare_does_not_shadow_the_intrinsic() { | ||||||||||||||||||
| let source = r#" | ||||||||||||||||||
| declare function require(name: string): any; | ||||||||||||||||||
| function probe(): string { | ||||||||||||||||||
| const fs = require("node:fs"); | ||||||||||||||||||
| return typeof fs.constants.O_RDONLY; | ||||||||||||||||||
| } | ||||||||||||||||||
| console.log(probe()); | ||||||||||||||||||
| "#; | ||||||||||||||||||
| let module = perry_parser::parse_typescript(source, "t.ts").expect("source parses"); | ||||||||||||||||||
| let hir = super::lower_module(&module, "t", "t.ts").expect("source lowers"); | ||||||||||||||||||
| let dump = format!("{hir:?}"); | ||||||||||||||||||
| assert!( | ||||||||||||||||||
| !dump.contains("ExternFuncRef { name: \"require\""), | ||||||||||||||||||
| "an ambient `declare function require` must not lower calls to an \ | ||||||||||||||||||
| extern `require` symbol — nothing defines it, so linking fails: {dump}" | ||||||||||||||||||
| ); | ||||||||||||||||||
| assert!( | ||||||||||||||||||
| dump.contains("\"fs\""), | ||||||||||||||||||
| "the require(\"node:fs\") call must resolve to the fs native module: {dump}" | ||||||||||||||||||
| ); | ||||||||||||||||||
|
Comment on lines
+1019
to
+1022
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Assert the native-module HIR variant. The string Proposed fix- dump.contains("\"fs\""),
+ dump.contains("NativeModuleRef(\"fs\")"),📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| /// The counterpart (#8343's intent, unchanged): a `function require(...)` WITH | ||||||||||||||||||
| /// a body — e.g. the CJS wrap's synthetic require — is a real user binding and | ||||||||||||||||||
| /// must keep shadowing the intrinsic, so the call stays a plain user-function | ||||||||||||||||||
| /// call instead of a native-module namespace binding. | ||||||||||||||||||
| #[test] | ||||||||||||||||||
| fn test_user_require_function_with_body_still_shadows_the_intrinsic() { | ||||||||||||||||||
| let source = r#" | ||||||||||||||||||
| function require(name: string): string { return "shadowed:" + name; } | ||||||||||||||||||
| const fs = require("node:fs"); | ||||||||||||||||||
| console.log(fs); | ||||||||||||||||||
| "#; | ||||||||||||||||||
| let module = perry_parser::parse_typescript(source, "t.ts").expect("source parses"); | ||||||||||||||||||
| let hir = super::lower_module(&module, "t", "t.ts").expect("source lowers"); | ||||||||||||||||||
| let dump = format!("{hir:?}"); | ||||||||||||||||||
| assert!( | ||||||||||||||||||
| !dump.contains("NativeModuleRef(\"fs\")"), | ||||||||||||||||||
| "a user `function require` with a body shadows the intrinsic — the \ | ||||||||||||||||||
| call must not be rewritten into a native-module namespace: {dump}" | ||||||||||||||||||
| ); | ||||||||||||||||||
| } | ||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Keep the release note focused on shipped behavior.
Remove internal guard names, test-suite inventory, and CI-specific failure details. Keep the defect cause, fixed behavior, and regression coverage in one release-note entry.
Proposed fix
Based on learnings: describe the final shipped behavior as one coherent release-note entry, and include root-cause and validation details for a defect fix.
📝 Committable suggestion
🤖 Prompt for AI Agents
Source: Learnings