blob: edc6fac7f0a84a93253696bb9051903eeace904a [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! {
13 /// A "Path" is essentially Rust's notion of a name.
14 ///
15 /// It's represented as a sequence of identifiers,
16 /// along with a bunch of supporting information.
17 ///
18 /// E.g. `std::cmp::PartialEq`
19 pub struct Path {
20 /// A `::foo` path, is relative to the crate root rather than current
21 /// module (like paths in an import).
22 pub leading_colon: Option<Token![::]>,
23 /// The segments in the path: the things separated by `::`.
24 pub segments: Punctuated<PathSegment, Token![::]>,
25 }
26}
27
28impl Path {
29 pub fn global(&self) -> bool {
30 self.leading_colon.is_some()
31 }
32}
33
34#[cfg(feature = "printing")]
35#[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
36#[cfg_attr(feature = "clone-impls", derive(Clone))]
37pub struct PathTokens<'a>(pub &'a Option<QSelf>, pub &'a Path);
38
39impl<T> From<T> for Path
40where
41 T: Into<PathSegment>,
42{
43 fn from(segment: T) -> Self {
44 let mut path = Path {
45 leading_colon: None,
46 segments: Punctuated::new(),
47 };
David Tolnay56080682018-01-06 14:01:52 -080048 path.segments.push_value(segment.into());
David Tolnay056de302018-01-05 14:29:05 -080049 path
50 }
51}
52
53ast_struct! {
54 /// A segment of a path: an identifier, an optional lifetime, and a set of types.
55 ///
56 /// E.g. `std`, `String` or `Box<T>`
57 pub struct PathSegment {
58 /// The identifier portion of this path segment.
59 pub ident: Ident,
60 /// Type/lifetime arguments attached to this path. They come in
61 /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that
62 /// this is more than just simple syntactic sugar; the use of
63 /// parens affects the region binding rules, so we preserve the
64 /// distinction.
65 pub arguments: PathArguments,
66 }
67}
68
69impl<T> From<T> for PathSegment
70where
71 T: Into<Ident>,
72{
73 fn from(ident: T) -> Self {
74 PathSegment {
75 ident: ident.into(),
76 arguments: PathArguments::None,
77 }
78 }
79}
80
81ast_enum! {
82 /// Arguments of a path segment.
83 ///
84 /// E.g. `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`
85 pub enum PathArguments {
86 None,
87 /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`
88 AngleBracketed(AngleBracketedGenericArguments),
89 /// The `(A, B)` and `C` in `Foo(A, B) -> C`
90 Parenthesized(ParenthesizedGenericArguments),
91 }
92}
93
94impl Default for PathArguments {
95 fn default() -> Self {
96 PathArguments::None
97 }
98}
99
100impl PathArguments {
101 pub fn is_empty(&self) -> bool {
102 match *self {
103 PathArguments::None => true,
104 PathArguments::AngleBracketed(ref bracketed) => bracketed.args.is_empty(),
105 PathArguments::Parenthesized(_) => false,
106 }
107 }
108}
109
110ast_enum! {
111 /// A individual generic argument, like `'a`, `T`, or `Item=T`.
112 pub enum GenericArgument {
113 /// The lifetime parameters for this path segment.
114 Lifetime(Lifetime),
115 /// The type parameters for this path segment, if present.
116 Type(Type),
117 /// Bindings (equality constraints) on associated types, if present.
118 ///
119 /// E.g., `Foo<A=Bar>`.
120 Binding(Binding),
121 /// Const expression. Must be inside of a block.
122 ///
123 /// NOTE: Identity expressions are represented as Type arguments, as
124 /// they are indistinguishable syntactically.
125 Const(Expr),
126 }
127}
128
129ast_struct! {
130 /// A path like `Foo<'a, T>`
131 pub struct AngleBracketedGenericArguments {
132 pub colon2_token: Option<Token![::]>,
133 pub lt_token: Token![<],
134 pub args: Punctuated<GenericArgument, Token![,]>,
135 pub gt_token: Token![>],
136 }
137}
138
139ast_struct! {
140 /// Bind a type to an associated type: `A=Foo`.
141 pub struct Binding {
142 pub ident: Ident,
143 pub eq_token: Token![=],
144 pub ty: Type,
145 }
146}
147
148ast_struct! {
149 /// A path like `Foo(A,B) -> C`
150 pub struct ParenthesizedGenericArguments {
151 pub paren_token: token::Paren,
152 /// `(A, B)`
153 pub inputs: Punctuated<Type, Token![,]>,
154 /// `C`
155 pub output: ReturnType,
156 }
157}
158
159ast_struct! {
160 /// The explicit Self type in a "qualified path". The actual
161 /// path, including the trait and the associated item, is stored
162 /// separately. `position` represents the index of the associated
163 /// item qualified with this Self type.
164 ///
165 /// ```text
166 /// <Vec<T> as a::b::Trait>::AssociatedItem
167 /// ^~~~~~ ~~~~~~~~~~~~~~^
168 /// ty position = 3
169 ///
170 /// <Vec<T>>::AssociatedItem
171 /// ^~~~~~ ^
172 /// ty position = 0
173 /// ```
174 pub struct QSelf {
175 pub lt_token: Token![<],
176 pub ty: Box<Type>,
177 pub position: usize,
178 pub as_token: Option<Token![as]>,
179 pub gt_token: Token![>],
180 }
181}
182
183#[cfg(feature = "parsing")]
184pub mod parsing {
185 use super::*;
186 use synom::Synom;
187
188 impl Synom for Path {
189 named!(parse -> Self, do_parse!(
190 colon: option!(punct!(::)) >>
191 segments: call!(Punctuated::<PathSegment, Token![::]>::parse_separated_nonempty) >>
David Tolnay56080682018-01-06 14:01:52 -0800192 cond_reduce!(segments.first().map_or(true, |seg| seg.value().ident != "dyn"), epsilon!()) >>
David Tolnay056de302018-01-05 14:29:05 -0800193 (Path {
194 leading_colon: colon,
195 segments: segments,
196 })
197 ));
198
199 fn description() -> Option<&'static str> {
200 Some("path")
201 }
202 }
203
204 #[cfg(not(feature = "full"))]
205 impl Synom for GenericArgument {
206 named!(parse -> Self, alt!(
207 call!(ty_no_eq_after) => { GenericArgument::Type }
208 |
209 syn!(Lifetime) => { GenericArgument::Lifetime }
210 |
211 syn!(Binding) => { GenericArgument::Binding }
212 ));
213 }
214
215 #[cfg(feature = "full")]
216 impl Synom for GenericArgument {
217 named!(parse -> Self, alt!(
218 call!(ty_no_eq_after) => { GenericArgument::Type }
219 |
220 syn!(Lifetime) => { GenericArgument::Lifetime }
221 |
222 syn!(Binding) => { GenericArgument::Binding }
223 |
224 syn!(ExprLit) => { |l| GenericArgument::Const(Expr::Lit(l)) }
225 |
226 syn!(ExprBlock) => { |b| GenericArgument::Const(Expr::Block(b)) }
227 ));
228
229 fn description() -> Option<&'static str> {
230 Some("generic argument")
231 }
232 }
233
234 impl Synom for AngleBracketedGenericArguments {
235 named!(parse -> Self, do_parse!(
236 colon2: option!(punct!(::)) >>
237 lt: punct!(<) >>
238 args: call!(Punctuated::parse_terminated) >>
239 gt: punct!(>) >>
240 (AngleBracketedGenericArguments {
241 colon2_token: colon2,
242 lt_token: lt,
243 args: args,
244 gt_token: gt,
245 })
246 ));
247
248 fn description() -> Option<&'static str> {
249 Some("angle bracketed generic arguments")
250 }
251 }
252
253 impl Synom for ParenthesizedGenericArguments {
254 named!(parse -> Self, do_parse!(
255 data: parens!(Punctuated::parse_terminated) >>
256 output: syn!(ReturnType) >>
257 (ParenthesizedGenericArguments {
258 paren_token: data.0,
259 inputs: data.1,
260 output: output,
261 })
262 ));
263
264 fn description() -> Option<&'static str> {
265 Some("parenthesized generic arguments: `Foo(A, B, ..) -> T`")
266 }
267 }
268
269 impl Synom for PathSegment {
270 named!(parse -> Self, alt!(
271 do_parse!(
272 ident: syn!(Ident) >>
273 arguments: syn!(AngleBracketedGenericArguments) >>
274 (PathSegment {
275 ident: ident,
276 arguments: PathArguments::AngleBracketed(arguments),
277 })
278 )
279 |
280 mod_style_path_segment
281 ));
282
283 fn description() -> Option<&'static str> {
284 Some("path segment")
285 }
286 }
287
288 impl Synom for Binding {
289 named!(parse -> Self, do_parse!(
290 id: syn!(Ident) >>
291 eq: punct!(=) >>
292 ty: syn!(Type) >>
293 (Binding {
294 ident: id,
295 eq_token: eq,
296 ty: ty,
297 })
298 ));
299
300 fn description() -> Option<&'static str> {
301 Some("associated type binding")
302 }
303 }
304
305 impl Path {
306 named!(pub parse_mod_style -> Self, do_parse!(
307 colon: option!(punct!(::)) >>
308 segments: call!(Punctuated::parse_separated_nonempty_with,
309 mod_style_path_segment) >>
310 (Path {
311 leading_colon: colon,
312 segments: segments,
313 })
314 ));
315 }
316
317 named!(mod_style_path_segment -> PathSegment, alt!(
318 syn!(Ident) => { Into::into }
319 |
320 keyword!(super) => { Into::into }
321 |
322 keyword!(self) => { Into::into }
323 |
324 keyword!(Self) => { Into::into }
325 |
326 keyword!(crate) => { Into::into }
327 ));
328
329 named!(pub qpath -> (Option<QSelf>, Path), alt!(
330 map!(syn!(Path), |p| (None, p))
331 |
332 do_parse!(
333 lt: punct!(<) >>
334 this: syn!(Type) >>
335 path: option!(tuple!(keyword!(as), syn!(Path))) >>
336 gt: punct!(>) >>
337 colon2: punct!(::) >>
338 rest: call!(Punctuated::parse_separated_nonempty) >>
339 ({
340 let (pos, as_, path) = match path {
341 Some((as_, mut path)) => {
342 let pos = path.segments.len();
343 path.segments.push_punct(colon2);
David Tolnay56080682018-01-06 14:01:52 -0800344 path.segments.extend(rest.into_pairs());
David Tolnay056de302018-01-05 14:29:05 -0800345 (pos, Some(as_), path)
346 }
347 None => {
348 (0, None, Path {
349 leading_colon: Some(colon2),
350 segments: rest,
351 })
352 }
353 };
354 (Some(QSelf {
355 lt_token: lt,
356 ty: Box::new(this),
357 position: pos,
358 as_token: as_,
359 gt_token: gt,
360 }), path)
361 })
362 )
363 |
364 map!(keyword!(self), |s| (None, s.into()))
365 ));
366
367 named!(pub ty_no_eq_after -> Type, do_parse!(
368 ty: syn!(Type) >>
369 not!(punct!(=)) >>
370 (ty)
371 ));
372}
373
374#[cfg(feature = "printing")]
375mod printing {
376 use super::*;
377 use quote::{ToTokens, Tokens};
378
379 impl ToTokens for Path {
380 fn to_tokens(&self, tokens: &mut Tokens) {
381 self.leading_colon.to_tokens(tokens);
382 self.segments.to_tokens(tokens);
383 }
384 }
385
386 impl ToTokens for PathSegment {
387 fn to_tokens(&self, tokens: &mut Tokens) {
388 self.ident.to_tokens(tokens);
389 self.arguments.to_tokens(tokens);
390 }
391 }
392
393 impl ToTokens for PathArguments {
394 fn to_tokens(&self, tokens: &mut Tokens) {
395 match *self {
396 PathArguments::None => {}
397 PathArguments::AngleBracketed(ref arguments) => {
398 arguments.to_tokens(tokens);
399 }
400 PathArguments::Parenthesized(ref arguments) => {
401 arguments.to_tokens(tokens);
402 }
403 }
404 }
405 }
406
407 impl ToTokens for GenericArgument {
408 #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))]
409 fn to_tokens(&self, tokens: &mut Tokens) {
410 match *self {
411 GenericArgument::Lifetime(ref lt) => lt.to_tokens(tokens),
412 GenericArgument::Type(ref ty) => ty.to_tokens(tokens),
413 GenericArgument::Binding(ref tb) => tb.to_tokens(tokens),
414 GenericArgument::Const(ref e) => match *e {
415 Expr::Lit(_) => e.to_tokens(tokens),
416
417 // NOTE: We should probably support parsing blocks with only
418 // expressions in them without the full feature for const
419 // generics.
420 #[cfg(feature = "full")]
421 Expr::Block(_) => e.to_tokens(tokens),
422
423 // ERROR CORRECTION: Add braces to make sure that the
424 // generated code is valid.
425 _ => token::Brace::default().surround(tokens, |tokens| {
426 e.to_tokens(tokens);
427 }),
428 },
429 }
430 }
431 }
432
433 impl ToTokens for AngleBracketedGenericArguments {
434 fn to_tokens(&self, tokens: &mut Tokens) {
435 self.colon2_token.to_tokens(tokens);
436 self.lt_token.to_tokens(tokens);
437 self.args.to_tokens(tokens);
438 self.gt_token.to_tokens(tokens);
439 }
440 }
441
442 impl ToTokens for Binding {
443 fn to_tokens(&self, tokens: &mut Tokens) {
444 self.ident.to_tokens(tokens);
445 self.eq_token.to_tokens(tokens);
446 self.ty.to_tokens(tokens);
447 }
448 }
449
450 impl ToTokens for ParenthesizedGenericArguments {
451 fn to_tokens(&self, tokens: &mut Tokens) {
452 self.paren_token.surround(tokens, |tokens| {
453 self.inputs.to_tokens(tokens);
454 });
455 self.output.to_tokens(tokens);
456 }
457 }
458}