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

test macro hygiene #24

Closed
Stargateur opened this issue Jul 19, 2022 · 4 comments
Closed

test macro hygiene #24

Stargateur opened this issue Jul 19, 2022 · 4 comments
Assignees

Comments

@Stargateur
Copy link

I have a trait that have a map method, it's look the macro use imported item from user code.

error[E0034]: multiple applicable items in scope
   --> network\src\tcp.rs:469:3
    |
469 |   #[test_log::test]
    |   ^^^^^^^^^^^^^^^^^ multiple `map` found
    |
    = note: candidate #1 is defined in an impl of the trait `binator_utils::Utils` for the type `T`
    = note: candidate #2 is defined in an impl of the trait `Iterator` for the type `std::str::Split<'a, P>`
    = note: this error originates in the attribute macro `test_log::test` (in Nightly builds, run with -Z macro-backtrace for more info)
help: disambiguate the associated function for candidate #1
    |
469 |   binator_utils::Utils::map(#[test_log::test], #[test_log::test])
    |
help: disambiguate the associated function for candidate #2
    |
469 |   Iterator::map(#[test_log::test], #[test_log::test])
    |

@d-e-s-o
Copy link
Owner

d-e-s-o commented Jul 19, 2022

Can you provide a minimal example illustrating the issue?

@Stargateur
Copy link
Author

Stargateur commented Jul 20, 2022

Yes, sorry.

test-log = { version = "0.2", features = ["trace"] }
env_logger = "0.9"
tracing = {version = "0.1", default-features = false}
tracing-subscriber = {version = "0.3", default-features = false, features = ["env-filter", "fmt"]}
trait Foo: Sized {
    fn map(self) -> Self {
        self
    }
}

impl<T> Foo for T {}

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

    #[test_log::test]
    fn foo() {
        42.map();
    }
}
error[E0034]: multiple applicable items in scope
  --> src\bin\lol.rs:15:5
   |
15 |     #[test_log::test]
   |     ^^^^^^^^^^^^^^^^^ multiple `map` found
   |
note: candidate #1 is defined in an impl of the trait `Foo` for the type `T`
  --> src\bin\lol.rs:4:5
   |
4  |     fn map(self) -> Self {
   |     ^^^^^^^^^^^^^^^^^^^^
   = note: candidate #2 is defined in an impl of the trait `Iterator` for the type `std::str::Split<'a, P>`
   = note: this error originates in the attribute macro `test_log::test` (in Nightly builds, run with -Z macro-backtrace for more info)
help: disambiguate the associated function for candidate #1
   |
15 |     Foo::map(#[test_log::test], #[test_log::test])
   |
help: disambiguate the associated function for candidate #2
   |
15 |     Iterator::map(#[test_log::test], #[test_log::test])
   |

cause

.map(|filter| match filter.trim() {

I believe #15 (comment) would help on this matter. But unsure, I don't know much about proc macro. rust-lang/rust#54727 is about proc macro hygiene.

@d-e-s-o
Copy link
Owner

d-e-s-o commented Jul 21, 2022

Interestingly, it's not even that the proc macro is polluting your namespace, it's that you are polluting the proc macro's namespace from what I understand. It's reasonably easy to fix in this case, even in a generic manner.

Happy to be proven wrong, but I am not convinced that #15 (comment) has any bearing on the issue. All it suggests is putting the proc macro logic into a different crate and then importing the macros through this crate. That shouldn't have any impact on method resolution or really much of the compilation process.

d-e-s-o added a commit that referenced this issue Jul 21, 2022
The initialization code that we generate as part of the procedural macro
effectively lives in the same namespace (for values, but also types) as
client code. That can actually cause issues due to missing proc macro
hygiene.
Specifically, we have seen cases where a trait, let's call it Foo, is
implemented for any T, and it contains a very generic method such as
map. In such a scenario, the emitted initialization code could
result in an ambiguous method call, should it use, say, Iterator::map
somewhere. That is because we do not use fully qualified calling syntax
everywhere in generated code.
The ultimate fix should come from the Rust side in the form of proper
proc macro hygiene, but for now we prevent problems like this by putting
all initialization code into a generated module. The module boundary
ensures that only explicitly imported functions are available -- and we
do not import anything.

Closes #24
d-e-s-o added a commit that referenced this issue Jul 21, 2022
The initialization code that we generate as part of the procedural macro
effectively lives in the same namespace (for values, but also types) as
client code. That can actually cause issues due to missing proc macro
hygiene.
Specifically, we have seen cases where a trait, let's call it Foo, is
implemented for any T, and it contains a very generic method such as
map. In such a scenario, the emitted initialization code could
result in an ambiguous method call, should it use, say, Iterator::map
somewhere. That is because we do not use fully qualified calling syntax
everywhere in generated code.
The ultimate fix should come from the Rust side in the form of proper
proc macro hygiene, but for now we prevent problems like this by putting
all initialization code into a generated module. The module boundary
ensures that only explicitly imported functions are available -- and we
do not import anything.

Closes #24
@d-e-s-o
Copy link
Owner

d-e-s-o commented Jul 21, 2022

Should be fixed now. Let me know if you encounter any problems with the fix. Should be able to cut a release in the next few days or so. Thanks for reporting this issue!

@d-e-s-o d-e-s-o self-assigned this Jul 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants