diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..349b5fc --- /dev/null +++ b/build.rs @@ -0,0 +1,33 @@ +use std::env; +use std::process::Command; +use std::str; + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + let version = match rustc_version() { + Some(version) => version, + None => return, + }; + + if version.minor < 54 { + // https://github.com/rust-lang/rust/pull/84717 + println!("cargo:rustc-cfg=no_literal_fromstr"); + } +} + +struct RustcVersion { + minor: u32, +} + +fn rustc_version() -> Option { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + let minor = pieces.next()?.parse().ok()?; + Some(RustcVersion { minor }) +} diff --git a/src/lib.rs b/src/lib.rs index 983ba06..80c1b98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -411,6 +411,26 @@ fn parse_bracket_as_segments(input: TokenStream, scope: Span) -> Result Result { let mut tokens = TokenStream::new(); + #[cfg(not(no_literal_fromstr))] + { + use proc_macro::{LexError, Literal}; + use std::str::FromStr; + + if pasted.starts_with(|ch: char| ch.is_ascii_digit()) { + let literal = match panic::catch_unwind(|| Literal::from_str(&pasted)) { + Ok(Ok(literal)) => TokenTree::Literal(literal), + Ok(Err(LexError { .. })) | Err(_) => { + return Err(Error::new( + span, + &format!("`{:?}` is not a valid literal", pasted), + )); + } + }; + tokens.extend(iter::once(literal)); + return Ok(tokens); + } + } + if pasted.starts_with('\'') { let mut apostrophe = TokenTree::Punct(Punct::new('\'', Spacing::Joint)); apostrophe.set_span(span); diff --git a/tests/test_expr.rs b/tests/test_expr.rs index 8ad5de9..5ce2549 100644 --- a/tests/test_expr.rs +++ b/tests/test_expr.rs @@ -26,7 +26,7 @@ fn test_repeat() { } #[test] -fn test_literals() { +fn test_literal_to_identifier() { const CONST0: &str = "const0"; let pasted = paste!([]); @@ -45,6 +45,17 @@ fn test_literals() { assert_eq!(pasted, CONST0); } +#[test] +fn test_literal_suffix() { + macro_rules! literal { + ($bit:tt) => { + paste!([<1_u $bit>]) + }; + } + + assert_eq!(literal!(32), 1); +} + #[test] fn test_underscore() { paste! { diff --git a/tests/ui/invalid-ident.stderr b/tests/ui/invalid-ident.stderr index f32d484..28593fb 100644 --- a/tests/ui/invalid-ident.stderr +++ b/tests/ui/invalid-ident.stderr @@ -1,8 +1,12 @@ -error: `"0f"` is not a valid identifier - --> tests/ui/invalid-ident.rs:4:8 +error: expected identifier, found `0f` + --> tests/ui/invalid-ident.rs:3:1 | -4 | fn [<0 f>]() {} - | ^^^^^^^ +3 | / paste! { +4 | | fn [<0 f>]() {} +5 | | } + | |_^ expected identifier + | + = note: this error originates in the macro `paste` (in Nightly builds, run with -Z macro-backtrace for more info) error: `"f\""` is not a valid identifier --> tests/ui/invalid-ident.rs:8:8