David Tolnay | 5553501 | 2018-01-05 16:39:23 -0800 | [diff] [blame] | 1 | // 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 Tolnay | a922130 | 2018-01-06 18:20:54 -0800 | [diff] [blame] | 9 | //! A trait that can provide the `Span` of the complete contents of a syntax |
| 10 | //! tree node. |
| 11 | //! |
David Tolnay | 461d98e | 2018-01-07 11:07:19 -0800 | [diff] [blame^] | 12 | //! *This module is available if Syn is built with both the `"parsing"` and |
| 13 | //! `"printing"` features.* |
| 14 | //! |
David Tolnay | a922130 | 2018-01-06 18:20:54 -0800 | [diff] [blame] | 15 | //! # 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 Tolnay | 0a6deb2 | 2018-01-06 18:26:05 -0800 | [diff] [blame] | 32 | //! #[macro_use] |
| 33 | //! extern crate quote; |
| 34 | //! |
| 35 | //! extern crate syn; |
| 36 | //! extern crate proc_macro; |
| 37 | //! extern crate proc_macro2; |
| 38 | //! |
| 39 | //! use syn::Type; |
| 40 | //! use syn::spanned::Spanned; |
| 41 | //! use proc_macro::TokenStream; |
| 42 | //! use proc_macro2::Span; |
| 43 | //! |
| 44 | //! # const IGNORE_TOKENS: &str = stringify! { |
| 45 | //! #[proc_macro_derive(MyMacro)] |
| 46 | //! # }; |
| 47 | //! pub fn my_macro(input: TokenStream) -> TokenStream { |
| 48 | //! # let ty = get_a_type(); |
| 49 | //! /* ... */ |
| 50 | //! |
| 51 | //! let def_site = Span::def_site(); |
| 52 | //! let ty_span = ty.span().resolved_at(def_site); |
| 53 | //! let assert_sync = quote_spanned! {ty_span=> |
| 54 | //! struct _AssertSync where #ty: Sync; |
| 55 | //! }; |
| 56 | //! |
| 57 | //! /* ... */ |
| 58 | //! # input |
| 59 | //! } |
David Tolnay | a922130 | 2018-01-06 18:20:54 -0800 | [diff] [blame] | 60 | //! # |
David Tolnay | 0a6deb2 | 2018-01-06 18:26:05 -0800 | [diff] [blame] | 61 | //! # fn get_a_type() -> Type { |
| 62 | //! # unimplemented!() |
David Tolnay | a922130 | 2018-01-06 18:20:54 -0800 | [diff] [blame] | 63 | //! # } |
| 64 | //! # |
| 65 | //! # fn main() {} |
| 66 | //! ``` |
| 67 | //! |
| 68 | //! By inserting this `assert_sync` fragment into the output code generated by |
| 69 | //! our macro, the user's code will fail to compile if `ty` does not implement |
| 70 | //! `Sync`. The errors they would see look like the following. |
| 71 | //! |
| 72 | //! ```text |
| 73 | //! error[E0277]: the trait bound `*const i32: std::marker::Sync` is not satisfied |
| 74 | //! --> src/main.rs:10:21 |
| 75 | //! | |
| 76 | //! 10 | bad_field: *const i32, |
| 77 | //! | ^^^^^^^^^^ `*const i32` cannot be shared between threads safely |
| 78 | //! ``` |
| 79 | //! |
| 80 | //! In this technique, using the `Type`'s span for the error message makes the |
| 81 | //! error appear in the correct place underlining the right type. But it is |
| 82 | //! **incredibly important** that the span for the assertion is **resolved** at |
| 83 | //! the procedural macro definition site rather than at the `Type`'s span. This |
| 84 | //! way we guarantee that it refers to the `Sync` trait that we expect. If the |
| 85 | //! assertion were **resolved** at the same place that `ty` is resolved, the |
| 86 | //! user could circumvent the check by defining their own `Sync` trait that is |
| 87 | //! implemented for their type. |
| 88 | |
David Tolnay | f790b61 | 2017-12-31 18:46:57 -0500 | [diff] [blame] | 89 | use proc_macro2::{Span, TokenStream}; |
David Tolnay | 61037c6 | 2018-01-05 16:21:03 -0800 | [diff] [blame] | 90 | use quote::{ToTokens, Tokens}; |
David Tolnay | f790b61 | 2017-12-31 18:46:57 -0500 | [diff] [blame] | 91 | |
David Tolnay | a922130 | 2018-01-06 18:20:54 -0800 | [diff] [blame] | 92 | /// A trait that can provide the `Span` of the complete contents of a syntax |
| 93 | /// tree node. |
| 94 | /// |
| 95 | /// This trait is automatically implemented for all types that implement |
| 96 | /// [`ToTokens`] from the `quote` crate. |
| 97 | /// |
| 98 | /// [`ToTokens`]: https://docs.rs/quote/0.4/quote/trait.ToTokens.html |
| 99 | /// |
| 100 | /// See the [module documentation] for an example. |
| 101 | /// |
| 102 | /// [module documentation]: index.html |
David Tolnay | 461d98e | 2018-01-07 11:07:19 -0800 | [diff] [blame^] | 103 | /// |
| 104 | /// *This trait is available if Syn is built with both the `"parsing"` and |
| 105 | /// `"printing"` features.* |
David Tolnay | f790b61 | 2017-12-31 18:46:57 -0500 | [diff] [blame] | 106 | pub trait Spanned { |
David Tolnay | a922130 | 2018-01-06 18:20:54 -0800 | [diff] [blame] | 107 | /// Returns a `Span` covering the complete contents of this syntax tree |
| 108 | /// node, or [`Span::call_site()`] if this node is empty. |
| 109 | /// |
| 110 | /// [`Span::call_site()`]: https://docs.rs/proc-macro2/0.1/proc_macro2/struct.Span.html#method.call_site |
David Tolnay | f790b61 | 2017-12-31 18:46:57 -0500 | [diff] [blame] | 111 | fn span(&self) -> Span; |
| 112 | } |
| 113 | |
| 114 | impl<T> Spanned for T |
| 115 | where |
| 116 | T: ToTokens, |
| 117 | { |
David Tolnay | 4d942b4 | 2018-01-02 22:14:04 -0800 | [diff] [blame] | 118 | #[cfg(procmacro2_semver_exempt)] |
David Tolnay | f790b61 | 2017-12-31 18:46:57 -0500 | [diff] [blame] | 119 | fn span(&self) -> Span { |
| 120 | let mut tokens = Tokens::new(); |
| 121 | self.to_tokens(&mut tokens); |
| 122 | let token_stream = TokenStream::from(tokens); |
| 123 | let mut iter = token_stream.into_iter(); |
| 124 | let mut span = match iter.next() { |
| 125 | Some(tt) => tt.span, |
| 126 | None => { |
| 127 | return Span::call_site(); |
| 128 | } |
| 129 | }; |
| 130 | for tt in iter { |
| 131 | if let Some(joined) = span.join(tt.span) { |
| 132 | span = joined; |
| 133 | } |
| 134 | } |
| 135 | span |
| 136 | } |
David Tolnay | 4d942b4 | 2018-01-02 22:14:04 -0800 | [diff] [blame] | 137 | |
| 138 | #[cfg(not(procmacro2_semver_exempt))] |
| 139 | fn span(&self) -> Span { |
| 140 | let mut tokens = Tokens::new(); |
| 141 | self.to_tokens(&mut tokens); |
| 142 | let token_stream = TokenStream::from(tokens); |
| 143 | let mut iter = token_stream.into_iter(); |
| 144 | |
| 145 | // We can't join spans without procmacro2_semver_exempt so just grab the |
| 146 | // first one. |
| 147 | match iter.next() { |
| 148 | Some(tt) => tt.span, |
| 149 | None => Span::call_site(), |
| 150 | } |
| 151 | } |
David Tolnay | f790b61 | 2017-12-31 18:46:57 -0500 | [diff] [blame] | 152 | } |