syn_pub_items/
spanned.rs

1//! A trait that can provide the `Span` of the complete contents of a syntax
2//! tree node.
3//!
4//! *This module is available if Syn is built with both the `"parsing"` and
5//! `"printing"` features.*
6//!
7//! # Example
8//!
9//! Suppose in a procedural macro we have a [`Type`] that we want to assert
10//! implements the [`Sync`] trait. Maybe this is the type of one of the fields
11//! of a struct for which we are deriving a trait implementation, and we need to
12//! be able to pass a reference to one of those fields across threads.
13//!
14//! [`Type`]: ../enum.Type.html
15//! [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
16//!
17//! If the field type does *not* implement `Sync` as required, we want the
18//! compiler to report an error pointing out exactly which type it was.
19//!
20//! The following macro code takes a variable `ty` of type `Type` and produces a
21//! static assertion that `Sync` is implemented for that type.
22//!
23//! ```edition2018
24//! # extern crate proc_macro;
25//! #
26//! use proc_macro::TokenStream;
27//! use proc_macro2::Span;
28//! use quote::quote_spanned;
29//! use syn::Type;
30//! use syn::spanned::Spanned;
31//!
32//! # const IGNORE_TOKENS: &str = stringify! {
33//! #[proc_macro_derive(MyMacro)]
34//! # };
35//! pub fn my_macro(input: TokenStream) -> TokenStream {
36//!     # let ty = get_a_type();
37//!     /* ... */
38//!
39//!     let assert_sync = quote_spanned! {ty.span()=>
40//!         struct _AssertSync where #ty: Sync;
41//!     };
42//!
43//!     /* ... */
44//!     # input
45//! }
46//! #
47//! # fn get_a_type() -> Type {
48//! #     unimplemented!()
49//! # }
50//! ```
51//!
52//! By inserting this `assert_sync` fragment into the output code generated by
53//! our macro, the user's code will fail to compile if `ty` does not implement
54//! `Sync`. The errors they would see look like the following.
55//!
56//! ```text
57//! error[E0277]: the trait bound `*const i32: std::marker::Sync` is not satisfied
58//!   --> src/main.rs:10:21
59//!    |
60//! 10 |     bad_field: *const i32,
61//!    |                ^^^^^^^^^^ `*const i32` cannot be shared between threads safely
62//! ```
63//!
64//! In this technique, using the `Type`'s span for the error message makes the
65//! error appear in the correct place underlining the right type.
66
67use proc_macro2::{Span, TokenStream};
68use quote::ToTokens;
69
70/// A trait that can provide the `Span` of the complete contents of a syntax
71/// tree node.
72///
73/// This trait is automatically implemented for all types that implement
74/// [`ToTokens`] from the `quote` crate. It is sealed and cannot be implemented
75/// outside of the Syn crate other than by implementing `ToTokens`.
76///
77/// [`ToTokens`]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
78///
79/// See the [module documentation] for an example.
80///
81/// [module documentation]: index.html
82///
83/// *This trait is available if Syn is built with both the `"parsing"` and
84/// `"printing"` features.*
85pub trait Spanned: private::Sealed {
86    /// Returns a `Span` covering the complete contents of this syntax tree
87    /// node, or [`Span::call_site()`] if this node is empty.
88    ///
89    /// [`Span::call_site()`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html#method.call_site
90    fn span(&self) -> Span;
91}
92
93mod private {
94    use quote::ToTokens;
95    pub trait Sealed {}
96    impl<T: ToTokens> Sealed for T {}
97}
98
99impl<T> Spanned for T
100where
101    T: ToTokens,
102{
103    fn span(&self) -> Span {
104        join_spans(self.into_token_stream())
105    }
106}
107
108fn join_spans(tokens: TokenStream) -> Span {
109    let mut iter = tokens.into_iter().filter_map(|tt| {
110        // FIXME: This shouldn't be required, since optimally spans should
111        // never be invalid. This filter_map can probably be removed when
112        // https://github.com/rust-lang/rust/issues/43081 is resolved.
113        let span = tt.span();
114        let debug = format!("{:?}", span);
115        if debug.ends_with("bytes(0..0)") {
116            None
117        } else {
118            Some(span)
119        }
120    });
121
122    let mut joined = match iter.next() {
123        Some(span) => span,
124        None => return Span::call_site(),
125    };
126
127    #[cfg(procmacro2_semver_exempt)]
128    {
129        for next in iter {
130            if let Some(span) = joined.join(next) {
131                joined = span;
132            }
133        }
134    }
135
136    #[cfg(not(procmacro2_semver_exempt))]
137    {
138        // We can't join spans without procmacro2_semver_exempt so just grab the
139        // first one.
140        joined = joined;
141    }
142
143    joined
144}