Skip to content

Commit 14c3247

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 14c3247

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: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,23 @@ 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+
match &self.kind {
266+
StmtKind::Local(local) => extend_span_attrs(self.span, &local.attrs),
267+
StmtKind::Item(item) => extend_span_attrs(self.span, &item.attrs),
268+
StmtKind::Expr(expr) | StmtKind::Semi(expr) => extend_span_attrs(self.span, &expr.attrs),
269+
StmtKind::MacCall(mac) => extend_span_attrs(self.span, &mac.attrs),
270+
StmtKind::Empty => self.span,
271+
}
256272
}
257273
}
258274

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

c2rust-refactor/src/transform/vars.rs

Lines changed: 36 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,21 @@ 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+
let stmt_span = l.span;
443+
curs.replace(move |_| mk().span(stmt_span).local_stmt(l));
419444

420445
let here = curs.mark();
421446
curs.seek(local_mark);
@@ -514,6 +539,7 @@ impl Transform for RemoveRedundantLetTypes {
514539
let pat = mcx.parse_stmts("let $pat:Pat : $ty:Ty = $init:Expr;");
515540
let repl = mcx.parse_stmts("let $pat = $init;");
516541
mut_visit_match_with(mcx, pat, krate, |ast, mcx| {
542+
let orig_span = ast.get(0).map(|s| s.span);
517543
let e = mcx.bindings.get::<_, P<Expr>>("$init").unwrap();
518544
let e_ty = cx.adjusted_node_type(e.id);
519545
let e_ty = tcx.normalize_erasing_regions(ParamEnv::empty(), e_ty);
@@ -523,6 +549,14 @@ impl Transform for RemoveRedundantLetTypes {
523549
let t_ty = tcx.normalize_erasing_regions(ParamEnv::empty(), t_ty);
524550
if e_ty == t_ty {
525551
*ast = repl.clone().subst(st, cx, &mcx.bindings);
552+
// Preserve the original statement's span after replacement.
553+
//
554+
// The subst() call creates a new Stmt from the replacement pattern, which has
555+
// a synthetic span from the pattern parse. Restoring the original span allows
556+
// the rewrite system to extract source text via the Splice trait.
557+
if let (Some(span), Some(first)) = (orig_span, ast.get_mut(0)) {
558+
first.span = span;
559+
}
526560
}
527561
})
528562
}

0 commit comments

Comments
 (0)