How to write unit tests for #![no_std] crate and avoid errors in Rust Analyzer

I'm writing a no_std crate and trying to find a nice way to add unit tests to my code without RA showing a bunch of errors because the tests rely in std. I searched and found several issues about this on GitHub and with the suggestions I put together this minimal example:

#![cfg_attr(not(test), no_std)]
#![cfg_attr(not(test), no_main)]

fn get_value() -> u32 {
    42
}

#[cfg_attr(not(test), panic_handler)]
fn panic(_info: &core::panic::PanicInfo) -> ! {
    loop {}
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn correct_value() {
        assert_eq!(get_value(), 42);
    }
}

In .cargo/config.toml I have:

[build]
target = "thumbv7em-none-eabihf"

I can compile the code with cargo build and run tests with cargo test --target x86_64-unknown-linux-gnu. The problem is I can't figure out how to make RA aware of this setup so it doesn't show errors everywhere.

(apologies for the screenshot)

I found some suggestions online to add a .vscode/settings.json file with these settings:

{
    "rust-analyzer.check.allTargets": false,
    "rust-analyzer.check.extraArgs": [
        "--target",
        "thumbv7em-none-eabihf",
    ],
}

Unfortunately this doesn't solve the errors. Is there a way to make RA check the test code with a different target than the one specified in .cargo/config.toml?

Don't you want the RA override to specify x86_64-unknown-linux-gnu rather than the embedded target? I'm not sure what benefit passing the same target there as the one you have set in the cargo config would have.

Usually I feel like you get better results if instead of using cfg_attr with no_std, you are unconditionally no_std and add a #[cfg(test)] extern crate std.

What's the purpose of the no_main attribute? Normally we talk about unit testing a library crate, and no_main would only be applicable to a binary crate.

1 Like

That's a good point. I changed my .vscode/settings.json to

{
    "rust-analyzer.check.allTargets": false,
    "rust-analyzer.check.targets": "x86_64-unknown-linux-gnu"
}

but this doesn't change the errors.

I added #[cfg(test)] extern crate std; to my code but RA complains

can't find crate for `std`
the `thumbv7em-none-eabihf` target may not support the standard library

I probably should have mentioned that this is a binary crate. IMO unit tests are equally useful in binary and library crates. I could extract the logic I want to test to a library crate without #![no_main] but that feel a bit cumbersome to just get some unit tests working.

I had the Cargo VS Code extension by panickbit installed which was calling cargo check without respecting the rust-analyzer settings.