Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

False positive error when using bilge #17873

Open
showier-drastic opened this issue Aug 13, 2024 · 2 comments
Open

False positive error when using bilge #17873

showier-drastic opened this issue Aug 13, 2024 · 2 comments
Labels
A-proc-macro proc macro C-bug Category: bug

Comments

@showier-drastic
Copy link

rust-analyzer version: rust-analyzer version: 0.3.2070-standalone (0daeb5c0b 2024-08-10) [/Users/user/.vscode/extensions/rust-lang.rust-analyzer-0.3.2070-darwin-arm64/server/rust-analyzer]

rustc version: rustc 1.80.1 (3f5fd8dd4 2024-08-06)

editor or extension: VSCode v0.3.2070

relevant settings: No special settings

repository link (if public, optional): https://github.com/showier-drastic/bilge-test

I'm using bilge. This library provides the #[bitsize(3)] macro. The code in the repo above compiles successfully without errors, but rust analyzer complains about it, every time #[bitsize] is used:

add #[bitsize] attribute above your derive attribute rust-analyzer[macro-error](https://rust-analyzer.github.io/manual.html#macro-error)

Upstream issue: hecatia-elegua/bilge#92

code snippet to reproduce:

#[bitsize(14)]
#[derive(FromBits)]
struct Register {
    header: u4,
    body: u7,
    footer: Footer,
}
@showier-drastic showier-drastic added the C-bug Category: bug label Aug 13, 2024
@ShoyuVanilla ShoyuVanilla added the A-proc-macro proc macro label Aug 13, 2024
@ChayimFriedman2
Copy link
Contributor

First of all, this macro does something exceedingly cursed, which IMO we should officially not support, because those who use this pattern deserve a punishment.

Jokes aside, the macro reuse the same attribute for both proc macro attribute and derive helper attribute. Somehow, rustc seams to bless this pattern. I bet this wasn't done knowingly, and I'm not sure whether the language team would be happy to commit to this, but rustc allows that so we are obliged :(

@alexkl-rugged-controls
Copy link

I'm not entirely sure of this, but it seems what is really happening is that bilge just happens to insert a macro before some derive macros, and rust-analyzer has a bug where it repeats derive macros once for every non-derive macro evaluation:

#[proc_macro_attribute]
pub fn test_attr(_: TokenStream, b: TokenStream) -> TokenStream {
    println!("Test Attr");
    b 
}

#[proc_macro_derive(TestDerive)]
pub fn test_derive(_: TokenStream) -> TokenStream {
    println!("Test Derive");
    TokenStream::new()
}

#[proc_macro_derive(TestDerive2)]
pub fn test_derive_2(_: TokenStream) -> TokenStream {
    println!("Test Derive 2");
    TokenStream::new()
}

#[derive(TestDerive, TestDerive2)]
#[test_attr]
#[test_attr]
struct _Foo {
    f: u8,
}
$ cargo test
   Compiling test_proc_macros v0.1.0 (/home/alexkl/src/test_proc_macros)
Test Derive
Test Derive 2
Test Attr
Test Attr
$ rust-analyzer diagnostics .
2024-11-22T23:04:15.004046Z ERROR proc-macro tried to print : Test Derive
2024-11-22T23:04:15.004331Z ERROR proc-macro tried to print : Test Derive 2
2024-11-22T23:04:15.004582Z ERROR proc-macro tried to print : Test Attr
2024-11-22T23:04:15.004942Z ERROR proc-macro tried to print : Test Derive
2024-11-22T23:04:15.005137Z ERROR proc-macro tried to print : Test Derive 2
2024-11-22T23:04:15.005355Z ERROR proc-macro tried to print : Test Attr
2024-11-22T23:04:15.005586Z ERROR proc-macro tried to print : Test Derive
2024-11-22T23:04:15.005753Z ERROR proc-macro tried to print : Test Derive 2

This seems pretty terrible for performance as it evaluates the macros num_attributes * num_derive_attributes times.

After each evaluation of an attribute, the attribute is stripped, and the set of derives is evaluated once more. This is why the bilge crate has the "false positive error", because what it wants to do is this:

// Original Code
#[bitsize(2)]
#[derive(DebugBits, Copy)]
struct Example {}

// Step 1
#[derive(DebugBits)] // While this is evaluated, it has access to `bitsize_internal(2)`
#[bitsize_internal(2)]
#[derive(Copy)]
struct Example {}

// Step 2
#[bitsize_internal(2)]
#[derive(Copy)]
struct Example {}
impl Debug for Example {/* Makes use of bitsize 2 */}

// Step 3
#[derive(Copy)] // Doesn't need bitsize 2, and should be implemented on the final struct.
struct Example { /* Rewritten fields */ }
impl Debug for Example {/* Makes use of bitsize 2 */}

// Done
struct Example { /* Rewritten fields */ }
impl Debug for Example {/* Makes use of bitsize 2 */}
impl Copy  for Example { }

But, with rust-analyzer what really happens is that when #[bitsize_internal] is being evaluated in Step 2->3, #[derive(DebugBits)] happens again, this time without access to #[bitsize(2)].

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-proc-macro proc macro C-bug Category: bug
Projects
None yet
Development

No branches or pull requests

4 participants