Skip to content

Commit e5f46c0

Browse files
committed
refactor: fix span handling in sink_lets and remove_redundant_let_types
Transforms that create new statement wrappers need to preserve source spans for the rewrite system to extract text during recovery. Changes: - Set stmt.span explicitly in transforms when wrapping locals - Extend Splice for Stmt to include attributes from inner nodes - Add fallback in rewrite_at_impl for macro-expanded code Fixes sink_lets and remove_redundant_let_types on libxml2.
1 parent f4be910 commit e5f46c0

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

c2rust-refactor/src/rewrite/strategy/print.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,24 @@ impl Splice for Ty {
252252

253253
impl Splice for Stmt {
254254
fn splice_span(&self) -> Span {
255-
self.span
255+
// Extend statement span to include attributes on the inner node.
256+
//
257+
// Transforms like sink_lets set stmt.span = local.span, but attributes are
258+
// stored on the inner Local/Item/Expr, not the Stmt wrapper. The pretty-printer
259+
// outputs attributes, but if stmt.span doesn't cover them, the rewrite system
260+
// can't extract the complete source text.
261+
//
262+
// extend_span_attrs() expands the span backward to include attributes that
263+
// appear before the node in source, checking syntax contexts to avoid crossing
264+
// macro boundaries.
265+
let attrs = match &self.kind {
266+
StmtKind::Local(local) => &local.attrs[..],
267+
StmtKind::Item(item) => &item.attrs[..],
268+
StmtKind::Expr(expr) | StmtKind::Semi(expr) => &expr.attrs[..],
269+
StmtKind::MacCall(mac) => &mac.attrs[..],
270+
StmtKind::Empty => &[],
271+
};
272+
extend_span_attrs(self.span, attrs)
256273
}
257274
}
258275

@@ -674,7 +691,29 @@ fn rewrite_at_impl<T>(old_span: Span, new: &T, mut rcx: RewriteCtxtRef) -> bool
674691
where
675692
T: PrintParse + RecoverChildren + Splice + MaybeGetNodeId,
676693
{
677-
let printed = add_comments(new.to_string(), new, &rcx);
694+
let mut printed = add_comments(new.to_string(), new, &rcx);
695+
696+
// Fallback: extract source snippet when pretty-printing produces empty output.
697+
//
698+
// When transforms move macro-expanded code (e.g., from #[derive]), the span may
699+
// point to generated code with no source file location. Pretty-printing such nodes
700+
// can produce empty output, which causes problems during the reparse/recovery phase.
701+
//
702+
// If the printed text is empty, try extracting the source directly using
703+
// splice_span() (which includes attributes). If source is available, use that
704+
// for reparsing instead of the empty pretty-printed text.
705+
if printed.trim().is_empty() {
706+
if let Ok(snippet) = rcx
707+
.session()
708+
.source_map()
709+
.span_to_snippet(new.splice_span())
710+
{
711+
if !snippet.trim().is_empty() {
712+
printed = snippet;
713+
}
714+
}
715+
}
716+
678717
let reparsed = T::parse(rcx.session(), &printed);
679718
let reparsed = reparsed.ast_deref();
680719

c2rust-refactor/src/transform/vars.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,18 @@ impl Transform for SinkLets {
192192
Some(x) => x; return);
193193

194194
let mut new_stmts = place_here.iter()
195-
.map(|&id| mk().local_stmt(&locals[&id].local))
195+
.map(|&id| {
196+
let local = &locals[&id].local;
197+
// Set the statement's span to match the local's span.
198+
//
199+
// By default, mk().local_stmt() creates a Stmt with DUMMY_SP. The rewrite system
200+
// uses stmt.span (via the Splice trait) to extract source text for recovery during
201+
// the print/reparse cycle. Without a valid span, source extraction fails.
202+
//
203+
// Setting stmt.span = local.span provides a valid source location. The Splice
204+
// implementation in print.rs will extend this to cover any attributes.
205+
mk().span(local.span).local_stmt(local)
206+
})
196207
.collect::<Vec<_>>();
197208
new_stmts.append(&mut b.stmts);
198209
b.stmts = new_stmts;
@@ -415,7 +426,20 @@ impl Transform for FoldLetAssign {
415426
let local_mark = local_pos.remove(&hir_id).unwrap();
416427
let mut l = local.clone();
417428
l.kind = LocalKind::Init(init);
418-
curs.replace(|_| mk().local_stmt(l));
429+
430+
// Preserve the original local's span when creating the combined statement.
431+
//
432+
// fold_let_assign transforms:
433+
// let x; <- original local
434+
// x = expr; <- assignment
435+
// into:
436+
// let x = expr; <- combined local
437+
//
438+
// The combined Local keeps l.span from the original declaration. Setting
439+
// stmt.span = l.span allows the rewrite system to extract source text using
440+
// the Splice trait. Without this, stmt.span would be DUMMY_SP and source
441+
// extraction would fail.
442+
curs.replace(move |_| mk().span(l.span).local_stmt(l));
419443

420444
let here = curs.mark();
421445
curs.seek(local_mark);
@@ -514,6 +538,7 @@ impl Transform for RemoveRedundantLetTypes {
514538
let pat = mcx.parse_stmts("let $pat:Pat : $ty:Ty = $init:Expr;");
515539
let repl = mcx.parse_stmts("let $pat = $init;");
516540
mut_visit_match_with(mcx, pat, krate, |ast, mcx| {
541+
let orig_span = ast.get(0).map(|s| s.span);
517542
let e = mcx.bindings.get::<_, P<Expr>>("$init").unwrap();
518543
let e_ty = cx.adjusted_node_type(e.id);
519544
let e_ty = tcx.normalize_erasing_regions(ParamEnv::empty(), e_ty);
@@ -523,6 +548,14 @@ impl Transform for RemoveRedundantLetTypes {
523548
let t_ty = tcx.normalize_erasing_regions(ParamEnv::empty(), t_ty);
524549
if e_ty == t_ty {
525550
*ast = repl.clone().subst(st, cx, &mcx.bindings);
551+
// Preserve the original statement's span after replacement.
552+
//
553+
// The subst() call creates a new Stmt from the replacement pattern, which has
554+
// a synthetic span from the pattern parse. Restoring the original span allows
555+
// the rewrite system to extract source text via the Splice trait.
556+
if let (Some(span), Some(first)) = (orig_span, ast.get_mut(0)) {
557+
first.span = span;
558+
}
526559
}
527560
})
528561
}

0 commit comments

Comments
 (0)