blob: a1a2a9ab2228e3223f41fe915cae98b2bdb1022a [file] [log] [blame]
David Tolnaya9221302018-01-06 18:20:54 -08001//! A trait that can provide the `Span` of the complete contents of a syntax
2//! tree node.
3//!
David Tolnay461d98e2018-01-07 11:07:19 -08004//! *This module is available if Syn is built with both the `"parsing"` and
5//! `"printing"` features.*
6//!
David Tolnaya9221302018-01-06 18:20:54 -08007//! # 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//! ```
David Tolnay9b00f652018-09-01 10:31:02 -070024//! # extern crate proc_macro;
25//! # extern crate proc_macro2;
David Tolnay9b00f652018-09-01 10:31:02 -070026//! # extern crate syn;
27//! #
David Tolnaya1c98072018-09-06 08:58:10 -070028//! #[macro_use]
29//! extern crate quote;
30//!
David Tolnay0a6deb22018-01-06 18:26:05 -080031//! use proc_macro::TokenStream;
32//! use proc_macro2::Span;
David Tolnay9b00f652018-09-01 10:31:02 -070033//! use syn::Type;
34//! use syn::spanned::Spanned;
David Tolnay0a6deb22018-01-06 18:26:05 -080035//!
36//! # const IGNORE_TOKENS: &str = stringify! {
37//! #[proc_macro_derive(MyMacro)]
38//! # };
39//! pub fn my_macro(input: TokenStream) -> TokenStream {
40//! # let ty = get_a_type();
41//! /* ... */
42//!
Alex Crichton9a4dca22018-03-28 06:32:19 -070043//! let assert_sync = quote_spanned! {ty.span()=>
David Tolnay0a6deb22018-01-06 18:26:05 -080044//! struct _AssertSync where #ty: Sync;
45//! };
46//!
47//! /* ... */
48//! # input
49//! }
David Tolnaya9221302018-01-06 18:20:54 -080050//! #
David Tolnay0a6deb22018-01-06 18:26:05 -080051//! # fn get_a_type() -> Type {
52//! # unimplemented!()
David Tolnaya9221302018-01-06 18:20:54 -080053//! # }
54//! #
55//! # fn main() {}
56//! ```
57//!
58//! By inserting this `assert_sync` fragment into the output code generated by
59//! our macro, the user's code will fail to compile if `ty` does not implement
60//! `Sync`. The errors they would see look like the following.
61//!
62//! ```text
63//! error[E0277]: the trait bound `*const i32: std::marker::Sync` is not satisfied
64//! --> src/main.rs:10:21
65//! |
66//! 10 | bad_field: *const i32,
67//! | ^^^^^^^^^^ `*const i32` cannot be shared between threads safely
68//! ```
69//!
70//! In this technique, using the `Type`'s span for the error message makes the
Alex Crichton9a4dca22018-03-28 06:32:19 -070071//! error appear in the correct place underlining the right type.
David Tolnaya9221302018-01-06 18:20:54 -080072
David Tolnayf790b612017-12-31 18:46:57 -050073use proc_macro2::{Span, TokenStream};
Alex Crichtona74a1c82018-05-16 10:20:44 -070074use quote::ToTokens;
David Tolnayf790b612017-12-31 18:46:57 -050075
David Tolnaya9221302018-01-06 18:20:54 -080076/// A trait that can provide the `Span` of the complete contents of a syntax
77/// tree node.
78///
79/// This trait is automatically implemented for all types that implement
David Tolnay6315c652018-08-24 16:05:45 -040080/// [`ToTokens`] from the `quote` crate. It is sealed and cannot be implemented
81/// outside of the Syn crate other than by implementing `ToTokens`.
David Tolnaya9221302018-01-06 18:20:54 -080082///
David Tolnay442cf762018-08-31 11:08:24 -070083/// [`ToTokens`]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
David Tolnaya9221302018-01-06 18:20:54 -080084///
85/// See the [module documentation] for an example.
86///
87/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -080088///
89/// *This trait is available if Syn is built with both the `"parsing"` and
90/// `"printing"` features.*
David Tolnay6315c652018-08-24 16:05:45 -040091pub trait Spanned: private::Sealed {
David Tolnaya9221302018-01-06 18:20:54 -080092 /// Returns a `Span` covering the complete contents of this syntax tree
93 /// node, or [`Span::call_site()`] if this node is empty.
94 ///
David Tolnay442cf762018-08-31 11:08:24 -070095 /// [`Span::call_site()`]: https://docs.rs/proc-macro2/0.4/proc_macro2/struct.Span.html#method.call_site
David Tolnayf790b612017-12-31 18:46:57 -050096 fn span(&self) -> Span;
97}
98
David Tolnay6315c652018-08-24 16:05:45 -040099mod private {
100 use quote::ToTokens;
101 pub trait Sealed {}
102 impl<T: ToTokens> Sealed for T {}
103}
104
David Tolnayf790b612017-12-31 18:46:57 -0500105impl<T> Spanned for T
106where
107 T: ToTokens,
108{
109 fn span(&self) -> Span {
David Tolnay785a79a2018-10-02 21:28:24 -0700110 join_spans(self.into_token_stream())
111 }
112}
113
114fn join_spans(tokens: TokenStream) -> Span {
David Tolnayf1a10262018-10-13 15:33:50 -0700115 let mut iter = tokens.into_iter().filter_map(|tt| {
116 // FIXME: This shouldn't be required, since optimally spans should
117 // never be invalid. This filter_map can probably be removed when
118 // https://github.com/rust-lang/rust/issues/43081 is resolved.
119 let span = tt.span();
120 let debug = format!("{:?}", span);
121 if debug.ends_with("bytes(0..0)") {
122 None
123 } else {
124 Some(span)
125 }
126 });
David Tolnay785a79a2018-10-02 21:28:24 -0700127
128 let mut joined = match iter.next() {
129 Some(span) => span,
130 None => return Span::call_site(),
131 };
132
133 #[cfg(procmacro2_semver_exempt)]
134 {
135 for next in iter {
136 if let Some(span) = joined.join(next) {
137 joined = span;
138 }
139 }
David Tolnayf790b612017-12-31 18:46:57 -0500140 }
David Tolnay4d942b42018-01-02 22:14:04 -0800141
142 #[cfg(not(procmacro2_semver_exempt))]
David Tolnay785a79a2018-10-02 21:28:24 -0700143 {
David Tolnay4d942b42018-01-02 22:14:04 -0800144 // We can't join spans without procmacro2_semver_exempt so just grab the
145 // first one.
David Tolnay785a79a2018-10-02 21:28:24 -0700146 joined = joined;
David Tolnay4d942b42018-01-02 22:14:04 -0800147 }
David Tolnay785a79a2018-10-02 21:28:24 -0700148
149 joined
David Tolnayf790b612017-12-31 18:46:57 -0500150}