How to write string literal containing double quote sign in proc macro?

Normally, in Rust, I do "something \" wrong". But if I used that in a proc macro, it was expanded to "something \\\" wrong".

How should I make it work?

--- Update:

I am using proc macro with lib d-plaindoux/celma. Sample code:

parsec_rules!(
  let bash_plain:{StringPart} = s=^("${" | "\"")* -> {mk_plain_part(s)}
)

It did not match anything. So I used https://github.com/dtolnay/cargo-expand to see the actual code (to be compiled), in the result of cargo expand, the escaped double quote was in form of | "\\\""). (other part of the code was omit).

Have you tried to produce a string literal with content something " wrong, literally? I guess the cooked string literals(standard notation to refer non-raw string literal) are internally stored as escaped form.

If I understand you correctly, I do not think so. I used cargo expand to get the source that would be compiled by rustc, in that source, it showed \\\". And interestingly, since the macro processor takes \ and " separately, I tried "something " wrong", it did not work at all.

But on the other hand, '\"' is handled as expected.

Can you show us the code you used for generating/injecting the string literal in question?

1 Like

I updated the original post.

So the issue does not come from proc-macros in general, but from a particular proc-macro-based library. Looking at their code, the following can be found:

So, as you can see, if their macro encounters an escape char (which they have special-cased), they ... simply treat it as a non-escape char (in other words, given what it currently does, they could have skipped that branch :sweat_smile:).

I think you should submit an issue (or a PR fixing it, if you can do that) to their repo mentioning this bug. For instance, it could be that a simple:

  } else if c.unwrap() == '\\' {
      let (c, nsp) = nsp.next();
-     rs.push('\\');
      rs.push(c.unwrap());

would do the trick (for the \" case only, though).

Ideally, I even think they should rather be doing:

} else if c.unwrap() == '\\' {
    let (c, nsp) = nsp.next();
    rs.push(match c {
        Some('\\') => '\\',
        Some('"') => '"',
        Some('n') => '\n',
        Some('t') => '\t',
        // etc. (see https://doc.rust-lang.org/reference/tokens.html#ascii-escapes)
        _ => return Reject(s, false),
    });
  • So, given this suggested change, a good thing for you to try, is to:

    1. go and fork their repo,

    2. apply my suggested change,

    3. test your use case by using your patched fork (tweak your Cargo.toml to point to your fork rather than the original repo (I'd have suggested using the [patch] section of the Cargo.toml to "shadow" a crates.io release with a git fork, but the crate in question does not seem to have been released)),

    4. Should it be successful, you can immediately use that fork to submit a PR to the repo;
      Should it be not, "fallback" to submitting an issue, in which you should mention this thread and what you tested with your fork.

3 Likes

@Yandros Thank you so much.