mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
fix(codegen): map sourcemaps from visible output starts (#21662)
## Summary move statement sourcemap anchors to the first emitted token after generated indentation ## Testing - cargo test -p oxc_codegen indented_statement_mappings_start_after_generated_indent -- --nocapture - cargo test -p oxc_codegen class_member_mappings_start_before_member_keys -- --nocapture - cargo test -p oxc_transformer --test integrations -- --nocapture ## AI Usage - Codex was used to help prepare this change.
This commit is contained in:
1 parent
0dce3c67dd
commit
503dc86d8b
2 files changed
+123
-17
No files matched your search
@@ -200,8 +200,8 @@ impl Gen for ExpressionStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, _ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
if !p.options.minify && (p.indent > 0 || p.print_next_indent_as_space) {
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.add_source_mapping(self.span);
|
||||
}
|
||||
p.start_of_stmt = p.code_len();
|
||||
p.print_expression(&self.expression);
|
||||
@@ -212,7 +212,6 @@ impl Gen for ExpressionStatement<'_> {
|
||||
impl Gen for IfStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
print_if(self, p, ctx);
|
||||
}
|
||||
@@ -220,6 +219,7 @@ impl Gen for IfStatement<'_> {
|
||||
|
||||
fn print_if(if_stmt: &IfStatement<'_>, p: &mut Codegen, ctx: Context) {
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(if_stmt.span);
|
||||
p.print_str("if");
|
||||
p.print_soft_space();
|
||||
p.print_ascii_byte(b'(');
|
||||
@@ -308,9 +308,9 @@ impl Gen for BlockStatement<'_> {
|
||||
impl Gen for ForStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("for");
|
||||
p.print_soft_space();
|
||||
p.print_ascii_byte(b'(');
|
||||
@@ -341,9 +341,9 @@ impl Gen for ForStatement<'_> {
|
||||
impl Gen for ForInStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("for");
|
||||
p.print_soft_space();
|
||||
p.print_ascii_byte(b'(');
|
||||
@@ -361,9 +361,9 @@ impl Gen for ForInStatement<'_> {
|
||||
impl Gen for ForOfStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("for");
|
||||
if self.r#await {
|
||||
p.print_str(" await");
|
||||
@@ -408,9 +408,9 @@ impl Gen for ForStatementLeft<'_> {
|
||||
impl Gen for WhileStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("while");
|
||||
p.print_soft_space();
|
||||
p.print_ascii_byte(b'(');
|
||||
@@ -423,9 +423,9 @@ impl Gen for WhileStatement<'_> {
|
||||
impl Gen for DoWhileStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("do");
|
||||
match &self.body {
|
||||
Statement::BlockStatement(block) => {
|
||||
@@ -455,8 +455,8 @@ impl Gen for DoWhileStatement<'_> {
|
||||
impl Gen for EmptyStatement {
|
||||
fn r#gen(&self, p: &mut Codegen, _ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_semicolon();
|
||||
p.print_soft_newline();
|
||||
}
|
||||
@@ -465,9 +465,9 @@ impl Gen for EmptyStatement {
|
||||
impl Gen for ContinueStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("continue");
|
||||
if let Some(label) = &self.label {
|
||||
p.print_soft_space();
|
||||
@@ -480,9 +480,9 @@ impl Gen for ContinueStatement<'_> {
|
||||
impl Gen for BreakStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("break");
|
||||
if let Some(label) = &self.label {
|
||||
p.print_soft_space();
|
||||
@@ -495,9 +495,9 @@ impl Gen for BreakStatement<'_> {
|
||||
impl Gen for SwitchStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("switch");
|
||||
p.print_soft_space();
|
||||
p.print_ascii_byte(b'(');
|
||||
@@ -548,9 +548,9 @@ impl Gen for SwitchCase<'_> {
|
||||
impl Gen for ReturnStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, _ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("return");
|
||||
if let Some(arg) = &self.argument {
|
||||
p.print_soft_space();
|
||||
@@ -564,10 +564,10 @@ impl Gen for LabeledStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
if !p.options.minify && (p.indent > 0 || p.print_next_indent_as_space) {
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
}
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
self.label.print(p, ctx);
|
||||
p.print_colon();
|
||||
p.print_body(&self.body, false, ctx);
|
||||
@@ -577,9 +577,9 @@ impl Gen for LabeledStatement<'_> {
|
||||
impl Gen for TryStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("try");
|
||||
p.print_soft_space();
|
||||
p.print_block_statement(&self.block, ctx);
|
||||
@@ -626,9 +626,9 @@ impl Gen for CatchClause<'_> {
|
||||
impl Gen for ThrowStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, _ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("throw");
|
||||
p.print_soft_space();
|
||||
p.print_expression(&self.argument);
|
||||
@@ -639,9 +639,9 @@ impl Gen for ThrowStatement<'_> {
|
||||
impl Gen for WithStatement<'_> {
|
||||
fn r#gen(&self, p: &mut Codegen, ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("with");
|
||||
p.print_ascii_byte(b'(');
|
||||
p.print_expression(&self.object);
|
||||
@@ -653,9 +653,9 @@ impl Gen for WithStatement<'_> {
|
||||
impl Gen for DebuggerStatement {
|
||||
fn r#gen(&self, p: &mut Codegen, _ctx: Context) {
|
||||
p.print_comments_at(self.span.start);
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_indent();
|
||||
p.print_space_before_identifier();
|
||||
p.add_source_mapping(self.span);
|
||||
p.print_str("debugger");
|
||||
p.print_semicolon_after_statement();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,81 @@ use oxc_span::{SourceType, Span};
|
||||
|
||||
use crate::tester::default_options;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Position {
|
||||
line: u32,
|
||||
col: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct Mapping {
|
||||
dst: Position,
|
||||
src: Position,
|
||||
}
|
||||
|
||||
fn pos(line: u32, col: u32) -> Position {
|
||||
Position { line, col }
|
||||
}
|
||||
|
||||
fn sourcemap_tokens(source_text: &str, source_type: SourceType) -> Vec<Mapping> {
|
||||
let allocator = Allocator::default();
|
||||
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
||||
assert!(ret.errors.is_empty(), "parse errors: {:?}", ret.errors);
|
||||
|
||||
Codegen::new()
|
||||
.with_options(default_options())
|
||||
.build(&ret.program)
|
||||
.map
|
||||
.expect("sourcemap should be generated")
|
||||
.get_tokens()
|
||||
.map(|token| Mapping {
|
||||
dst: pos(token.get_dst_line(), token.get_dst_col()),
|
||||
src: pos(token.get_src_line(), token.get_src_col()),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn has_mapping(tokens: &[Mapping], src: Position, dst: Position) -> bool {
|
||||
tokens.iter().any(|token| {
|
||||
token.src.line == src.line
|
||||
&& token.src.col == src.col
|
||||
&& token.dst.line == dst.line
|
||||
&& token.dst.col == dst.col
|
||||
})
|
||||
}
|
||||
|
||||
fn first_generated_position_for_source(tokens: &[Mapping], src: Position) -> Option<Position> {
|
||||
tokens
|
||||
.iter()
|
||||
.filter(|token| token.src.line == src.line && token.src.col == src.col)
|
||||
.map(|token| token.dst)
|
||||
.min_by_key(|position| (position.line, position.col))
|
||||
}
|
||||
|
||||
fn assert_source_maps_after_indent(
|
||||
tokens: &[Mapping],
|
||||
src: Position,
|
||||
wrong_dst: Position,
|
||||
correct_dst: Position,
|
||||
) {
|
||||
assert!(!has_mapping(tokens, src, wrong_dst));
|
||||
assert!(has_mapping(tokens, src, correct_dst));
|
||||
}
|
||||
|
||||
fn assert_member_start_maps_before_key(
|
||||
tokens: &[Mapping],
|
||||
member_start: Position,
|
||||
key_start: Position,
|
||||
) {
|
||||
let member_start = first_generated_position_for_source(tokens, member_start)
|
||||
.expect("member start should be mapped");
|
||||
let key_start = first_generated_position_for_source(tokens, key_start)
|
||||
.expect("member key should be mapped");
|
||||
|
||||
assert_eq!(member_start.line, key_start.line);
|
||||
assert!(member_start.col < key_start.col);
|
||||
}
|
||||
|
||||
/// Upstream may have modified the AST to include incorrect spans.
|
||||
/// e.g. <https://github.com/rolldown/rolldown/blob/v1.0.0-beta.19/crates/rolldown/src/utils/ecma_visitors/mod.rs>
|
||||
#[test]
|
||||
@@ -89,6 +164,37 @@ fn no_invalid_tokens_beyond_source() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn indented_statement_mappings_start_after_generated_indent() {
|
||||
let tokens = sourcemap_tokens(
|
||||
r"if (foo) {
|
||||
bar();
|
||||
}",
|
||||
SourceType::mjs(),
|
||||
);
|
||||
|
||||
assert_source_maps_after_indent(&tokens, pos(1, 2), pos(1, 0), pos(1, 1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn class_member_mappings_start_before_member_keys() {
|
||||
let tokens = sourcemap_tokens(
|
||||
r"class Foo {
|
||||
get value() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static load() {
|
||||
return 1;
|
||||
}
|
||||
}",
|
||||
SourceType::ts(),
|
||||
);
|
||||
|
||||
assert_member_start_maps_before_key(&tokens, pos(1, 2), pos(1, 6));
|
||||
assert_member_start_maps_before_key(&tokens, pos(5, 2), pos(5, 9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(not(target_endian = "big"), target_pointer_width = "64"))] // we run big endian tests on docker that does not have node installed; skip 32-bit as well
|
||||
fn stacktrace_is_correct() {
|
||||
|
||||
Reference in new issue
Block a user