blob: a6a21208d8f723d2cd8fbc679be52ad36a241f8d [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 Tolnayfd5b1172018-12-31 17:54:36 -050026//! # extern crate quote;
David Tolnay9b00f652018-09-01 10:31:02 -070027//! #
David Tolnay0a6deb22018-01-06 18:26:05 -080028//! use proc_macro::TokenStream;
29//! use proc_macro2::Span;
David Tolnayfd5b1172018-12-31 17:54:36 -050030//! use quote::quote_spanned;
David Tolnay9b00f652018-09-01 10:31:02 -070031//! use syn::Type;
32//! use syn::spanned::Spanned;
David Tolnay0a6deb22018-01-06 18:26:05 -080033//!
34//! # const IGNORE_TOKENS: &str = stringify! {
35//! #[proc_macro_derive(MyMacro)]
36//! # };
37//! pub fn my_macro(input: TokenStream) -> TokenStream {
38//! # let ty = get_a_type();
39//! /* ... */
40//!
Alex Crichton9a4dca22018-03-28 06:32:19 -070041//! let assert_sync = quote_spanned! {ty.span()=>
David Tolnay0a6deb22018-01-06 18:26:05 -080042//! struct _AssertSync where #ty: Sync;
43//! };
44//!
45//! /* ... */
46//! # input
47//! }
David Tolnaya9221302018-01-06 18:20:54 -080048//! #
David Tolnay0a6deb22018-01-06 18:26:05 -080049//! # fn get_a_type() -> Type {
50//! # unimplemented!()
David Tolnaya9221302018-01-06 18:20:54 -080051//! # }
David Tolnaya9221302018-01-06 18:20:54 -080052//! ```
53//!
54//! By inserting this `assert_sync` fragment into the output code generated by
55//! our macro, the user's code will fail to compile if `ty` does not implement
56//! `Sync`. The errors they would see look like the following.
57//!
58//! ```text
59//! error[E0277]: the trait bound `*const i32: std::marker::Sync` is not satisfied
60//! --> src/main.rs:10:21
61//! |
62//! 10 | bad_field: *const i32,
63//! | ^^^^^^^^^^ `*const i32` cannot be shared between threads safely
64//! ```
65//!
66//! In this technique, using the `Type`'s span for the error message makes the
Alex Crichton9a4dca22018-03-28 06:32:19 -070067//! error appear in the correct place underlining the right type.
David Tolnaya9221302018-01-06 18:20:54 -080068
David Tolnayf790b612017-12-31 18:46:57 -050069use proc_macro2::{Span, TokenStream};
Alex Crichtona74a1c82018-05-16 10:20:44 -070070use quote::ToTokens;
David Tolnayf790b612017-12-31 18:46:57 -050071
David Tolnaya9221302018-01-06 18:20:54 -080072/// A trait that can provide the `Span` of the complete contents of a syntax
73/// tree node.
74///
75/// This trait is automatically implemented for all types that implement
David Tolnay6315c652018-08-24 16:05:45 -040076/// [`ToTokens`] from the `quote` crate. It is sealed and cannot be implemented
77/// outside of the Syn crate other than by implementing `ToTokens`.
David Tolnaya9221302018-01-06 18:20:54 -080078///
David Tolnay442cf762018-08-31 11:08:24 -070079/// [`ToTokens`]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
David Tolnaya9221302018-01-06 18:20:54 -080080///
81/// See the [module documentation] for an example.
82///
83/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -080084///
85/// *This trait is available if Syn is built with both the `"parsing"` and
86/// `"printing"` features.*
David Tolnay6315c652018-08-24 16:05:45 -040087pub trait Spanned: private::Sealed {
David Tolnaya9221302018-01-06 18:20:54 -080088 /// Returns a `Span` covering the complete contents of this syntax tree
89 /// node, or [`Span::call_site()`] if this node is empty.
90 ///
David Tolnay442cf762018-08-31 11:08:24 -070091 /// [`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 -050092 fn span(&self) -> Span;
93}
94
David Tolnay6315c652018-08-24 16:05:45 -040095mod private {
96 use quote::ToTokens;
97 pub trait Sealed {}
98 impl<T: ToTokens> Sealed for T {}
99}
100
David Tolnayf790b612017-12-31 18:46:57 -0500101impl<T> Spanned for T
102where
103 T: ToTokens,
104{
105 fn span(&self) -> Span {
David Tolnay785a79a2018-10-02 21:28:24 -0700106 join_spans(self.into_token_stream())
107 }
108}
109
110fn join_spans(tokens: TokenStream) -> Span {
David Tolnayf1a10262018-10-13 15:33:50 -0700111 let mut iter = tokens.into_iter().filter_map(|tt| {
112 // FIXME: This shouldn't be required, since optimally spans should
113 // never be invalid. This filter_map can probably be removed when
114 // https://github.com/rust-lang/rust/issues/43081 is resolved.
115 let span = tt.span();
116 let debug = format!("{:?}", span);
117 if debug.ends_with("bytes(0..0)") {
118 None
119 } else {
120 Some(span)
121 }
122 });
David Tolnay785a79a2018-10-02 21:28:24 -0700123
124 let mut joined = match iter.next() {
125 Some(span) => span,
126 None => return Span::call_site(),
127 };
128
129 #[cfg(procmacro2_semver_exempt)]
130 {
131 for next in iter {
132 if let Some(span) = joined.join(next) {
133 joined = span;
134 }
135 }
David Tolnayf790b612017-12-31 18:46:57 -0500136 }
David Tolnay4d942b42018-01-02 22:14:04 -0800137
138 #[cfg(not(procmacro2_semver_exempt))]
David Tolnay785a79a2018-10-02 21:28:24 -0700139 {
David Tolnay4d942b42018-01-02 22:14:04 -0800140 // We can't join spans without procmacro2_semver_exempt so just grab the
141 // first one.
David Tolnay785a79a2018-10-02 21:28:24 -0700142 joined = joined;
David Tolnay4d942b42018-01-02 22:14:04 -0800143 }
David Tolnay785a79a2018-10-02 21:28:24 -0700144
145 joined
David Tolnayf790b612017-12-31 18:46:57 -0500146}