about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2021-05-20 00:18:56 +0200
committerGitHub <noreply@github.com>2021-05-20 00:18:56 +0200
commita1ac37289404cef53467e09bd3ff7e13ceb18bb6 (patch)
treed9f3c530fed7be0793b6c220a8695996bd96fcea /compiler
parentf94942d8421dc4b1da86d07069571ddb43127235 (diff)
parent34585cb678bc492be7e48ff48a2633f4ce1dc5ae (diff)
downloadrust-a1ac37289404cef53467e09bd3ff7e13ceb18bb6.tar.gz
rust-a1ac37289404cef53467e09bd3ff7e13ceb18bb6.zip
Rollup merge of #84717 - dtolnay:literalfromstr, r=petrochenkov
impl FromStr for proc_macro::Literal

Note that unlike `impl FromStr for proc_macro::TokenStream`, this impl does not permit whitespace or comments. The input string must consist of nothing but your literal.

- `"1".parse::<Literal>()` ⟶ ok

- `"1.0".parse::<Literal>()` ⟶ ok

- `"'a'".parse::<Literal>()` ⟶ ok

- `"\"\n\"".parse::<Literal>()` ⟶ ok

- `"0 1".parse::<Literal>()` ⟶ LexError

- `" 0".parse::<Literal>()` ⟶ LexError

- `"0 ".parse::<Literal>()` ⟶ LexError

- `"/* comment */0".parse::<Literal>()` ⟶ LexError

- `"0/* comment */".parse::<Literal>()` ⟶ LexError

- `"0// comment".parse::<Literal>()` ⟶ LexError

---

## Use case

```rust
let hex_int: Literal = format!("0x{:x}", int).parse().unwrap();
```

The only way this is expressible in the current API is significantly worse.

```rust
let hex_int = match format!("0x{:x}", int)
    .parse::<TokenStream>()
    .unwrap()
    .into_iter()
    .next()
    .unwrap()
{
    TokenTree::Literal(literal) => literal,
    _ => unreachable!(),
};
```
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_expand/src/proc_macro_server.rs31
1 files changed, 28 insertions, 3 deletions
diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs
index 7bf6502c976..92315c4d4f6 100644
--- a/compiler/rustc_expand/src/proc_macro_server.rs
+++ b/compiler/rustc_expand/src/proc_macro_server.rs
@@ -1,9 +1,7 @@
 use crate::base::{ExtCtxt, ResolverExpand};
 
 use rustc_ast as ast;
-use rustc_ast::token;
-use rustc_ast::token::Nonterminal;
-use rustc_ast::token::NtIdent;
+use rustc_ast::token::{self, Nonterminal, NtIdent, TokenKind};
 use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens};
 use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
 use rustc_ast_pretty::pprust;
@@ -541,6 +539,33 @@ impl server::Ident for Rustc<'_> {
 }
 
 impl server::Literal for Rustc<'_> {
+    fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
+        let override_span = None;
+        let stream = parse_stream_from_source_str(
+            FileName::proc_macro_source_code(s),
+            s.to_owned(),
+            self.sess,
+            override_span,
+        );
+        if stream.len() != 1 {
+            return Err(());
+        }
+        let tree = stream.into_trees().next().unwrap();
+        let token = match tree {
+            tokenstream::TokenTree::Token(token) => token,
+            tokenstream::TokenTree::Delimited { .. } => return Err(()),
+        };
+        let span_data = token.span.data();
+        if (span_data.hi.0 - span_data.lo.0) as usize != s.len() {
+            // There is a comment or whitespace adjacent to the literal.
+            return Err(());
+        }
+        let lit = match token.kind {
+            TokenKind::Literal(lit) => lit,
+            _ => return Err(()),
+        };
+        Ok(Literal { lit, span: self.call_site })
+    }
     fn debug_kind(&mut self, literal: &Self::Literal) -> String {
         format!("{:?}", literal.lit.kind)
     }