Skip to content

Commit

Permalink
Merge pull request #331 from mystor/ident_new_raw
Browse files Browse the repository at this point in the history
Mark Ident::new_raw as no longer semver-exempt
  • Loading branch information
dtolnay committed Jun 20, 2022
2 parents 11dea16 + 0e97a92 commit 32a0b27
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 14 deletions.
4 changes: 4 additions & 0 deletions build.rs
Expand Up @@ -84,6 +84,10 @@ fn main() {
println!("cargo:rustc-cfg=no_hygiene");
}

if version.minor < 47 {
println!("cargo:rustc-cfg=no_ident_new_raw");
}

if version.minor < 54 {
println!("cargo:rustc-cfg=no_literal_from_str");
}
Expand Down
11 changes: 11 additions & 0 deletions src/fallback.rs
Expand Up @@ -640,13 +640,24 @@ impl Ident {
fn _new(string: &str, raw: bool, span: Span) -> Self {
validate_ident(string);

if raw && !Self::can_be_raw(string) {
panic!("`{}` cannot be a raw identifier", string);
}

Ident {
sym: string.to_owned(),
span,
raw,
}
}

fn can_be_raw(string: &str) -> bool {
match string {
"" | "_" | "super" | "self" | "Self" | "crate" | "$crate" | "{{root}}" => false,
_ => true,
}
}

pub fn new(string: &str, span: Span) -> Self {
Ident::_new(string, false, span)
}
Expand Down
4 changes: 0 additions & 4 deletions src/lib.rs
Expand Up @@ -953,10 +953,6 @@ impl Ident {
}

/// Same as `Ident::new`, but creates a raw identifier (`r#ident`).
///
/// This method is semver exempt and not exposed by default.
#[cfg(procmacro2_semver_exempt)]
#[cfg_attr(doc_cfg, doc(cfg(procmacro2_semver_exempt)))]
pub fn new_raw(string: &str, span: Span) -> Self {
Ident::_new_raw(string, span)
}
Expand Down
23 changes: 15 additions & 8 deletions src/wrapper.rs
Expand Up @@ -694,16 +694,23 @@ impl Ident {

pub fn new_raw(string: &str, span: Span) -> Self {
match span {
#[cfg(not(no_ident_new_raw))]
Span::Compiler(s) => Ident::Compiler(proc_macro::Ident::new_raw(string, s)),
#[cfg(no_ident_new_raw)]
Span::Compiler(s) => {
let p: proc_macro::TokenStream = string.parse().unwrap();
let ident = match p.into_iter().next() {
Some(proc_macro::TokenTree::Ident(mut i)) => {
i.set_span(s);
i
let _ = proc_macro::Ident::new(string, s);
// 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`.
if let Ok(ts) = format!("r#{}", string).parse::<proc_macro::TokenStream>() {
let mut iter = ts.into_iter();
if let (Some(proc_macro::TokenTree::Ident(mut id)), None) = (iter.next(), iter.next()) {
id.set_span(s);
return Ident::Compiler(id);
}
_ => panic!(),
};
Ident::Compiler(ident)
}
panic!("not allowed as a raw identifier: {}", string)
}
Span::Fallback(s) => Ident::Fallback(fallback::Ident::new_raw(string, s)),
}
Expand Down
12 changes: 10 additions & 2 deletions tests/test.rs
Expand Up @@ -15,14 +15,22 @@ fn idents() {
}

#[test]
#[cfg(procmacro2_semver_exempt)]
fn raw_idents() {
assert_eq!(
Ident::new_raw("String", Span::call_site()).to_string(),
"r#String"
);
assert_eq!(Ident::new_raw("fn", Span::call_site()).to_string(), "r#fn");
assert_eq!(Ident::new_raw("_", Span::call_site()).to_string(), "r#_");
}

#[test]
#[should_panic(expected = "`_` cannot be a raw identifier")]
fn ident_raw_blocked() {
Ident::new_raw("_", Span::call_site());
Ident::new_raw("super", Span::call_site());
Ident::new_raw("self", Span::call_site());
Ident::new_raw("Self", Span::call_site());
Ident::new_raw("crate", Span::call_site());
}

#[test]
Expand Down

0 comments on commit 32a0b27

Please sign in to comment.