Skip to content

Unexpected warning with use statement #30159

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

Closed
Nashenas88 opened this issue Dec 2, 2015 · 5 comments
Closed

Unexpected warning with use statement #30159

Nashenas88 opened this issue Dec 2, 2015 · 5 comments

Comments

@Nashenas88
Copy link
Contributor

Playground link for all of the examples.

Using this code:

trait Trait {
    fn func(&self);
}

pub struct Struct;

impl Trait for Struct {
    fn func(&self) { }
}

#[cfg(test)]
mod test_level_0 {
    use super::Trait;
    pub use super::*;

    mod test_level_1 {
        pub use super::*;

        #[test]
        fn test() {
            Struct.func();
        }
    }
}

Gives me this warning:

<anon>:13:9: 13:21 warning: unused import, #[warn(unused_imports)] on by default
<anon>:13     use super::Trait;
                  ^~~~~~~~~~~~

But, removing that line gives me this error:

<anon>:20:20: 20:26 error: no method named `func` found for type `Struct` in the current scope
<anon>:20             Struct.func();
                             ^~~~~~
<anon>:20:20: 20:26 help: items from traits can only be used if the trait is in scope; the following trait is implemented but not in scope, perhaps add a `use` for it:
<anon>:20:20: 20:26 help: candidate #1: use `Trait`
error: aborting due to previous error
playpen: application terminated with error code 101

Switching the code to use these use statements:

trait Trait {
    fn func(&self);
}

pub struct Struct;

impl Trait for Struct {
    fn func(&self) { }
}

#[cfg(test)]
mod test_level_0 {
    use super::Trait;
    pub use super::{Trait, Struct};

    mod test_level_1 {
        pub use super::*;

        #[test]
        fn test() {
            Struct.func();
        }
    }
}

GIves me:

<anon>:14:21: 14:26 error: `Trait` is private, and cannot be reexported [E0365]
<anon>:14     pub use super::{Trait, Struct};
                              ^~~~~
<anon>:14:21: 14:26 help: see the detailed explanation for E0365
<anon>:14:21: 14:26 note: Consider declaring module `Trait` as a `pub mod`
<anon>:14     pub use super::{Trait, Struct};
                              ^~~~~
<anon>:14:21: 14:26 error: a trait named `Trait` has already been imported in this module [E0252]
<anon>:14     pub use super::{Trait, Struct};
                              ^~~~~
<anon>:14:21: 14:26 help: see the detailed explanation for E0252
<anon>:13:5: 13:22 note: previous import of `Trait` here
<anon>:13     use super::Trait;
              ^~~~~~~~~~~~~~~~~
error: aborting due to 2 previous errors
playpen: application terminated with error code 101

And my last attempt:

trait Trait {
    fn func(&self);
}

pub struct Struct;

impl Trait for Struct {
    fn func(&self) { }
}

#[cfg(test)]
mod test_level_0 {
    use super::Trait;
    pub use super::Struct;

    mod test_level_1 {
        pub use super::*;

        #[test]
        fn test() {
            Struct.func();
        }
    }
}

Gives me:

<anon>:21:20: 21:26 error: no method named `func` found for type `Struct` in the current scope
<anon>:21             Struct.func();
                             ^~~~~~
<anon>:21:20: 21:26 help: items from traits can only be used if the trait is in scope; the following trait is implemented but not in scope, perhaps add a `use` for it:
<anon>:21:20: 21:26 help: candidate #1: use `Trait`
error: aborting due to previous error
playpen: application terminated with error code 101

So it seems that useing a private item followed by pub use super::* will export the private item publicly. I could be misunderstanding something here, but where I'm running into this, test_level_1 is actually a plugin that does pub use super::*, so the warning version is the only one that will compile. In other words, I can't just write the mods as:

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

    mod test_level_1 {
        use Trait;
        pub use super::*;

        #[test]
        fn test() {
            Struct.func();
        }
    }
}
@bluss
Copy link
Member

bluss commented Dec 2, 2015

cc @petrochenkov, it's related to privacy

@bstrie
Copy link
Contributor

bstrie commented Dec 2, 2015

@sanxiyn You mentioned on IRC that you would like to see this nominated, can you leave a comment describing why?

@sanxiyn
Copy link
Member

sanxiyn commented Dec 2, 2015

I find it worrying that glob import is not equivalent to explicitly named import.

@petrochenkov
Copy link
Contributor

This is some fun bug in glob imports (in name resolution, not in privacy, strictly speaking).
Glob imports normally import only pub items, but it seems like a pub glob import from a module suddenly marks all other imports from this module as public (effectively).
This compiles, but it shouldn't, because the name S shouldn't be in the module m at all. (Or at least it should be private, but it's not how non-pub imports work currently.)

struct S;

mod m {
    use super::S;
    pub use super::*;
}

fn main() {
    let s = m::S;
}

Slight variations like

    // use super::S;
    pub use super::*;

or

    use super::S;
    // pub use super::*;

or even

    pub use super::*;
    use super::S;

fail with unresolved namem::S`, i.e. the order of imports is significant! (cc @vberger)

The incorrect unused import warning may be a consequence of this problem, or a totally separate bug, I don't know.

@jseyfried
Copy link
Contributor

@sanxiyn @petrochenkov the incorrect unused import is a consequence of that problem.
The problem is that the visibility of a glob import overwrites the visibilities of any existing imports that share a name with a private item in the module being glob imported from.
For example,

pub fn f() {}
pub mod foo {
    fn f() {}
}

mod bar {
    pub use f;
    pub use f as g;
    use foo::*; // This makes first import private but does not affect the second import.
}

mod baz {
    use f;
    pub use foo::*; // This makes the above import public
}

In the original example, the visibility of the first import use super::Trait; was made public by pub use super::*; since the first import shares a name (Trait) with a private item in the module being glob imported from (super). Were it not for this bug, Trait would be private, the second glob import would not import it, and the code would not compile.

This problem is due to a bug in merge_import_resolution, specifically this line, which overwrites the visibility of an imported name regardless of whether it gets duplicate checked and defined in either namespace.

I fixed this problem in PR #30294 while fixing a very closely related issue.

@sanxiyn sanxiyn closed this as completed Dec 11, 2015
jseyfried added a commit to jseyfried/rust that referenced this issue Dec 11, 2015
bors added a commit that referenced this issue Dec 13, 2015
r? @nrc
Since PR #30294 unintentionally fixed issue #30159, it can cause breakage for a different reason than I originally stated in the PR (see #30159, I characterized the issue precisely there).

This commit limits the scope of the breakage to what I originally stated in the PR by "unfixing" the backwards incompatible part of #30159.

I think fixing #30159 has enough potential for breakage to warrant a crater run. If you disagree, I can cancel this PR, leaving it fixed.
jseyfried added a commit to jseyfried/rust that referenced this issue Jan 11, 2016
jseyfried added a commit to jseyfried/rust that referenced this issue Jan 31, 2016
bors added a commit that referenced this issue Feb 1, 2016
This reverts PR #30324, fixing bug #30159 in which a public a glob import makes public any preceding imports that share a name with an item in the module being glob imported from.

For example,
```rust
pub fn f() {}
pub mod foo {
    fn f() {}
}

mod bar {
    use f;
    use f as g;
    pub use foo::*; // This makes the first import public but does not affect the second import.
}
```

This is a [breaking-change].
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

6 participants