blob: b6c7f6d936c253e95fd5e5556ae9e73d73ec1b10 [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 punctuated::Punctuated;
10use super::*;
11
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 Tolnaye826d812018-01-06 23:59:39 -080097 /// Bracketed or parenthesized arguments of a path segment.
David Tolnay056de302018-01-05 14:29:05 -080098 ///
David Tolnaye826d812018-01-06 23:59:39 -080099 /// E.g. `<K, V>` as in `HashMap<K, V>` or `(A, B) -> C` as in `Fn(A, B) -> C`
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 Tolnay056de302018-01-05 14:29:05 -0800103 pub enum PathArguments {
104 None,
David Tolnaye826d812018-01-06 23:59:39 -0800105 /// The `<'a, T>` in `std::slice::iter<'a, T>`.
David Tolnay056de302018-01-05 14:29:05 -0800106 AngleBracketed(AngleBracketedGenericArguments),
David Tolnaye826d812018-01-06 23:59:39 -0800107 /// The `(A, B)` and `C` in `Fn(A, B) -> C`.
David Tolnay056de302018-01-05 14:29:05 -0800108 Parenthesized(ParenthesizedGenericArguments),
109 }
110}
111
112impl Default for PathArguments {
113 fn default() -> Self {
114 PathArguments::None
115 }
116}
117
118impl PathArguments {
119 pub fn is_empty(&self) -> bool {
120 match *self {
121 PathArguments::None => true,
122 PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
123 PathArguments::Parenthesized(_) => false,
124 }
125 }
126}
127
128ast_enum! {
David Tolnaya454c8f2018-01-07 01:01:10 -0800129 /// An individual generic argument, like `'a`, `T`, or `Item = T`.
David Tolnay461d98e2018-01-07 11:07:19 -0800130 ///
131 /// *This type is available if Syn is built with the `"derive"` or `"full"`
132 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800133 pub enum GenericArgument {
David Tolnaye826d812018-01-06 23:59:39 -0800134 /// A lifetime argument.
David Tolnay056de302018-01-05 14:29:05 -0800135 Lifetime(Lifetime),
David Tolnaye826d812018-01-06 23:59:39 -0800136 /// A type argument.
David Tolnay056de302018-01-05 14:29:05 -0800137 Type(Type),
David Tolnaye826d812018-01-06 23:59:39 -0800138 /// A binding (equality constraint) on an associated type.
David Tolnay056de302018-01-05 14:29:05 -0800139 ///
David Tolnaye826d812018-01-06 23:59:39 -0800140 /// E.g. in `Iterator<Item = u8>`.
David Tolnay056de302018-01-05 14:29:05 -0800141 Binding(Binding),
David Tolnaye826d812018-01-06 23:59:39 -0800142 /// A const expression. Must be inside of a block.
David Tolnay056de302018-01-05 14:29:05 -0800143 ///
144 /// NOTE: Identity expressions are represented as Type arguments, as
145 /// they are indistinguishable syntactically.
146 Const(Expr),
147 }
148}
149
150ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800151 /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
152 /// V>`.
David Tolnay461d98e2018-01-07 11:07:19 -0800153 ///
154 /// *This type is available if Syn is built with the `"derive"` or `"full"`
155 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800156 pub struct AngleBracketedGenericArguments {
157 pub colon2_token: Option<Token![::]>,
158 pub lt_token: Token![<],
159 pub args: Punctuated<GenericArgument, Token![,]>,
160 pub gt_token: Token![>],
161 }
162}
163
164ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800165 /// A binding (equality constraint) on an associated type: `Item = u8`.
David Tolnay461d98e2018-01-07 11:07:19 -0800166 ///
167 /// *This type is available if Syn is built with the `"derive"` or `"full"`
168 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800169 pub struct Binding {
170 pub ident: Ident,
171 pub eq_token: Token![=],
172 pub ty: Type,
173 }
174}
175
176ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800177 /// Arguments of a function path segment: the `(A, B)` and `C` in `Fn(A,B)
178 /// -> C`.
David Tolnay461d98e2018-01-07 11:07:19 -0800179 ///
180 /// *This type is available if Syn is built with the `"derive"` or `"full"`
181 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800182 pub struct ParenthesizedGenericArguments {
183 pub paren_token: token::Paren,
184 /// `(A, B)`
185 pub inputs: Punctuated<Type, Token![,]>,
186 /// `C`
187 pub output: ReturnType,
188 }
189}
190
191ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800192 /// The explicit Self type in a qualified path: the `T` in `<T as
David Tolnay05658502018-01-07 09:56:37 -0800193 /// Display>::fmt`.
David Tolnaye826d812018-01-06 23:59:39 -0800194 ///
195 /// The actual path, including the trait and the associated item, is stored
196 /// separately. The `position` field represents the index of the associated
David Tolnay056de302018-01-05 14:29:05 -0800197 /// item qualified with this Self type.
198 ///
199 /// ```text
200 /// <Vec<T> as a::b::Trait>::AssociatedItem
201 /// ^~~~~~ ~~~~~~~~~~~~~~^
202 /// ty position = 3
203 ///
204 /// <Vec<T>>::AssociatedItem
205 /// ^~~~~~ ^
206 /// ty position = 0
207 /// ```
David Tolnay461d98e2018-01-07 11:07:19 -0800208 ///
209 /// *This type is available if Syn is built with the `"derive"` or `"full"`
210 /// feature.*
David Tolnay056de302018-01-05 14:29:05 -0800211 pub struct QSelf {
212 pub lt_token: Token![<],
213 pub ty: Box<Type>,
214 pub position: usize,
215 pub as_token: Option<Token![as]>,
216 pub gt_token: Token![>],
217 }
218}
219
220#[cfg(feature = "parsing")]
221pub mod parsing {
222 use super::*;
223 use synom::Synom;
224
225 impl Synom for Path {
226 named!(parse -> Self, do_parse!(
227 colon: option!(punct!(::)) >>
228 segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
David Tolnay56080682018-01-06 14:01:52 -0800229 cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn"), epsilon!()) >>
David Tolnay056de302018-01-05 14:29:05 -0800230 (Path {
231 leading_colon: colon,
232 segments: segments,
233 })
234 ));
235
236 fn description() -> Option<&'static str> {
237 Some("path")
238 }
239 }
240
241 #[cfg(not(feature = "full"))]
242 impl Synom for GenericArgument {
243 named!(parse -> Self, alt!(
244 call!(ty_no_eq_after) => { GenericArgument::Type }
245 |
246 syn!(Lifetime) => { GenericArgument::Lifetime }
247 |
248 syn!(Binding) => { GenericArgument::Binding }
249 ));
250 }
251
252 #[cfg(feature = "full")]
253 impl Synom for GenericArgument {
254 named!(parse -> Self, alt!(
255 call!(ty_no_eq_after) => { GenericArgument::Type }
256 |
257 syn!(Lifetime) => { GenericArgument::Lifetime }
258 |
259 syn!(Binding) => { GenericArgument::Binding }
260 |
261 syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
262 |
263 syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
264 ));
265
266 fn description() -> Option<&'static str> {
267 Some("generic argument")
268 }
269 }
270
271 impl Synom for AngleBracketedGenericArguments {
272 named!(parse -> Self, do_parse!(
273 colon2: option!(punct!(::)) >>
274 lt: punct!(<) >>
275 args: call!(Punctuated::parse_terminated) >>
276 gt: punct!(>) >>
277 (AngleBracketedGenericArguments {
278 colon2_token: colon2,
279 lt_token: lt,
280 args: args,
281 gt_token: gt,
282 })
283 ));
284
285 fn description() -> Option<&'static str> {
286 Some("angle bracketed generic arguments")
287 }
288 }
289
290 impl Synom for ParenthesizedGenericArguments {
291 named!(parse -> Self, do_parse!(
292 data: parens!(Punctuated::parse_terminated) >>
293 output: syn!(ReturnType) >>
294 (ParenthesizedGenericArguments {
295 paren_token: data.0,
296 inputs: data.1,
297 output: output,
298 })
299 ));
300
301 fn description() -> Option<&'static str> {
302 Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
303 }
304 }
305
306 impl Synom for PathSegment {
307 named!(parse -> Self, alt!(
308 do_parse!(
309 ident: syn!(Ident) >>
310 arguments: syn!(AngleBracketedGenericArguments) >>
311 (PathSegment {
312 ident: ident,
313 arguments: PathArguments::AngleBracketed(arguments),
314 })
315 )
316 |
317 mod_style_path_segment
318 ));
319
320 fn description() -> Option<&'static str> {
321 Some("path segment")
322 }
323 }
324
325 impl Synom for Binding {
326 named!(parse -> Self, do_parse!(
327 id: syn!(Ident) >>
328 eq: punct!(=) >>
329 ty: syn!(Type) >>
330 (Binding {
331 ident: id,
332 eq_token: eq,
333 ty: ty,
334 })
335 ));
336
337 fn description() -> Option<&'static str> {
338 Some("associated type binding")
339 }
340 }
341
342 impl Path {
343 named!(pub parse_mod_style -> Self, do_parse!(
344 colon: option!(punct!(::)) >>
345 segments: call!(Punctuated::parse_separated_nonempty_with,
346 mod_style_path_segment) >>
347 (Path {
348 leading_colon: colon,
349 segments: segments,
350 })
351 ));
352 }
353
354 named!(mod_style_path_segment -> PathSegment, alt!(
355 syn!(Ident) => { Into::into }
356 |
357 keyword!(super) => { Into::into }
358 |
359 keyword!(self) => { Into::into }
360 |
361 keyword!(Self) => { Into::into }
362 |
363 keyword!(crate) => { Into::into }
364 ));
365
366 named!(pub qpath -> (Option<QSelf>, Path), alt!(
367 map!(syn!(Path), |p| (None, p))
368 |
369 do_parse!(
370 lt: punct!(<) >>
371 this: syn!(Type) >>
372 path: option!(tuple!(keyword!(as), syn!(Path))) >>
373 gt: punct!(>) >>
374 colon2: punct!(::) >>
375 rest: call!(Punctuated::parse_separated_nonempty) >>
376 ({
377 let (pos, as_, path) = match path {
378 Some((as_, mut path)) => {
379 let pos = path.segments.len();
380 path.segments.push_punct(colon2);
David Tolnay56080682018-01-06 14:01:52 -0800381 path.segments.extend(rest.into_pairs());
David Tolnay056de302018-01-05 14:29:05 -0800382 (pos, Some(as_), path)
383 }
384 None => {
385 (0, None, Path {
386 leading_colon: Some(colon2),
387 segments: rest,
388 })
389 }
390 };
391 (Some(QSelf {
392 lt_token: lt,
393 ty: Box::new(this),
394 position: pos,
395 as_token: as_,
396 gt_token: gt,
397 }), path)
398 })
399 )
400 |
401 map!(keyword!(self), |s| (None, s.into()))
402 ));
403
404 named!(pub ty_no_eq_after -> Type, do_parse!(
405 ty: syn!(Type) >>
406 not!(punct!(=)) >>
407 (ty)
408 ));
409}
410
411#[cfg(feature = "printing")]
412mod printing {
413 use super::*;
414 use quote::{ToTokens, Tokens};
415
416 impl ToTokens for Path {
417 fn to_tokens(&self, tokens: &mut Tokens) {
418 self.leading_colon.to_tokens(tokens);
419 self.segments.to_tokens(tokens);
420 }
421 }
422
423 impl ToTokens for PathSegment {
424 fn to_tokens(&self, tokens: &mut Tokens) {
425 self.ident.to_tokens(tokens);
426 self.arguments.to_tokens(tokens);
427 }
428 }
429
430 impl ToTokens for PathArguments {
431 fn to_tokens(&self, tokens: &mut Tokens) {
432 match *self {
433 PathArguments::None => {}
434 PathArguments::AngleBracketed(ref arguments) => {
435 arguments.to_tokens(tokens);
436 }
437 PathArguments::Parenthesized(ref arguments) => {
438 arguments.to_tokens(tokens);
439 }
440 }
441 }
442 }
443
444 impl ToTokens for GenericArgument {
445 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
446 fn to_tokens(&self, tokens: &mut Tokens) {
447 match *self {
448 GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
449 GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
450 GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
451 GenericArgument::Const(ref e) => match *e {
452 Expr::Lit(_) => e.to_tokens(tokens),
453
454 // NOTE: We should probably support parsing blocks with only
455 // expressions in them without the full feature for const
456 // generics.
457 #[cfg(feature = "full")]
458 Expr::Block(_) => e.to_tokens(tokens),
459
460 // ERROR CORRECTION: Add braces to make sure that the
461 // generated code is valid.
462 _ => token::Brace::default().surround(tokens, |tokens| {
463 e.to_tokens(tokens);
464 }),
465 },
466 }
467 }
468 }
469
470 impl ToTokens for AngleBracketedGenericArguments {
471 fn to_tokens(&self, tokens: &mut Tokens) {
472 self.colon2_token.to_tokens(tokens);
473 self.lt_token.to_tokens(tokens);
474 self.args.to_tokens(tokens);
475 self.gt_token.to_tokens(tokens);
476 }
477 }
478
479 impl ToTokens for Binding {
480 fn to_tokens(&self, tokens: &mut Tokens) {
481 self.ident.to_tokens(tokens);
482 self.eq_token.to_tokens(tokens);
483 self.ty.to_tokens(tokens);
484 }
485 }
486
487 impl ToTokens for ParenthesizedGenericArguments {
488 fn to_tokens(&self, tokens: &mut Tokens) {
489 self.paren_token.surround(tokens, |tokens| {
490 self.inputs.to_tokens(tokens);
491 });
492 self.output.to_tokens(tokens);
493 }
494 }
David Tolnay05658502018-01-07 09:56:37 -0800495
496 impl<'a> ToTokens for PathTokens<'a> {
497 fn to_tokens(&self, tokens: &mut Tokens) {
498 let qself = match *self.0 {
499 Some(ref qself) => qself,
500 None => return self.1.to_tokens(tokens),
501 };
502 qself.lt_token.to_tokens(tokens);
503 qself.ty.to_tokens(tokens);
504
505 // XXX: Gross.
506 let pos = if qself.position > 0 && qself.position >= self.1.segments.len() {
507 self.1.segments.len() - 1
508 } else {
509 qself.position
510 };
511 let mut segments = self.1.segments.pairs();
512 if pos > 0 {
513 TokensOrDefault(&qself.as_token).to_tokens(tokens);
514 self.1.leading_colon.to_tokens(tokens);
515 for (i, segment) in segments.by_ref().take(pos).enumerate() {
516 if i + 1 == pos {
517 segment.value().to_tokens(tokens);
518 qself.gt_token.to_tokens(tokens);
519 segment.punct().to_tokens(tokens);
520 } else {
521 segment.to_tokens(tokens);
522 }
523 }
524 } else {
525 qself.gt_token.to_tokens(tokens);
526 self.1.leading_colon.to_tokens(tokens);
527 }
528 for segment in segments {
529 segment.to_tokens(tokens);
530 }
531 }
532 }
David Tolnay056de302018-01-05 14:29:05 -0800533}