mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
Assign process.emitWarning
pi's CLI setup replaces process.emitWarning to silence warnings. The assignment now stores the replacement: later process.emitWarning(...) calls apply it to their arguments, and runtime-built warnings (Node's own modules call process.emitWarning) reach it as (message, type[, code]) instead of the report and the 'warning' listeners. process.title writes already lowered; the corpus program pins both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1c1400272f
commit
5e964965f6
@@ -1306,6 +1306,7 @@ export const LIB_FN_SYMS: Record<string, string> = {
|
||||
"process.emitWarningText": "scr_process_emit_warning_text",
|
||||
"process.deprecationFlag": "scr_process_deprecation_flag",
|
||||
"process.setDeprecationFlag": "scr_process_set_deprecation_flag",
|
||||
"process.setEmitWarning": "scr_process_set_emit_warning",
|
||||
"process.onUncaughtException": "scr_process_on_uncaught_exception",
|
||||
"process.offUncaughtException": "scr_process_off_uncaught_exception",
|
||||
"process.onUnhandledRejection": "scr_process_on_unhandled_rejection",
|
||||
|
||||
@@ -6758,6 +6758,32 @@ export function lowerExprStatement(lowerer: Lowerer, expr: ts.Expression): IrStm
|
||||
loc,
|
||||
};
|
||||
}
|
||||
// process.emitWarning = fn: an ordinary writable property. Later
|
||||
// process.emitWarning(...) calls, and the warnings Node's own
|
||||
// modules emit through process.emitWarning, call the replacement
|
||||
// instead (silencing them when it does nothing).
|
||||
if (flag === "emitWarning") {
|
||||
const loc = locOf(expr);
|
||||
let rhs: ts.Expression = expr.right;
|
||||
while (
|
||||
ts.isParenthesizedExpression(rhs) ||
|
||||
ts.isAsExpression(rhs) ||
|
||||
ts.isSatisfiesExpression(rhs)
|
||||
)
|
||||
rhs = rhs.expression;
|
||||
const value = lowerer.lowerExprExpecting(rhs, DYN);
|
||||
return {
|
||||
kind: "exprStmt",
|
||||
expr: {
|
||||
kind: "libCall",
|
||||
fn: "process.setEmitWarning",
|
||||
args: [value],
|
||||
type: VOID,
|
||||
loc,
|
||||
},
|
||||
loc,
|
||||
};
|
||||
}
|
||||
// process.title = name: libuv's uv_set_process_title (the
|
||||
// runtime truncates to the original argument area on POSIX).
|
||||
if (flag === "title") {
|
||||
|
||||
@@ -4112,6 +4112,7 @@ export const LIB_FN_SIGS = defineLibFnSignatures({
|
||||
* warnings and DeprecationWarning emissions read their truthiness. */
|
||||
"process.deprecationFlag": { argTypes: [F64], result: DYN },
|
||||
"process.setDeprecationFlag": { argTypes: [F64, DYN], result: VOID },
|
||||
"process.setEmitWarning": { argTypes: [DYN], result: VOID },
|
||||
/** AsyncLocalStorage (node:async_hooks — scr_async.c): stores are f64
|
||||
* handles; contexts are immutable fiber-carried snapshots (spawned
|
||||
* fibers inherit the spawner's — Node's init-time capture). run/exit
|
||||
|
||||
@@ -67,6 +67,7 @@ const DYN_ASYNC_LIB_FNS: ReadonlySet<IrLibFn> = new Set([
|
||||
"process.emitWarningText",
|
||||
"process.deprecationFlag",
|
||||
"process.setDeprecationFlag",
|
||||
"process.setEmitWarning",
|
||||
// The performance timeline (scr_perf.c, linked with this unit for its
|
||||
// MaxPerformanceEntryBufferExceededWarning).
|
||||
"perf.mark",
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"order": [
|
||||
"<repo>/tests/corpus/6231-process-emit-warning-assign.ts"
|
||||
],
|
||||
"diags": []
|
||||
}
|
||||
@@ -212,6 +212,12 @@ void scr_process_on_warning(ScrDyn *fn);
|
||||
void scr_process_off_warning(ScrDyn *fn);
|
||||
void scr_process_emit_warning(ScrDyn *args);
|
||||
void scr_emit_warning(const char *name, const char *code, ScrStr *message);
|
||||
/* process.emitWarning = fn (borrowed; the runtime keeps its own
|
||||
* reference). Later process.emitWarning(...) calls apply the replacement
|
||||
* to their arguments, and runtime-built warnings (Node's own modules call
|
||||
* process.emitWarning) reach it as (message, type[, code]) instead of the
|
||||
* report and the 'warning' listeners. */
|
||||
void scr_process_set_emit_warning(ScrDyn *fn);
|
||||
/* scr_emit_warning once a 'warning' listener exists (scr_lib.c; set by the
|
||||
* listener registration): units that cannot link the dyn-async unit route
|
||||
* their warnings through it, and print the default report otherwise. */
|
||||
|
||||
@@ -860,10 +860,53 @@ ScrDyn *scr_warning_new(ScrStr *message, const char *type, const ScrStr *code) {
|
||||
return w;
|
||||
}
|
||||
|
||||
/* The program's process.emitWarning replacement, or NULL while the
|
||||
* property holds Node's own function. */
|
||||
static SCR_TL ScrDyn *scr_emit_warning_override = NULL;
|
||||
|
||||
static void scr_emit_warning_override_teardown(void) {
|
||||
scr_dyn_release(scr_emit_warning_override);
|
||||
scr_emit_warning_override = NULL;
|
||||
}
|
||||
|
||||
void scr_process_set_emit_warning(ScrDyn *fn) {
|
||||
if (scr_emit_warning_override == NULL) scr_atexit(scr_emit_warning_override_teardown);
|
||||
ScrDyn *previous = scr_emit_warning_override;
|
||||
scr_emit_warning_override = scr_dyn_retain(fn);
|
||||
scr_dyn_release(previous);
|
||||
/* Units that report their own warnings without a listener route them
|
||||
* here once a replacement exists. */
|
||||
scr_warning_emit_hook = scr_emit_warning;
|
||||
}
|
||||
|
||||
/* A runtime-built warning while a replacement is installed: Node's modules
|
||||
* call process.emitWarning(message, type, code), so the replacement sees
|
||||
* those arguments; nothing is reported. A throw stays pending. */
|
||||
static void scr_emit_warning_replaced(ScrStr *message, ScrStr *type, ScrStr *code) {
|
||||
ScrDyn *args[3];
|
||||
size_t argc = 0;
|
||||
args[argc++] = scr_dyn_new_str(message);
|
||||
ScrStr *fallback = type ? NULL : scr_str_new("Warning", 7);
|
||||
args[argc++] = scr_dyn_new_str(type ? type : fallback);
|
||||
scr_str_release(fallback);
|
||||
if (code) args[argc++] = scr_dyn_new_str(code);
|
||||
ScrDyn *r = scr_dyn_call(scr_emit_warning_override, args, argc, "process.emitWarning");
|
||||
scr_dyn_release(r);
|
||||
for (size_t i = 0; i < argc; i++) scr_dyn_release(args[i]);
|
||||
}
|
||||
|
||||
/* A warning built from C parts (the runtime deprecation sites): name
|
||||
* defaults to "Warning"; code/detail optional. Reported synchronously.
|
||||
* Borrows message. */
|
||||
void scr_emit_warning(const char *name, const char *code, ScrStr *message) {
|
||||
if (scr_emit_warning_override != NULL) {
|
||||
ScrStr *type_text = scr_str_new(name ? name : "Warning", strlen(name ? name : "Warning"));
|
||||
ScrStr *code_text = code ? scr_str_new(code, strlen(code)) : NULL;
|
||||
scr_emit_warning_replaced(message, type_text, code_text);
|
||||
scr_str_release(type_text);
|
||||
scr_str_release(code_text);
|
||||
return;
|
||||
}
|
||||
ScrStr *code_text = code ? scr_str_new(code, strlen(code)) : NULL;
|
||||
ScrDyn *w = scr_warning_new(message, name ? name : "Warning", code_text);
|
||||
scr_str_release(code_text);
|
||||
@@ -894,6 +937,16 @@ static void scr_warning_defer(ScrDyn *w, void (*fire)(ScrClosure *)) {
|
||||
}
|
||||
|
||||
void scr_warning_emit_deferred(ScrDyn *w) {
|
||||
if (scr_emit_warning_override != NULL) {
|
||||
const ScrDyn *em = scr_dyn_obj_get(w, "message", 7);
|
||||
const ScrDyn *ec = scr_dyn_obj_get(w, "code", 4);
|
||||
ScrStr *name = scr_warning_name(w);
|
||||
ScrStr *message = em && em->kind == SCR_DYN_STR ? scr_str_retain(em->v.str) : scr_str_new("", 0);
|
||||
scr_emit_warning_replaced(message, name, ec && ec->kind == SCR_DYN_STR ? ec->v.str : NULL);
|
||||
scr_str_release(message);
|
||||
scr_str_release(name);
|
||||
return;
|
||||
}
|
||||
if (scr_warning_is_deprecation(w)) {
|
||||
if (scr_process_deprecation_flag_on(0)) return;
|
||||
if (scr_process_deprecation_flag_on(1)) {
|
||||
@@ -959,6 +1012,11 @@ static void scr_warn_bad_arg(const char *arg, const char *want) {
|
||||
* ERR_INVALID_ARG_TYPE TypeErrors at the call; non-string details are
|
||||
* ignored. The warning itself is emitted on the next tick. */
|
||||
void scr_process_emit_warning(ScrDyn *args) {
|
||||
if (scr_emit_warning_override != NULL) {
|
||||
ScrDyn *r = scr_dyn_apply(scr_emit_warning_override, args, "process.emitWarning");
|
||||
scr_dyn_release(r);
|
||||
return;
|
||||
}
|
||||
size_t argc = args->kind == SCR_DYN_ARR ? args->v.arr.len : 0;
|
||||
ScrDyn *const *items = args->kind == SCR_DYN_ARR ? args->v.arr.items : NULL;
|
||||
ScrDyn *warning = argc >= 1 ? items[0] : NULL;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
// process.emitWarning and process.title are ordinary writable properties
|
||||
// (pi's CLI setup assigns both). After the replacement, process.emitWarning
|
||||
// calls reach it with their own arguments, no report reaches stderr, and
|
||||
// the warnings Node's own modules emit through process.emitWarning (a
|
||||
// util.deprecate wrapper's first call) reach it too.
|
||||
import { deprecate } from "node:util";
|
||||
|
||||
process.title = "pi";
|
||||
console.log("title", process.title);
|
||||
process.env.PI_CODING_AGENT = "true";
|
||||
console.log("env", process.env.PI_CODING_AGENT);
|
||||
|
||||
const seen: string[] = [];
|
||||
process.on("warning", (w) => seen.push(`listener ${w.name}`));
|
||||
function show(a: unknown): string {
|
||||
if (a instanceof Error) return `Error(${a.message})`;
|
||||
return typeof a === "object" ? JSON.stringify(a) : String(a);
|
||||
}
|
||||
process.emitWarning = ((warning: unknown, type: unknown, code: unknown) => {
|
||||
seen.push(`${show(warning)}|${show(type)}|${show(code)}`);
|
||||
}) as typeof process.emitWarning;
|
||||
|
||||
process.emitWarning("plain");
|
||||
process.emitWarning("typed", "CustomWarning", "CW_1");
|
||||
process.emitWarning("options", { type: "OptWarning", code: "OW_1", detail: "d" });
|
||||
process.emitWarning(new Error("as error"));
|
||||
const old = deprecate(() => "result", "old() is deprecated", "DEP_SCRIPTC_1");
|
||||
console.log("deprecated call", old());
|
||||
console.log("second call", old());
|
||||
console.log("sync", seen.join(" ; "));
|
||||
|
||||
// A replacement that does nothing silences warnings entirely.
|
||||
process.emitWarning = (() => {}) as typeof process.emitWarning;
|
||||
process.emitWarning("silenced");
|
||||
setTimeout(() => console.log("after tick", seen.length), 0);
|
||||
Reference in New Issue
Block a user