blob: fdf9dd888fa63a9d59bb88d49abcca90c77ec240 [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//!
12//! # Example
13//!
14//! Suppose in a procedural macro we have a [`Type`] that we want to assert
15//! implements the [`Sync`] trait. Maybe this is the type of one of the fields
16//! of a struct for which we are deriving a trait implementation, and we need to
17//! be able to pass a reference to one of those fields across threads.
18//!
19//! [`Type`]: ../enum.Type.html
20//! [`Sync`]: https://doc.rust-lang.org/std/marker/trait.Sync.html
21//!
22//! If the field type does *not* implement `Sync` as required, we want the
23//! compiler to report an error pointing out exactly which type it was.
24//!
25//! The following macro code takes a variable `ty` of type `Type` and produces a
26//! static assertion that `Sync` is implemented for that type.
27//!
28//! ```
29//! # #[macro_use]
30//! # extern crate quote;
31//! #
32//! # extern crate syn;
33//! # extern crate proc_macro2;
34//! #
35//! # use syn::Type;
36//! # use syn::spanned::Spanned;
37//! # use proc_macro2::Span;
38//! #
39//! # fn example(ty: Type) {
40//! let def_site = Span::def_site();
41//! let ty_span = ty.span().resolved_at(def_site);
42//! let assert_sync = quote_spanned! {ty_span=>
43//! struct _AssertSync where #ty: Sync;
44//! };
45//! # }
46//! #
47//! # fn main() {}
48//! ```
49//!
50//! By inserting this `assert_sync` fragment into the output code generated by
51//! our macro, the user's code will fail to compile if `ty` does not implement
52//! `Sync`. The errors they would see look like the following.
53//!
54//! ```text
55//! error[E0277]: the trait bound `*const i32: std::marker::Sync` is not satisfied
56//! --> src/main.rs:10:21
57//! |
58//! 10 | bad_field: *const i32,
59//! | ^^^^^^^^^^ `*const i32` cannot be shared between threads safely
60//! ```
61//!
62//! In this technique, using the `Type`'s span for the error message makes the
63//! error appear in the correct place underlining the right type. But it is
64//! **incredibly important** that the span for the assertion is **resolved** at
65//! the procedural macro definition site rather than at the `Type`'s span. This
66//! way we guarantee that it refers to the `Sync` trait that we expect. If the
67//! assertion were **resolved** at the same place that `ty` is resolved, the
68//! user could circumvent the check by defining their own `Sync` trait that is
69//! implemented for their type.
70
David Tolnayf790b612017-12-31 18:46:57 -050071use proc_macro2::{Span, TokenStream};
David Tolnay61037c62018-01-05 16:21:03 -080072use quote::{ToTokens, Tokens};
David Tolnayf790b612017-12-31 18:46:57 -050073
David Tolnaya9221302018-01-06 18:20:54 -080074/// A trait that can provide the `Span` of the complete contents of a syntax
75/// tree node.
76///
77/// This trait is automatically implemented for all types that implement
78/// [`ToTokens`] from the `quote` crate.
79///
80/// [`ToTokens`]: https://docs.rs/quote/0.4/quote/trait.ToTokens.html
81///
82/// See the [module documentation] for an example.
83///
84/// [module documentation]: index.html
David Tolnayf790b612017-12-31 18:46:57 -050085pub trait Spanned {
David Tolnaya9221302018-01-06 18:20:54 -080086 /// 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()`]: https://docs.rs/proc-macro2/0.1/proc_macro2/struct.Span.html#method.call_site
David Tolnayf790b612017-12-31 18:46:57 -050090 fn span(&self) -> Span;
91}
92
93impl<T> Spanned for T
94where
95 T: ToTokens,
96{
David Tolnay4d942b42018-01-02 22:14:04 -080097 #[cfg(procmacro2_semver_exempt)]
David Tolnayf790b612017-12-31 18:46:57 -050098 fn span(&self) -> Span {
99 let mut tokens = Tokens::new();
100 self.to_tokens(&mut tokens);
101 let token_stream = TokenStream::from(tokens);
102 let mut iter = token_stream.into_iter();
103 let mut span = match iter.next() {
104 Some(tt) => tt.span,
105 None => {
106 return Span::call_site();
107 }
108 };
109 for tt in iter {
110 if let Some(joined) = span.join(tt.span) {
111 span = joined;
112 }
113 }
114 span
115 }
David Tolnay4d942b42018-01-02 22:14:04 -0800116
117 #[cfg(not(procmacro2_semver_exempt))]
118 fn span(&self) -> Span {
119 let mut tokens = Tokens::new();
120 self.to_tokens(&mut tokens);
121 let token_stream = TokenStream::from(tokens);
122 let mut iter = token_stream.into_iter();
123
124 // We can't join spans without procmacro2_semver_exempt so just grab the
125 // first one.
126 match iter.next() {
127 Some(tt) => tt.span,
128 None => Span::call_site(),
129 }
130 }
David Tolnayf790b612017-12-31 18:46:57 -0500131}