blob: a86ba0d793c3bc6803e5a85bb1b4565945d08bed [file] [log] [blame]
cad9789bb9452019-01-20 18:33:48 -05001//! Extensions to the parsing API with niche applicability.
2
3use super::*;
4
5/// Extensions to the `ParseStream` API to support speculative parsing.
6pub trait Speculative {
7 /// Advance this parse stream to the position of a forked parse stream.
8 ///
David Tolnayfa033fa2019-06-23 14:07:36 -07009 /// This is the opposite operation to [`ParseStream::fork`]. You can fork a
10 /// parse stream, perform some speculative parsing, then join the original
11 /// stream to the fork to "commit" the parsing from the fork to the main
12 /// stream.
cad9789bb9452019-01-20 18:33:48 -050013 ///
14 /// If you can avoid doing this, you should, as it limits the ability to
15 /// generate useful errors. That said, it is often the only way to parse
16 /// syntax of the form `A* B*` for arbitrary syntax `A` and `B`. The problem
17 /// is that when the fork fails to parse an `A`, it's impossible to tell
18 /// whether that was because of a syntax error and the user meant to provide
David Tolnayfa033fa2019-06-23 14:07:36 -070019 /// an `A`, or that the `A`s are finished and its time to start parsing
20 /// `B`s. Use with care.
cad9789bb9452019-01-20 18:33:48 -050021 ///
David Tolnayfa033fa2019-06-23 14:07:36 -070022 /// Also note that if `A` is a subset of `B`, `A* B*` can be parsed by
23 /// parsing `B*` and removing the leading members of `A` from the
24 /// repetition, bypassing the need to involve the downsides associated with
25 /// speculative parsing.
cad9789bb9452019-01-20 18:33:48 -050026 ///
27 /// [`ParseStream::fork`]: ../struct.ParseBuffer.html#method.fork
28 ///
29 /// # Example
30 ///
31 /// There has been chatter about the possibility of making the colons in the
32 /// turbofish syntax like `path::to::<T>` no longer required by accepting
David Tolnayfa033fa2019-06-23 14:07:36 -070033 /// `path::to<T>` in expression position. Specifically, according to [RFC
34 /// 2544], [`PathSegment`] parsing should always try to consume a following
35 /// `<` token as the start of generic arguments, and reset to the `<` if
36 /// that fails (e.g. the token is acting as a less-than operator).
cad9789bb9452019-01-20 18:33:48 -050037 ///
David Tolnayfa033fa2019-06-23 14:07:36 -070038 /// This is the exact kind of parsing behavior which requires the "fork,
39 /// try, commit" behavior that [`ParseStream::fork`] discourages. With
40 /// `advance_to`, we can avoid having to parse the speculatively parsed
41 /// content a second time.
cad9789bb9452019-01-20 18:33:48 -050042 ///
43 /// This change in behavior can be implemented in syn by replacing just the
44 /// `Parse` implementation for `PathSegment`:
45 ///
46 /// ```edition2018
47 /// # use syn::ext::IdentExt;
48 /// use syn::parse::discouraged::Speculative;
49 /// # use syn::parse::{Parse, ParseStream};
50 /// # use syn::{Ident, PathArguments, Result, Token};
51 ///
52 /// pub struct PathSegment {
53 /// pub ident: Ident,
54 /// pub arguments: PathArguments,
55 /// }
56 ///
57 /// # impl<T> From<T> for PathSegment
58 /// # where
59 /// # T: Into<Ident>,
60 /// # {
61 /// # fn from(ident: T) -> Self {
62 /// # PathSegment {
63 /// # ident: ident.into(),
64 /// # arguments: PathArguments::None,
65 /// # }
66 /// # }
67 /// # }
68 ///
69 ///
70 /// impl Parse for PathSegment {
71 /// fn parse(input: ParseStream) -> Result<Self> {
72 /// if input.peek(Token![super])
73 /// || input.peek(Token![self])
74 /// || input.peek(Token![Self])
75 /// || input.peek(Token![crate])
76 /// || input.peek(Token![extern])
77 /// {
78 /// let ident = input.call(Ident::parse_any)?;
79 /// return Ok(PathSegment::from(ident));
80 /// }
81 ///
82 /// let ident = input.parse()?;
83 /// if input.peek(Token![::]) && input.peek3(Token![<]) {
84 /// return Ok(PathSegment {
85 /// ident: ident,
86 /// arguments: PathArguments::AngleBracketed(input.parse()?),
87 /// });
88 /// }
89 /// if input.peek(Token![<]) && !input.peek(Token![<=]) {
90 /// let fork = input.fork();
91 /// if let Ok(arguments) = fork.parse() {
92 /// input.advance_to(&fork);
93 /// return Ok(PathSegment {
94 /// ident: ident,
95 /// arguments: PathArguments::AngleBracketed(arguments),
96 /// });
97 /// }
98 /// }
99 /// Ok(PathSegment::from(ident))
100 /// }
101 /// }
102 ///
103 /// # syn::parse_str::<PathSegment>("a<b,c>").unwrap();
104 /// ```
105 ///
cad97810c8902019-06-18 17:39:32 -0400106 /// # Drawbacks
107 ///
108 /// The main drawback of this style of speculative parsing is in error
109 /// presentation. Even if the lookahead is the "correct" parse, the error
110 /// that is shown is that of the "fallback" parse. To use the same example
111 /// as the turbofish above, take the following unfinished "turbofish":
112 ///
113 /// ```text
114 /// let _ = f<&'a fn(), for<'a> serde::>();
115 /// ```
116 ///
117 /// If this is parsed as generic arguments, we can provide the error message
118 ///
119 /// ```text
120 /// error: expected identifier
121 /// --> src.rs:L:C
122 /// |
123 /// L | let _ = f<&'a fn(), for<'a> serde::>();
124 /// | ^
125 /// ```
126 ///
127 /// but if parsed using the above speculative parsing, it falls back to
David Tolnayfa033fa2019-06-23 14:07:36 -0700128 /// assuming that the `<` is a less-than when it fails to parse the generic
129 /// arguments, and tries to interpret the `&'a` as the start of a labelled
130 /// loop, resulting in the much less helpful error
cad97810c8902019-06-18 17:39:32 -0400131 ///
132 /// ```text
133 /// error: expected `:`
134 /// --> src.rs:L:C
135 /// |
136 /// L | let _ = f<&'a fn(), for<'a> serde::>();
137 /// | ^^
138 /// ```
139 ///
140 /// This can be mitigated with various heuristics (two examples: show both
141 /// forks' parse errors, or show the one that consumed more tokens), but
142 /// when you can control the grammar, sticking to something that can be
143 /// parsed LL(3) and without the LL(*) speculative parsing this makes
144 /// possible, displaying reasonable errors becomes much more simple.
145 ///
David Tolnayc9493ba2019-06-23 14:06:30 -0700146 /// [RFC 2544]: https://github.com/rust-lang/rfcs/pull/2544
cad9789bb9452019-01-20 18:33:48 -0500147 /// [`PathSegment`]: ../../struct.PathSegment.html
148 ///
David Tolnay9f7b18a2019-06-23 14:00:59 -0700149 /// # Performance
150 ///
151 /// This method performs a cheap fixed amount of work that does not depend
152 /// on how far apart the two streams are positioned.
153 ///
cad9789bb9452019-01-20 18:33:48 -0500154 /// # Panics
155 ///
David Tolnay87628522019-06-23 14:06:08 -0700156 /// The forked stream in the argument of `advance_to` must have been
157 /// obtained by forking `self`. Attempting to advance to any other stream
158 /// will cause a panic.
cad9789bb9452019-01-20 18:33:48 -0500159 fn advance_to(&self, fork: &Self);
160}
161
162impl<'a> Speculative for ParseBuffer<'a> {
163 fn advance_to(&self, fork: &Self) {
David Tolnay6db0f2a2019-06-23 13:37:39 -0700164 if !private::same_scope(self.cursor(), fork.cursor()) {
165 panic!("Fork was not derived from the advancing parse stream");
166 }
167
cad9789bb9452019-01-20 18:33:48 -0500168 // See comment on `cell` in the struct definition.
David Tolnay53689542019-06-23 13:02:47 -0700169 self.cell
170 .set(unsafe { mem::transmute::<Cursor, Cursor<'static>>(fork.cursor()) })
cad9789bb9452019-01-20 18:33:48 -0500171 }
172}