Skip to content

Commit

Permalink
Merge pull request #246 from dtolnay/spaninfer
Browse files Browse the repository at this point in the history
Help span argument of quote_spanned get inferred to Span
  • Loading branch information
dtolnay authored Mar 13, 2023
2 parents 91dfd00 + f3f7140 commit 51bb1e7
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 56 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::get_span($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::get_span($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::get_span($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::get_span($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::get_span($span).__into_span();
$crate::quote_each_token_spanned!{_s _span $($tt)*}
_s
}};
Expand Down
44 changes: 32 additions & 12 deletions src/runtime.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use self::get_span::{GetSpanInner, GetSpan};
use crate::{IdentFragment, ToTokens, TokenStreamExt};
use core::fmt;
use core::iter;
use core::ops::BitOr;
use proc_macro2::extra::DelimSpan;
use proc_macro2::{Group, Ident, Punct, Spacing, TokenTree};

pub use core::option::Option;
Expand Down Expand Up @@ -166,21 +166,41 @@ impl<T: ToTokens> ToTokens for RepInterp<T> {
}
}

pub trait IntoSpan {
fn into_span(self) -> Span;
#[inline]
pub fn get_span<T>(span: T) -> GetSpan<T> {
GetSpan(GetSpanInner(span))
}

impl IntoSpan for Span {
#[inline]
fn into_span(self) -> Span {
self
mod get_span {
use core::ops::Deref;
use proc_macro2::extra::DelimSpan;
use proc_macro2::Span;

pub struct GetSpan<T>(pub(crate) GetSpanInner<T>);

pub struct GetSpanInner<T>(pub(crate) T);

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

impl IntoSpan for DelimSpan {
#[inline]
fn into_span(self) -> Span {
self.join()
impl GetSpanInner<DelimSpan> {
#[inline]
pub fn __into_span(&self) -> Span {
self.0.join()
}
}

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

#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
}

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 `__private::get_span::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
- `__private::get_span::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 51bb1e7

Please sign in to comment.