Factor out common logic from Spanned implementations
To compile the common code just once, rather than per distinct type T.
This also fixes a bug in the cfg(procmacro2_semver_exempt) case that
made Spanned::span always return call_site.
diff --git a/src/spanned.rs b/src/spanned.rs
index 767a6a7..a6f9aea 100644
--- a/src/spanned.rs
+++ b/src/spanned.rs
@@ -110,47 +110,51 @@
impl<T: ToTokens> Sealed for T {}
}
-// FIXME: This shouldn't be required, since optimally
-// spans should never be invalid. This can probably be removed when
-// https://github.com/rust-lang/rust/issues/43081 is resolved.
-fn check_invalid_span(span: Span) -> Option<Span> {
- let debug = format!("{:?}", span);
- if debug.ends_with("bytes(0..0)") {
- None
- } else {
- Some(span)
- }
-}
-
impl<T> Spanned for T
where
T: ToTokens,
{
- #[cfg(procmacro2_semver_exempt)]
fn span(&self) -> Span {
- let mut tokens = TokenStream::new();
- self.to_tokens(&mut tokens);
- tokens
- .into_iter()
- .fold(None::<Span>, |span, tt| {
- check_invalid_span(tt.span()).map_or(span, |new_span| {
- span.map(|span| span.join(new_span).unwrap_or(span))
- })
- })
- .unwrap_or(Span::call_site())
+ join_spans(self.into_token_stream())
+ }
+}
+
+fn join_spans(tokens: TokenStream) -> Span {
+ let mut iter = tokens
+ .into_iter()
+ .filter_map(|tt| {
+ // FIXME: This shouldn't be required, since optimally spans should
+ // never be invalid. This filter_map can probably be removed when
+ // https://github.com/rust-lang/rust/issues/43081 is resolved.
+ let span = tt.span();
+ let debug = format!("{:?}", span);
+ if debug.ends_with("bytes(0..0)") {
+ None
+ } else {
+ Some(span)
+ }
+ });
+
+ let mut joined = match iter.next() {
+ Some(span) => span,
+ None => return Span::call_site(),
+ };
+
+ #[cfg(procmacro2_semver_exempt)]
+ {
+ for next in iter {
+ if let Some(span) = joined.join(next) {
+ joined = span;
+ }
+ }
}
#[cfg(not(procmacro2_semver_exempt))]
- fn span(&self) -> Span {
- let mut tokens = TokenStream::new();
- self.to_tokens(&mut tokens);
-
+ {
// We can't join spans without procmacro2_semver_exempt so just grab the
// first one.
- tokens
- .into_iter()
- .next()
- .and_then(|tt| check_invalid_span(tt.span()))
- .unwrap_or(Span::call_site())
+ joined = joined;
}
+
+ joined
}