dtolnay/cargo-expand

`SourceFile` in `proc-macro2` w/ `cfg(procmacro2_semver_exempt)` behaves differently between `cargo expand` and `cargo build`.

nathan-at-least opened this issue · 1 comments

I've created a small repo to distill a confusion I've run across while developing a proc-macro crate.

In the that repo, the macro iterates over attr and item and asserts that the SourceFile::is_real is true. This passes successfully with cargo expand but fails with cargo build or cargo test. If I remove a #[cfg(test)] prefix in the macro expansion, both commands succeed.

From a repo clone:

$ cd usercode         
$ RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo expand --tests --lib                                                                       [22/1897]
   Compiling mymacro v0.1.0 (/home/user/hack/proc-macro2-sourefile-confusion/mymacro)
   Compiling usercode v0.1.0 (/home/user/hack/proc-macro2-sourefile-confusion/usercode)
    Finished test [unoptimized + debuginfo] target(s) in 1.24s                                                                                         
                                                                                                                                                       
#![feature(prelude_import)]     
#[prelude_import]           
use std::prelude::rust_2021::*;
#[macro_use]     
extern crate std;
use mymacro::my_macro;
extern crate test;             
#[cfg(test)]                                                               
#[rustc_test_marker]         
pub const foo: test::TestDescAndFn = test::TestDescAndFn {
    desc: test::TestDesc {
        name: test::StaticTestName("foo"),               
        ignore: false,                                                     
        ignore_message: ::core::option::Option::None,    
        compile_fail: false,
        no_run: false,
        should_panic: test::ShouldPanic::No,                              
        test_type: test::TestType::UnitTest,
    },
    testfn: test::StaticTestFn(|| test::assert_test_result(foo())),
};
#[cfg(test)]
fn foo() {
    if !false {
        ::core::panicking::panic("assertion failed: false")
    };
}
#[rustc_main]
pub fn main() -> () {
    extern crate test;
    test::test_main_static(&[&foo])
}

From my untrained eye, this looks like the correct expansion of the macro, which implies the is_real assertions pass. However, build fails on those assertions:

$ RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build --tests --lib
   Compiling mymacro v0.1.0 (/home/user/hack/proc-macro2-sourefile-confusion/mymacro)
   Compiling usercode v0.1.0 (/home/user/hack/proc-macro2-sourefile-confusion/usercode)
error: custom attribute panicked
 --> usercode/src/lib.rs:3:1
  |
3 | #[my_macro()]
  | ^^^^^^^^^^^^^
  |
  = help: message: SourceFile {
              path: "<parsed string 2>",
              is_real: false,
          }

error: could not compile `usercode` due to previous error
warning: build failed, waiting for other jobs to finish...
error: could not compile `usercode` due to previous error

Apparently the issue is related to cfg(test). My question is why does cargo expand appear to succeed where build or test do not?

I can only guess, but probably you are not using a nightly rustc for the build, which is not gonna have an is_real implementation. Building with nightly would have the same behavior as the nightly that cargo expand runs.