1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! Shared lexer→Log error-reporting cluster.
//!
//! js_parser, json, and toml lexers each carried a near-identical 50-line block
//! of `{syntax_error, add_error, add_range_error, add_default_error,
//! add_syntax_error}` that gate on `is_log_disabled`, dedup against
//! `prev_error_loc`, push into `Log`, then record the loc. Zig has two copies
//! (the comptime-generic `NewLexer` covers JS+JSON, toml is hand-duplicated);
//! Rust grew a third when json was split out. This trait collapses all three.
//!
//! The trait carries a `'s` lifetime so `source()` can hand back the lexer's
//! stored `&'s Source` *without* borrowing `self` — that is what lets the
//! provided bodies call `self.log_mut()` afterwards without a split-borrow
//! conflict.
use core::fmt;
use crate::{AddErrorOptions, Loc, Log, Range, Source, usize2loc};
pub trait LexerLog<'s> {
/// Per-lexer error variant returned from the `*_error` family
/// (`Error::SyntaxError` for js/toml, `bun_core::err!("SyntaxError")` for
/// the JSON-subset lexer).
type Err;
// ── required state accessors ────────────────────────────────────────
fn log_mut(&mut self) -> &mut Log;
/// NB: returns the lexer-stored `&'s Source`, *not* a `&self`-tied borrow.
fn source(&self) -> &'s Source;
fn prev_error_loc_mut(&mut self) -> &mut Loc;
fn start(&self) -> usize;
fn syntax_err() -> Self::Err;
/// js/json gate every push on this; toml has no flag (default `false`).
#[inline]
fn is_log_disabled(&self) -> bool {
false
}
/// toml threads `should_redact_logs` into every message; js/json don't.
#[inline]
fn should_redact(&self) -> bool {
false
}
// ── provided cluster ────────────────────────────────────────────────
#[cold]
fn add_error(&mut self, loc: usize, args: fmt::Arguments<'_>) {
if self.is_log_disabled() {
return;
}
let l = usize2loc(loc);
if l.eql(*self.prev_error_loc_mut()) {
return;
}
let source = self.source();
let redact = self.should_redact();
self.log_mut().add_error_fmt_opts(
args,
AddErrorOptions {
source: Some(source),
loc: l,
redact_sensitive_information: redact,
..Default::default()
},
);
*self.prev_error_loc_mut() = l;
}
#[cold]
fn add_range_error(&mut self, r: Range, args: fmt::Arguments<'_>) -> Result<(), Self::Err> {
if self.is_log_disabled() {
return Ok(());
}
if r.loc.eql(*self.prev_error_loc_mut()) {
return Ok(());
}
let source = self.source();
let redact = self.should_redact();
self.log_mut().add_error_fmt_opts(
args,
AddErrorOptions {
source: Some(source),
loc: r.loc,
len: r.len,
redact_sensitive_information: redact,
..Default::default()
},
);
*self.prev_error_loc_mut() = r.loc;
Ok(())
}
#[cold]
fn syntax_error(&mut self) -> Result<(), Self::Err> {
// Only add this if there is not already an error — a more descriptive
// one may already have been emitted.
if !self.log_mut().has_errors() {
self.add_error(self.start(), format_args!("Syntax Error"));
}
Err(Self::syntax_err())
}
#[cold]
fn add_default_error(&mut self, msg: &[u8]) -> Result<(), Self::Err> {
self.add_error(self.start(), format_args!("{}", bstr::BStr::new(msg)));
Err(Self::syntax_err())
}
#[cold]
fn add_syntax_error(&mut self, loc: usize, args: fmt::Arguments<'_>) -> Result<(), Self::Err> {
self.add_error(loc, args);
Err(Self::syntax_err())
}
}