Skip to content

Conversation

@GuillaumeGomez
Copy link

@GuillaumeGomez GuillaumeGomez commented Jun 24, 2025

Since rust-lang/rust#139367 was merged a while ago, I think it's ok to start making use of it as a nightly experiment if you're ok with it.

If this PR is merged, I plan to send a follow-up on syn for Literal::value to make use of these methods instead (when nightly feature is enabled) so the unescaping source code could be cfg-ed out.

Hopefully, when the API gets stabilized, we could get some nice numbers on compilation improvements for a lot of crates (although minor, considering how much your crates are used, definitely worth it).

So what do you think?

Copy link
Owner

@dtolnay dtolnay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. Since the API of these methods is likely to change, in order to move this forward they need to be behind the procmacro2_semver_exempt cfg like the unstable Span methods such as def_site — a Cargo feature is not appropriate for this. Separately, each method needs a fallback implementation with identical behavior that works in non-macro code.

@GuillaumeGomez
Copy link
Author

Makes sense! A bit busy currently but I'll try to come back to this as soon as possible.

@GuillaumeGomez GuillaumeGomez force-pushed the new-literal-api branch 2 times, most recently from 3381417 to c40fc5e Compare October 2, 2025 15:43
@GuillaumeGomez
Copy link
Author

Removed the feature and switched to the cfg, however I don't know how to make the rustc-literal-escaper dependency optional with a cfg. Or maybe you have another idea or how to unescape (like copy-pasting code from syn)?

@GuillaumeGomez
Copy link
Author

Do I need to create a new cfg for the 1.89 version requirement?

unicode-ident = "1.0"

[target.'cfg(procmacro2_semver_exempt)'.dependencies]
rustc-literal-escaper = "0.0.5"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dependency's compiler version support is not compatible with making it a dependency of proc-macro2. Also it has unreasonably many publishers for something that would become a widely used dependency. Neither of these factors is necessarily immediately disqualifying while the dependency is gated by our semver exempt cfg, but they mean that this implementation is not going to work once it comes time to stabilize the new methods. I would prefer going directly to a different implementation that does not involve adding this dependency.

}

/// Returns the unescaped string value if the current literal is a string or a string literal.
#[cfg(all(procmacro2_semver_exempt, feature = "proc-macro"))]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cannot be conditional on feature = "proc-macro". We need to provide methods that work identically whether or not proc-macro is being used.

Literal::_new(unsafe { imp::Literal::from_str_unchecked(repr) })
}

/// Returns the unescaped string value if the current literal is a string or a string literal.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear to me what distinction this is making between a literal that is a string vs a string literal.

match self.inner {
imp::Literal::Compiler(ref compiler_lit) => compiler_lit.byte_str_value(),
imp::Literal::Fallback(ref fallback) => {
if !fallback.repr.starts_with('c') {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong prefix for a byte string.

Please add a test that would catch this.

Comment on lines +1302 to +1303
error = Some(ConversionErrorKind::FailedToUnescape(unsafe {
std::mem::transmute(err)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This unsafe transmute is unsound. In general the version of rustc-literal-escaper in the proc-macro2 user's dependency graph is going to be different than the version of rustc-literal-escaper compiled into the standard library.

}
let mut error = None;
let mut buf = String::with_capacity(fallback.repr.len());
rustc_literal_escaper::unescape_str(&fallback.repr, |_, res| match res {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the documentation of this function it "takes the contents of a string literal (without quotes)".

Please add a test that would catch this.

Comment on lines +1290 to +1291
if !fallback.repr.starts_with('"') {
return Err(ConversionErrorKind::InvalidLiteralKind);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the intended behavior on raw string literals?

#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(
all(procmacro2_semver_exempt, feature = "proc-macro"),
feature(proc_macro_value)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not worth introducing the use of an unstable feature for this API. That makes sense for things like Span::def_site and Literal::subspan where a stable implementation is impossible. But for the methods in this PR they should just follow the stable "fallback" implementation in both cases, without being coupled to the API in nightly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants