• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
67 use proc_macro2::{Span, TokenStream};
68 use 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`]: quote::ToTokens
78 ///
79 /// See the [module documentation] for an example.
80 ///
81 /// [module documentation]: self
82 ///
83 /// *This trait is available if Syn is built with both the `"parsing"` and
84 /// `"printing"` features.*
85 pub 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()`]: proc_macro2::Span::call_site
span(&self) -> Span90     fn span(&self) -> Span;
91 }
92 
93 mod private {
94     use quote::ToTokens;
95     pub trait Sealed {}
96     impl<T: ToTokens> Sealed for T {}
97 }
98 
99 impl<T> Spanned for T
100 where
101     T: ToTokens,
102 {
span(&self) -> Span103     fn span(&self) -> Span {
104         join_spans(self.into_token_stream())
105     }
106 }
107 
join_spans(tokens: TokenStream) -> Span108 fn 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 }
145