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

Lint when shadowing a glob-reexported item with a local declaration #111336

Closed
obi1kenobi opened this issue May 8, 2023 · 7 comments · Fixed by #111378
Closed

Lint when shadowing a glob-reexported item with a local declaration #111336

obi1kenobi opened this issue May 8, 2023 · 7 comments · Fixed by #111378
Assignees
Labels
A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. A-resolve Area: Path resolution T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@obi1kenobi
Copy link
Member

obi1kenobi commented May 8, 2023

Code

mod inner {
    pub struct Foo {};
}

mod other {
    struct Foo;
}

pub use inner::*;

// Adding either of the following two lines
// would shadow `inner::Foo` and
// hide the name `Foo` from the public API.
//
// (1)
// struct Foo;
//
// (2)
// use other::Foo;

Current output

compiles without errors, warnings, or clippy lints

Desired output

struct Foo;
^^^^^^^^^^
shadows `Foo` re-exported from `pub use inner::*`
note: the shadowing prevents `Foo` from being exported by this module, which may break downstream uses

***

use other::Foo;
           ^^^
           shadows `Foo` re-exported from `pub use inner::*`
note: the shadowing prevents `Foo` from being exported by this module, which may break downstream uses

Rationale and extra context

Similar to #107563

Whereas that issue tackled the case of glob-glob name conflicts, this issue is about glob-vs-local name conflicts in which case the local name shadows the one imported from the glob. This is a footgun and can lead to semver-major breaking changes: uncommenting either line (1) or (2) in the code example above is a major breaking change for the containing crate, since it causes the name Foo to stop being exported and makes Foo become an unnamable (Voldemort) type.

This is not a hypothetical issue. Around a year ago, the opencl3 crate seems to have suffered a regression when a large number of its items were accidentally shadowed. Neither code review nor tests seem to have been effective at preventing the regression. The fixing PR for that regression adds a test for the public re-export of one of the affected items.

This seems like a perfect situation for a lint. Shadowing a glob-reexported item with a local declaration is pretty much never what you want. If you wanted an unnamable type, there's a better way, and if you didn't want the item to be reexported then tweak the module structure so it doesn't get reexported.

I plan to publish a blog post with more details about this in a few hours, it will be available at the following link: https://predr.ag/blog/breaking-semver-in-rust-by-adding-private-type-or-import/

Other cases

No response

Anything else?

No response

@obi1kenobi obi1kenobi added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 8, 2023
@jyn514 jyn514 added A-resolve Area: Path resolution A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. and removed A-diagnostics Area: Messages for errors, warnings, and lints labels May 8, 2023
@jieyouxu
Copy link
Member

jieyouxu commented May 8, 2023

I would like to take a stab at this
@rustbot claim

@scottmcm
Copy link
Member

scottmcm commented May 12, 2023

(Great blog post!)

I wonder if we could go even further here and start linting on any crate export that goes through a glob? (Maybe with some carveouts for, say, modules that reexport an entire other module via glob but include nothing else, or something?)

We're definitely not going to prevent glob imports in general -- especially crate-local ones -- but maybe some sort of "look, this glob escapes the crate" checking could make sense. (Like how the pub-in-priv lints are starting to get smarter about whether things are exported, not just how they're tagged in the definition.)

@obi1kenobi
Copy link
Member Author

I'm not familiar with the lint system internals, but from a purely "protecting the author from unpleasantness" perspective I'd be in favor of such lints.

I continued looking into glob-related risks after publishing the blog posts, and I've come up with things that are arguably even worse than what the blog post showed. All the examples require globs somewhere, so the globs are the thing to target with lints:

I'm adding examples like these to the test suite of trustfall-rustdoc-adapter (a component of cargo-semver-checks that allows querying a single rustdoc JSON file), to make sure cargo-semver-checks catches semver breakage caused by such issues. You're welcome to reuse any of the code examples there: obi1kenobi/trustfall-rustdoc-adapter#172

@obi1kenobi
Copy link
Member Author

Nicely done, @jieyouxu! Excited to try this lint out in nightly!

Am I correct in understanding that this lint found some unintentional true-positive instances of glob re-export shadowing in rustc itself, as well as one intentional use as pub use foo::{* except Bar}?

@jieyouxu
Copy link
Member

jieyouxu commented May 29, 2023

Am I correct in understanding that this lint found some unintentional true-positive instances of glob re-export shadowing in rustc itself

Yes, rustc internally does have a couple of true-positive instances of publicly exported glob re-export items being shadowed by private items.

as well as one intentional use as pub use foo::{* except Bar}?

This is not an intentional use, I was just describing that it was effectively doing {* except Bar} because of the unintentional shadowing.

@obi1kenobi
Copy link
Member Author

Awesome, thanks for the clarification. It's not every day one finds things to lint in rustc :)

Thanks again for putting this together!

@petrochenkov
Copy link
Contributor

In this case it's more likely to find something in rustc than in published crates, because this lint is more relevant for crates with stable interfaces, but in rustc the interfaces between crates are almost entirely unstable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. A-resolve Area: Path resolution T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants