#! [allow(
clippy::float_cmp,
clippy::needless_lifetimes,
clippy::needless_raw_string_hashes,
clippy::non_ascii_literal,
clippy::single_match_else,
clippy::uninlined_format_args
)]
#[ macro_use]
mod macros;
use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::ffi::CStr;
use std::str::FromStr;
use syn::{Lit, LitFloat, LitInt, LitStr};
#[ track_caller]
fn lit(s: &str) -> Lit {
let mut tokens = TokenStream::from_str(s).unwrap().into_iter();
match tokens.next().unwrap() {
TokenTree::Literal(lit) => {
assert!(tokens.next().is_none());
Lit::new(lit)
}
wrong => panic!("{:?}" , wrong),
}
}
#[ test]
fn strings() {
#[ track_caller]
fn test_string(s: &str, value: &str) {
let s = s.trim();
match lit(s) {
Lit::Str(lit) => {
assert_eq!(lit.value(), value);
let again = lit.into_token_stream().to_string();
if again != s {
test_string(&again, value);
}
}
wrong => panic!("{:?}" , wrong),
}
}
test_string(r#" "" "#, " ");
test_string(r#" "a" "#, " a");
test_string(r#" "\n" "#, " \n");
test_string(r#" "\r" "#, " \r");
test_string(r#" "\t" "#, " \t");
test_string(r#" "" "#, " "); // NOTE: This is an emoji
test_string(r#" "\" " " #, "\" ");
test_string(r#" "'" "#, " '");
test_string(r#" "\u{1F415}" "#, " \u{1 F415}");
test_string(r#" "\u{1_2__3_}" "#, " \u{123 }");
test_string(
"\" contains\nnewlines\\\nescaped newlines\"" ,
"contains\nnewlinesescaped newlines" ,
);
test_string(
"\" escaped newline\\\n \x0C unsupported whitespace\"" ,
"escaped newline\x0C unsupported whitespace" ,
);
test_string("r\" raw\nstring\\\nhere\"" , "raw\nstring\\\nhere" );
test_string("\" ...\"q" , "..." );
test_string("r\" ...\"q" , "..." );
test_string("r##\" ...\"##q" , "..." );
}
#[ test]
fn byte_strings() {
#[ track_caller]
fn test_byte_string(s: &str, value: &[u8]) {
let s = s.trim();
match lit(s) {
Lit::ByteStr(lit) => {
assert_eq!(lit.value(), value);
let again = lit.into_token_stream().to_string();
if again != s {
test_byte_string(&again, value);
}
}
wrong => panic!("{:?}" , wrong),
}
}
test_byte_string(r#" b"" "#, b" ");
test_byte_string(r#" b"a" "#, b" a");
test_byte_string(r#" b"\n" "#, b" \n");
test_byte_string(r#" b"\r" "#, b" \r");
test_byte_string(r#" b"\t" "#, b" \t");
test_byte_string(r#" b"\" " " #, b"\" ");
test_byte_string(r#" b"'" "#, b" '");
test_byte_string(
"b\" contains\nnewlines\\\nescaped newlines\"" ,
b"contains\nnewlinesescaped newlines" ,
);
test_byte_string("br\" raw\nstring\\\nhere\"" , b"raw\nstring\\\nhere" );
test_byte_string("b\" ...\"q" , b"..." );
test_byte_string("br\" ...\"q" , b"..." );
test_byte_string("br##\" ...\"##q" , b"..." );
}
#[ test]
fn c_strings() {
#[ track_caller]
fn test_c_string(s: &str, value: &CStr) {
let s = s.trim();
match lit(s) {
Lit::CStr(lit) => {
assert_eq!(*lit.value(), *value);
let again = lit.into_token_stream().to_string();
if again != s {
test_c_string(&again, value);
}
}
wrong => panic!("{:?}" , wrong),
}
}
test_c_string(r#" c"" "#, c" ");
test_c_string(r#" c"a" "#, c" a");
test_c_string(r#" c"\n" "#, c" \n");
test_c_string(r#" c"\r" "#, c" \r");
test_c_string(r#" c"\t" "#, c" \t");
test_c_string(r#" c"\\" "#, c" \\");
test_c_string(r#" c"\'" "#, c" '");
test_c_string(r#" c"\" " " #, c"\" ");
test_c_string(
"c\" contains\nnewlines\\\nescaped newlines\"" ,
c"contains\nnewlinesescaped newlines" ,
);
test_c_string("cr\" raw\nstring\\\nhere\"" , c"raw\nstring\\\nhere" );
test_c_string("c\" ...\"q" , c"..." );
test_c_string("cr\" ...\"" , c"..." );
test_c_string("cr##\" ...\"##" , c"..." );
test_c_string(
r#" c"hello\x80我叫\u{1F980}" "#, // from the RFC
c"hello\x80我叫\u{1F980}" ,
);
}
#[ test]
fn bytes() {
#[ track_caller]
fn test_byte(s: &str, value: u8) {
let s = s.trim();
match lit(s) {
Lit::Byte(lit) => {
assert_eq!(lit.value(), value);
let again = lit.into_token_stream().to_string();
assert_eq!(again, s);
}
wrong => panic!("{:?}" , wrong),
}
}
test_byte(r#" b'a' "#, b'a');
test_byte(r#" b'\n' "#, b'\n');
test_byte(r#" b'\r' "#, b'\r');
test_byte(r#" b'\t' "#, b'\t');
test_byte(r#" b'\' ' "#, b' \'' );
test_byte(r#" b'"' "#, b'" ');
test_byte(r#" b'a' q "#, b'a');
}
#[ test]
fn chars() {
#[ track_caller]
fn test_char(s: &str, value: char) {
let s = s.trim();
match lit(s) {
Lit::Char(lit) => {
assert_eq!(lit.value(), value);
let again = lit.into_token_stream().to_string();
if again != s {
test_char(&again, value);
}
}
wrong => panic!("{:?}" , wrong),
}
}
test_char(r#" 'a' "#, 'a');
test_char(r#" '\n' "#, '\n');
test_char(r#" '\r' "#, '\r');
test_char(r#" '\t' "#, '\t');
test_char(r#" '' "#, ''); // NOTE: This is an emoji
test_char(r#" '\' ' "#, ' \'' );
test_char(r#" '"' "#, '" ');
test_char(r#" '\u{1F415}' "#, '\u{1F415}');
test_char(r#" 'a' q "#, 'a');
}
#[ test]
fn ints() {
#[ track_caller]
fn test_int(s: &str, value: u64, suffix: &str) {
match lit(s) {
Lit::Int(lit) => {
assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
assert_eq!(lit.suffix(), suffix);
let again = lit.into_token_stream().to_string();
if again != s {
test_int(&again, value, suffix);
}
}
wrong => panic!("{:?}" , wrong),
}
}
test_int("5" , 5 , "" );
test_int("5u32" , 5 , "u32" );
test_int("0E" , 0 , "E" );
test_int("0ECMA" , 0 , "ECMA" );
test_int("0o0A" , 0 , "A" );
test_int("5_0" , 50 , "" );
test_int("5_____0_____" , 50 , "" );
test_int("0x7f" , 127 , "" );
test_int("0x7F" , 127 , "" );
test_int("0b1001" , 9 , "" );
test_int("0o73" , 59 , "" );
test_int("0x7Fu8" , 127 , "u8" );
test_int("0b1001i8" , 9 , "i8" );
test_int("0o73u32" , 59 , "u32" );
test_int("0x__7___f_" , 127 , "" );
test_int("0x__7___F_" , 127 , "" );
test_int("0b_1_0__01" , 9 , "" );
test_int("0o_7__3" , 59 , "" );
test_int("0x_7F__u8" , 127 , "u8" );
test_int("0b__10__0_1i8" , 9 , "i8" );
test_int("0o__7__________________3u32" , 59 , "u32" );
test_int("0e1\u{5c5}" , 0 , "e1\u{5c5}" );
}
#[ test]
fn floats() {
#[ track_caller]
fn test_float(s: &str, value: f64, suffix: &str) {
match lit(s) {
Lit::Float(lit) => {
assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
assert_eq!(lit.suffix(), suffix);
let again = lit.into_token_stream().to_string();
if again != s {
test_float(&again, value, suffix);
}
}
wrong => panic!("{:?}" , wrong),
}
}
test_float("5.5" , 5 .5 , "" );
test_float("5.5E12" , 5 .5 e12, "" );
test_float("5.5e12" , 5 .5 e12, "" );
test_float("1.0__3e-12" , 1 .03 e-12 , "" );
test_float("1.03e+12" , 1 .03 e12, "" );
test_float("9e99e99" , 9 e99, "e99" );
test_float("1e_0" , 1 .0 , "" );
test_float("0.0ECMA" , 0 .0 , "ECMA" );
}
#[ test]
fn negative() {
let span = Span::call_site();
assert_eq!("-1" , LitInt::new("-1" , span).to_string());
assert_eq!("-1i8" , LitInt::new("-1i8" , span).to_string());
assert_eq!("-1i16" , LitInt::new("-1i16" , span).to_string());
assert_eq!("-1i32" , LitInt::new("-1i32" , span).to_string());
assert_eq!("-1i64" , LitInt::new("-1i64" , span).to_string());
assert_eq!("-1.5" , LitFloat::new("-1.5" , span).to_string());
assert_eq!("-1.5f32" , LitFloat::new("-1.5f32" , span).to_string());
assert_eq!("-1.5f64" , LitFloat::new("-1.5f64" , span).to_string());
}
#[ test]
fn suffix() {
#[ track_caller]
fn get_suffix(token: &str) -> String {
let lit = syn::parse_str::<Lit>(token).unwrap();
match lit {
Lit::Str(lit) => lit.suffix().to_owned(),
Lit::ByteStr(lit) => lit.suffix().to_owned(),
Lit::CStr(lit) => lit.suffix().to_owned(),
Lit::Byte(lit) => lit.suffix().to_owned(),
Lit::Char(lit) => lit.suffix().to_owned(),
Lit::Int(lit) => lit.suffix().to_owned(),
Lit::Float(lit) => lit.suffix().to_owned(),
_ => unimplemented!(),
}
}
assert_eq!(get_suffix("\" \"s" ), "s" );
assert_eq!(get_suffix("r\" \"r" ), "r" );
assert_eq!(get_suffix("r#\" \"#r" ), "r" );
assert_eq!(get_suffix("b\" \"b" ), "b" );
assert_eq!(get_suffix("br\" \"br" ), "br" );
assert_eq!(get_suffix("br#\" \"#br" ), "br" );
assert_eq!(get_suffix("c\" \"c" ), "c" );
assert_eq!(get_suffix("cr\" \"cr" ), "cr" );
assert_eq!(get_suffix("cr#\" \"#cr" ), "cr" );
assert_eq!(get_suffix("'c'c" ), "c" );
assert_eq!(get_suffix("b'b'b" ), "b" );
assert_eq!(get_suffix("1i32" ), "i32" );
assert_eq!(get_suffix("1_i32" ), "i32" );
assert_eq!(get_suffix("1.0f32" ), "f32" );
assert_eq!(get_suffix("1.0_f32" ), "f32" );
}
#[ test]
fn test_deep_group_empty() {
let tokens = TokenStream::from_iter([TokenTree::Group(Group::new(
Delimiter::None,
TokenStream::from_iter([TokenTree::Group(Group::new(
Delimiter::None,
TokenStream::from_iter([TokenTree::Literal(Literal::string("hi" ))]),
))]),
))]);
snapshot!(tokens as Lit, @r#" "hi" "# );
}
#[ test]
fn test_error() {
let err = syn::parse_str::<LitStr>("..." ).unwrap_err();
assert_eq!("expected string literal" , err.to_string());
let err = syn::parse_str::<LitStr>("5" ).unwrap_err();
assert_eq!("expected string literal" , err.to_string());
}
Messung V0.5 in Prozent C=74 H=98 G=86
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-18)
¤
*© Formatika GbR, Deutschland