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