blob: 6dd50cb9aca8215e55d3c10585fb177dd9077628 [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
David Tolnaya9221302018-01-06 18:20:54 -08009//! A trait that can provide the `Span` of the complete contents of a syntax
10//! tree node.
11//!
David Tolnay461d98e2018-01-07 11:07:19 -080012//! *This module is available if Syn is built with both the `"parsing"` and
13//! `"printing"` features.*
14//!
David Tolnaya9221302018-01-06 18:20:54 -080015//! # Example
16//!
17//! Suppose in a procedural macro we have a [`Type`] that we want to assert
18//! implements the [`Sync`] trait. Maybe this is the type of one of the fields
19//! of a struct for which we are deriving a trait implementation, and we need to
20//! be able to pass a reference to one of those fields across threads.
21//!
22//! [`Type`]: ../enum.Type.html
23//! [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
24//!
25//! If the field type does *not* implement `Sync` as required, we want the
26//! compiler to report an error pointing out exactly which type it was.
27//!
28//! The following macro code takes a variable `ty` of type `Type` and produces a
29//! static assertion that `Sync` is implemented for that type.
30//!
31//! ```
David Tolnay9b00f652018-09-01 10:31:02 -070032//! # extern crate proc_macro;
33//! # extern crate proc_macro2;
34//! # extern crate quote;
35//! # extern crate syn;
36//! #
David Tolnay0a6deb22018-01-06 18:26:05 -080037//! use proc_macro::TokenStream;
38//! use proc_macro2::Span;
David Tolnay9b00f652018-09-01 10:31:02 -070039//! use quote::quote_spanned;
40//! use syn::Type;
41//! use syn::spanned::Spanned;
David Tolnay0a6deb22018-01-06 18:26:05 -080042//!
43//! # const IGNORE_TOKENS: &str = stringify! {
44//! #[proc_macro_derive(MyMacro)]
45//! # };
46//! pub fn my_macro(input: TokenStream) -> TokenStream {
47//! # let ty = get_a_type();
48//! /* ... */
49//!
Alex Crichton9a4dca22018-03-28 06:32:19 -070050//! let assert_sync = quote_spanned! {ty.span()=>
David Tolnay0a6deb22018-01-06 18:26:05 -080051//! struct _AssertSync where #ty: Sync;
52//! };
53//!
54//! /* ... */
55//! # input
56//! }
David Tolnaya9221302018-01-06 18:20:54 -080057//! #
David Tolnay0a6deb22018-01-06 18:26:05 -080058//! # fn get_a_type() -> Type {
59//! # unimplemented!()
David Tolnaya9221302018-01-06 18:20:54 -080060//! # }
61//! #
62//! # fn main() {}
63//! ```
64//!
65//! By inserting this `assert_sync` fragment into the output code generated by
66//! our macro, the user's code will fail to compile if `ty` does not implement
67//! `Sync`. The errors they would see look like the following.
68//!
69//! ```text
70//! error[E0277]: the trait bound `*const i32: std::marker::Sync` is not satisfied
71//! --> src/main.rs:10:21
72//! |
73//! 10 | bad_field: *const i32,
74//! | ^^^^^^^^^^ `*const i32` cannot be shared between threads safely
75//! ```
76//!
77//! In this technique, using the `Type`'s span for the error message makes the
Alex Crichton9a4dca22018-03-28 06:32:19 -070078//! error appear in the correct place underlining the right type.
David Tolnaya9221302018-01-06 18:20:54 -080079
David Tolnayf790b612017-12-31 18:46:57 -050080use proc_macro2::{Span, TokenStream};
Alex Crichtona74a1c82018-05-16 10:20:44 -070081use quote::ToTokens;
David Tolnayf790b612017-12-31 18:46:57 -050082
David Tolnaya9221302018-01-06 18:20:54 -080083/// A trait that can provide the `Span` of the complete contents of a syntax
84/// tree node.
85///
86/// This trait is automatically implemented for all types that implement
David Tolnay6315c652018-08-24 16:05:45 -040087/// [`ToTokens`] from the `quote` crate. It is sealed and cannot be implemented
88/// outside of the Syn crate other than by implementing `ToTokens`.
David Tolnaya9221302018-01-06 18:20:54 -080089///
David Tolnay442cf762018-08-31 11:08:24 -070090/// [`ToTokens`]: https://docs.rs/quote/0.6/quote/trait.ToTokens.html
David Tolnaya9221302018-01-06 18:20:54 -080091///
92/// See the [module documentation] for an example.
93///
94/// [module documentation]: index.html
David Tolnay461d98e2018-01-07 11:07:19 -080095///
96/// *This trait is available if Syn is built with both the `"parsing"` and
97/// `"printing"` features.*
David Tolnay6315c652018-08-24 16:05:45 -040098pub trait Spanned: private::Sealed {
David Tolnaya9221302018-01-06 18:20:54 -080099 /// Returns a `Span` covering the complete contents of this syntax tree
100 /// node, or [`Span::call_site()`] if this node is empty.
101 ///
David Tolnay442cf762018-08-31 11:08:24 -0700102 /// [`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 -0500103 fn span(&self) -> Span;
104}
105
David Tolnay6315c652018-08-24 16:05:45 -0400106mod private {
107 use quote::ToTokens;
108 pub trait Sealed {}
109 impl<T: ToTokens> Sealed for T {}
110}
111
David Tolnayf790b612017-12-31 18:46:57 -0500112impl<T> Spanned for T
113where
114 T: ToTokens,
115{
David Tolnay4d942b42018-01-02 22:14:04 -0800116 #[cfg(procmacro2_semver_exempt)]
David Tolnayf790b612017-12-31 18:46:57 -0500117 fn span(&self) -> Span {
hcplaa511792018-05-29 07:13:01 +0300118 let mut tokens = TokenStream::new();
David Tolnayf790b612017-12-31 18:46:57 -0500119 self.to_tokens(&mut tokens);
David Tolnay106db5e2018-05-20 19:56:38 -0700120 let mut iter = tokens.into_iter();
David Tolnayf790b612017-12-31 18:46:57 -0500121 let mut span = match iter.next() {
David Tolnay4fbab462018-03-31 17:15:56 +0200122 Some(tt) => tt.span(),
David Tolnayf790b612017-12-31 18:46:57 -0500123 None => {
124 return Span::call_site();
125 }
126 };
127 for tt in iter {
David Tolnay4fbab462018-03-31 17:15:56 +0200128 if let Some(joined) = span.join(tt.span()) {
David Tolnayf790b612017-12-31 18:46:57 -0500129 span = joined;
130 }
131 }
132 span
133 }
David Tolnay4d942b42018-01-02 22:14:04 -0800134
135 #[cfg(not(procmacro2_semver_exempt))]
136 fn span(&self) -> Span {
hcplaa511792018-05-29 07:13:01 +0300137 let mut tokens = TokenStream::new();
David Tolnay4d942b42018-01-02 22:14:04 -0800138 self.to_tokens(&mut tokens);
David Tolnay106db5e2018-05-20 19:56:38 -0700139 let mut iter = tokens.into_iter();
David Tolnay4d942b42018-01-02 22:14:04 -0800140
141 // We can't join spans without procmacro2_semver_exempt so just grab the
142 // first one.
143 match iter.next() {
Alex Crichton9a4dca22018-03-28 06:32:19 -0700144 Some(tt) => tt.span(),
David Tolnay4d942b42018-01-02 22:14:04 -0800145 None => Span::call_site(),
146 }
147 }
David Tolnayf790b612017-12-31 18:46:57 -0500148}