blob: f27d6b24fc3dc0fe80c0c6726cdc73198555b91b [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::*;
Alex Crichtona74a1c82018-05-16 10:20:44 -070010use proc_macro2::Ident;
David Tolnay94d2b792018-04-29 12:26:10 -070011use punctuated::Punctuated;
David Tolnay056de302018-01-05 14:29:05 -080012
13ast_struct! {
David Tolnay05658502018-01-07 09:56:37 -080014 /// A path at which a named item is exported: `std::collections::HashMap`.
David Tolnay461d98e2018-01-07 11:07:19 -080015 ///
16 /// *This type is available if Syn is built with the `"derive"` or `"full"`
17 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -080018 pub struct Path {
David Tolnay056de302018-01-05 14:29:05 -080019 pub leading_colon: Option<Token![::]>,
David Tolnay056de302018-01-05 14:29:05 -080020 pub segments: Punctuated<PathSegment, Token![::]>,
21 }
22}
23
24impl Path {
25 pub fn global(&self) -> bool {
26 self.leading_colon.is_some()
27 }
28}
29
David Tolnay05658502018-01-07 09:56:37 -080030/// A helper for printing a self-type qualified path as tokens.
31///
32/// ```rust
33/// extern crate syn;
34/// extern crate quote;
35///
36/// use syn::{QSelf, Path, PathTokens};
37/// use quote::{Tokens, ToTokens};
38///
39/// struct MyNode {
40/// qself: Option<QSelf>,
41/// path: Path,
42/// }
43///
44/// impl ToTokens for MyNode {
Alex Crichtona74a1c82018-05-16 10:20:44 -070045/// fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay05658502018-01-07 09:56:37 -080046/// PathTokens(&self.qself, &self.path).to_tokens(tokens);
47/// }
48/// }
49/// #
50/// # fn main() {}
51/// ```
David Tolnay461d98e2018-01-07 11:07:19 -080052///
53/// *This type is available if Syn is built with the `"derive"` or `"full"`
54/// feature and the `"printing"` feature.*
David Tolnay056de302018-01-05 14:29:05 -080055#[cfg(feature = "printing")]
56#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
57#[cfg_attr(feature = "clone-impls", derive(Clone))]
58pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);
59
60impl<T> From<T> for Path
61where
62 T: Into<PathSegment>,
63{
64 fn from(segment: T) -> Self {
65 let mut path = Path {
66 leading_colon: None,
67 segments: Punctuated::new(),
68 };
David Tolnay56080682018-01-06 14:01:52 -080069 path.segments.push_value(segment.into());
David Tolnay056de302018-01-05 14:29:05 -080070 path
71 }
72}
73
74ast_struct! {
David Tolnay05658502018-01-07 09:56:37 -080075 /// A segment of a path together with any path arguments on that segment.
David Tolnay461d98e2018-01-07 11:07:19 -080076 ///
77 /// *This type is available if Syn is built with the `"derive"` or `"full"`
78 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -080079 pub struct PathSegment {
David Tolnay056de302018-01-05 14:29:05 -080080 pub ident: Ident,
David Tolnay056de302018-01-05 14:29:05 -080081 pub arguments: PathArguments,
82 }
83}
84
85impl<T> From<T> for PathSegment
86where
87 T: Into<Ident>,
88{
89 fn from(ident: T) -> Self {
90 PathSegment {
91 ident: ident.into(),
92 arguments: PathArguments::None,
93 }
94 }
95}
96
97ast_enum! {
David Tolnayc0435192018-01-07 11:46:08 -080098 /// Angle bracketed or parenthesized arguments of a path segment.
David Tolnay461d98e2018-01-07 11:07:19 -080099 ///
100 /// *This type is available if Syn is built with the `"derive"` or `"full"`
101 /// feature.*
David Tolnayc0435192018-01-07 11:46:08 -0800102 ///
103 /// ## Angle bracketed
104 ///
105 /// The `<'a, T>` in `std::slice::iter<'a, T>`.
106 ///
107 /// ## Parenthesized
108 ///
109 /// The `(A, B) -> C` in `Fn(A, B) -> C`.
David Tolnay056de302018-01-05 14:29:05 -0800110 pub enum PathArguments {
111 None,
David Tolnaye826d812018-01-06 23:59:39 -0800112 /// The `<'a, T>` in `std::slice::iter<'a, T>`.
David Tolnay056de302018-01-05 14:29:05 -0800113 AngleBracketed(AngleBracketedGenericArguments),
David Tolnayc0435192018-01-07 11:46:08 -0800114 /// The `(A, B) -> C` in `Fn(A, B) -> C`.
David Tolnay056de302018-01-05 14:29:05 -0800115 Parenthesized(ParenthesizedGenericArguments),
116 }
117}
118
119impl Default for PathArguments {
120 fn default() -> Self {
121 PathArguments::None
122 }
123}
124
125impl PathArguments {
126 pub fn is_empty(&self) -> bool {
127 match *self {
128 PathArguments::None => true,
129 PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
130 PathArguments::Parenthesized(_) => false,
131 }
132 }
133}
134
135ast_enum! {
David Tolnaya454c8f2018-01-07 01:01:10 -0800136 /// An individual generic argument, like `'a`, `T`, or `Item = T`.
David Tolnay461d98e2018-01-07 11:07:19 -0800137 ///
138 /// *This type is available if Syn is built with the `"derive"` or `"full"`
139 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800140 pub enum GenericArgument {
David Tolnaye826d812018-01-06 23:59:39 -0800141 /// A lifetime argument.
David Tolnay056de302018-01-05 14:29:05 -0800142 Lifetime(Lifetime),
David Tolnaye826d812018-01-06 23:59:39 -0800143 /// A type argument.
David Tolnay056de302018-01-05 14:29:05 -0800144 Type(Type),
David Tolnayc0435192018-01-07 11:46:08 -0800145 /// A binding (equality constraint) on an associated type: the `Item =
146 /// u8` in `Iterator<Item = u8>`.
David Tolnay056de302018-01-05 14:29:05 -0800147 Binding(Binding),
David Tolnaye826d812018-01-06 23:59:39 -0800148 /// A const expression. Must be inside of a block.
David Tolnay056de302018-01-05 14:29:05 -0800149 ///
150 /// NOTE: Identity expressions are represented as Type arguments, as
151 /// they are indistinguishable syntactically.
152 Const(Expr),
153 }
154}
155
156ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800157 /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
158 /// V>`.
David Tolnay461d98e2018-01-07 11:07:19 -0800159 ///
160 /// *This type is available if Syn is built with the `"derive"` or `"full"`
161 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800162 pub struct AngleBracketedGenericArguments {
163 pub colon2_token: Option<Token![::]>,
164 pub lt_token: Token![<],
165 pub args: Punctuated<GenericArgument, Token![,]>,
166 pub gt_token: Token![>],
167 }
168}
169
170ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800171 /// A binding (equality constraint) on an associated type: `Item = u8`.
David Tolnay461d98e2018-01-07 11:07:19 -0800172 ///
173 /// *This type is available if Syn is built with the `"derive"` or `"full"`
174 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800175 pub struct Binding {
176 pub ident: Ident,
177 pub eq_token: Token![=],
178 pub ty: Type,
179 }
180}
181
182ast_struct! {
David Tolnayc0435192018-01-07 11:46:08 -0800183 /// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) ->
184 /// C`.
David Tolnay461d98e2018-01-07 11:07:19 -0800185 ///
186 /// *This type is available if Syn is built with the `"derive"` or `"full"`
187 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800188 pub struct ParenthesizedGenericArguments {
189 pub paren_token: token::Paren,
190 /// `(A, B)`
191 pub inputs: Punctuated<Type, Token![,]>,
192 /// `C`
193 pub output: ReturnType,
194 }
195}
196
197ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800198 /// The explicit Self type in a qualified path: the `T` in `<T as
David Tolnay05658502018-01-07 09:56:37 -0800199 /// Display>::fmt`.
David Tolnaye826d812018-01-06 23:59:39 -0800200 ///
201 /// The actual path, including the trait and the associated item, is stored
202 /// separately. The `position` field represents the index of the associated
David Tolnay056de302018-01-05 14:29:05 -0800203 /// item qualified with this Self type.
204 ///
205 /// ```text
206 /// <Vec<T> as a::b::Trait>::AssociatedItem
207 /// ^~~~~~ ~~~~~~~~~~~~~~^
208 /// ty position = 3
209 ///
210 /// <Vec<T>>::AssociatedItem
211 /// ^~~~~~ ^
212 /// ty position = 0
213 /// ```
David Tolnay461d98e2018-01-07 11:07:19 -0800214 ///
215 /// *This type is available if Syn is built with the `"derive"` or `"full"`
216 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800217 pub struct QSelf {
218 pub lt_token: Token![<],
219 pub ty: Box<Type>,
220 pub position: usize,
221 pub as_token: Option<Token![as]>,
222 pub gt_token: Token![>],
223 }
224}
225
226#[cfg(feature = "parsing")]
227pub mod parsing {
228 use super::*;
229 use synom::Synom;
230
231 impl Synom for Path {
232 named!(parse -> Self, do_parse!(
233 colon: option!(punct!(::)) >>
234 segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
Alex Crichtona74a1c82018-05-16 10:20:44 -0700235 cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident.to_string() != "dyn")) >>
David Tolnay056de302018-01-05 14:29:05 -0800236 (Path {
237 leading_colon: colon,
238 segments: segments,
239 })
240 ));
241
242 fn description() -> Option<&'static str> {
243 Some("path")
244 }
245 }
246
247 #[cfg(not(feature = "full"))]
248 impl Synom for GenericArgument {
249 named!(parse -> Self, alt!(
250 call!(ty_no_eq_after) => { GenericArgument::Type }
251 |
252 syn!(Lifetime) => { GenericArgument::Lifetime }
253 |
254 syn!(Binding) => { GenericArgument::Binding }
255 ));
256 }
257
258 #[cfg(feature = "full")]
259 impl Synom for GenericArgument {
260 named!(parse -> Self, alt!(
261 call!(ty_no_eq_after) => { GenericArgument::Type }
262 |
263 syn!(Lifetime) => { GenericArgument::Lifetime }
264 |
265 syn!(Binding) => { GenericArgument::Binding }
266 |
267 syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
268 |
269 syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
270 ));
271
272 fn description() -> Option<&'static str> {
273 Some("generic argument")
274 }
275 }
276
277 impl Synom for AngleBracketedGenericArguments {
278 named!(parse -> Self, do_parse!(
279 colon2: option!(punct!(::)) >>
280 lt: punct!(<) >>
281 args: call!(Punctuated::parse_terminated) >>
282 gt: punct!(>) >>
283 (AngleBracketedGenericArguments {
284 colon2_token: colon2,
285 lt_token: lt,
286 args: args,
287 gt_token: gt,
288 })
289 ));
290
291 fn description() -> Option<&'static str> {
292 Some("angle bracketed generic arguments")
293 }
294 }
295
296 impl Synom for ParenthesizedGenericArguments {
297 named!(parse -> Self, do_parse!(
298 data: parens!(Punctuated::parse_terminated) >>
299 output: syn!(ReturnType) >>
300 (ParenthesizedGenericArguments {
301 paren_token: data.0,
302 inputs: data.1,
303 output: output,
304 })
305 ));
306
307 fn description() -> Option<&'static str> {
308 Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
309 }
310 }
311
312 impl Synom for PathSegment {
313 named!(parse -> Self, alt!(
314 do_parse!(
315 ident: syn!(Ident) >>
316 arguments: syn!(AngleBracketedGenericArguments) >>
317 (PathSegment {
318 ident: ident,
319 arguments: PathArguments::AngleBracketed(arguments),
320 })
321 )
322 |
323 mod_style_path_segment
324 ));
325
326 fn description() -> Option<&'static str> {
327 Some("path segment")
328 }
329 }
330
331 impl Synom for Binding {
332 named!(parse -> Self, do_parse!(
333 id: syn!(Ident) >>
334 eq: punct!(=) >>
335 ty: syn!(Type) >>
336 (Binding {
337 ident: id,
338 eq_token: eq,
339 ty: ty,
340 })
341 ));
342
343 fn description() -> Option<&'static str> {
344 Some("associated type binding")
345 }
346 }
347
348 impl Path {
349 named!(pub parse_mod_style -> Self, do_parse!(
350 colon: option!(punct!(::)) >>
351 segments: call!(Punctuated::parse_separated_nonempty_with,
352 mod_style_path_segment) >>
353 (Path {
354 leading_colon: colon,
355 segments: segments,
356 })
357 ));
358 }
359
360 named!(mod_style_path_segment -> PathSegment, alt!(
361 syn!(Ident) => { Into::into }
362 |
363 keyword!(super) => { Into::into }
364 |
365 keyword!(self) => { Into::into }
366 |
367 keyword!(Self) => { Into::into }
368 |
369 keyword!(crate) => { Into::into }
370 ));
371
372 named!(pub qpath -> (Option<QSelf>, Path), alt!(
373 map!(syn!(Path), |p| (None, p))
374 |
375 do_parse!(
376 lt: punct!(<) >>
377 this: syn!(Type) >>
378 path: option!(tuple!(keyword!(as), syn!(Path))) >>
379 gt: punct!(>) >>
380 colon2: punct!(::) >>
381 rest: call!(Punctuated::parse_separated_nonempty) >>
382 ({
383 let (pos, as_, path) = match path {
384 Some((as_, mut path)) => {
385 let pos = path.segments.len();
386 path.segments.push_punct(colon2);
David Tolnay56080682018-01-06 14:01:52 -0800387 path.segments.extend(rest.into_pairs());
David Tolnay056de302018-01-05 14:29:05 -0800388 (pos, Some(as_), path)
389 }
390 None => {
391 (0, None, Path {
392 leading_colon: Some(colon2),
393 segments: rest,
394 })
395 }
396 };
397 (Some(QSelf {
398 lt_token: lt,
399 ty: Box::new(this),
400 position: pos,
401 as_token: as_,
402 gt_token: gt,
403 }), path)
404 })
405 )
406 |
407 map!(keyword!(self), |s| (None, s.into()))
408 ));
409
410 named!(pub ty_no_eq_after -> Type, do_parse!(
411 ty: syn!(Type) >>
412 not!(punct!(=)) >>
413 (ty)
414 ));
415}
416
417#[cfg(feature = "printing")]
418mod printing {
419 use super::*;
Alex Crichtona74a1c82018-05-16 10:20:44 -0700420 use quote::ToTokens;
421 use proc_macro2::TokenStream;
David Tolnay056de302018-01-05 14:29:05 -0800422
423 impl ToTokens for Path {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700424 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800425 self.leading_colon.to_tokens(tokens);
426 self.segments.to_tokens(tokens);
427 }
428 }
429
430 impl ToTokens for PathSegment {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700431 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800432 self.ident.to_tokens(tokens);
433 self.arguments.to_tokens(tokens);
434 }
435 }
436
437 impl ToTokens for PathArguments {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700438 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800439 match *self {
440 PathArguments::None => {}
441 PathArguments::AngleBracketed(ref arguments) => {
442 arguments.to_tokens(tokens);
443 }
444 PathArguments::Parenthesized(ref arguments) => {
445 arguments.to_tokens(tokens);
446 }
447 }
448 }
449 }
450
451 impl ToTokens for GenericArgument {
452 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
Alex Crichtona74a1c82018-05-16 10:20:44 -0700453 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800454 match *self {
455 GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
456 GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
457 GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
458 GenericArgument::Const(ref e) => match *e {
459 Expr::Lit(_) => e.to_tokens(tokens),
460
461 // NOTE: We should probably support parsing blocks with only
462 // expressions in them without the full feature for const
463 // generics.
464 #[cfg(feature = "full")]
465 Expr::Block(_) => e.to_tokens(tokens),
466
467 // ERROR CORRECTION: Add braces to make sure that the
468 // generated code is valid.
469 _ => token::Brace::default().surround(tokens, |tokens| {
470 e.to_tokens(tokens);
471 }),
472 },
473 }
474 }
475 }
476
477 impl ToTokens for AngleBracketedGenericArguments {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700478 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800479 self.colon2_token.to_tokens(tokens);
480 self.lt_token.to_tokens(tokens);
David Tolnay298570b2018-01-11 16:38:36 -0800481
482 // Print lifetimes before types and consts, all before bindings,
483 // regardless of their order in self.args.
484 //
485 // TODO: ordering rules for const arguments vs type arguments have
486 // not been settled yet. https://github.com/rust-lang/rust/issues/44580
487 let mut trailing_or_empty = true;
488 for param in self.args.pairs() {
489 if let GenericArgument::Lifetime(_) = **param.value() {
490 param.to_tokens(tokens);
491 trailing_or_empty = param.punct().is_some();
492 }
493 }
494 for param in self.args.pairs() {
495 match **param.value() {
496 GenericArgument::Type(_) | GenericArgument::Const(_) => {
497 if !trailing_or_empty {
498 <Token![,]>::default().to_tokens(tokens);
499 }
500 param.to_tokens(tokens);
501 trailing_or_empty = param.punct().is_some();
502 }
503 GenericArgument::Lifetime(_) | GenericArgument::Binding(_) => {}
504 }
505 }
506 for param in self.args.pairs() {
507 if let GenericArgument::Binding(_) = **param.value() {
508 if !trailing_or_empty {
509 <Token![,]>::default().to_tokens(tokens);
510 trailing_or_empty = true;
511 }
512 param.to_tokens(tokens);
513 }
514 }
515
David Tolnay056de302018-01-05 14:29:05 -0800516 self.gt_token.to_tokens(tokens);
517 }
518 }
519
520 impl ToTokens for Binding {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700521 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800522 self.ident.to_tokens(tokens);
523 self.eq_token.to_tokens(tokens);
524 self.ty.to_tokens(tokens);
525 }
526 }
527
528 impl ToTokens for ParenthesizedGenericArguments {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700529 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay056de302018-01-05 14:29:05 -0800530 self.paren_token.surround(tokens, |tokens| {
531 self.inputs.to_tokens(tokens);
532 });
533 self.output.to_tokens(tokens);
534 }
535 }
David Tolnay05658502018-01-07 09:56:37 -0800536
537 impl<'a> ToTokens for PathTokens<'a> {
Alex Crichtona74a1c82018-05-16 10:20:44 -0700538 fn to_tokens(&self, tokens: &mut TokenStream) {
David Tolnay05658502018-01-07 09:56:37 -0800539 let qself = match *self.0 {
540 Some(ref qself) => qself,
541 None => return self.1.to_tokens(tokens),
542 };
543 qself.lt_token.to_tokens(tokens);
544 qself.ty.to_tokens(tokens);
545
546 // XXX: Gross.
547 let pos = if qself.position > 0 && qself.position >= self.1.segments.len() {
548 self.1.segments.len() - 1
549 } else {
550 qself.position
551 };
552 let mut segments = self.1.segments.pairs();
553 if pos > 0 {
554 TokensOrDefault(&qself.as_token).to_tokens(tokens);
555 self.1.leading_colon.to_tokens(tokens);
556 for (i, segment) in segments.by_ref().take(pos).enumerate() {
557 if i + 1 == pos {
558 segment.value().to_tokens(tokens);
559 qself.gt_token.to_tokens(tokens);
560 segment.punct().to_tokens(tokens);
561 } else {
562 segment.to_tokens(tokens);
563 }
564 }
565 } else {
566 qself.gt_token.to_tokens(tokens);
567 self.1.leading_colon.to_tokens(tokens);
568 }
569 for segment in segments {
570 segment.to_tokens(tokens);
571 }
572 }
573 }
David Tolnay056de302018-01-05 14:29:05 -0800574}