matklad / xshell

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`cmd!` panics when string contains backslash followed by a line break

Aloso opened this issue · comments

This panics during macro expansion:

cmd!(
    sh,
    "grcov . --binary-path ./target/debug/deps -s . -t {fmt} --branch --ignore-not-existing \
    --ignore ../* --ignore /* --ignore xtask/* --ignore */src/tests/* -o {file}"
)
commented

Thought I'd add some extra information. I recompiled a sample on nightly -Z macro-backtrace enabled.

use xshell::{cmd, Shell};

fn main() -> anyhow::Result<()> {
    let sh = Shell::new()?;

    cmd!(
        sh,
        "scoop \
        --help"
    );

    Ok(())
}
error[E0765]: unterminated double quote string
   --> source\github\xshell\src\lib.rs:348:32
    |
343 |   macro_rules! cmd {
    |   ---------------- in this expansion of `cmd!` (#1)
...
348 |           let cmd: $crate::Cmd = $crate::__cmd!(f $cmd);
    |                                  ^^^^^^^^^^^^^^^^^^^^^^ in this macro invocation (#2)
    |
   ::: source\github\xshell\xshell-macros\src\lib.rs:12:1
    |
12  |   pub fn __cmd(macro_arg: TokenStream) -> TokenStream {
    |   --------------------------------------------------- in this expansion of `$crate::__cmd!` (#2)
    |
   ::: examples\65.rs:6:5
    |
6   | /     cmd!(
7   | |         sh,
8   | |         "\
9   | |         --help"
10  | |     );
    | |_____- in this macro invocation (#1)

error: proc macro panicked
   --> source\github\xshell\src\lib.rs:348:32
    |
343 |   macro_rules! cmd {
    |   ---------------- in this expansion of `cmd!`
...
348 |           let cmd: $crate::Cmd = $crate::__cmd!(f $cmd);
    |                                  ^^^^^^^^^^^^^^^^^^^^^^
    |
   ::: examples\65.rs:6:5
    |
6   | /     cmd!(
7   | |         sh,
8   | |         "\
9   | |         --help"
10  | |     );
    | |_____- in this macro invocation

For more information about this error, try `rustc --explain E0765`.
error: could not compile `xshell` (example "65") due to 2 previous errors

Yeah, this is a bug, will get to fixing this one day if no-one beats me to it.

The semantics we want here is the following:

  • macro argument is a string literal, thing like "hello '\u{1F600} !'", with " and all
  • string value of a string literal is computed, yielding "hello '😄 !'"
  • the value is lexed into array of arguments ["hello", "😄 !"]

That's the same semantics of escapes that println! uses:

fn main() {
    println!("{\x7d", 93)
}

The problem with implementing this is that the first step, parsing string literal into string value, is part of Rust semantics, and ideally should be implemented by the compiler. So the proper solution here is to go & RFC https://internals.rust-lang.org/t/getting-value-out-of-proc-macro-literal/14140.

While we are waiting for that to happen, we should approximate what rustc is doing. I don't think we need to aim to be 100% correct here, but we obviously should fix panics

And see also #4 (comment), we need to figure out the correct semantics for quotes as well...