diff options
author | Nika Layzell <nika@thelayzells.com> | 2019-07-14 13:35:21 -0400 |
---|---|---|
committer | Nika Layzell <nika@thelayzells.com> | 2019-07-14 20:57:38 -0400 |
commit | e9bf09fbf32b9e0dece9284e2cbb693c69cf0b09 (patch) | |
tree | 8073fe56111e060a61b0ec1fb239e129d7a27eec /src/runtime.rs | |
parent | 5673e5f28b88e24de6a3af12a62b83d3b9d44214 (diff) | |
download | platform_external_rust_crates_quote-e9bf09fbf32b9e0dece9284e2cbb693c69cf0b09.tar.gz platform_external_rust_crates_quote-e9bf09fbf32b9e0dece9284e2cbb693c69cf0b09.tar.bz2 platform_external_rust_crates_quote-e9bf09fbf32b9e0dece9284e2cbb693c69cf0b09.zip |
Add a format_ident! helper macro (#110)
This is similar to the initial proposal in #110, with a few changes.
1. If an `Ident` was used to provide a fragment within the final formatted
string, the default behaviour has been changed to inheret the `Span` from it.
The idea here is to produce good spans for formatted identifiers by default,
attributing them to the source of their name.
The `span` named argument can still be used to override this decision.
2. `Ident::new(...)` doesn't support creating raw identifiers, and
`Ident::new_raw` is still unstable, so the initial behaviour of directly
passing the result of `format!` into `Ident::new(...)` turned out not to
work.
Instead, a helper method was added, `__rt::mk_ident`, which will handle
creating raw identifiers using the roundabout "parse as TokenStream and
unwrap" approach.
I thought it was important to support raw identifiers in `format_ident!` as
people may want to be able to write `format_ident!("r#{}_{}", a, b)` or
similar to ensure that their identifiers are never confused for keywords.
3. It turns out it's basically impossible to compare spans right now, and
there's no stable way to produce a span other than `Span::call_site()`, so no
tests check that the spans are passed around correctly.
Diffstat (limited to 'src/runtime.rs')
-rw-r--r-- | src/runtime.rs | 89 |
1 files changed, 88 insertions, 1 deletions
diff --git a/src/runtime.rs b/src/runtime.rs index 576f98b..2eec861 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,6 +1,7 @@ use ext::TokenStreamExt; pub use proc_macro2::*; -use ToTokens; +use {ToTokens, IdentFragment}; +use std::fmt; /// Extension traits used by the implementation of `quote!`. These are defined /// in separate traits, rather than as a single trait due to ambiguity issues. @@ -263,3 +264,89 @@ push_punct!(push_shr_eq '>' '>' '='); push_punct!(push_star '*'); push_punct!(push_sub '-'); push_punct!(push_sub_eq '-' '='); + +// Helper method for constructing identifiers from the `format_ident!` macro, +// handling `r#` prefixes. +// +// Directly parsing the input string may produce a valid identifier, +// although the input string was invalid, due to ignored characters such as +// whitespace and comments. Instead, we always create a non-raw identifier +// to validate that the string is OK, and only parse again if needed. +// +// The `is_ident` method defined above is insufficient for validation, as it +// will reject non-ASCII identifiers. +pub fn mk_ident(id: &str, span: Option<Span>) -> Ident { + let span = span.unwrap_or_else(Span::call_site); + + let is_raw = id.starts_with("r#"); + let unraw = Ident::new(if is_raw { &id[2..] } else { id }, span); + if !is_raw { + return unraw; + } + + // At this point, the identifier is raw, and the unraw-ed version of it was + // successfully converted into an identifier. Try to produce a valid raw + // identifier by running the `TokenStream` parser, and unwrapping the first + // token as an `Ident`. + // + // FIXME: When `Ident::new_raw` becomes stable, this method should be + // updated to call it when available. + match id.parse::<TokenStream>() { + Ok(ts) => { + let mut iter = ts.into_iter(); + match (iter.next(), iter.next()) { + (Some(TokenTree::Ident(mut id)), None) => { + id.set_span(span); + id + } + _ => unreachable!("valid raw ident fails to parse"), + } + } + Err(_) => unreachable!("valid raw ident fails to parse"), + } +} + +// Adapts from `IdentFragment` to `fmt::Display` for use by the `format_ident!` +// macro, and exposes span information from these fragments. +// +// This struct also has forwarding implementations of the formatting traits +// `Octal`, `LowerHex`, `UpperHex`, and `Binary` to allow for their use within +// `format_ident!`. +#[derive(Copy, Clone)] +pub struct IdentFragmentAdapter<T: IdentFragment>(pub T); + +impl<T: IdentFragment> IdentFragmentAdapter<T> { + pub fn span(&self) -> Option<Span> { + self.0.span() + } +} + +impl<T: IdentFragment> fmt::Display for IdentFragmentAdapter<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + IdentFragment::fmt(&self.0, f) + } +} + +impl<T: IdentFragment + fmt::Octal> fmt::Octal for IdentFragmentAdapter<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Octal::fmt(&self.0, f) + } +} + +impl<T: IdentFragment + fmt::LowerHex> fmt::LowerHex for IdentFragmentAdapter<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::LowerHex::fmt(&self.0, f) + } +} + +impl<T: IdentFragment + fmt::UpperHex> fmt::UpperHex for IdentFragmentAdapter<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::UpperHex::fmt(&self.0, f) + } +} + +impl<T: IdentFragment + fmt::Binary> fmt::Binary for IdentFragmentAdapter<T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Binary::fmt(&self.0, f) + } +} |