Auto merge of #87262 - dtolnay:negative, r=Aaron1011
Support negative numbers in Literal::from_str proc_macro::Literal has allowed negative numbers in a single literal token ever since Rust 1.29, using https://doc.rust-lang.org/stable/proc_macro/struct.Literal.html#method.isize_unsuffixed and similar constructors. ```rust let lit = proc_macro::Literal::isize_unsuffixed(-10); ``` However, the suite of constructors on Literal is not sufficient for all use cases, for example arbitrary precision floats, or custom suffixes in FFI macros. ```rust let lit = proc_macro::Literal::f64_unsuffixed(0.101001000100001000001000000100000001); // :( let lit = proc_macro::Literal::i???_suffixed(10ulong); // :( ``` For those, macros construct the literal using from_str instead, which preserves arbitrary precision, custom suffixes, base, and digit grouping. ```rust let lit = "0.101001000100001000001000000100000001".parse::<Literal>().unwrap(); let lit = "10ulong".parse::<Literal>().unwrap(); let lit = "0b1000_0100_0010_0001".parse::<Literal>().unwrap(); ``` However, until this PR it was not possible to construct a literal token that is **both** negative **and** preserving of arbitrary precision etc. This PR fixes `Literal::from_str` to recognize negative integer and float literals.
This commit is contained in:
commit
e91405b9d5
3 changed files with 57 additions and 23 deletions
|
@ -1,7 +1,7 @@
|
|||
use crate::base::{ExtCtxt, ResolverExpand};
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::token::{self, Nonterminal, NtIdent, TokenKind};
|
||||
use rustc_ast::token::{self, Nonterminal, NtIdent};
|
||||
use rustc_ast::tokenstream::{self, CanSynthesizeMissingTokens};
|
||||
use rustc_ast::tokenstream::{DelimSpan, Spacing::*, TokenStream, TreeAndSpacing};
|
||||
use rustc_ast_pretty::pprust;
|
||||
|
@ -537,30 +537,49 @@ 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,
|
||||
let name = FileName::proc_macro_source_code(s);
|
||||
let mut parser = rustc_parse::new_parser_from_source_str(self.sess, name, s.to_owned());
|
||||
|
||||
let first_span = parser.token.span.data();
|
||||
let minus_present = parser.eat(&token::BinOp(token::Minus));
|
||||
|
||||
let lit_span = parser.token.span.data();
|
||||
let mut lit = match parser.token.kind {
|
||||
token::Literal(lit) => lit,
|
||||
_ => return Err(()),
|
||||
};
|
||||
|
||||
// Check no comment or whitespace surrounding the (possibly negative)
|
||||
// literal, or more tokens after it.
|
||||
if (lit_span.hi.0 - first_span.lo.0) as usize != s.len() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
if minus_present {
|
||||
// If minus is present, check no comment or whitespace in between it
|
||||
// and the literal token.
|
||||
if first_span.hi.0 != lit_span.lo.0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// Check literal is a kind we allow to be negated in a proc macro token.
|
||||
match lit.kind {
|
||||
token::LitKind::Bool
|
||||
| token::LitKind::Byte
|
||||
| token::LitKind::Char
|
||||
| token::LitKind::Str
|
||||
| token::LitKind::StrRaw(_)
|
||||
| token::LitKind::ByteStr
|
||||
| token::LitKind::ByteStrRaw(_)
|
||||
| token::LitKind::Err => return Err(()),
|
||||
token::LitKind::Integer | token::LitKind::Float => {}
|
||||
}
|
||||
|
||||
// Synthesize a new symbol that includes the minus sign.
|
||||
let symbol = Symbol::intern(&s[..1 + lit.symbol.len()]);
|
||||
lit = token::Lit::new(lit.kind, symbol, lit.suffix);
|
||||
}
|
||||
|
||||
Ok(Literal { lit, span: self.call_site })
|
||||
}
|
||||
fn debug_kind(&mut self, literal: &Self::Literal) -> String {
|
||||
|
|
|
@ -1600,6 +1600,10 @@ impl Symbol {
|
|||
self.0.as_u32()
|
||||
}
|
||||
|
||||
pub fn len(self) -> usize {
|
||||
with_interner(|interner| interner.get(self).len())
|
||||
}
|
||||
|
||||
pub fn is_empty(self) -> bool {
|
||||
self == kw::Empty
|
||||
}
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
use proc_macro::Literal;
|
||||
|
||||
pub fn test() {
|
||||
test_display_literal();
|
||||
test_parse_literal();
|
||||
}
|
||||
|
||||
fn test_display_literal() {
|
||||
assert_eq!(Literal::isize_unsuffixed(-10).to_string(), "- 10");
|
||||
assert_eq!(Literal::isize_suffixed(-10).to_string(), "- 10isize");
|
||||
}
|
||||
|
||||
fn test_parse_literal() {
|
||||
assert_eq!("1".parse::<Literal>().unwrap().to_string(), "1");
|
||||
assert_eq!("1.0".parse::<Literal>().unwrap().to_string(), "1.0");
|
||||
|
@ -12,7 +18,10 @@ fn test_parse_literal() {
|
|||
assert_eq!("b\"\"".parse::<Literal>().unwrap().to_string(), "b\"\"");
|
||||
assert_eq!("r##\"\"##".parse::<Literal>().unwrap().to_string(), "r##\"\"##");
|
||||
assert_eq!("10ulong".parse::<Literal>().unwrap().to_string(), "10ulong");
|
||||
assert_eq!("-10ulong".parse::<Literal>().unwrap().to_string(), "- 10ulong");
|
||||
|
||||
assert!("true".parse::<Literal>().is_err());
|
||||
assert!(".8".parse::<Literal>().is_err());
|
||||
assert!("0 1".parse::<Literal>().is_err());
|
||||
assert!("'a".parse::<Literal>().is_err());
|
||||
assert!(" 0".parse::<Literal>().is_err());
|
||||
|
@ -20,4 +29,6 @@ fn test_parse_literal() {
|
|||
assert!("/* comment */0".parse::<Literal>().is_err());
|
||||
assert!("0/* comment */".parse::<Literal>().is_err());
|
||||
assert!("0// comment".parse::<Literal>().is_err());
|
||||
assert!("- 10".parse::<Literal>().is_err());
|
||||
assert!("-'x'".parse::<Literal>().is_err());
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue