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