meta.rs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. // SPDX-License-Identifier: Apache-2.0 OR MIT
  2. //! Facility for interpreting structured content inside of an `Attribute`.
  3. use crate::error::{Error, Result};
  4. use crate::ext::IdentExt as _;
  5. use crate::lit::Lit;
  6. use crate::parse::{ParseStream, Parser};
  7. use crate::path::{Path, PathSegment};
  8. use crate::punctuated::Punctuated;
  9. use proc_macro2::Ident;
  10. use std::fmt::Display;
  11. /// Make a parser that is usable with `parse_macro_input!` in a
  12. /// `#[proc_macro_attribute]` macro.
  13. ///
  14. /// *Warning:* When parsing attribute args **other than** the
  15. /// `proc_macro::TokenStream` input of a `proc_macro_attribute`, you do **not**
  16. /// need this function. In several cases your callers will get worse error
  17. /// messages if you use this function, because the surrounding delimiter's span
  18. /// is concealed from attribute macros by rustc. Use
  19. /// [`Attribute::parse_nested_meta`] instead.
  20. ///
  21. /// [`Attribute::parse_nested_meta`]: crate::Attribute::parse_nested_meta
  22. ///
  23. /// # Example
  24. ///
  25. /// This example implements an attribute macro whose invocations look like this:
  26. ///
  27. /// ```
  28. /// # const IGNORE: &str = stringify! {
  29. /// #[tea(kind = "EarlGrey", hot)]
  30. /// struct Picard {...}
  31. /// # };
  32. /// ```
  33. ///
  34. /// The "parameters" supported by the attribute are:
  35. ///
  36. /// - `kind = "..."`
  37. /// - `hot`
  38. /// - `with(sugar, milk, ...)`, a comma-separated list of ingredients
  39. ///
  40. /// ```
  41. /// # extern crate proc_macro;
  42. /// #
  43. /// use proc_macro::TokenStream;
  44. /// use syn::{parse_macro_input, LitStr, Path};
  45. ///
  46. /// # const IGNORE: &str = stringify! {
  47. /// #[proc_macro_attribute]
  48. /// # };
  49. /// pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
  50. /// let mut kind: Option<LitStr> = None;
  51. /// let mut hot: bool = false;
  52. /// let mut with: Vec<Path> = Vec::new();
  53. /// let tea_parser = syn::meta::parser(|meta| {
  54. /// if meta.path.is_ident("kind") {
  55. /// kind = Some(meta.value()?.parse()?);
  56. /// Ok(())
  57. /// } else if meta.path.is_ident("hot") {
  58. /// hot = true;
  59. /// Ok(())
  60. /// } else if meta.path.is_ident("with") {
  61. /// meta.parse_nested_meta(|meta| {
  62. /// with.push(meta.path);
  63. /// Ok(())
  64. /// })
  65. /// } else {
  66. /// Err(meta.error("unsupported tea property"))
  67. /// }
  68. /// });
  69. ///
  70. /// parse_macro_input!(args with tea_parser);
  71. /// eprintln!("kind={kind:?} hot={hot} with={with:?}");
  72. ///
  73. /// /* ... */
  74. /// # TokenStream::new()
  75. /// }
  76. /// ```
  77. ///
  78. /// The `syn::meta` library will take care of dealing with the commas including
  79. /// trailing commas, and producing sensible error messages on unexpected input.
  80. ///
  81. /// ```console
  82. /// error: expected `,`
  83. /// --> src/main.rs:3:37
  84. /// |
  85. /// 3 | #[tea(kind = "EarlGrey", with(sugar = "lol", milk))]
  86. /// | ^
  87. /// ```
  88. ///
  89. /// # Example
  90. ///
  91. /// Same as above but we factor out most of the logic into a separate function.
  92. ///
  93. /// ```
  94. /// # extern crate proc_macro;
  95. /// #
  96. /// use proc_macro::TokenStream;
  97. /// use syn::meta::ParseNestedMeta;
  98. /// use syn::parse::{Parser, Result};
  99. /// use syn::{parse_macro_input, LitStr, Path};
  100. ///
  101. /// # const IGNORE: &str = stringify! {
  102. /// #[proc_macro_attribute]
  103. /// # };
  104. /// pub fn tea(args: TokenStream, input: TokenStream) -> TokenStream {
  105. /// let mut attrs = TeaAttributes::default();
  106. /// let tea_parser = syn::meta::parser(|meta| attrs.parse(meta));
  107. /// parse_macro_input!(args with tea_parser);
  108. ///
  109. /// /* ... */
  110. /// # TokenStream::new()
  111. /// }
  112. ///
  113. /// #[derive(Default)]
  114. /// struct TeaAttributes {
  115. /// kind: Option<LitStr>,
  116. /// hot: bool,
  117. /// with: Vec<Path>,
  118. /// }
  119. ///
  120. /// impl TeaAttributes {
  121. /// fn parse(&mut self, meta: ParseNestedMeta) -> Result<()> {
  122. /// if meta.path.is_ident("kind") {
  123. /// self.kind = Some(meta.value()?.parse()?);
  124. /// Ok(())
  125. /// } else /* just like in last example */
  126. /// # { unimplemented!() }
  127. ///
  128. /// }
  129. /// }
  130. /// ```
  131. pub fn parser(logic: impl FnMut(ParseNestedMeta) -> Result<()>) -> impl Parser<Output = ()> {
  132. |input: ParseStream| {
  133. if input.is_empty() {
  134. Ok(())
  135. } else {
  136. parse_nested_meta(input, logic)
  137. }
  138. }
  139. }
  140. /// Context for parsing a single property in the conventional syntax for
  141. /// structured attributes.
  142. ///
  143. /// # Examples
  144. ///
  145. /// Refer to usage examples on the following two entry-points:
  146. ///
  147. /// - [`Attribute::parse_nested_meta`] if you have an entire `Attribute` to
  148. /// parse. Always use this if possible. Generally this is able to produce
  149. /// better error messages because `Attribute` holds span information for all
  150. /// of the delimiters therein.
  151. ///
  152. /// - [`syn::meta::parser`] if you are implementing a `proc_macro_attribute`
  153. /// macro and parsing the arguments to the attribute macro, i.e. the ones
  154. /// written in the same attribute that dispatched the macro invocation. Rustc
  155. /// does not pass span information for the surrounding delimiters into the
  156. /// attribute macro invocation in this situation, so error messages might be
  157. /// less precise.
  158. ///
  159. /// [`Attribute::parse_nested_meta`]: crate::Attribute::parse_nested_meta
  160. /// [`syn::meta::parser`]: crate::meta::parser
  161. #[non_exhaustive]
  162. pub struct ParseNestedMeta<'a> {
  163. pub path: Path,
  164. pub input: ParseStream<'a>,
  165. }
  166. impl<'a> ParseNestedMeta<'a> {
  167. /// Used when parsing `key = "value"` syntax.
  168. ///
  169. /// All it does is advance `meta.input` past the `=` sign in the input. You
  170. /// could accomplish the same effect by writing
  171. /// `meta.parse::<Token![=]>()?`, so at most it is a minor convenience to
  172. /// use `meta.value()?`.
  173. ///
  174. /// # Example
  175. ///
  176. /// ```
  177. /// use syn::{parse_quote, Attribute, LitStr};
  178. ///
  179. /// let attr: Attribute = parse_quote! {
  180. /// #[tea(kind = "EarlGrey")]
  181. /// };
  182. /// // conceptually:
  183. /// if attr.path().is_ident("tea") { // this parses the `tea`
  184. /// attr.parse_nested_meta(|meta| { // this parses the `(`
  185. /// if meta.path.is_ident("kind") { // this parses the `kind`
  186. /// let value = meta.value()?; // this parses the `=`
  187. /// let s: LitStr = value.parse()?; // this parses `"EarlGrey"`
  188. /// if s.value() == "EarlGrey" {
  189. /// // ...
  190. /// }
  191. /// Ok(())
  192. /// } else {
  193. /// Err(meta.error("unsupported attribute"))
  194. /// }
  195. /// })?;
  196. /// }
  197. /// # anyhow::Ok(())
  198. /// ```
  199. pub fn value(&self) -> Result<ParseStream<'a>> {
  200. self.input.parse::<Token![=]>()?;
  201. Ok(self.input)
  202. }
  203. /// Used when parsing `list(...)` syntax **if** the content inside the
  204. /// nested parentheses is also expected to conform to Rust's structured
  205. /// attribute convention.
  206. ///
  207. /// # Example
  208. ///
  209. /// ```
  210. /// use syn::{parse_quote, Attribute};
  211. ///
  212. /// let attr: Attribute = parse_quote! {
  213. /// #[tea(with(sugar, milk))]
  214. /// };
  215. ///
  216. /// if attr.path().is_ident("tea") {
  217. /// attr.parse_nested_meta(|meta| {
  218. /// if meta.path.is_ident("with") {
  219. /// meta.parse_nested_meta(|meta| { // <---
  220. /// if meta.path.is_ident("sugar") {
  221. /// // Here we can go even deeper if needed.
  222. /// Ok(())
  223. /// } else if meta.path.is_ident("milk") {
  224. /// Ok(())
  225. /// } else {
  226. /// Err(meta.error("unsupported ingredient"))
  227. /// }
  228. /// })
  229. /// } else {
  230. /// Err(meta.error("unsupported tea property"))
  231. /// }
  232. /// })?;
  233. /// }
  234. /// # anyhow::Ok(())
  235. /// ```
  236. ///
  237. /// # Counterexample
  238. ///
  239. /// If you don't need `parse_nested_meta`'s help in parsing the content
  240. /// written within the nested parentheses, keep in mind that you can always
  241. /// just parse it yourself from the exposed ParseStream. Rust syntax permits
  242. /// arbitrary tokens within those parentheses so for the crazier stuff,
  243. /// `parse_nested_meta` is not what you want.
  244. ///
  245. /// ```
  246. /// use syn::{parenthesized, parse_quote, Attribute, LitInt};
  247. ///
  248. /// let attr: Attribute = parse_quote! {
  249. /// #[repr(align(32))]
  250. /// };
  251. ///
  252. /// let mut align: Option<LitInt> = None;
  253. /// if attr.path().is_ident("repr") {
  254. /// attr.parse_nested_meta(|meta| {
  255. /// if meta.path.is_ident("align") {
  256. /// let content;
  257. /// parenthesized!(content in meta.input);
  258. /// align = Some(content.parse()?);
  259. /// Ok(())
  260. /// } else {
  261. /// Err(meta.error("unsupported repr"))
  262. /// }
  263. /// })?;
  264. /// }
  265. /// # anyhow::Ok(())
  266. /// ```
  267. pub fn parse_nested_meta(
  268. &self,
  269. logic: impl FnMut(ParseNestedMeta) -> Result<()>,
  270. ) -> Result<()> {
  271. let content;
  272. parenthesized!(content in self.input);
  273. parse_nested_meta(&content, logic)
  274. }
  275. /// Report that the attribute's content did not conform to expectations.
  276. ///
  277. /// The span of the resulting error will cover `meta.path` *and* everything
  278. /// that has been parsed so far since it.
  279. ///
  280. /// There are 2 ways you might call this. First, if `meta.path` is not
  281. /// something you recognize:
  282. ///
  283. /// ```
  284. /// # use syn::Attribute;
  285. /// #
  286. /// # fn example(attr: &Attribute) -> syn::Result<()> {
  287. /// attr.parse_nested_meta(|meta| {
  288. /// if meta.path.is_ident("kind") {
  289. /// // ...
  290. /// Ok(())
  291. /// } else {
  292. /// Err(meta.error("unsupported tea property"))
  293. /// }
  294. /// })?;
  295. /// # Ok(())
  296. /// # }
  297. /// ```
  298. ///
  299. /// In this case, it behaves exactly like
  300. /// `syn::Error::new_spanned(&meta.path, "message...")`.
  301. ///
  302. /// ```console
  303. /// error: unsupported tea property
  304. /// --> src/main.rs:3:26
  305. /// |
  306. /// 3 | #[tea(kind = "EarlGrey", wat = "foo")]
  307. /// | ^^^
  308. /// ```
  309. ///
  310. /// More usefully, the second place is if you've already parsed a value but
  311. /// have decided not to accept the value:
  312. ///
  313. /// ```
  314. /// # use syn::Attribute;
  315. /// #
  316. /// # fn example(attr: &Attribute) -> syn::Result<()> {
  317. /// use syn::Expr;
  318. ///
  319. /// attr.parse_nested_meta(|meta| {
  320. /// if meta.path.is_ident("kind") {
  321. /// let expr: Expr = meta.value()?.parse()?;
  322. /// match expr {
  323. /// Expr::Lit(expr) => /* ... */
  324. /// # unimplemented!(),
  325. /// Expr::Path(expr) => /* ... */
  326. /// # unimplemented!(),
  327. /// Expr::Macro(expr) => /* ... */
  328. /// # unimplemented!(),
  329. /// _ => Err(meta.error("tea kind must be a string literal, path, or macro")),
  330. /// }
  331. /// } else /* as above */
  332. /// # { unimplemented!() }
  333. ///
  334. /// })?;
  335. /// # Ok(())
  336. /// # }
  337. /// ```
  338. ///
  339. /// ```console
  340. /// error: tea kind must be a string literal, path, or macro
  341. /// --> src/main.rs:3:7
  342. /// |
  343. /// 3 | #[tea(kind = async { replicator.await })]
  344. /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  345. /// ```
  346. ///
  347. /// Often you may want to use `syn::Error::new_spanned` even in this
  348. /// situation. In the above code, that would be:
  349. ///
  350. /// ```
  351. /// # use syn::{Error, Expr};
  352. /// #
  353. /// # fn example(expr: Expr) -> syn::Result<()> {
  354. /// match expr {
  355. /// Expr::Lit(expr) => /* ... */
  356. /// # unimplemented!(),
  357. /// Expr::Path(expr) => /* ... */
  358. /// # unimplemented!(),
  359. /// Expr::Macro(expr) => /* ... */
  360. /// # unimplemented!(),
  361. /// _ => Err(Error::new_spanned(expr, "unsupported expression type for `kind`")),
  362. /// }
  363. /// # }
  364. /// ```
  365. ///
  366. /// ```console
  367. /// error: unsupported expression type for `kind`
  368. /// --> src/main.rs:3:14
  369. /// |
  370. /// 3 | #[tea(kind = async { replicator.await })]
  371. /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^
  372. /// ```
  373. pub fn error(&self, msg: impl Display) -> Error {
  374. let start_span = self.path.segments[0].ident.span();
  375. let end_span = self.input.cursor().prev_span();
  376. crate::error::new2(start_span, end_span, msg)
  377. }
  378. }
  379. pub(crate) fn parse_nested_meta(
  380. input: ParseStream,
  381. mut logic: impl FnMut(ParseNestedMeta) -> Result<()>,
  382. ) -> Result<()> {
  383. loop {
  384. let path = input.call(parse_meta_path)?;
  385. logic(ParseNestedMeta { path, input })?;
  386. if input.is_empty() {
  387. return Ok(());
  388. }
  389. input.parse::<Token![,]>()?;
  390. if input.is_empty() {
  391. return Ok(());
  392. }
  393. }
  394. }
  395. // Like Path::parse_mod_style, but accepts keywords in the path.
  396. fn parse_meta_path(input: ParseStream) -> Result<Path> {
  397. Ok(Path {
  398. leading_colon: input.parse()?,
  399. segments: {
  400. let mut segments = Punctuated::new();
  401. if input.peek(Ident::peek_any) {
  402. let ident = Ident::parse_any(input)?;
  403. segments.push_value(PathSegment::from(ident));
  404. } else if input.is_empty() {
  405. return Err(input.error("expected nested attribute"));
  406. } else if input.peek(Lit) {
  407. return Err(input.error("unexpected literal in nested attribute, expected ident"));
  408. } else {
  409. return Err(input.error("unexpected token in nested attribute, expected ident"));
  410. }
  411. while input.peek(Token![::]) {
  412. let punct = input.parse()?;
  413. segments.push_punct(punct);
  414. let ident = Ident::parse_any(input)?;
  415. segments.push_value(PathSegment::from(ident));
  416. }
  417. segments
  418. },
  419. })
  420. }