Skip to content

Commit

Permalink
Help span argument of quote_spanned get inferred to Span
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed Mar 13, 2023
1 parent 91dfd00 commit 3eb1954
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 53 deletions.
10 changes: 5 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -619,28 +619,28 @@ macro_rules! quote_spanned {
#[macro_export]
macro_rules! quote_spanned {
($span:expr=>) => {{
let _ = $crate::__private::IntoSpan::into_span($span);
let _ = $crate::__private::GetSpan($span).__into_span();
$crate::__private::TokenStream::new()
}};

// Special case rule for a single tt, for performance.
($span:expr=> $tt:tt) => {{
let mut _s = $crate::__private::TokenStream::new();
let _span = $crate::__private::IntoSpan::into_span($span);
let _span = $crate::__private::GetSpan($span).__into_span();
$crate::quote_token_spanned!{$tt _s _span}
_s
}};

// Special case rules for two tts, for performance.
($span:expr=> # $var:ident) => {{
let mut _s = $crate::__private::TokenStream::new();
let _ = $crate::__private::IntoSpan::into_span($span);
let _ = $crate::__private::GetSpan($span).__into_span();
$crate::ToTokens::to_tokens(&$var, &mut _s);
_s
}};
($span:expr=> $tt1:tt $tt2:tt) => {{
let mut _s = $crate::__private::TokenStream::new();
let _span = $crate::__private::IntoSpan::into_span($span);
let _span = $crate::__private::GetSpan($span).__into_span();
$crate::quote_token_spanned!{$tt1 _s _span}
$crate::quote_token_spanned!{$tt2 _s _span}
_s
Expand All @@ -649,7 +649,7 @@ macro_rules! quote_spanned {
// Rule for any other number of tokens.
($span:expr=> $($tt:tt)*) => {{
let mut _s = $crate::__private::TokenStream::new();
let _span = $crate::__private::IntoSpan::into_span($span);
let _span = $crate::__private::GetSpan($span).__into_span();
$crate::quote_each_token_spanned!{_s _span $($tt)*}
_s
}};
Expand Down
31 changes: 22 additions & 9 deletions src/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{IdentFragment, ToTokens, TokenStreamExt};
use core::fmt;
use core::iter;
use core::ops::BitOr;
use core::ops::{BitOr, Deref};
use proc_macro2::extra::DelimSpan;
use proc_macro2::{Group, Ident, Punct, Spacing, TokenTree};

Expand Down Expand Up @@ -166,21 +166,34 @@ impl<T: ToTokens> ToTokens for RepInterp<T> {
}
}

pub trait IntoSpan {
fn into_span(self) -> Span;
#[repr(transparent)]
pub struct GetSpan<T>(pub T);

#[repr(transparent)]
pub struct WrapperDelimSpan {
span: DelimSpan,
}

impl GetSpan<Span> {
#[inline]
pub fn __into_span(self) -> Span {
self.0
}
}

impl IntoSpan for Span {
impl WrapperDelimSpan {
#[inline]
fn into_span(self) -> Span {
self
pub fn __into_span(&self) -> Span {
self.span.join()
}
}

impl IntoSpan for DelimSpan {
impl Deref for GetSpan<DelimSpan> {
type Target = WrapperDelimSpan;

#[inline]
fn into_span(self) -> Span {
self.join()
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const GetSpan<DelimSpan> as *const WrapperDelimSpan) }
}
}

Expand Down
31 changes: 30 additions & 1 deletion tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
clippy::used_underscore_binding
)]

extern crate proc_macro;

use std::borrow::Cow;
use std::collections::BTreeSet;

use proc_macro2::{Ident, Span, TokenStream};
use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, TokenStreamExt};

struct X;
Expand Down Expand Up @@ -518,3 +520,30 @@ fn test_quote_raw_id() {
let id = quote!(r#raw_id);
assert_eq!(id.to_string(), "r#raw_id");
}

#[test]
fn test_type_inference_for_span() {
trait CallSite {
fn get() -> Self;
}

impl CallSite for Span {
fn get() -> Self {
Span::call_site()
}
}

let span = Span::call_site();
let _ = quote_spanned!(span=> ...);

let delim_span = Group::new(Delimiter::Parenthesis, TokenStream::new()).delim_span();
let _ = quote_spanned!(delim_span=> ...);

let inferred = CallSite::get();
let _ = quote_spanned!(inferred=> ...);

if false {
let proc_macro_span = proc_macro::Span::call_site();
let _ = quote_spanned!(proc_macro_span.into()=> ...);
}
}
16 changes: 0 additions & 16 deletions tests/ui/ambiguous-span.rs

This file was deleted.

13 changes: 0 additions & 13 deletions tests/ui/ambiguous-span.stderr

This file was deleted.

15 changes: 6 additions & 9 deletions tests/ui/wrong-type-span.stderr
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
error[E0277]: the trait bound `&str: IntoSpan` is not satisfied
--> tests/ui/wrong-type-span.rs:6:20
error[E0599]: no method named `__into_span` found for struct `GetSpan<&str>` in the current scope
--> tests/ui/wrong-type-span.rs:6:5
|
6 | quote_spanned!(span=> #x);
| ---------------^^^^------
| | |
| | the trait `IntoSpan` is not implemented for `&str`
| required by a bound introduced by this call
| ^^^^^^^^^^^^^^^^^^^^^^^^^ method not found in `GetSpan<&str>`
|
= help: the following other types implement trait `IntoSpan`:
Span
proc_macro2::extra::DelimSpan
= note: the method was found for
- `GetSpan<Span>`
= note: this error originates in the macro `quote_spanned` (in Nightly builds, run with -Z macro-backtrace for more info)

0 comments on commit 3eb1954

Please sign in to comment.