Skip to content

Commit ed9a29a

Browse files
committed
Auto merge of #50665 - alexcrichton:fix-single-item-path-warnings, r=oli-obk
rustc: Fix `crate` lint for single-item paths This commit fixes recommending the `crate` prefix when migrating to 2018 for paths that look like `use foo;` or `use {bar, baz}` Closes #50660
2 parents b559710 + dff9ee1 commit ed9a29a

File tree

8 files changed

+287
-47
lines changed

8 files changed

+287
-47
lines changed

src/librustc/lint/builtin.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ declare_lint! {
261261
}
262262

263263
declare_lint! {
264-
pub ABSOLUTE_PATH_STARTING_WITH_MODULE,
264+
pub ABSOLUTE_PATH_NOT_STARTING_WITH_CRATE,
265265
Allow,
266266
"fully qualified paths that start with a module name \
267267
instead of `crate`, `self`, or an extern crate name"
@@ -328,7 +328,7 @@ impl LintPass for HardwiredLints {
328328
TYVAR_BEHIND_RAW_POINTER,
329329
ELIDED_LIFETIME_IN_PATH,
330330
BARE_TRAIT_OBJECT,
331-
ABSOLUTE_PATH_STARTING_WITH_MODULE,
331+
ABSOLUTE_PATH_NOT_STARTING_WITH_CRATE,
332332
UNSTABLE_NAME_COLLISION,
333333
)
334334
}

src/librustc_lint/lib.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ extern crate rustc_target;
4040
extern crate syntax_pos;
4141

4242
use rustc::lint;
43-
use rustc::lint::builtin::{BARE_TRAIT_OBJECT, ABSOLUTE_PATH_STARTING_WITH_MODULE};
43+
use rustc::lint::builtin::{BARE_TRAIT_OBJECT, ABSOLUTE_PATH_NOT_STARTING_WITH_CRATE};
4444
use rustc::session;
4545
use rustc::util;
4646

@@ -283,7 +283,7 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
283283
// standard library, and thus should never be removed or changed to an error.
284284
},
285285
FutureIncompatibleInfo {
286-
id: LintId::of(ABSOLUTE_PATH_STARTING_WITH_MODULE),
286+
id: LintId::of(ABSOLUTE_PATH_NOT_STARTING_WITH_CRATE),
287287
reference: "issue TBD",
288288
edition: Some(Edition::Edition2018),
289289
},
@@ -322,4 +322,6 @@ pub fn register_builtins(store: &mut lint::LintStore, sess: Option<&Session>) {
322322
"converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
323323
store.register_removed("resolve_trait_on_defaulted_unit",
324324
"converted into hard error, see https://github.com/rust-lang/rust/issues/48950");
325+
store.register_removed("absolute_path_starting_with_module",
326+
"renamed to `absolute_path_not_starting_with_crate`");
325327
}

src/librustc_resolve/lib.rs

+80-27
Original file line numberDiff line numberDiff line change
@@ -3232,6 +3232,7 @@ impl<'a> Resolver<'a> {
32323232
-> PathResult<'a> {
32333233
let mut module = None;
32343234
let mut allow_super = true;
3235+
let mut second_binding = None;
32353236

32363237
for (i, &ident) in path.iter().enumerate() {
32373238
debug!("resolve_path ident {} {:?}", i, ident);
@@ -3321,7 +3322,9 @@ impl<'a> Resolver<'a> {
33213322
.map(MacroBinding::binding)
33223323
} else {
33233324
match self.resolve_ident_in_lexical_scope(ident, ns, record_used, path_span) {
3325+
// we found a locally-imported or available item/module
33243326
Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
3327+
// we found a local variable or type param
33253328
Some(LexicalScopeBinding::Def(def))
33263329
if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => {
33273330
return PathResult::NonModule(PathResolution::with_unresolved_segments(
@@ -3334,13 +3337,22 @@ impl<'a> Resolver<'a> {
33343337

33353338
match binding {
33363339
Ok(binding) => {
3340+
if i == 1 {
3341+
second_binding = Some(binding);
3342+
}
33373343
let def = binding.def();
33383344
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(def);
33393345
if let Some(next_module) = binding.module() {
33403346
module = Some(next_module);
33413347
} else if def == Def::Err {
33423348
return PathResult::NonModule(err_path_resolution());
33433349
} else if opt_ns.is_some() && (is_last || maybe_assoc) {
3350+
self.lint_if_path_starts_with_module(
3351+
node_id,
3352+
path,
3353+
path_span,
3354+
second_binding,
3355+
);
33443356
return PathResult::NonModule(PathResolution::with_unresolved_segments(
33453357
def, path.len() - i - 1
33463358
));
@@ -3349,33 +3361,6 @@ impl<'a> Resolver<'a> {
33493361
format!("Not a module `{}`", ident),
33503362
is_last);
33513363
}
3352-
3353-
if let Some(id) = node_id {
3354-
if i == 1 && self.session.features_untracked().crate_in_paths
3355-
&& !self.session.rust_2018() {
3356-
let prev_name = path[0].name;
3357-
if prev_name == keywords::Extern.name() ||
3358-
prev_name == keywords::CrateRoot.name() {
3359-
let mut is_crate = false;
3360-
if let NameBindingKind::Import { directive: d, .. } = binding.kind {
3361-
if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass {
3362-
is_crate = true;
3363-
}
3364-
}
3365-
3366-
if !is_crate {
3367-
let diag = lint::builtin::BuiltinLintDiagnostics
3368-
::AbsPathWithModule(path_span);
3369-
self.session.buffer_lint_with_diagnostic(
3370-
lint::builtin::ABSOLUTE_PATH_STARTING_WITH_MODULE,
3371-
id, path_span,
3372-
"Absolute paths must start with `self`, `super`, \
3373-
`crate`, or an external crate name in the 2018 edition",
3374-
diag);
3375-
}
3376-
}
3377-
}
3378-
}
33793364
}
33803365
Err(Undetermined) => return PathResult::Indeterminate,
33813366
Err(Determined) => {
@@ -3408,9 +3393,77 @@ impl<'a> Resolver<'a> {
34083393
}
34093394
}
34103395

3396+
self.lint_if_path_starts_with_module(node_id, path, path_span, second_binding);
3397+
34113398
PathResult::Module(module.unwrap_or(self.graph_root))
34123399
}
34133400

3401+
fn lint_if_path_starts_with_module(&self,
3402+
id: Option<NodeId>,
3403+
path: &[Ident],
3404+
path_span: Span,
3405+
second_binding: Option<&NameBinding>) {
3406+
let id = match id {
3407+
Some(id) => id,
3408+
None => return,
3409+
};
3410+
3411+
let first_name = match path.get(0) {
3412+
Some(ident) => ident.name,
3413+
None => return,
3414+
};
3415+
3416+
// We're only interested in `use` paths which should start with
3417+
// `{{root}}` or `extern` currently.
3418+
if first_name != keywords::Extern.name() && first_name != keywords::CrateRoot.name() {
3419+
return
3420+
}
3421+
3422+
match path.get(1) {
3423+
// If this import looks like `crate::...` it's already good
3424+
Some(name) if name.name == keywords::Crate.name() => return,
3425+
// Otherwise go below to see if it's an extern crate
3426+
Some(_) => {}
3427+
// If the path has length one (and it's `CrateRoot` most likely)
3428+
// then we don't know whether we're gonna be importing a crate or an
3429+
// item in our crate. Defer this lint to elsewhere
3430+
None => return,
3431+
}
3432+
3433+
// If the first element of our path was actually resolved to an
3434+
// `ExternCrate` (also used for `crate::...`) then no need to issue a
3435+
// warning, this looks all good!
3436+
if let Some(binding) = second_binding {
3437+
if let NameBindingKind::Import { directive: d, .. } = binding.kind {
3438+
if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass {
3439+
return
3440+
}
3441+
}
3442+
}
3443+
3444+
self.lint_path_starts_with_module(id, path_span);
3445+
}
3446+
3447+
fn lint_path_starts_with_module(&self, id: NodeId, span: Span) {
3448+
// In the 2018 edition this lint is a hard error, so nothing to do
3449+
if self.session.rust_2018() {
3450+
return
3451+
}
3452+
// In the 2015 edition there's no use in emitting lints unless the
3453+
// crate's already enabled the feature that we're going to suggest
3454+
if !self.session.features_untracked().crate_in_paths {
3455+
return
3456+
}
3457+
let diag = lint::builtin::BuiltinLintDiagnostics
3458+
::AbsPathWithModule(span);
3459+
self.session.buffer_lint_with_diagnostic(
3460+
lint::builtin::ABSOLUTE_PATH_NOT_STARTING_WITH_CRATE,
3461+
id, span,
3462+
"absolute paths must start with `self`, `super`, \
3463+
`crate`, or an external crate name in the 2018 edition",
3464+
diag);
3465+
}
3466+
34143467
// Resolve a local definition, potentially adjusting for closures.
34153468
fn adjust_local_def(&mut self,
34163469
ns: Namespace,

src/librustc_resolve/resolve_imports.rs

+25
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
640640
fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Span, String)> {
641641
self.current_module = directive.parent;
642642
let ImportDirective { ref module_path, span, .. } = *directive;
643+
let mut warn_if_binding_comes_from_local_crate = false;
643644

644645
// FIXME: Last path segment is treated specially in import resolution, so extern crate
645646
// mode for absolute paths needs some special support for single-segment imports.
@@ -653,6 +654,9 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
653654
return Some((directive.span,
654655
"cannot glob-import all possible crates".to_string()));
655656
}
657+
GlobImport { .. } if self.session.features_untracked().extern_absolute_paths => {
658+
self.lint_path_starts_with_module(directive.id, span);
659+
}
656660
SingleImport { source, target, .. } => {
657661
let crate_root = if source.name == keywords::Crate.name() &&
658662
module_path[0].name != keywords::Extern.name() {
@@ -676,6 +680,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
676680
self.populate_module_if_necessary(crate_root);
677681
Some(crate_root)
678682
} else {
683+
warn_if_binding_comes_from_local_crate = true;
679684
None
680685
};
681686

@@ -870,6 +875,26 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
870875
}
871876
}
872877

878+
if warn_if_binding_comes_from_local_crate {
879+
let mut warned = false;
880+
self.per_ns(|this, ns| {
881+
let binding = match result[ns].get().ok() {
882+
Some(b) => b,
883+
None => return
884+
};
885+
if let NameBindingKind::Import { directive: d, .. } = binding.kind {
886+
if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass {
887+
return
888+
}
889+
}
890+
if warned {
891+
return
892+
}
893+
warned = true;
894+
this.lint_path_starts_with_module(directive.id, span);
895+
});
896+
}
897+
873898
// Record what this import resolves to for later uses in documentation,
874899
// this may resolve to either a value or a type, but for documentation
875900
// purposes it's good enough to just favor one over the other.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
pub fn foo() {}

src/test/ui/edition-lint-paths.fixed

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:edition-lint-paths.rs
12+
// run-rustfix
13+
14+
#![feature(rust_2018_preview)]
15+
#![deny(absolute_path_not_starting_with_crate)]
16+
#![allow(unused)]
17+
18+
extern crate edition_lint_paths;
19+
20+
pub mod foo {
21+
use edition_lint_paths;
22+
use crate::bar::Bar;
23+
//~^ ERROR absolute
24+
//~| WARN this was previously accepted
25+
use super::bar::Bar2;
26+
use crate::bar::Bar3;
27+
28+
use crate::bar;
29+
//~^ ERROR absolute
30+
//~| WARN this was previously accepted
31+
use crate::{bar as something_else};
32+
33+
use {crate::Bar as SomethingElse, crate::main};
34+
//~^ ERROR absolute
35+
//~| WARN this was previously accepted
36+
//~| ERROR absolute
37+
//~| WARN this was previously accepted
38+
39+
use crate::{Bar as SomethingElse2, main as another_main};
40+
41+
pub fn test() {
42+
}
43+
}
44+
45+
use crate::bar::Bar;
46+
//~^ ERROR absolute
47+
//~| WARN this was previously accepted
48+
49+
pub mod bar {
50+
use edition_lint_paths as foo;
51+
pub struct Bar;
52+
pub type Bar2 = Bar;
53+
pub type Bar3 = Bar;
54+
}
55+
56+
mod baz {
57+
use crate::*;
58+
//~^ ERROR absolute
59+
//~| WARN this was previously accepted
60+
}
61+
62+
fn main() {
63+
let x = crate::bar::Bar;
64+
//~^ ERROR absolute
65+
//~| WARN this was previously accepted
66+
let x = bar::Bar;
67+
let x = ::crate::bar::Bar;
68+
let x = self::bar::Bar;
69+
foo::test();
70+
71+
{
72+
use edition_lint_paths as bar;
73+
edition_lint_paths::foo();
74+
bar::foo();
75+
::edition_lint_paths::foo();
76+
}
77+
}

0 commit comments

Comments
 (0)