blob: 19c1f0576f14a2b6f5e84f9c59364d2633984a93 [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 Tolnay056de302018-01-05 14:29:05 -08009use super::*;
David Tolnay94d2b792018-04-29 12:26:10 -070010use punctuated::Punctuated;
David Tolnay056de302018-01-05 14:29:05 -080011
12ast_struct! {
David Tolnay05658502018-01-07 09:56:37 -080013 /// A path at which a named item is exported: `std::collections::HashMap`.
David Tolnay461d98e2018-01-07 11:07:19 -080014 ///
15 /// *This type is available if Syn is built with the `"derive"` or `"full"`
16 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -080017 pub struct Path {
David Tolnay056de302018-01-05 14:29:05 -080018 pub leading_colon: Option<Token![::]>,
David Tolnay056de302018-01-05 14:29:05 -080019 pub segments: Punctuated<PathSegment, Token![::]>,
20 }
21}
22
23impl Path {
24 pub fn global(&self) -> bool {
25 self.leading_colon.is_some()
26 }
27}
28
David Tolnay05658502018-01-07 09:56:37 -080029/// A helper for printing a self-type qualified path as tokens.
30///
31/// ```rust
32/// extern crate syn;
33/// extern crate quote;
Alex Crichtonf0fea9a2018-05-17 11:19:34 -070034/// extern crate proc_macro2;
David Tolnay05658502018-01-07 09:56:37 -080035///
36/// use syn::{QSelf, Path, PathTokens};
Alex Crichtonf0fea9a2018-05-17 11:19:34 -070037/// use proc_macro2::TokenStream;
38/// use quote::ToTokens;
David Tolnay05658502018-01-07 09:56:37 -080039///
40/// struct MyNode {
41/// qself: Option<QSelf>,
42/// path: Path,
43/// }
44///
45/// impl ToTokens for MyNode {
Alex Crichtona74a1c82018-05-16 10:20:44 -070046/// fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay05658502018-01-07 09:56:37 -080047/// PathTokens(&self.qself, &self.path).to_tokens(tokens);
48/// }
49/// }
50/// #
51/// # fn main() {}
52/// ```
David Tolnay461d98e2018-01-07 11:07:19 -080053///
54/// *This type is available if Syn is built with the `"derive"` or `"full"`
55/// feature and the `"printing"` feature.*
David Tolnay056de302018-01-05 14:29:05 -080056#[cfg(feature = "printing")]
57#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
58#[cfg_attr(feature = "clone-impls", derive(Clone))]
59pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);
60
61impl<T> From<T> for Path
62where
63 T: Into<PathSegment>,
64{
65 fn from(segment: T) -> Self {
66 let mut path = Path {
67 leading_colon: None,
68 segments: Punctuated::new(),
69 };
David Tolnay56080682018-01-06 14:01:52 -080070 path.segments.push_value(segment.into());
David Tolnay056de302018-01-05 14:29:05 -080071 path
72 }
73}
74
75ast_struct! {
David Tolnay05658502018-01-07 09:56:37 -080076 /// A segment of a path together with any path arguments on that segment.
David Tolnay461d98e2018-01-07 11:07:19 -080077 ///
78 /// *This type is available if Syn is built with the `"derive"` or `"full"`
79 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -080080 pub struct PathSegment {
David Tolnay056de302018-01-05 14:29:05 -080081 pub ident: Ident,
David Tolnay056de302018-01-05 14:29:05 -080082 pub arguments: PathArguments,
83 }
84}
85
86impl<T> From<T> for PathSegment
87where
88 T: Into<Ident>,
89{
90 fn from(ident: T) -> Self {
91 PathSegment {
92 ident: ident.into(),
93 arguments: PathArguments::None,
94 }
95 }
96}
97
98ast_enum! {
David Tolnayc0435192018-01-07 11:46:08 -080099 /// Angle bracketed or parenthesized arguments of a path segment.
David Tolnay461d98e2018-01-07 11:07:19 -0800100 ///
101 /// *This type is available if Syn is built with the `"derive"` or `"full"`
102 /// feature.*
David Tolnayc0435192018-01-07 11:46:08 -0800103 ///
104 /// ## Angle bracketed
105 ///
106 /// The `<'a, T>` in `std::slice::iter<'a, T>`.
107 ///
108 /// ## Parenthesized
109 ///
110 /// The `(A, B) -> C` in `Fn(A, B) -> C`.
David Tolnay056de302018-01-05 14:29:05 -0800111 pub enum PathArguments {
112 None,
David Tolnaye826d812018-01-06 23:59:39 -0800113 /// The `<'a, T>` in `std::slice::iter<'a, T>`.
David Tolnay056de302018-01-05 14:29:05 -0800114 AngleBracketed(AngleBracketedGenericArguments),
David Tolnayc0435192018-01-07 11:46:08 -0800115 /// The `(A, B) -> C` in `Fn(A, B) -> C`.
David Tolnay056de302018-01-05 14:29:05 -0800116 Parenthesized(ParenthesizedGenericArguments),
117 }
118}
119
120impl Default for PathArguments {
121 fn default() -> Self {
122 PathArguments::None
123 }
124}
125
126impl PathArguments {
127 pub fn is_empty(&self) -> bool {
128 match *self {
129 PathArguments::None => true,
130 PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
131 PathArguments::Parenthesized(_) => false,
132 }
133 }
134}
135
136ast_enum! {
David Tolnaya454c8f2018-01-07 01:01:10 -0800137 /// An individual generic argument, like `'a`, `T`, or `Item = T`.
David Tolnay461d98e2018-01-07 11:07:19 -0800138 ///
139 /// *This type is available if Syn is built with the `"derive"` or `"full"`
140 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800141 pub enum GenericArgument {
David Tolnaye826d812018-01-06 23:59:39 -0800142 /// A lifetime argument.
David Tolnay056de302018-01-05 14:29:05 -0800143 Lifetime(Lifetime),
David Tolnaye826d812018-01-06 23:59:39 -0800144 /// A type argument.
David Tolnay056de302018-01-05 14:29:05 -0800145 Type(Type),
David Tolnayc0435192018-01-07 11:46:08 -0800146 /// A binding (equality constraint) on an associated type: the `Item =
147 /// u8` in `Iterator<Item = u8>`.
David Tolnay056de302018-01-05 14:29:05 -0800148 Binding(Binding),
David Tolnaye826d812018-01-06 23:59:39 -0800149 /// A const expression. Must be inside of a block.
David Tolnay056de302018-01-05 14:29:05 -0800150 ///
151 /// NOTE: Identity expressions are represented as Type arguments, as
152 /// they are indistinguishable syntactically.
153 Const(Expr),
154 }
155}
156
157ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800158 /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
159 /// V>`.
David Tolnay461d98e2018-01-07 11:07:19 -0800160 ///
161 /// *This type is available if Syn is built with the `"derive"` or `"full"`
162 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800163 pub struct AngleBracketedGenericArguments {
164 pub colon2_token: Option<Token![::]>,
165 pub lt_token: Token![<],
166 pub args: Punctuated<GenericArgument, Token![,]>,
167 pub gt_token: Token![>],
168 }
169}
170
171ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800172 /// A binding (equality constraint) on an associated type: `Item = u8`.
David Tolnay461d98e2018-01-07 11:07:19 -0800173 ///
174 /// *This type is available if Syn is built with the `"derive"` or `"full"`
175 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800176 pub struct Binding {
177 pub ident: Ident,
178 pub eq_token: Token![=],
179 pub ty: Type,
180 }
181}
182
183ast_struct! {
David Tolnayc0435192018-01-07 11:46:08 -0800184 /// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
185 /// C`.
David Tolnay461d98e2018-01-07 11:07:19 -0800186 ///
187 /// *This type is available if Syn is built with the `"derive"` or `"full"`
188 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800189 pub struct ParenthesizedGenericArguments {
190 pub paren_token: token::Paren,
191 /// `(A, B)`
192 pub inputs: Punctuated<Type, Token![,]>,
193 /// `C`
194 pub output: ReturnType,
195 }
196}
197
198ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800199 /// The explicit Self type in a qualified path: the `T` in `<T as
David Tolnay05658502018-01-07 09:56:37 -0800200 /// Display>::fmt`.
David Tolnaye826d812018-01-06 23:59:39 -0800201 ///
202 /// The actual path, including the trait and the associated item, is stored
203 /// separately. The `position` field represents the index of the associated
David Tolnay056de302018-01-05 14:29:05 -0800204 /// item qualified with this Self type.
205 ///
206 /// ```text
207 /// <Vec<T> as a::b::Trait>::AssociatedItem
208 /// ^~~~~~ ~~~~~~~~~~~~~~^
209 /// ty position = 3
210 ///
211 /// <Vec<T>>::AssociatedItem
212 /// ^~~~~~ ^
213 /// ty position = 0
214 /// ```
David Tolnay461d98e2018-01-07 11:07:19 -0800215 ///
216 /// *This type is available if Syn is built with the `"derive"` or `"full"`
217 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800218 pub struct QSelf {
219 pub lt_token: Token![<],
220 pub ty: Box<Type>,
221 pub position: usize,
222 pub as_token: Option<Token![as]>,
223 pub gt_token: Token![>],
224 }
225}
226
227#[cfg(feature = "parsing")]
228pub mod parsing {
229 use super::*;
230 use synom::Synom;
231
232 impl Synom for Path {
233 named!(parse -> Self, do_parse!(
234 colon: option!(punct!(::)) >>
235 segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
David Tolnay96c6fbe2018-01-11 17:51:56 -0800236 cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn")) >>
David Tolnay056de302018-01-05 14:29:05 -0800237 (Path {
238 leading_colon: colon,
239 segments: segments,
240 })
241 ));
242
243 fn description() -> Option<&'static str> {
244 Some("path")
245 }
246 }
247
248 #[cfg(not(feature = "full"))]
249 impl Synom for GenericArgument {
250 named!(parse -> Self, alt!(
251 call!(ty_no_eq_after) => { GenericArgument::Type }
252 |
253 syn!(Lifetime) => { GenericArgument::Lifetime }
254 |
255 syn!(Binding) => { GenericArgument::Binding }
256 ));
257 }
258
259 #[cfg(feature = "full")]
260 impl Synom for GenericArgument {
261 named!(parse -> Self, alt!(
262 call!(ty_no_eq_after) => { GenericArgument::Type }
263 |
264 syn!(Lifetime) => { GenericArgument::Lifetime }
265 |
266 syn!(Binding) => { GenericArgument::Binding }
267 |
268 syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
269 |
270 syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
271 ));
272
273 fn description() -> Option<&'static str> {
274 Some("generic argument")
275 }
276 }
277
278 impl Synom for AngleBracketedGenericArguments {
279 named!(parse -> Self, do_parse!(
280 colon2: option!(punct!(::)) >>
281 lt: punct!(<) >>
282 args: call!(Punctuated::parse_terminated) >>
283 gt: punct!(>) >>
284 (AngleBracketedGenericArguments {
285 colon2_token: colon2,
286 lt_token: lt,
287 args: args,
288 gt_token: gt,
289 })
290 ));
291
292 fn description() -> Option<&'static str> {
293 Some("angle bracketed generic arguments")
294 }
295 }
296
297 impl Synom for ParenthesizedGenericArguments {
298 named!(parse -> Self, do_parse!(
299 data: parens!(Punctuated::parse_terminated) >>
Geoffry Songac02b182018-05-19 22:11:31 -0700300 output: call!(ReturnType::without_plus) >>
David Tolnay056de302018-01-05 14:29:05 -0800301 (ParenthesizedGenericArguments {
302 paren_token: data.0,
303 inputs: data.1,
304 output: output,
305 })
306 ));
307
308 fn description() -> Option<&'static str> {
309 Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
310 }
311 }
312
313 impl Synom for PathSegment {
314 named!(parse -> Self, alt!(
315 do_parse!(
316 ident: syn!(Ident) >>
317 arguments: syn!(AngleBracketedGenericArguments) >>
318 (PathSegment {
319 ident: ident,
320 arguments: PathArguments::AngleBracketed(arguments),
321 })
322 )
323 |
324 mod_style_path_segment
325 ));
326
327 fn description() -> Option<&'static str> {
328 Some("path segment")
329 }
330 }
331
332 impl Synom for Binding {
333 named!(parse -> Self, do_parse!(
334 id: syn!(Ident) >>
335 eq: punct!(=) >>
336 ty: syn!(Type) >>
337 (Binding {
338 ident: id,
339 eq_token: eq,
340 ty: ty,
341 })
342 ));
343
344 fn description() -> Option<&'static str> {
345 Some("associated type binding")
346 }
347 }
348
349 impl Path {
350 named!(pub parse_mod_style -> Self, do_parse!(
351 colon: option!(punct!(::)) >>
352 segments: call!(Punctuated::parse_separated_nonempty_with,
353 mod_style_path_segment) >>
354 (Path {
355 leading_colon: colon,
356 segments: segments,
357 })
358 ));
359 }
360
361 named!(mod_style_path_segment -> PathSegment, alt!(
362 syn!(Ident) => { Into::into }
363 |
364 keyword!(super) => { Into::into }
365 |
366 keyword!(self) => { Into::into }
367 |
368 keyword!(Self) => { Into::into }
369 |
370 keyword!(crate) => { Into::into }
371 ));
372
373 named!(pub qpath -> (Option<QSelf>, Path), alt!(
374 map!(syn!(Path), |p| (None, p))
375 |
376 do_parse!(
377 lt: punct!(<) >>
378 this: syn!(Type) >>
379 path: option!(tuple!(keyword!(as), syn!(Path))) >>
380 gt: punct!(>) >>
381 colon2: punct!(::) >>
382 rest: call!(Punctuated::parse_separated_nonempty) >>
383 ({
384 let (pos, as_, path) = match path {
385 Some((as_, mut path)) => {
386 let pos = path.segments.len();
387 path.segments.push_punct(colon2);
David Tolnay56080682018-01-06 14:01:52 -0800388 path.segments.extend(rest.into_pairs());
David Tolnay056de302018-01-05 14:29:05 -0800389 (pos, Some(as_), path)
390 }
391 None => {
392 (0, None, Path {
393 leading_colon: Some(colon2),
394 segments: rest,
395 })
396 }
397 };
398 (Some(QSelf {
399 lt_token: lt,
400 ty: Box::new(this),
401 position: pos,
402 as_token: as_,
403 gt_token: gt,
404 }), path)
405 })
406 )
407 |
408 map!(keyword!(self), |s| (None, s.into()))
409 ));
410
411 named!(pub ty_no_eq_after -> Type, do_parse!(
412 ty: syn!(Type) >>
413 not!(punct!(=)) >>
414 (ty)
415 ));
416}
417
418#[cfg(feature = "printing")]
419mod printing {
420 use super::*;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700421 use proc_macro2::TokenStream;
David Tolnay65fb5662018-05-20 20:02:28 -0700422 use quote::ToTokens;
David Tolnay056de302018-01-05 14:29:05 -0800423
424 impl ToTokens for Path {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700425 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800426 self.leading_colon.to_tokens(tokens);
427 self.segments.to_tokens(tokens);
428 }
429 }
430
431 impl ToTokens for PathSegment {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700432 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800433 self.ident.to_tokens(tokens);
434 self.arguments.to_tokens(tokens);
435 }
436 }
437
438 impl ToTokens for PathArguments {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700439 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800440 match *self {
441 PathArguments::None => {}
442 PathArguments::AngleBracketed(ref arguments) => {
443 arguments.to_tokens(tokens);
444 }
445 PathArguments::Parenthesized(ref arguments) => {
446 arguments.to_tokens(tokens);
447 }
448 }
449 }
450 }
451
452 impl ToTokens for GenericArgument {
453 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Alex Crichtona74a1c82018-05-16 10:20:44 -0700454 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800455 match *self {
456 GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
457 GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
458 GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
459 GenericArgument::Const(ref e) => match *e {
460 Expr::Lit(_) => e.to_tokens(tokens),
461
462 // NOTE: We should probably support parsing blocks with only
463 // expressions in them without the full feature for const
464 // generics.
465 #[cfg(feature = "full")]
466 Expr::Block(_) => e.to_tokens(tokens),
467
468 // ERROR CORRECTION: Add braces to make sure that the
469 // generated code is valid.
470 _ => token::Brace::default().surround(tokens, |tokens| {
471 e.to_tokens(tokens);
472 }),
473 },
474 }
475 }
476 }
477
478 impl ToTokens for AngleBracketedGenericArguments {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700479 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800480 self.colon2_token.to_tokens(tokens);
481 self.lt_token.to_tokens(tokens);
David Tolnay298570b2018-01-11 16:38:36 -0800482
483 // Print lifetimes before types and consts, all before bindings,
484 // regardless of their order in self.args.
485 //
486 // TODO: ordering rules for const arguments vs type arguments have
487 // not been settled yet. https://github.com/rust-lang/rust/issues/44580
488 let mut trailing_or_empty = true;
489 for param in self.args.pairs() {
490 if let GenericArgument::Lifetime(_) = **param.value() {
491 param.to_tokens(tokens);
492 trailing_or_empty = param.punct().is_some();
493 }
494 }
495 for param in self.args.pairs() {
496 match **param.value() {
497 GenericArgument::Type(_) | GenericArgument::Const(_) => {
498 if !trailing_or_empty {
499 <Token![,]>::default().to_tokens(tokens);
500 }
501 param.to_tokens(tokens);
502 trailing_or_empty = param.punct().is_some();
503 }
504 GenericArgument::Lifetime(_) | GenericArgument::Binding(_) => {}
505 }
506 }
507 for param in self.args.pairs() {
508 if let GenericArgument::Binding(_) = **param.value() {
509 if !trailing_or_empty {
510 <Token![,]>::default().to_tokens(tokens);
511 trailing_or_empty = true;
512 }
513 param.to_tokens(tokens);
514 }
515 }
516
David Tolnay056de302018-01-05 14:29:05 -0800517 self.gt_token.to_tokens(tokens);
518 }
519 }
520
521 impl ToTokens for Binding {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700522 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800523 self.ident.to_tokens(tokens);
524 self.eq_token.to_tokens(tokens);
525 self.ty.to_tokens(tokens);
526 }
527 }
528
529 impl ToTokens for ParenthesizedGenericArguments {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700530 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800531 self.paren_token.surround(tokens, |tokens| {
532 self.inputs.to_tokens(tokens);
533 });
534 self.output.to_tokens(tokens);
535 }
536 }
David Tolnay05658502018-01-07 09:56:37 -0800537
538 impl<'a> ToTokens for PathTokens<'a> {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700539 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay05658502018-01-07 09:56:37 -0800540 let qself = match *self.0 {
541 Some(ref qself) => qself,
542 None => return self.1.to_tokens(tokens),
543 };
544 qself.lt_token.to_tokens(tokens);
545 qself.ty.to_tokens(tokens);
546
547 // XXX: Gross.
548 let pos = if qself.position > 0 && qself.position >= self.1.segments.len() {
549 self.1.segments.len() - 1
550 } else {
551 qself.position
552 };
553 let mut segments = self.1.segments.pairs();
554 if pos > 0 {
555 TokensOrDefault(&qself.as_token).to_tokens(tokens);
556 self.1.leading_colon.to_tokens(tokens);
557 for (i, segment) in segments.by_ref().take(pos).enumerate() {
558 if i + 1 == pos {
559 segment.value().to_tokens(tokens);
560 qself.gt_token.to_tokens(tokens);
561 segment.punct().to_tokens(tokens);
562 } else {
563 segment.to_tokens(tokens);
564 }
565 }
566 } else {
567 qself.gt_token.to_tokens(tokens);
568 self.1.leading_colon.to_tokens(tokens);
569 }
570 for segment in segments {
571 segment.to_tokens(tokens);
572 }
573 }
574 }
David Tolnay056de302018-01-05 14:29:05 -0800575}