Under certain circumstances involving pub(crate) use Trait
, the compiler issues an "unused import" warning even though the import is actually being used.
This is an example (playground link):
mod foo {
pub(crate) use std::ascii::AsciiExt;
}
mod bar {
use foo::*;
pub fn bar() -> bool {
"bar".is_ascii()
}
}
fn main() {
println!("{}", bar::bar());
}
Since the body of bar::bar()
uses the AsciiExt
trait (AsciiExt
provides .is_ascii()
), there shouldn't be a warning.
Instead, this happened:
warning: unused import: `std::ascii::AsciiExt`
--> src/main.rs:2:20
|
2 | pub(crate) use std::ascii::AsciiExt;
| ^^^^^^^^^^^^^^^^^^^^
|
= note: #[warn(unused_imports)] on by default
Applying any of the following changes makes the warning go away:
pub(crate) use std::ascii::AsciiExt
to pub use std::ascii::AsciiExt
use foo::*
to use foo::AsciiExt
"bar".is_ascii()
to AsciiExt::is_ascii("bar")
This occurs for all versions of the compiler on the playground:
Also, on my machine, rustc --version --verbose
:
rustc 1.21.0 (3b72af97e 2017-10-09)
binary: rustc
commit-hash: 3b72af97e42989b2fe104d8edbaee123cdf7c58f
commit-date: 2017-10-09
host: x86_64-unknown-linux-gnu
release: 1.21.0
LLVM version: 4.0
Just ran into this myself today. I'm surprised I haven't run into it earlier.
My own findings are aligned with the OP's:
// build with cargo test
mod tr {
pub trait Trait: Sized { fn frob(self); }
impl Trait for () { fn frob(self) {} }
}
#[cfg(test)]
mod c {
use tr::Trait;
mod c {
use super::*;
#[test] fn lol() { ().frob(); }
}
}
use tr::Trait
with a copy of contents of mod tr
suppresses the warning (so the warning only applies to imports of traits, not traits themselves)It has been around since the introduction of item-like imports. Or at least since 1.15, which is the first version in which the snippet compiles.
Compiling unused v0.1.0 (file:///home/lampam/cpp/throwaway/unused)
warning: unused import: `tr::Trait`, #[warn(unused_imports)] on by default
--> src/lib.rs:12:9
|
12 | use tr::Trait;
| ^^^^^^^^^
Finished debug [unoptimized + debuginfo] target(s) in 0.38 secs
Running target/debug/deps/unused-a116605afa74f946
Because I am interested in beginning to learn more about the compiler internals, I tried tracing through the relevant logic in the source code. Here's what I think happens:
rustc_resolve
, so it defers them to be checked by rustc_typeck
.used_trait_imports
".used_trait_imports
table.
DEBUG:rustc_typeck::check::method: used_trait_import: DefId { krate: CrateNum(0), index: DefIndex(0:11) => lib[317d]::c[0]::c[0]::{{?}}[0] }
{{?}}
refers to the glob import.As a result, the glob import is marked as used, but not the trait import.
Does it sound like this "amateur analysis" is on the right track? I wonder which component ought to be changed to fix this... (could the glob import be followed by method resolution to insert more entries into the table?)
This seems to be similar to #43970.
Status update: this bug is still present. I get hit by it increasingly often as I like to use miniature inline modules (with use super::*
) for the purposes of ad-hoc namespacing, privacy barriers, and organization.
I still don't have the chops to fix it myself (at least, not without a fair investment of time learning compiler internals).
^--- I took a trip around the tracker and rounded up these four juicy, delicious duplicate bug reports, for anybody with an appetite
I just ran into this kind of issue myself. It looks like if you import a trait and use it only in submodules you will face this false warning. Here is another playground that shows the bug (importing an existing trait from another crate) => Playground
Would be fantastic if this could get fixed.
@sanxiyn in #50537, you wrote that you know how to fix this. Maybe if you give some guidance someone could pick up this issue? (I don't feel qualified myself.)
Sorry I dropped the ball, didn't I? @ExpHP's analysis above is completely correct, and "follow glob" suggestion is also what I thought. Source: I wrote the most of relevant code.
I probably don't have the time to do this myself in near future...
Is this a thing that can be fixed just by modifying one crate? Or is it much more complicated (due to e.g. incrementalization, query system, rainbow unicorn dust...)
If it is something a beginner could do, do you have any hints on the proper way to do it? Any method names you can easily recall? I guess basically what I need is to be able to get the DefId
for a particular trait in the module referenced by the glob import (assuming that the module is crate-local), but the internal API surface of the compiler is huge :stuck_out_tongue: ...
So, I've made a fix for this -- the meat of it is in Resolver::get_traits_in_module_containing_item
: If a binding comes from an import, just follow the import's binding and collect all the import_id's, instead of stopping after the first (repeat until you reach the actual Def
).
I've verified that it actually fixes the problem, and I'm running the full test suite now.
Only thing is that then I'd have to change
pub struct TraitCandidate {
pub def_id: DefId,
pub import_id: Option<NodeId>,
}
into
pub struct TraitCandidate {
pub def_id: DefId,
pub import_ids: Vec<NodeId>,
}
(and similar change to Pick
). I'm just concerned this will lead to increased malloc usage. Is there a "SmallVec" or similar that could be used, because I guess it's going to be very rare that there should be more than one or levels of trait imports.
Please advice!
That's marvelous!
Once the test suite passes, I think you should go ahead and submit a PR. This will give it higher visibility to the people who are best equipped to answer your concern, and it will also enable them to queue up a rustc-perf comparison if deemed necessary.
I'm looking into the test failures.
When does a bug like this get merged to stable?
It takes 3 months unless explicit backport is done.
Every three months, the current master becomes the new beta, and the current beta becomes the new stable.
Therefore, any change takes 3-6 months to get into stable, depending on its timing. Since a new version of rust was just released, there are 3 months remaining for this change.
Eh no. master->beta and beta->stable happens every 1.5 months. So 3 months is correct.
It's been nearly a year since then but the bug still remains.
I'm afraid I cannot reproduce a minimal example because I am a completely new to Rust...
It's been nearly a year since then but the bug still remains.
I'm afraid I cannot reproduce a minimal example because I am a completely new to Rust...
The bug is closed, and all the examples given are fixes -- so why do you think the bug still exists?
A trick: If you get unused import warnings when building, but you code fails to compile if you remove them, it could be because they are used in conditionally imported code such as tests. If that is the case, you should move the imports into the conditional section. That was the problem I had as a beginner to begin with, which lead me to this bug...
You are completely right, that was why I was getting such an error! Thanks for telling me, and sorry for every participants of this issue to have sent such a silly notification...
Most helpful comment
So, I've made a fix for this -- the meat of it is in
Resolver::get_traits_in_module_containing_item
: If a binding comes from an import, just follow the import's binding and collect all the import_id's, instead of stopping after the first (repeat until you reach the actualDef
).I've verified that it actually fixes the problem, and I'm running the full test suite now.
Only thing is that then I'd have to change
into
(and similar change to
Pick
). I'm just concerned this will lead to increased malloc usage. Is there a "SmallVec" or similar that could be used, because I guess it's going to be very rare that there should be more than one or levels of trait imports.Please advice!