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 for removing extern crate has false positive when crate is used #50672

Closed
alexcrichton opened this issue May 11, 2018 · 12 comments · Fixed by #51015
Closed

Lint for removing extern crate has false positive when crate is used #50672

alexcrichton opened this issue May 11, 2018 · 12 comments · Fixed by #51015
Assignees
Labels
A-rust-2018-preview Area: The 2018 edition preview

Comments

@alexcrichton
Copy link
Member

The following code

#![feature(rust_2018_preview)]
#![deny(unnecessary_extern_crate)]
#![allow(unused_imports)]

extern crate libc;

mod foo {
    use crate::libc;
}

fn main() {}

generates an error:

error: `extern crate` is unnecessary in the new edition
 --> src/main.rs:5:1
  |
5 | extern crate libc;
  | ^^^^^^^^^^^^^^^^^^ help: remove it
  |
note: lint level defined here
 --> src/main.rs:2:9
  |
2 | #![deny(unnecessary_extern_crate)]
  |         ^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

error: Could not compile `playground`.

but if the extern crate libc; is removed it no longer compiles!

This situation has come up on two occasions so far:

  1. In attempting to fix 2018 compatibility lint isn't firing for all imports that need to be rewritten #50660 the lint started firing for use libc; and rustfix rewrote it to use crate::libc;. This worked in the 2015 edition because extern crate libc; was declared at the top. Later this unnecessary_extern_crate lint ended up showing the error above.
  2. Discovered in rustc: Fix crate lint for single-item paths  #50665 the use *; statement is entirely disallowed in the 2018 edition, so we instead recommend the replacement use crate::*;. This, however, will also break if one of the identifiers used is an extern crate as the lint currently suggests removing extern crate
@alexcrichton alexcrichton added the A-rust-2018-preview Area: The 2018 edition preview label May 11, 2018
@alexcrichton
Copy link
Member Author

cc @Manishearth

@Manishearth
Copy link
Member

I and @nikomatsakis discussed this a bunch when implementing this and decided not to fix this problem. An earlier commit of mine there had an attempt which would try and ask you to directly import crates when used as use (0+ local modules in chain)::cratename::(optional foo). It's tricky to get right though. I can dig up the commit if we want that.

@Manishearth
Copy link
Member

The fix is in Manishearth@c904d98

It's not perfect for a bunch of reasons (also might not do precisely what I said in the commit above, but close)

@alexcrichton
Copy link
Member Author

While I can't say for sure I'd be pretty certain that this pattern shows up in the wild (especially use foo::krate_name::bar). If we go with the status quo then it means crates with this pattern won't be able to use rustfix as currently it's an all-or-nothing wrt fixes. Given the experience I've had so far with rustfix I think we want to strive for the highest fidelity lints we can which to me means that we'll likely want to fix this.

@Manishearth
Copy link
Member

Manishearth commented May 11, 2018

Okay, so paging this in a bit more, while Manishearth@c904d98 is a pretty decent lint, we can't write suggestions for it. The problem is that use trees get split out in HIR, which means that the suggestions here can't work to well in the presence of these. It's hard to even detect that there's a use tree involved (in some cases)

Also, doing the span-splicing to emit the correct suggestion is tricky as well, we don't store spans for path segments.

@Manishearth
Copy link
Member

So basically this is doable as a rustfixable lint, but it will be buggy and/or very imperfect.

@nikomatsakis
Copy link
Contributor

I have a few questions. My memory is that, when an extern crate appears somewhere other than the crate root, we are going to leave it in place, and let the user fix it manually.

However, if the extern crate appeared at the crate root, we would remove it, and then try not to rewrite paths like use foo::bar (which is technically accessing crate::foo::bar today, so to speak).

This approach breaks if the code uses use crate::foo::bar, but that's not a common pattern today. But where else does it break?

@nikomatsakis
Copy link
Contributor

One answer to my question would be use self::foo::bar or use super::foo::bar.

@nikomatsakis
Copy link
Contributor

I am reminded that I initially wanted to do a kind of global pass to see where and how the extern crate was used, and only suggest removing it if it is not used from paths like those above. I'm not sure how easy/hard that would be to do but it seems like it would address this issue.

@nikomatsakis
Copy link
Contributor

OK, so, it seems like the lint that @Manishearth pointed us at (Manishearth@c904d98) does the following:

  • It detects when a crate is used through some module-like path (e.g., use self::foo) and warns about it.

As @Manishearth said, writing suggestions for this case is tricky, at least in its full generality. But we could certainly do something like the following:

  • Issue the lint (without a suggestion) for the path; and,
  • Use these results to inform the "unused crate" lint.

That means we'd suppress the "not actionable" error but issue lints that guide you towards making the extern crate into something we can remove.

However, there is still the downside that the paths must be manually changed. I suspect we can overcome that in practice a lot of the time — I mean there are tricky cases, but we can look for the plain vanilla case and just issue the suggestion in that case (for example, by checking that the span of the use statement begins with use self::foo::bar:: or whatever).

@alexcrichton
Copy link
Member Author

I discussed with @nikomatsakis a bit on discord and agree that perhaps the best thing to do here is to avoid linting anything at all, but rather wait till the lint is higher accuracy to turn it on by default.

@nikomatsakis
Copy link
Contributor

Pending fix in #51015

bors added a commit that referenced this issue Jun 2, 2018
…diom, r=alexcrichton

merge unused-extern-crate and unnecessary-extern-crate lints

Extend the `unused_extern_crates` lint to offer a suggestion to remove the extern crate and remove the `unnecessary_extern_crate` lint.

Still a few minor issues to fix:
- [x] this *does* now leave a blank line... (defer to #51176)
  - idea: extend the span to be replaced by 1 character if the next character is a `\n`
- [x] what about macros? do we need to watch out for that? (defer to #48704)
- [x] also it doesn't work for `extern crate foo; fn main() { foo::bar(); }`
  - this is subtle: the `foo` might be shadowing a glob import too, can't always remove
  - defer to #51177
- [x] we also don't do the `pub use` rewrite thang (#51013)

Spun off from #51010

Fixes #50672

r? @alexcrichton
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-rust-2018-preview Area: The 2018 edition preview
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants