lookahead.rs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // SPDX-License-Identifier: Apache-2.0 OR MIT
  2. use crate::buffer::Cursor;
  3. use crate::error::{self, Error};
  4. use crate::sealed::lookahead::Sealed;
  5. use crate::span::IntoSpans;
  6. use crate::token::{CustomToken, Token};
  7. use proc_macro2::{Delimiter, Span};
  8. use std::cell::RefCell;
  9. /// Support for checking the next token in a stream to decide how to parse.
  10. ///
  11. /// An important advantage over [`ParseStream::peek`] is that here we
  12. /// automatically construct an appropriate error message based on the token
  13. /// alternatives that get peeked. If you are producing your own error message,
  14. /// go ahead and use `ParseStream::peek` instead.
  15. ///
  16. /// Use [`ParseStream::lookahead1`] to construct this object.
  17. ///
  18. /// [`ParseStream::peek`]: crate::parse::ParseBuffer::peek
  19. /// [`ParseStream::lookahead1`]: crate::parse::ParseBuffer::lookahead1
  20. ///
  21. /// Consuming tokens from the source stream after constructing a lookahead
  22. /// object does not also advance the lookahead object.
  23. ///
  24. /// # Example
  25. ///
  26. /// ```
  27. /// use syn::{ConstParam, Ident, Lifetime, LifetimeParam, Result, Token, TypeParam};
  28. /// use syn::parse::{Parse, ParseStream};
  29. ///
  30. /// // A generic parameter, a single one of the comma-separated elements inside
  31. /// // angle brackets in:
  32. /// //
  33. /// // fn f<T: Clone, 'a, 'b: 'a, const N: usize>() { ... }
  34. /// //
  35. /// // On invalid input, lookahead gives us a reasonable error message.
  36. /// //
  37. /// // error: expected one of: identifier, lifetime, `const`
  38. /// // |
  39. /// // 5 | fn f<!Sized>() {}
  40. /// // | ^
  41. /// enum GenericParam {
  42. /// Type(TypeParam),
  43. /// Lifetime(LifetimeParam),
  44. /// Const(ConstParam),
  45. /// }
  46. ///
  47. /// impl Parse for GenericParam {
  48. /// fn parse(input: ParseStream) -> Result<Self> {
  49. /// let lookahead = input.lookahead1();
  50. /// if lookahead.peek(Ident) {
  51. /// input.parse().map(GenericParam::Type)
  52. /// } else if lookahead.peek(Lifetime) {
  53. /// input.parse().map(GenericParam::Lifetime)
  54. /// } else if lookahead.peek(Token![const]) {
  55. /// input.parse().map(GenericParam::Const)
  56. /// } else {
  57. /// Err(lookahead.error())
  58. /// }
  59. /// }
  60. /// }
  61. /// ```
  62. pub struct Lookahead1<'a> {
  63. scope: Span,
  64. cursor: Cursor<'a>,
  65. comparisons: RefCell<Vec<&'static str>>,
  66. }
  67. pub(crate) fn new(scope: Span, cursor: Cursor) -> Lookahead1 {
  68. Lookahead1 {
  69. scope,
  70. cursor,
  71. comparisons: RefCell::new(Vec::new()),
  72. }
  73. }
  74. fn peek_impl(
  75. lookahead: &Lookahead1,
  76. peek: fn(Cursor) -> bool,
  77. display: fn() -> &'static str,
  78. ) -> bool {
  79. if peek(lookahead.cursor) {
  80. return true;
  81. }
  82. lookahead.comparisons.borrow_mut().push(display());
  83. false
  84. }
  85. impl<'a> Lookahead1<'a> {
  86. /// Looks at the next token in the parse stream to determine whether it
  87. /// matches the requested type of token.
  88. ///
  89. /// # Syntax
  90. ///
  91. /// Note that this method does not use turbofish syntax. Pass the peek type
  92. /// inside of parentheses.
  93. ///
  94. /// - `input.peek(Token![struct])`
  95. /// - `input.peek(Token![==])`
  96. /// - `input.peek(Ident)`&emsp;*(does not accept keywords)*
  97. /// - `input.peek(Ident::peek_any)`
  98. /// - `input.peek(Lifetime)`
  99. /// - `input.peek(token::Brace)`
  100. pub fn peek<T: Peek>(&self, token: T) -> bool {
  101. let _ = token;
  102. peek_impl(self, T::Token::peek, T::Token::display)
  103. }
  104. /// Triggers an error at the current position of the parse stream.
  105. ///
  106. /// The error message will identify all of the expected token types that
  107. /// have been peeked against this lookahead instance.
  108. pub fn error(self) -> Error {
  109. let mut comparisons = self.comparisons.into_inner();
  110. comparisons.retain_mut(|display| {
  111. if *display == "`)`" {
  112. *display = match self.cursor.scope_delimiter() {
  113. Delimiter::Parenthesis => "`)`",
  114. Delimiter::Brace => "`}`",
  115. Delimiter::Bracket => "`]`",
  116. Delimiter::None => return false,
  117. }
  118. }
  119. true
  120. });
  121. match comparisons.len() {
  122. 0 => {
  123. if self.cursor.eof() {
  124. Error::new(self.scope, "unexpected end of input")
  125. } else {
  126. Error::new(self.cursor.span(), "unexpected token")
  127. }
  128. }
  129. 1 => {
  130. let message = format!("expected {}", comparisons[0]);
  131. error::new_at(self.scope, self.cursor, message)
  132. }
  133. 2 => {
  134. let message = format!("expected {} or {}", comparisons[0], comparisons[1]);
  135. error::new_at(self.scope, self.cursor, message)
  136. }
  137. _ => {
  138. let join = comparisons.join(", ");
  139. let message = format!("expected one of: {}", join);
  140. error::new_at(self.scope, self.cursor, message)
  141. }
  142. }
  143. }
  144. }
  145. /// Types that can be parsed by looking at just one token.
  146. ///
  147. /// Use [`ParseStream::peek`] to peek one of these types in a parse stream
  148. /// without consuming it from the stream.
  149. ///
  150. /// This trait is sealed and cannot be implemented for types outside of Syn.
  151. ///
  152. /// [`ParseStream::peek`]: crate::parse::ParseBuffer::peek
  153. pub trait Peek: Sealed {
  154. // Not public API.
  155. #[doc(hidden)]
  156. type Token: Token;
  157. }
  158. /// Pseudo-token used for peeking the end of a parse stream.
  159. ///
  160. /// This type is only useful as an argument to one of the following functions:
  161. ///
  162. /// - [`ParseStream::peek`][crate::parse::ParseBuffer::peek]
  163. /// - [`ParseStream::peek2`][crate::parse::ParseBuffer::peek2]
  164. /// - [`ParseStream::peek3`][crate::parse::ParseBuffer::peek3]
  165. /// - [`Lookahead1::peek`]
  166. ///
  167. /// The peek will return `true` if there are no remaining tokens after that
  168. /// point in the parse stream.
  169. ///
  170. /// # Example
  171. ///
  172. /// Suppose we are parsing attributes containing core::fmt inspired formatting
  173. /// arguments:
  174. ///
  175. /// - `#[fmt("simple example")]`
  176. /// - `#[fmt("interpolation e{}ample", self.x)]`
  177. /// - `#[fmt("interpolation e{x}ample")]`
  178. ///
  179. /// and we want to recognize the cases where no interpolation occurs so that
  180. /// more efficient code can be generated.
  181. ///
  182. /// The following implementation uses `input.peek(Token![,]) &&
  183. /// input.peek2(End)` to recognize the case of a trailing comma without
  184. /// consuming the comma from the parse stream, because if it isn't a trailing
  185. /// comma, that same comma needs to be parsed as part of `args`.
  186. ///
  187. /// ```
  188. /// use proc_macro2::TokenStream;
  189. /// use quote::quote;
  190. /// use syn::parse::{End, Parse, ParseStream, Result};
  191. /// use syn::{parse_quote, Attribute, LitStr, Token};
  192. ///
  193. /// struct FormatArgs {
  194. /// template: LitStr, // "...{}..."
  195. /// args: TokenStream, // , self.x
  196. /// }
  197. ///
  198. /// impl Parse for FormatArgs {
  199. /// fn parse(input: ParseStream) -> Result<Self> {
  200. /// let template: LitStr = input.parse()?;
  201. ///
  202. /// let args = if input.is_empty()
  203. /// || input.peek(Token![,]) && input.peek2(End)
  204. /// {
  205. /// input.parse::<Option<Token![,]>>()?;
  206. /// TokenStream::new()
  207. /// } else {
  208. /// input.parse()?
  209. /// };
  210. ///
  211. /// Ok(FormatArgs {
  212. /// template,
  213. /// args,
  214. /// })
  215. /// }
  216. /// }
  217. ///
  218. /// fn main() -> Result<()> {
  219. /// let attrs: Vec<Attribute> = parse_quote! {
  220. /// #[fmt("simple example")]
  221. /// #[fmt("interpolation e{}ample", self.x)]
  222. /// #[fmt("interpolation e{x}ample")]
  223. /// };
  224. ///
  225. /// for attr in &attrs {
  226. /// let FormatArgs { template, args } = attr.parse_args()?;
  227. /// let requires_fmt_machinery =
  228. /// !args.is_empty() || template.value().contains(['{', '}']);
  229. /// let out = if requires_fmt_machinery {
  230. /// quote! {
  231. /// ::core::write!(__formatter, #template #args)
  232. /// }
  233. /// } else {
  234. /// quote! {
  235. /// __formatter.write_str(#template)
  236. /// }
  237. /// };
  238. /// println!("{}", out);
  239. /// }
  240. /// Ok(())
  241. /// }
  242. /// ```
  243. ///
  244. /// Implementing this parsing logic without `peek2(End)` is more clumsy because
  245. /// we'd need a parse stream actually advanced past the comma before being able
  246. /// to find out whether there is anything after it. It would look something
  247. /// like:
  248. ///
  249. /// ```
  250. /// # use proc_macro2::TokenStream;
  251. /// # use syn::parse::{ParseStream, Result};
  252. /// # use syn::Token;
  253. /// #
  254. /// # fn parse(input: ParseStream) -> Result<()> {
  255. /// use syn::parse::discouraged::Speculative as _;
  256. ///
  257. /// let ahead = input.fork();
  258. /// ahead.parse::<Option<Token![,]>>()?;
  259. /// let args = if ahead.is_empty() {
  260. /// input.advance_to(&ahead);
  261. /// TokenStream::new()
  262. /// } else {
  263. /// input.parse()?
  264. /// };
  265. /// # Ok(())
  266. /// # }
  267. /// ```
  268. ///
  269. /// or:
  270. ///
  271. /// ```
  272. /// # use proc_macro2::TokenStream;
  273. /// # use syn::parse::{ParseStream, Result};
  274. /// # use syn::Token;
  275. /// #
  276. /// # fn parse(input: ParseStream) -> Result<()> {
  277. /// use quote::ToTokens as _;
  278. ///
  279. /// let comma: Option<Token![,]> = input.parse()?;
  280. /// let mut args = TokenStream::new();
  281. /// if !input.is_empty() {
  282. /// comma.to_tokens(&mut args);
  283. /// input.parse::<TokenStream>()?.to_tokens(&mut args);
  284. /// }
  285. /// # Ok(())
  286. /// # }
  287. /// ```
  288. pub struct End;
  289. impl Copy for End {}
  290. impl Clone for End {
  291. fn clone(&self) -> Self {
  292. *self
  293. }
  294. }
  295. impl Peek for End {
  296. type Token = Self;
  297. }
  298. impl CustomToken for End {
  299. fn peek(cursor: Cursor) -> bool {
  300. cursor.eof()
  301. }
  302. fn display() -> &'static str {
  303. "`)`" // Lookahead1 error message will fill in the expected close delimiter
  304. }
  305. }
  306. impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Peek for F {
  307. type Token = T;
  308. }
  309. pub enum TokenMarker {}
  310. impl<S> IntoSpans<S> for TokenMarker {
  311. fn into_spans(self) -> S {
  312. match self {}
  313. }
  314. }
  315. impl<F: Copy + FnOnce(TokenMarker) -> T, T: Token> Sealed for F {}
  316. impl Sealed for End {}