From f005e9fe96a938947e8f8e3c85268a2b2ed686c1 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Thu, 14 Oct 2021 13:28:25 -0500 Subject: [PATCH] Guess semicolon span for macro statements --- compiler/rustc_span/src/source_map.rs | 38 +++++++++++++++++++ .../rustc_typeck/src/check/fn_ctxt/_impl.rs | 9 ++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index b79f00a8a36..74958c49849 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -653,6 +653,18 @@ impl SourceMap { }) } + /// Extends the given `Span` while the next character matches the predicate + pub fn span_extend_while( + &self, + span: Span, + f: impl Fn(char) -> bool, + ) -> Result { + self.span_to_source(span, |s, _start, end| { + let n = s[end..].char_indices().find(|&(_, c)| !f(c)).map_or(s.len() - end, |(i, _)| i); + Ok(span.with_hi(span.hi() + BytePos(n as u32))) + }) + } + /// Extends the given `Span` to just after the next occurrence of `c`. pub fn span_extend_to_next_char(&self, sp: Span, c: char, accept_newlines: bool) -> Span { if let Ok(next_source) = self.span_to_next_source(sp) { @@ -1013,6 +1025,32 @@ impl SourceMap { let source_file = &self.files()[source_file_index]; source_file.is_imported() } + + /// Gets the span of a statement. If the statement is a macro expansion, the + /// span in the context of the block span is found. The trailing semicolon is included + /// on a best-effort basis. + pub fn stmt_span(&self, stmt_span: Span, block_span: Span) -> Span { + if !stmt_span.from_expansion() { + return stmt_span; + } + let mac_call = original_sp(stmt_span, block_span); + self.mac_call_stmt_semi_span(mac_call).map_or(mac_call, |s| mac_call.with_hi(s.hi())) + } + + /// Tries to find the span of the semicolon of a macro call statement. + /// The input must be the *call site* span of a statement from macro expansion. + /// + /// v output + /// mac!(); + /// ^^^^^^ input + pub fn mac_call_stmt_semi_span(&self, mac_call: Span) -> Option { + let span = self.span_extend_while(mac_call, char::is_whitespace).ok()?; + let span = span.shrink_to_hi().with_hi(BytePos(span.hi().0.checked_add(1)?)); + if self.span_to_snippet(span).as_deref() != Ok(";") { + return None; + } + Some(span) + } } #[derive(Clone)] diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs index 7b9629e534b..ac4bb652244 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs @@ -1171,8 +1171,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { return None; } - let original_span = original_sp(last_stmt.span, blk.span); - Some((original_span.with_lo(original_span.hi() - BytePos(1)), needs_box)) + let span = if last_stmt.span.from_expansion() { + let mac_call = original_sp(last_stmt.span, blk.span); + self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? + } else { + last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) + }; + Some((span, needs_box)) } // Instantiates the given path, which must refer to an item with the given