syn_path/
lib.rs

1#![warn(rust_2018_idioms, unreachable_pub)]
2#![deny(elided_lifetimes_in_paths)]
3#![forbid(unsafe_code)]
4
5//! This crate contains macros to construct [`syn`]-types that contain paths inside a
6//! procedural macro.
7//!
8//!  - The [`path!`] macro constructs a [`syn::Path`].
9//!  - The [`type_path!`] macro constructs a [`syn::TypePath`].
10//!  - The [`ty!`] macro constructs a [`syn::Type`].
11
12#[doc(hidden)]
13pub mod private {
14	pub use proc_macro2::{Ident, Span};
15	pub use std::{
16		boxed::Box,
17		option::Option::{None, Some},
18		stringify
19	};
20	pub use syn::{punctuated::Punctuated, Path, PathSegment, QSelf, Type, TypePath};
21
22	#[inline]
23	pub fn default<T: Default>() -> T {
24		T::default()
25	}
26
27	#[inline]
28	pub const fn len<T, const LEN: usize>(_: &[T; LEN]) -> usize {
29		LEN
30	}
31}
32
33/// This macro takes type paths of the form `my_crate::my_mod::FooBar` and
34/// `<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType` and
35/// turns them into a [`syn::Type`].
36#[macro_export]
37macro_rules! ty {
38	(:: $($segment:ident)::*) => {
39		$crate::private::Type::Path($crate::type_path!(:: $($segment)::*))
40	};
41	($($segment:ident)::*) => {
42		$crate::private::Type::Path($crate::type_path!($($segment)::*))
43	};
44
45	(< $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
46		$crate::private::Type::Path($crate::type_path!(<$($ty_segment)::*>::$($segment)::*))
47	};
48	(< :: $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
49		$crate::private::Type::Path($crate::type_path!(<:: $($ty_segment)::*>::$($segment)::*))
50	};
51
52	(< $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
53		$crate::private::Type::Path(
54			$crate::type_path!(<$($ty_segment)::* as $($as_segment)::*>::$($segment)::*)
55		)
56	};
57	(< :: $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
58		$crate::private::Type::Path(
59			$crate::type_path!(<:: $($ty_segment)::* as $($as_segment)::*>::$($segment)::*)
60		)
61	};
62	(< $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
63		$crate::private::Type::Path(
64			$crate::type_path!(<$($ty_segment)::* as :: $($as_segment)::*>::$($segment)::*)
65		)
66	};
67	(< :: $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
68		$crate::private::Type::Path(
69			$crate::type_path!(<:: $($ty_segment)::* as :: $($as_segment)::*>::$($segment)::*)
70		)
71	};
72}
73
74/// This macro takes type paths of the form `my_crate::my_mod::FooBar` and
75/// `<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType` and
76/// turns them into a [`syn::TypePath`].
77#[macro_export]
78macro_rules! type_path {
79	(:: $($segment:ident)::*) => {
80		$crate::type_path_impl!($crate::private::None, $crate::path!(:: $($segment)::*))
81	};
82	($($segment:ident)::*) => {
83		$crate::type_path_impl!($crate::private::None, $crate::path!($($segment)::*))
84	};
85
86	(< $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
87		$crate::type_path_impl!(
88			$crate::private::Some($crate::private::QSelf {
89				lt_token: $crate::private::default(),
90				ty: Box::new($crate::ty!($($ty_segment)::*)),
91				position: 0,
92				as_token: $crate::private::None,
93				gt_token: $crate::private::default()
94			}),
95			$crate::path!(:: $($segment)::*)
96		)
97	};
98	(< :: $($ty_segment:ident)::* > :: $($segment:ident)::*) => {
99		$crate::type_path_impl!(
100			$crate::private::Some($crate::private::QSelf {
101				lt_token: $crate::private::default(),
102				ty: Box::new($crate::ty!(:: $($ty_segment)::*)),
103				position: 0,
104				as_token: $crate::private::None,
105				gt_token: $crate::private::default()
106			}),
107			$crate::path!(:: $($segment)::*)
108		)
109	};
110
111	(< $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
112		$crate::type_path_impl!(
113			$crate::private::Some($crate::private::QSelf {
114				lt_token: $crate::private::default(),
115				ty: Box::new($crate::ty!($($ty_segment)::*)),
116				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
117				as_token: $crate::private::Some($crate::private::default()),
118				gt_token: $crate::private::default()
119			}),
120			$crate::path!($($as_segment)::* :: $($segment)::*)
121		)
122	};
123	(< :: $($ty_segment:ident)::* as $($as_segment:ident)::* > :: $($segment:ident)::*) => {
124		$crate::type_path_impl!(
125			$crate::private::Some($crate::private::QSelf {
126				lt_token: $crate::private::default(),
127				ty: Box::new($crate::ty!(:: $($ty_segment)::*)),
128				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
129				as_token: $crate::private::Some($crate::private::default()),
130				gt_token: $crate::private::default()
131			}),
132			$crate::path!($($as_segment)::* :: $($segment)::*)
133		)
134	};
135	(< $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
136		$crate::type_path_impl!(
137			$crate::private::Some($crate::private::QSelf {
138				lt_token: $crate::private::default(),
139				ty: Box::new($crate::ty!($($ty_segment)::*)),
140				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
141				as_token: $crate::private::Some($crate::private::default()),
142				gt_token: $crate::private::default()
143			}),
144			$crate::path!(:: $($as_segment)::* :: $($segment)::*)
145		)
146	};
147	(< :: $($ty_segment:ident)::* as :: $($as_segment:ident)::* > :: $($segment:ident)::*) => {
148		$crate::type_path_impl!(
149			$crate::private::Some($crate::private::QSelf {
150				lt_token: $crate::private::default(),
151				ty: Box::new($crate::ty!(:: $($ty_segment)::*)),
152				position: $crate::private::len(&[$($crate::private::stringify!($as_segment)),*]),
153				as_token: $crate::private::Some($crate::private::default()),
154				gt_token: $crate::private::default()
155			}),
156			$crate::path!(:: $($as_segment)::* :: $($segment)::*)
157		)
158	};
159}
160
161#[macro_export]
162#[doc(hidden)]
163macro_rules! type_path_impl {
164	($qself:expr, $path:expr) => {
165		$crate::private::TypePath {
166			qself: $qself,
167			path: $path
168		}
169	};
170}
171
172/// This macro takes paths of the form `my_crate::my_mod::FooBar` and
173/// `::my_crate::my_mod::FooBar` and turns them into a [`syn::Path`].
174#[macro_export]
175macro_rules! path {
176	(:: $($segment:ident)::*) => {
177		$crate::path_impl!($crate::private::Some($crate::private::default()), $($segment),*)
178	};
179	($($segment:ident)::*) => {
180		$crate::path_impl!($crate::private::None, $($segment),*)
181	};
182}
183
184#[macro_export]
185#[doc(hidden)]
186macro_rules! path_impl {
187	($leading_colon:expr, $($segment:ident),*) => {
188		{
189			#[allow(unused_mut)]
190			let mut segments: $crate::private::Punctuated<$crate::private::PathSegment, _> = $crate::private::default();
191			$(
192				segments.push($crate::private::PathSegment {
193					ident: $crate::private::Ident::new(
194						$crate::private::stringify!($segment),
195						$crate::private::Span::call_site()
196					),
197					arguments: $crate::private::default()
198				});
199			)*
200			$crate::private::Path {
201				leading_colon: $leading_colon,
202				segments
203			}
204		}
205	};
206}
207
208#[cfg(test)]
209mod tests {
210	use std::fmt::Debug;
211	use syn::parse::Parse;
212
213	#[track_caller]
214	fn assert_eq<T>(t: T, s: &str)
215	where
216		T: Debug + Eq + Parse
217	{
218		let expected: T = syn::parse_str(s).unwrap();
219		assert_eq!(expected, t);
220	}
221
222	// ### ty! macro tests
223
224	#[test]
225	fn type_with_leading_colon() {
226		assert_eq(
227			ty!(::my_crate::my_mod::FooBar),
228			"::my_crate::my_mod::FooBar"
229		);
230	}
231
232	#[test]
233	fn type_without_leading_colon() {
234		assert_eq(ty!(my_crate::my_mod::FooBar), "my_crate::my_mod::FooBar");
235	}
236
237	#[test]
238	fn type_with_qself_with_leading_colon() {
239		assert_eq(
240			ty!(<::my_crate::my_mod::FooBar>::MyType),
241			"<::my_crate::my_mod::FooBar>::MyType"
242		);
243	}
244
245	#[test]
246	fn type_with_qself_without_leading_colon() {
247		assert_eq(
248			ty!(<my_crate::my_mod::FooBar>::MyType),
249			"<my_crate::my_mod::FooBar>::MyType"
250		);
251	}
252
253	#[test]
254	fn type_with_qself_with_leading_colon_with_as_with_leading_colon() {
255		assert_eq(
256			ty!(<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType),
257			"<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
258		);
259	}
260
261	#[test]
262	fn type_with_qself_with_leading_colon_with_as_without_leading_colon() {
263		assert_eq(
264			ty!(<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
265			"<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
266		);
267	}
268
269	#[test]
270	fn type_with_qself_without_leading_colon_with_as_with_leading_colon() {
271		assert_eq(
272			ty!(<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType),
273			"<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
274		);
275	}
276
277	#[test]
278	fn type_with_qself_without_leading_colon_with_as_without_leading_colon() {
279		assert_eq(
280			ty!(<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
281			"<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
282		);
283	}
284
285	// ### type_path! macro tests
286
287	#[test]
288	fn type_path_with_leading_colon() {
289		assert_eq(
290			type_path!(::my_crate::my_mod::FooBar),
291			"::my_crate::my_mod::FooBar"
292		);
293	}
294
295	#[test]
296	fn type_path_without_leading_colon() {
297		assert_eq(
298			type_path!(my_crate::my_mod::FooBar),
299			"my_crate::my_mod::FooBar"
300		);
301	}
302
303	#[test]
304	fn type_path_with_qself_with_leading_colon() {
305		assert_eq(
306			type_path!(<::my_crate::my_mod::FooBar>::MyType),
307			"<::my_crate::my_mod::FooBar>::MyType"
308		);
309	}
310
311	#[test]
312	fn type_path_with_qself_without_leading_colon() {
313		assert_eq(
314			type_path!(<my_crate::my_mod::FooBar>::MyType),
315			"<my_crate::my_mod::FooBar>::MyType"
316		);
317	}
318
319	#[test]
320	fn type_path_with_qself_with_leading_colon_with_as_with_leading_colon() {
321		assert_eq(
322			type_path!(
323				<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType
324			),
325			"<::my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
326		);
327	}
328
329	#[test]
330	fn type_path_with_qself_with_leading_colon_with_as_without_leading_colon() {
331		assert_eq(
332			type_path!(<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
333			"<::my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
334		);
335	}
336
337	#[test]
338	fn type_path_with_qself_without_leading_colon_with_as_with_leading_colon() {
339		assert_eq(
340			type_path!(<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType),
341			"<my_crate::my_mod::FooBar as ::my_crate::my_mod::MyTrait>::MyType"
342		);
343	}
344
345	#[test]
346	fn type_path_with_qself_without_leading_colon_with_as_without_leading_colon() {
347		assert_eq(
348			type_path!(<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType),
349			"<my_crate::my_mod::FooBar as my_crate::my_mod::MyTrait>::MyType"
350		);
351	}
352
353	// ### path! macro tests
354
355	#[test]
356	fn path_with_leading_colon() {
357		assert_eq(
358			path!(::my_crate::my_mod::FooBar),
359			"::my_crate::my_mod::FooBar"
360		);
361	}
362
363	#[test]
364	fn path_without_leading_colon() {
365		assert_eq(path!(my_crate::my_mod::FooBar), "my_crate::my_mod::FooBar");
366	}
367}