Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mark Ident::new_raw as no longer semver-exempt #331

Merged
merged 2 commits into from Jun 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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