]>
Commit | Line | Data |
---|---|---|
e74abb32 XL |
1 | use super::*; |
2 | use crate::punctuated::Punctuated; | |
3 | ||
4 | ast_struct! { | |
f035d41b | 5 | /// A path at which a named item is exported (e.g. `std::collections::HashMap`). |
e74abb32 | 6 | /// |
f035d41b | 7 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 8 | /// feature.* |
5869c6ff | 9 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
10 | pub struct Path { |
11 | pub leading_colon: Option<Token![::]>, | |
12 | pub segments: Punctuated<PathSegment, Token![::]>, | |
13 | } | |
14 | } | |
15 | ||
16 | impl<T> From<T> for Path | |
17 | where | |
18 | T: Into<PathSegment>, | |
19 | { | |
20 | fn from(segment: T) -> Self { | |
21 | let mut path = Path { | |
22 | leading_colon: None, | |
23 | segments: Punctuated::new(), | |
24 | }; | |
25 | path.segments.push_value(segment.into()); | |
26 | path | |
27 | } | |
28 | } | |
29 | ||
30 | ast_struct! { | |
31 | /// A segment of a path together with any path arguments on that segment. | |
32 | /// | |
f035d41b | 33 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 34 | /// feature.* |
5869c6ff | 35 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
36 | pub struct PathSegment { |
37 | pub ident: Ident, | |
38 | pub arguments: PathArguments, | |
39 | } | |
40 | } | |
41 | ||
42 | impl<T> From<T> for PathSegment | |
43 | where | |
44 | T: Into<Ident>, | |
45 | { | |
46 | fn from(ident: T) -> Self { | |
47 | PathSegment { | |
48 | ident: ident.into(), | |
49 | arguments: PathArguments::None, | |
50 | } | |
51 | } | |
52 | } | |
53 | ||
54 | ast_enum! { | |
55 | /// Angle bracketed or parenthesized arguments of a path segment. | |
56 | /// | |
f035d41b | 57 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 XL |
58 | /// feature.* |
59 | /// | |
60 | /// ## Angle bracketed | |
61 | /// | |
62 | /// The `<'a, T>` in `std::slice::iter<'a, T>`. | |
63 | /// | |
64 | /// ## Parenthesized | |
65 | /// | |
66 | /// The `(A, B) -> C` in `Fn(A, B) -> C`. | |
5869c6ff | 67 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
68 | pub enum PathArguments { |
69 | None, | |
70 | /// The `<'a, T>` in `std::slice::iter<'a, T>`. | |
71 | AngleBracketed(AngleBracketedGenericArguments), | |
72 | /// The `(A, B) -> C` in `Fn(A, B) -> C`. | |
73 | Parenthesized(ParenthesizedGenericArguments), | |
74 | } | |
75 | } | |
76 | ||
77 | impl Default for PathArguments { | |
78 | fn default() -> Self { | |
79 | PathArguments::None | |
80 | } | |
81 | } | |
82 | ||
83 | impl PathArguments { | |
84 | pub fn is_empty(&self) -> bool { | |
85 | match self { | |
86 | PathArguments::None => true, | |
87 | PathArguments::AngleBracketed(bracketed) => bracketed.args.is_empty(), | |
88 | PathArguments::Parenthesized(_) => false, | |
89 | } | |
90 | } | |
91 | ||
9ffffee4 FG |
92 | pub fn is_none(&self) -> bool { |
93 | match self { | |
e74abb32 XL |
94 | PathArguments::None => true, |
95 | PathArguments::AngleBracketed(_) | PathArguments::Parenthesized(_) => false, | |
96 | } | |
97 | } | |
98 | } | |
99 | ||
100 | ast_enum! { | |
101 | /// An individual generic argument, like `'a`, `T`, or `Item = T`. | |
102 | /// | |
f035d41b | 103 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 104 | /// feature.* |
5869c6ff | 105 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
106 | pub enum GenericArgument { |
107 | /// A lifetime argument. | |
108 | Lifetime(Lifetime), | |
109 | /// A type argument. | |
110 | Type(Type), | |
e74abb32 XL |
111 | /// A const expression. Must be inside of a block. |
112 | /// | |
113 | /// NOTE: Identity expressions are represented as Type arguments, as | |
114 | /// they are indistinguishable syntactically. | |
115 | Const(Expr), | |
2b03887a FG |
116 | /// A binding (equality constraint) on an associated type: the `Item = |
117 | /// u8` in `Iterator<Item = u8>`. | |
118 | Binding(Binding), | |
119 | /// An associated type bound: `Iterator<Item: Display>`. | |
120 | Constraint(Constraint), | |
e74abb32 XL |
121 | } |
122 | } | |
123 | ||
124 | ast_struct! { | |
125 | /// Angle bracketed arguments of a path segment: the `<K, V>` in `HashMap<K, | |
126 | /// V>`. | |
127 | /// | |
f035d41b | 128 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 129 | /// feature.* |
5869c6ff | 130 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
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 | ||
139 | ast_struct! { | |
140 | /// A binding (equality constraint) on an associated type: `Item = u8`. | |
141 | /// | |
f035d41b | 142 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 143 | /// feature.* |
5869c6ff | 144 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
145 | pub struct Binding { |
146 | pub ident: Ident, | |
147 | pub eq_token: Token![=], | |
148 | pub ty: Type, | |
149 | } | |
150 | } | |
151 | ||
152 | ast_struct! { | |
153 | /// An associated type bound: `Iterator<Item: Display>`. | |
154 | /// | |
f035d41b | 155 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 156 | /// feature.* |
5869c6ff | 157 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
158 | pub struct Constraint { |
159 | pub ident: Ident, | |
160 | pub colon_token: Token![:], | |
161 | pub bounds: Punctuated<TypeParamBound, Token![+]>, | |
162 | } | |
163 | } | |
164 | ||
165 | ast_struct! { | |
166 | /// Arguments of a function path segment: the `(A, B) -> C` in `Fn(A,B) -> | |
167 | /// C`. | |
168 | /// | |
f035d41b | 169 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 170 | /// feature.* |
5869c6ff | 171 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
172 | pub struct ParenthesizedGenericArguments { |
173 | pub paren_token: token::Paren, | |
174 | /// `(A, B)` | |
175 | pub inputs: Punctuated<Type, Token![,]>, | |
176 | /// `C` | |
177 | pub output: ReturnType, | |
178 | } | |
179 | } | |
180 | ||
181 | ast_struct! { | |
182 | /// The explicit Self type in a qualified path: the `T` in `<T as | |
183 | /// Display>::fmt`. | |
184 | /// | |
185 | /// The actual path, including the trait and the associated item, is stored | |
186 | /// separately. The `position` field represents the index of the associated | |
187 | /// item qualified with this Self type. | |
188 | /// | |
189 | /// ```text | |
190 | /// <Vec<T> as a::b::Trait>::AssociatedItem | |
191 | /// ^~~~~~ ~~~~~~~~~~~~~~^ | |
192 | /// ty position = 3 | |
193 | /// | |
194 | /// <Vec<T>>::AssociatedItem | |
195 | /// ^~~~~~ ^ | |
196 | /// ty position = 0 | |
197 | /// ``` | |
198 | /// | |
f035d41b | 199 | /// *This type is available only if Syn is built with the `"derive"` or `"full"` |
e74abb32 | 200 | /// feature.* |
5869c6ff | 201 | #[cfg_attr(doc_cfg, doc(cfg(any(feature = "full", feature = "derive"))))] |
e74abb32 XL |
202 | pub struct QSelf { |
203 | pub lt_token: Token![<], | |
204 | pub ty: Box<Type>, | |
205 | pub position: usize, | |
206 | pub as_token: Option<Token![as]>, | |
207 | pub gt_token: Token![>], | |
208 | } | |
209 | } | |
210 | ||
211 | #[cfg(feature = "parsing")] | |
212 | pub mod parsing { | |
213 | use super::*; | |
214 | ||
e74abb32 XL |
215 | use crate::ext::IdentExt; |
216 | use crate::parse::{Parse, ParseStream, Result}; | |
217 | ||
5869c6ff | 218 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
219 | impl Parse for Path { |
220 | fn parse(input: ParseStream) -> Result<Self> { | |
221 | Self::parse_helper(input, false) | |
222 | } | |
223 | } | |
224 | ||
5869c6ff | 225 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
226 | impl Parse for GenericArgument { |
227 | fn parse(input: ParseStream) -> Result<Self> { | |
228 | if input.peek(Lifetime) && !input.peek2(Token![+]) { | |
229 | return Ok(GenericArgument::Lifetime(input.parse()?)); | |
230 | } | |
231 | ||
232 | if input.peek(Ident) && input.peek2(Token![=]) { | |
5e7ed085 FG |
233 | let ident: Ident = input.parse()?; |
234 | let eq_token: Token![=] = input.parse()?; | |
235 | ||
236 | let ty = if input.peek(Lit) { | |
237 | let begin = input.fork(); | |
238 | input.parse::<Lit>()?; | |
239 | Type::Verbatim(verbatim::between(begin, input)) | |
240 | } else if input.peek(token::Brace) { | |
241 | let begin = input.fork(); | |
242 | ||
243 | #[cfg(feature = "full")] | |
244 | { | |
245 | input.parse::<ExprBlock>()?; | |
246 | } | |
247 | ||
248 | #[cfg(not(feature = "full"))] | |
249 | { | |
250 | let content; | |
251 | braced!(content in input); | |
252 | content.parse::<Expr>()?; | |
253 | } | |
254 | ||
255 | Type::Verbatim(verbatim::between(begin, input)) | |
256 | } else { | |
257 | input.parse()? | |
258 | }; | |
259 | ||
260 | return Ok(GenericArgument::Binding(Binding { | |
261 | ident, | |
262 | eq_token, | |
263 | ty, | |
264 | })); | |
e74abb32 XL |
265 | } |
266 | ||
267 | #[cfg(feature = "full")] | |
268 | { | |
269 | if input.peek(Ident) && input.peek2(Token![:]) && !input.peek2(Token![::]) { | |
270 | return Ok(GenericArgument::Constraint(input.parse()?)); | |
271 | } | |
5869c6ff | 272 | } |
e74abb32 | 273 | |
cdc7bbd5 XL |
274 | if input.peek(Lit) || input.peek(token::Brace) { |
275 | return const_argument(input).map(GenericArgument::Const); | |
5869c6ff | 276 | } |
e74abb32 | 277 | |
cdc7bbd5 XL |
278 | #[cfg(feature = "full")] |
279 | let begin = input.fork(); | |
5869c6ff | 280 | |
cdc7bbd5 XL |
281 | let argument: Type = input.parse()?; |
282 | ||
283 | #[cfg(feature = "full")] | |
284 | { | |
285 | if match &argument { | |
286 | Type::Path(argument) | |
287 | if argument.qself.is_none() | |
288 | && argument.path.leading_colon.is_none() | |
289 | && argument.path.segments.len() == 1 => | |
290 | { | |
291 | match argument.path.segments[0].arguments { | |
292 | PathArguments::AngleBracketed(_) => true, | |
293 | _ => false, | |
294 | } | |
295 | } | |
296 | _ => false, | |
297 | } && if input.peek(Token![=]) { | |
298 | input.parse::<Token![=]>()?; | |
299 | input.parse::<Type>()?; | |
300 | true | |
301 | } else if input.peek(Token![:]) { | |
302 | input.parse::<Token![:]>()?; | |
303 | input.call(constraint_bounds)?; | |
304 | true | |
305 | } else { | |
306 | false | |
307 | } { | |
5869c6ff | 308 | let verbatim = verbatim::between(begin, input); |
cdc7bbd5 | 309 | return Ok(GenericArgument::Type(Type::Verbatim(verbatim))); |
5869c6ff | 310 | } |
e74abb32 XL |
311 | } |
312 | ||
cdc7bbd5 XL |
313 | Ok(GenericArgument::Type(argument)) |
314 | } | |
315 | } | |
316 | ||
317 | pub fn const_argument(input: ParseStream) -> Result<Expr> { | |
318 | let lookahead = input.lookahead1(); | |
319 | ||
320 | if input.peek(Lit) { | |
321 | let lit = input.parse()?; | |
322 | return Ok(Expr::Lit(lit)); | |
323 | } | |
324 | ||
17df50a5 XL |
325 | #[cfg(feature = "full")] |
326 | { | |
327 | if input.peek(Ident) { | |
328 | let ident: Ident = input.parse()?; | |
329 | return Ok(Expr::Path(ExprPath { | |
330 | attrs: Vec::new(), | |
331 | qself: None, | |
332 | path: Path::from(ident), | |
333 | })); | |
334 | } | |
335 | } | |
336 | ||
cdc7bbd5 XL |
337 | if input.peek(token::Brace) { |
338 | #[cfg(feature = "full")] | |
339 | { | |
340 | let block: ExprBlock = input.parse()?; | |
341 | return Ok(Expr::Block(block)); | |
342 | } | |
343 | ||
344 | #[cfg(not(feature = "full"))] | |
345 | { | |
346 | let begin = input.fork(); | |
347 | let content; | |
348 | braced!(content in input); | |
349 | content.parse::<Expr>()?; | |
350 | let verbatim = verbatim::between(begin, input); | |
351 | return Ok(Expr::Verbatim(verbatim)); | |
352 | } | |
e74abb32 | 353 | } |
cdc7bbd5 XL |
354 | |
355 | Err(lookahead.error()) | |
e74abb32 XL |
356 | } |
357 | ||
5869c6ff | 358 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
359 | impl Parse for AngleBracketedGenericArguments { |
360 | fn parse(input: ParseStream) -> Result<Self> { | |
361 | Ok(AngleBracketedGenericArguments { | |
362 | colon2_token: input.parse()?, | |
363 | lt_token: input.parse()?, | |
364 | args: { | |
365 | let mut args = Punctuated::new(); | |
366 | loop { | |
367 | if input.peek(Token![>]) { | |
368 | break; | |
369 | } | |
370 | let value = input.parse()?; | |
371 | args.push_value(value); | |
372 | if input.peek(Token![>]) { | |
373 | break; | |
374 | } | |
375 | let punct = input.parse()?; | |
376 | args.push_punct(punct); | |
377 | } | |
378 | args | |
379 | }, | |
380 | gt_token: input.parse()?, | |
381 | }) | |
382 | } | |
383 | } | |
384 | ||
5869c6ff | 385 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
386 | impl Parse for ParenthesizedGenericArguments { |
387 | fn parse(input: ParseStream) -> Result<Self> { | |
388 | let content; | |
389 | Ok(ParenthesizedGenericArguments { | |
390 | paren_token: parenthesized!(content in input), | |
391 | inputs: content.parse_terminated(Type::parse)?, | |
392 | output: input.call(ReturnType::without_plus)?, | |
393 | }) | |
394 | } | |
395 | } | |
396 | ||
5869c6ff | 397 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
398 | impl Parse for PathSegment { |
399 | fn parse(input: ParseStream) -> Result<Self> { | |
400 | Self::parse_helper(input, false) | |
401 | } | |
402 | } | |
403 | ||
404 | impl PathSegment { | |
405 | fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> { | |
f035d41b | 406 | if input.peek(Token![super]) || input.peek(Token![self]) || input.peek(Token![crate]) { |
e74abb32 XL |
407 | let ident = input.call(Ident::parse_any)?; |
408 | return Ok(PathSegment::from(ident)); | |
409 | } | |
410 | ||
411 | let ident = if input.peek(Token![Self]) { | |
412 | input.call(Ident::parse_any)? | |
413 | } else { | |
414 | input.parse()? | |
415 | }; | |
416 | ||
417 | if !expr_style && input.peek(Token![<]) && !input.peek(Token![<=]) | |
418 | || input.peek(Token![::]) && input.peek3(Token![<]) | |
419 | { | |
420 | Ok(PathSegment { | |
421 | ident, | |
422 | arguments: PathArguments::AngleBracketed(input.parse()?), | |
423 | }) | |
424 | } else { | |
425 | Ok(PathSegment::from(ident)) | |
426 | } | |
427 | } | |
428 | } | |
429 | ||
5869c6ff | 430 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
431 | impl Parse for Binding { |
432 | fn parse(input: ParseStream) -> Result<Self> { | |
433 | Ok(Binding { | |
434 | ident: input.parse()?, | |
435 | eq_token: input.parse()?, | |
436 | ty: input.parse()?, | |
437 | }) | |
438 | } | |
439 | } | |
440 | ||
441 | #[cfg(feature = "full")] | |
5869c6ff | 442 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
443 | impl Parse for Constraint { |
444 | fn parse(input: ParseStream) -> Result<Self> { | |
445 | Ok(Constraint { | |
446 | ident: input.parse()?, | |
447 | colon_token: input.parse()?, | |
cdc7bbd5 | 448 | bounds: constraint_bounds(input)?, |
e74abb32 XL |
449 | }) |
450 | } | |
451 | } | |
452 | ||
cdc7bbd5 XL |
453 | #[cfg(feature = "full")] |
454 | fn constraint_bounds(input: ParseStream) -> Result<Punctuated<TypeParamBound, Token![+]>> { | |
455 | let mut bounds = Punctuated::new(); | |
456 | loop { | |
457 | if input.peek(Token![,]) || input.peek(Token![>]) { | |
458 | break; | |
459 | } | |
460 | let value = input.parse()?; | |
461 | bounds.push_value(value); | |
462 | if !input.peek(Token![+]) { | |
463 | break; | |
464 | } | |
465 | let punct = input.parse()?; | |
466 | bounds.push_punct(punct); | |
467 | } | |
468 | Ok(bounds) | |
469 | } | |
470 | ||
e74abb32 XL |
471 | impl Path { |
472 | /// Parse a `Path` containing no path arguments on any of its segments. | |
473 | /// | |
f035d41b | 474 | /// *This function is available only if Syn is built with the `"parsing"` |
e74abb32 XL |
475 | /// feature.* |
476 | /// | |
477 | /// # Example | |
478 | /// | |
479 | /// ``` | |
480 | /// use syn::{Path, Result, Token}; | |
481 | /// use syn::parse::{Parse, ParseStream}; | |
482 | /// | |
483 | /// // A simplified single `use` statement like: | |
484 | /// // | |
485 | /// // use std::collections::HashMap; | |
486 | /// // | |
487 | /// // Note that generic parameters are not allowed in a `use` statement | |
488 | /// // so the following must not be accepted. | |
489 | /// // | |
490 | /// // use a::<b>::c; | |
491 | /// struct SingleUse { | |
492 | /// use_token: Token![use], | |
493 | /// path: Path, | |
494 | /// } | |
495 | /// | |
496 | /// impl Parse for SingleUse { | |
497 | /// fn parse(input: ParseStream) -> Result<Self> { | |
498 | /// Ok(SingleUse { | |
499 | /// use_token: input.parse()?, | |
500 | /// path: input.call(Path::parse_mod_style)?, | |
501 | /// }) | |
502 | /// } | |
503 | /// } | |
504 | /// ``` | |
5869c6ff | 505 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
506 | pub fn parse_mod_style(input: ParseStream) -> Result<Self> { |
507 | Ok(Path { | |
508 | leading_colon: input.parse()?, | |
509 | segments: { | |
510 | let mut segments = Punctuated::new(); | |
511 | loop { | |
512 | if !input.peek(Ident) | |
513 | && !input.peek(Token![super]) | |
514 | && !input.peek(Token![self]) | |
515 | && !input.peek(Token![Self]) | |
516 | && !input.peek(Token![crate]) | |
e74abb32 XL |
517 | { |
518 | break; | |
519 | } | |
520 | let ident = Ident::parse_any(input)?; | |
521 | segments.push_value(PathSegment::from(ident)); | |
522 | if !input.peek(Token![::]) { | |
523 | break; | |
524 | } | |
525 | let punct = input.parse()?; | |
526 | segments.push_punct(punct); | |
527 | } | |
528 | if segments.is_empty() { | |
529 | return Err(input.error("expected path")); | |
530 | } else if segments.trailing_punct() { | |
531 | return Err(input.error("expected path segment")); | |
532 | } | |
533 | segments | |
534 | }, | |
535 | }) | |
536 | } | |
537 | ||
538 | /// Determines whether this is a path of length 1 equal to the given | |
539 | /// ident. | |
540 | /// | |
541 | /// For them to compare equal, it must be the case that: | |
542 | /// | |
543 | /// - the path has no leading colon, | |
544 | /// - the number of path segments is 1, | |
545 | /// - the first path segment has no angle bracketed or parenthesized | |
546 | /// path arguments, and | |
547 | /// - the ident of the first path segment is equal to the given one. | |
548 | /// | |
f035d41b | 549 | /// *This function is available only if Syn is built with the `"parsing"` |
e74abb32 XL |
550 | /// feature.* |
551 | /// | |
552 | /// # Example | |
553 | /// | |
554 | /// ``` | |
555 | /// use syn::{Attribute, Error, Meta, NestedMeta, Result}; | |
556 | /// # use std::iter::FromIterator; | |
557 | /// | |
558 | /// fn get_serde_meta_items(attr: &Attribute) -> Result<Vec<NestedMeta>> { | |
559 | /// if attr.path.is_ident("serde") { | |
560 | /// match attr.parse_meta()? { | |
561 | /// Meta::List(meta) => Ok(Vec::from_iter(meta.nested)), | |
562 | /// bad => Err(Error::new_spanned(bad, "unrecognized attribute")), | |
563 | /// } | |
564 | /// } else { | |
565 | /// Ok(Vec::new()) | |
566 | /// } | |
567 | /// } | |
568 | /// ``` | |
5869c6ff | 569 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
570 | pub fn is_ident<I: ?Sized>(&self, ident: &I) -> bool |
571 | where | |
572 | Ident: PartialEq<I>, | |
573 | { | |
574 | match self.get_ident() { | |
575 | Some(id) => id == ident, | |
576 | None => false, | |
577 | } | |
578 | } | |
579 | ||
580 | /// If this path consists of a single ident, returns the ident. | |
581 | /// | |
582 | /// A path is considered an ident if: | |
583 | /// | |
584 | /// - the path has no leading colon, | |
585 | /// - the number of path segments is 1, and | |
586 | /// - the first path segment has no angle bracketed or parenthesized | |
587 | /// path arguments. | |
588 | /// | |
f035d41b | 589 | /// *This function is available only if Syn is built with the `"parsing"` |
e74abb32 | 590 | /// feature.* |
5869c6ff | 591 | #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] |
e74abb32 XL |
592 | pub fn get_ident(&self) -> Option<&Ident> { |
593 | if self.leading_colon.is_none() | |
594 | && self.segments.len() == 1 | |
595 | && self.segments[0].arguments.is_none() | |
596 | { | |
597 | Some(&self.segments[0].ident) | |
598 | } else { | |
599 | None | |
600 | } | |
601 | } | |
602 | ||
5869c6ff XL |
603 | pub(crate) fn parse_helper(input: ParseStream, expr_style: bool) -> Result<Self> { |
604 | let mut path = Path { | |
e74abb32 XL |
605 | leading_colon: input.parse()?, |
606 | segments: { | |
607 | let mut segments = Punctuated::new(); | |
608 | let value = PathSegment::parse_helper(input, expr_style)?; | |
609 | segments.push_value(value); | |
e74abb32 XL |
610 | segments |
611 | }, | |
5869c6ff XL |
612 | }; |
613 | Path::parse_rest(input, &mut path, expr_style)?; | |
614 | Ok(path) | |
615 | } | |
616 | ||
617 | pub(crate) fn parse_rest( | |
618 | input: ParseStream, | |
619 | path: &mut Self, | |
620 | expr_style: bool, | |
621 | ) -> Result<()> { | |
a2a8927a | 622 | while input.peek(Token![::]) && !input.peek3(token::Paren) { |
5869c6ff XL |
623 | let punct: Token![::] = input.parse()?; |
624 | path.segments.push_punct(punct); | |
625 | let value = PathSegment::parse_helper(input, expr_style)?; | |
626 | path.segments.push_value(value); | |
627 | } | |
628 | Ok(()) | |
e74abb32 XL |
629 | } |
630 | } | |
631 | ||
632 | pub fn qpath(input: ParseStream, expr_style: bool) -> Result<(Option<QSelf>, Path)> { | |
633 | if input.peek(Token![<]) { | |
634 | let lt_token: Token![<] = input.parse()?; | |
635 | let this: Type = input.parse()?; | |
636 | let path = if input.peek(Token![as]) { | |
637 | let as_token: Token![as] = input.parse()?; | |
638 | let path: Path = input.parse()?; | |
639 | Some((as_token, path)) | |
640 | } else { | |
641 | None | |
642 | }; | |
643 | let gt_token: Token![>] = input.parse()?; | |
644 | let colon2_token: Token![::] = input.parse()?; | |
645 | let mut rest = Punctuated::new(); | |
646 | loop { | |
647 | let path = PathSegment::parse_helper(input, expr_style)?; | |
648 | rest.push_value(path); | |
649 | if !input.peek(Token![::]) { | |
650 | break; | |
651 | } | |
652 | let punct: Token![::] = input.parse()?; | |
653 | rest.push_punct(punct); | |
654 | } | |
655 | let (position, as_token, path) = match path { | |
656 | Some((as_token, mut path)) => { | |
657 | let pos = path.segments.len(); | |
658 | path.segments.push_punct(colon2_token); | |
659 | path.segments.extend(rest.into_pairs()); | |
660 | (pos, Some(as_token), path) | |
661 | } | |
662 | None => { | |
663 | let path = Path { | |
664 | leading_colon: Some(colon2_token), | |
665 | segments: rest, | |
666 | }; | |
667 | (0, None, path) | |
668 | } | |
669 | }; | |
670 | let qself = QSelf { | |
671 | lt_token, | |
672 | ty: Box::new(this), | |
673 | position, | |
674 | as_token, | |
675 | gt_token, | |
676 | }; | |
677 | Ok((Some(qself), path)) | |
678 | } else { | |
679 | let path = Path::parse_helper(input, expr_style)?; | |
680 | Ok((None, path)) | |
681 | } | |
682 | } | |
683 | } | |
684 | ||
685 | #[cfg(feature = "printing")] | |
04454e1e | 686 | pub(crate) mod printing { |
e74abb32 | 687 | use super::*; |
29967ef6 | 688 | use crate::print::TokensOrDefault; |
e74abb32 XL |
689 | use proc_macro2::TokenStream; |
690 | use quote::ToTokens; | |
29967ef6 | 691 | use std::cmp; |
e74abb32 | 692 | |
5869c6ff | 693 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
694 | impl ToTokens for Path { |
695 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
696 | self.leading_colon.to_tokens(tokens); | |
697 | self.segments.to_tokens(tokens); | |
698 | } | |
699 | } | |
700 | ||
5869c6ff | 701 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
702 | impl ToTokens for PathSegment { |
703 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
704 | self.ident.to_tokens(tokens); | |
705 | self.arguments.to_tokens(tokens); | |
706 | } | |
707 | } | |
708 | ||
5869c6ff | 709 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
710 | impl ToTokens for PathArguments { |
711 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
712 | match self { | |
713 | PathArguments::None => {} | |
714 | PathArguments::AngleBracketed(arguments) => { | |
715 | arguments.to_tokens(tokens); | |
716 | } | |
717 | PathArguments::Parenthesized(arguments) => { | |
718 | arguments.to_tokens(tokens); | |
719 | } | |
720 | } | |
721 | } | |
722 | } | |
723 | ||
5869c6ff | 724 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
725 | impl ToTokens for GenericArgument { |
726 | #[allow(clippy::match_same_arms)] | |
727 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
728 | match self { | |
729 | GenericArgument::Lifetime(lt) => lt.to_tokens(tokens), | |
730 | GenericArgument::Type(ty) => ty.to_tokens(tokens), | |
e74abb32 XL |
731 | GenericArgument::Const(e) => match *e { |
732 | Expr::Lit(_) => e.to_tokens(tokens), | |
733 | ||
734 | // NOTE: We should probably support parsing blocks with only | |
735 | // expressions in them without the full feature for const | |
736 | // generics. | |
737 | #[cfg(feature = "full")] | |
738 | Expr::Block(_) => e.to_tokens(tokens), | |
739 | ||
740 | // ERROR CORRECTION: Add braces to make sure that the | |
741 | // generated code is valid. | |
742 | _ => token::Brace::default().surround(tokens, |tokens| { | |
743 | e.to_tokens(tokens); | |
744 | }), | |
745 | }, | |
2b03887a FG |
746 | GenericArgument::Binding(tb) => tb.to_tokens(tokens), |
747 | GenericArgument::Constraint(tc) => tc.to_tokens(tokens), | |
e74abb32 XL |
748 | } |
749 | } | |
750 | } | |
751 | ||
5869c6ff | 752 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
753 | impl ToTokens for AngleBracketedGenericArguments { |
754 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
755 | self.colon2_token.to_tokens(tokens); | |
756 | self.lt_token.to_tokens(tokens); | |
757 | ||
2b03887a FG |
758 | // Print lifetimes before types/consts/bindings, regardless of their |
759 | // order in self.args. | |
e74abb32 XL |
760 | let mut trailing_or_empty = true; |
761 | for param in self.args.pairs() { | |
762 | match **param.value() { | |
763 | GenericArgument::Lifetime(_) => { | |
764 | param.to_tokens(tokens); | |
765 | trailing_or_empty = param.punct().is_some(); | |
766 | } | |
767 | GenericArgument::Type(_) | |
2b03887a | 768 | | GenericArgument::Const(_) |
e74abb32 XL |
769 | | GenericArgument::Binding(_) |
770 | | GenericArgument::Constraint(_) => {} | |
771 | } | |
772 | } | |
773 | for param in self.args.pairs() { | |
774 | match **param.value() { | |
2b03887a FG |
775 | GenericArgument::Type(_) |
776 | | GenericArgument::Const(_) | |
777 | | GenericArgument::Binding(_) | |
778 | | GenericArgument::Constraint(_) => { | |
e74abb32 XL |
779 | if !trailing_or_empty { |
780 | <Token![,]>::default().to_tokens(tokens); | |
e74abb32 XL |
781 | } |
782 | param.to_tokens(tokens); | |
5869c6ff | 783 | trailing_or_empty = param.punct().is_some(); |
e74abb32 | 784 | } |
2b03887a | 785 | GenericArgument::Lifetime(_) => {} |
e74abb32 XL |
786 | } |
787 | } | |
788 | ||
789 | self.gt_token.to_tokens(tokens); | |
790 | } | |
791 | } | |
792 | ||
5869c6ff | 793 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
794 | impl ToTokens for Binding { |
795 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
796 | self.ident.to_tokens(tokens); | |
797 | self.eq_token.to_tokens(tokens); | |
798 | self.ty.to_tokens(tokens); | |
799 | } | |
800 | } | |
801 | ||
5869c6ff | 802 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
803 | impl ToTokens for Constraint { |
804 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
805 | self.ident.to_tokens(tokens); | |
806 | self.colon_token.to_tokens(tokens); | |
807 | self.bounds.to_tokens(tokens); | |
808 | } | |
809 | } | |
810 | ||
5869c6ff | 811 | #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] |
e74abb32 XL |
812 | impl ToTokens for ParenthesizedGenericArguments { |
813 | fn to_tokens(&self, tokens: &mut TokenStream) { | |
814 | self.paren_token.surround(tokens, |tokens| { | |
815 | self.inputs.to_tokens(tokens); | |
816 | }); | |
817 | self.output.to_tokens(tokens); | |
818 | } | |
819 | } | |
820 | ||
04454e1e FG |
821 | pub(crate) fn print_path(tokens: &mut TokenStream, qself: &Option<QSelf>, path: &Path) { |
822 | let qself = match qself { | |
823 | Some(qself) => qself, | |
824 | None => { | |
825 | path.to_tokens(tokens); | |
826 | return; | |
e74abb32 | 827 | } |
04454e1e FG |
828 | }; |
829 | qself.lt_token.to_tokens(tokens); | |
830 | qself.ty.to_tokens(tokens); | |
831 | ||
832 | let pos = cmp::min(qself.position, path.segments.len()); | |
833 | let mut segments = path.segments.pairs(); | |
834 | if pos > 0 { | |
835 | TokensOrDefault(&qself.as_token).to_tokens(tokens); | |
836 | path.leading_colon.to_tokens(tokens); | |
837 | for (i, segment) in segments.by_ref().take(pos).enumerate() { | |
838 | if i + 1 == pos { | |
839 | segment.value().to_tokens(tokens); | |
840 | qself.gt_token.to_tokens(tokens); | |
841 | segment.punct().to_tokens(tokens); | |
842 | } else { | |
843 | segment.to_tokens(tokens); | |
844 | } | |
e74abb32 | 845 | } |
04454e1e FG |
846 | } else { |
847 | qself.gt_token.to_tokens(tokens); | |
848 | path.leading_colon.to_tokens(tokens); | |
849 | } | |
850 | for segment in segments { | |
851 | segment.to_tokens(tokens); | |
e74abb32 XL |
852 | } |
853 | } | |
854 | } |