blob: 27091b265f7430a0d76c03833f23e9dcf7dab668 [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 Tolnay056de302018-01-05 14:29:05 -080014 pub struct Path {
David Tolnay056de302018-01-05 14:29:05 -080015 pub leading_colon: Option<Token![::]>,
David Tolnay056de302018-01-05 14:29:05 -080016 pub segments: Punctuated<PathSegment, Token![::]>,
17 }
18}
19
20impl Path {
21 pub fn global(&self) -> bool {
22 self.leading_colon.is_some()
23 }
24}
25
David Tolnay05658502018-01-07 09:56:37 -080026/// A helper for printing a self-type qualified path as tokens.
27///
28/// ```rust
29/// extern crate syn;
30/// extern crate quote;
31///
32/// use syn::{QSelf, Path, PathTokens};
33/// use quote::{Tokens, ToTokens};
34///
35/// struct MyNode {
36/// qself: Option<QSelf>,
37/// path: Path,
38/// }
39///
40/// impl ToTokens for MyNode {
41/// fn to_tokens(&self, tokens: &mut Tokens) {
42/// PathTokens(&self.qself, &self.path).to_tokens(tokens);
43/// }
44/// }
45/// #
46/// # fn main() {}
47/// ```
David Tolnay056de302018-01-05 14:29:05 -080048#[cfg(feature = "printing")]
49#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
50#[cfg_attr(feature = "clone-impls", derive(Clone))]
51pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);
52
53impl<T> From<T> for Path
54where
55 T: Into<PathSegment>,
56{
57 fn from(segment: T) -> Self {
58 let mut path = Path {
59 leading_colon: None,
60 segments: Punctuated::new(),
61 };
David Tolnay56080682018-01-06 14:01:52 -080062 path.segments.push_value(segment.into());
David Tolnay056de302018-01-05 14:29:05 -080063 path
64 }
65}
66
67ast_struct! {
David Tolnay05658502018-01-07 09:56:37 -080068 /// A segment of a path together with any path arguments on that segment.
David Tolnay056de302018-01-05 14:29:05 -080069 pub struct PathSegment {
David Tolnay056de302018-01-05 14:29:05 -080070 pub ident: Ident,
David Tolnay056de302018-01-05 14:29:05 -080071 pub arguments: PathArguments,
72 }
73}
74
75impl<T> From<T> for PathSegment
76where
77 T: Into<Ident>,
78{
79 fn from(ident: T) -> Self {
80 PathSegment {
81 ident: ident.into(),
82 arguments: PathArguments::None,
83 }
84 }
85}
86
87ast_enum! {
David Tolnaye826d812018-01-06 23:59:39 -080088 /// Bracketed or parenthesized arguments of a path segment.
David Tolnay056de302018-01-05 14:29:05 -080089 ///
David Tolnaye826d812018-01-06 23:59:39 -080090 /// E.g. `<K, V>` as in `HashMap<K, V>` or `(A, B) -> C` as in `Fn(A, B) -> C`
David Tolnay056de302018-01-05 14:29:05 -080091 pub enum PathArguments {
92 None,
David Tolnaye826d812018-01-06 23:59:39 -080093 /// The `<'a, T>` in `std::slice::iter<'a, T>`.
David Tolnay056de302018-01-05 14:29:05 -080094 AngleBracketed(AngleBracketedGenericArguments),
David Tolnaye826d812018-01-06 23:59:39 -080095 /// The `(A, B)` and `C` in `Fn(A, B) -> C`.
David Tolnay056de302018-01-05 14:29:05 -080096 Parenthesized(ParenthesizedGenericArguments),
97 }
98}
99
100impl Default for PathArguments {
101 fn default() -> Self {
102 PathArguments::None
103 }
104}
105
106impl PathArguments {
107 pub fn is_empty(&self) -> bool {
108 match *self {
109 PathArguments::None => true,
110 PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
111 PathArguments::Parenthesized(_) => false,
112 }
113 }
114}
115
116ast_enum! {
David Tolnaya454c8f2018-01-07 01:01:10 -0800117 /// An individual generic argument, like `'a`, `T`, or `Item = T`.
David Tolnay056de302018-01-05 14:29:05 -0800118 pub enum GenericArgument {
David Tolnaye826d812018-01-06 23:59:39 -0800119 /// A lifetime argument.
David Tolnay056de302018-01-05 14:29:05 -0800120 Lifetime(Lifetime),
David Tolnaye826d812018-01-06 23:59:39 -0800121 /// A type argument.
David Tolnay056de302018-01-05 14:29:05 -0800122 Type(Type),
David Tolnaye826d812018-01-06 23:59:39 -0800123 /// A binding (equality constraint) on an associated type.
David Tolnay056de302018-01-05 14:29:05 -0800124 ///
David Tolnaye826d812018-01-06 23:59:39 -0800125 /// E.g. in `Iterator<Item = u8>`.
David Tolnay056de302018-01-05 14:29:05 -0800126 Binding(Binding),
David Tolnaye826d812018-01-06 23:59:39 -0800127 /// A const expression. Must be inside of a block.
David Tolnay056de302018-01-05 14:29:05 -0800128 ///
129 /// NOTE: Identity expressions are represented as Type arguments, as
130 /// they are indistinguishable syntactically.
131 Const(Expr),
132 }
133}
134
135ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800136 /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K,
137 /// V>`.
David Tolnay056de302018-01-05 14:29:05 -0800138 pub struct AngleBracketedGenericArguments {
139 pub colon2_token: Option<Token![::]>,
140 pub lt_token: Token![<],
141 pub args: Punctuated<GenericArgument, Token![,]>,
142 pub gt_token: Token![>],
143 }
144}
145
146ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800147 /// A binding (equality constraint) on an associated type: `Item = u8`.
David Tolnay056de302018-01-05 14:29:05 -0800148 pub struct Binding {
149 pub ident: Ident,
150 pub eq_token: Token![=],
151 pub ty: Type,
152 }
153}
154
155ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800156 /// Arguments of a function path segment: the `(A, B)` and `C` in `Fn(A,B)
157 /// -> C`.
David Tolnay056de302018-01-05 14:29:05 -0800158 pub struct ParenthesizedGenericArguments {
159 pub paren_token: token::Paren,
160 /// `(A, B)`
161 pub inputs: Punctuated<Type, Token![,]>,
162 /// `C`
163 pub output: ReturnType,
164 }
165}
166
167ast_struct! {
David Tolnaye826d812018-01-06 23:59:39 -0800168 /// The explicit Self type in a qualified path: the `T` in `<T as
David Tolnay05658502018-01-07 09:56:37 -0800169 /// Display>::fmt`.
David Tolnaye826d812018-01-06 23:59:39 -0800170 ///
171 /// The actual path, including the trait and the associated item, is stored
172 /// separately. The `position` field represents the index of the associated
David Tolnay056de302018-01-05 14:29:05 -0800173 /// item qualified with this Self type.
174 ///
175 /// ```text
176 /// <Vec<T> as a::b::Trait>::AssociatedItem
177 /// ^~~~~~ ~~~~~~~~~~~~~~^
178 /// ty position = 3
179 ///
180 /// <Vec<T>>::AssociatedItem
181 /// ^~~~~~ ^
182 /// ty position = 0
183 /// ```
184 pub struct QSelf {
185 pub lt_token: Token![<],
186 pub ty: Box<Type>,
187 pub position: usize,
188 pub as_token: Option<Token![as]>,
189 pub gt_token: Token![>],
190 }
191}
192
193#[cfg(feature = "parsing")]
194pub mod parsing {
195 use super::*;
196 use synom::Synom;
197
198 impl Synom for Path {
199 named!(parse -> Self, do_parse!(
200 colon: option!(punct!(::)) >>
201 segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
David Tolnay56080682018-01-06 14:01:52 -0800202 cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn"), epsilon!()) >>
David Tolnay056de302018-01-05 14:29:05 -0800203 (Path {
204 leading_colon: colon,
205 segments: segments,
206 })
207 ));
208
209 fn description() -> Option<&'static str> {
210 Some("path")
211 }
212 }
213
214 #[cfg(not(feature = "full"))]
215 impl Synom for GenericArgument {
216 named!(parse -> Self, alt!(
217 call!(ty_no_eq_after) => { GenericArgument::Type }
218 |
219 syn!(Lifetime) => { GenericArgument::Lifetime }
220 |
221 syn!(Binding) => { GenericArgument::Binding }
222 ));
223 }
224
225 #[cfg(feature = "full")]
226 impl Synom for GenericArgument {
227 named!(parse -> Self, alt!(
228 call!(ty_no_eq_after) => { GenericArgument::Type }
229 |
230 syn!(Lifetime) => { GenericArgument::Lifetime }
231 |
232 syn!(Binding) => { GenericArgument::Binding }
233 |
234 syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
235 |
236 syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
237 ));
238
239 fn description() -> Option<&'static str> {
240 Some("generic argument")
241 }
242 }
243
244 impl Synom for AngleBracketedGenericArguments {
245 named!(parse -> Self, do_parse!(
246 colon2: option!(punct!(::)) >>
247 lt: punct!(<) >>
248 args: call!(Punctuated::parse_terminated) >>
249 gt: punct!(>) >>
250 (AngleBracketedGenericArguments {
251 colon2_token: colon2,
252 lt_token: lt,
253 args: args,
254 gt_token: gt,
255 })
256 ));
257
258 fn description() -> Option<&'static str> {
259 Some("angle bracketed generic arguments")
260 }
261 }
262
263 impl Synom for ParenthesizedGenericArguments {
264 named!(parse -> Self, do_parse!(
265 data: parens!(Punctuated::parse_terminated) >>
266 output: syn!(ReturnType) >>
267 (ParenthesizedGenericArguments {
268 paren_token: data.0,
269 inputs: data.1,
270 output: output,
271 })
272 ));
273
274 fn description() -> Option<&'static str> {
275 Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
276 }
277 }
278
279 impl Synom for PathSegment {
280 named!(parse -> Self, alt!(
281 do_parse!(
282 ident: syn!(Ident) >>
283 arguments: syn!(AngleBracketedGenericArguments) >>
284 (PathSegment {
285 ident: ident,
286 arguments: PathArguments::AngleBracketed(arguments),
287 })
288 )
289 |
290 mod_style_path_segment
291 ));
292
293 fn description() -> Option<&'static str> {
294 Some("path segment")
295 }
296 }
297
298 impl Synom for Binding {
299 named!(parse -> Self, do_parse!(
300 id: syn!(Ident) >>
301 eq: punct!(=) >>
302 ty: syn!(Type) >>
303 (Binding {
304 ident: id,
305 eq_token: eq,
306 ty: ty,
307 })
308 ));
309
310 fn description() -> Option<&'static str> {
311 Some("associated type binding")
312 }
313 }
314
315 impl Path {
316 named!(pub parse_mod_style -> Self, do_parse!(
317 colon: option!(punct!(::)) >>
318 segments: call!(Punctuated::parse_separated_nonempty_with,
319 mod_style_path_segment) >>
320 (Path {
321 leading_colon: colon,
322 segments: segments,
323 })
324 ));
325 }
326
327 named!(mod_style_path_segment -> PathSegment, alt!(
328 syn!(Ident) => { Into::into }
329 |
330 keyword!(super) => { Into::into }
331 |
332 keyword!(self) => { Into::into }
333 |
334 keyword!(Self) => { Into::into }
335 |
336 keyword!(crate) => { Into::into }
337 ));
338
339 named!(pub qpath -> (Option<QSelf>, Path), alt!(
340 map!(syn!(Path), |p| (None, p))
341 |
342 do_parse!(
343 lt: punct!(<) >>
344 this: syn!(Type) >>
345 path: option!(tuple!(keyword!(as), syn!(Path))) >>
346 gt: punct!(>) >>
347 colon2: punct!(::) >>
348 rest: call!(Punctuated::parse_separated_nonempty) >>
349 ({
350 let (pos, as_, path) = match path {
351 Some((as_, mut path)) => {
352 let pos = path.segments.len();
353 path.segments.push_punct(colon2);
David Tolnay56080682018-01-06 14:01:52 -0800354 path.segments.extend(rest.into_pairs());
David Tolnay056de302018-01-05 14:29:05 -0800355 (pos, Some(as_), path)
356 }
357 None => {
358 (0, None, Path {
359 leading_colon: Some(colon2),
360 segments: rest,
361 })
362 }
363 };
364 (Some(QSelf {
365 lt_token: lt,
366 ty: Box::new(this),
367 position: pos,
368 as_token: as_,
369 gt_token: gt,
370 }), path)
371 })
372 )
373 |
374 map!(keyword!(self), |s| (None, s.into()))
375 ));
376
377 named!(pub ty_no_eq_after -> Type, do_parse!(
378 ty: syn!(Type) >>
379 not!(punct!(=)) >>
380 (ty)
381 ));
382}
383
384#[cfg(feature = "printing")]
385mod printing {
386 use super::*;
387 use quote::{ToTokens, Tokens};
388
389 impl ToTokens for Path {
390 fn to_tokens(&self, tokens: &mut Tokens) {
391 self.leading_colon.to_tokens(tokens);
392 self.segments.to_tokens(tokens);
393 }
394 }
395
396 impl ToTokens for PathSegment {
397 fn to_tokens(&self, tokens: &mut Tokens) {
398 self.ident.to_tokens(tokens);
399 self.arguments.to_tokens(tokens);
400 }
401 }
402
403 impl ToTokens for PathArguments {
404 fn to_tokens(&self, tokens: &mut Tokens) {
405 match *self {
406 PathArguments::None => {}
407 PathArguments::AngleBracketed(ref arguments) => {
408 arguments.to_tokens(tokens);
409 }
410 PathArguments::Parenthesized(ref arguments) => {
411 arguments.to_tokens(tokens);
412 }
413 }
414 }
415 }
416
417 impl ToTokens for GenericArgument {
418 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
419 fn to_tokens(&self, tokens: &mut Tokens) {
420 match *self {
421 GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
422 GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
423 GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
424 GenericArgument::Const(ref e) => match *e {
425 Expr::Lit(_) => e.to_tokens(tokens),
426
427 // NOTE: We should probably support parsing blocks with only
428 // expressions in them without the full feature for const
429 // generics.
430 #[cfg(feature = "full")]
431 Expr::Block(_) => e.to_tokens(tokens),
432
433 // ERROR CORRECTION: Add braces to make sure that the
434 // generated code is valid.
435 _ => token::Brace::default().surround(tokens, |tokens| {
436 e.to_tokens(tokens);
437 }),
438 },
439 }
440 }
441 }
442
443 impl ToTokens for AngleBracketedGenericArguments {
444 fn to_tokens(&self, tokens: &mut Tokens) {
445 self.colon2_token.to_tokens(tokens);
446 self.lt_token.to_tokens(tokens);
447 self.args.to_tokens(tokens);
448 self.gt_token.to_tokens(tokens);
449 }
450 }
451
452 impl ToTokens for Binding {
453 fn to_tokens(&self, tokens: &mut Tokens) {
454 self.ident.to_tokens(tokens);
455 self.eq_token.to_tokens(tokens);
456 self.ty.to_tokens(tokens);
457 }
458 }
459
460 impl ToTokens for ParenthesizedGenericArguments {
461 fn to_tokens(&self, tokens: &mut Tokens) {
462 self.paren_token.surround(tokens, |tokens| {
463 self.inputs.to_tokens(tokens);
464 });
465 self.output.to_tokens(tokens);
466 }
467 }
David Tolnay05658502018-01-07 09:56:37 -0800468
469 impl<'a> ToTokens for PathTokens<'a> {
470 fn to_tokens(&self, tokens: &mut Tokens) {
471 let qself = match *self.0 {
472 Some(ref qself) => qself,
473 None => return self.1.to_tokens(tokens),
474 };
475 qself.lt_token.to_tokens(tokens);
476 qself.ty.to_tokens(tokens);
477
478 // XXX: Gross.
479 let pos = if qself.position > 0 && qself.position >= self.1.segments.len() {
480 self.1.segments.len() - 1
481 } else {
482 qself.position
483 };
484 let mut segments = self.1.segments.pairs();
485 if pos > 0 {
486 TokensOrDefault(&qself.as_token).to_tokens(tokens);
487 self.1.leading_colon.to_tokens(tokens);
488 for (i, segment) in segments.by_ref().take(pos).enumerate() {
489 if i + 1 == pos {
490 segment.value().to_tokens(tokens);
491 qself.gt_token.to_tokens(tokens);
492 segment.punct().to_tokens(tokens);
493 } else {
494 segment.to_tokens(tokens);
495 }
496 }
497 } else {
498 qself.gt_token.to_tokens(tokens);
499 self.1.leading_colon.to_tokens(tokens);
500 }
501 for segment in segments {
502 segment.to_tokens(tokens);
503 }
504 }
505 }
David Tolnay056de302018-01-05 14:29:05 -0800506}