-
Notifications
You must be signed in to change notification settings - Fork 13.3k
rustc: Fix crate
lint for single-item paths
#50665
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3232,6 +3232,7 @@ impl<'a> Resolver<'a> { | |
-> PathResult<'a> { | ||
let mut module = None; | ||
let mut allow_super = true; | ||
let mut second_binding = None; | ||
|
||
for (i, &ident) in path.iter().enumerate() { | ||
debug!("resolve_path ident {} {:?}", i, ident); | ||
|
@@ -3321,7 +3322,9 @@ impl<'a> Resolver<'a> { | |
.map(MacroBinding::binding) | ||
} else { | ||
match self.resolve_ident_in_lexical_scope(ident, ns, record_used, path_span) { | ||
// we found a locally-imported or available item/module | ||
Some(LexicalScopeBinding::Item(binding)) => Ok(binding), | ||
// we found a local variable or type param | ||
Some(LexicalScopeBinding::Def(def)) | ||
if opt_ns == Some(TypeNS) || opt_ns == Some(ValueNS) => { | ||
return PathResult::NonModule(PathResolution::with_unresolved_segments( | ||
|
@@ -3334,13 +3337,22 @@ impl<'a> Resolver<'a> { | |
|
||
match binding { | ||
Ok(binding) => { | ||
if i == 1 { | ||
second_binding = Some(binding); | ||
} | ||
let def = binding.def(); | ||
let maybe_assoc = opt_ns != Some(MacroNS) && PathSource::Type.is_expected(def); | ||
if let Some(next_module) = binding.module() { | ||
module = Some(next_module); | ||
} else if def == Def::Err { | ||
return PathResult::NonModule(err_path_resolution()); | ||
} else if opt_ns.is_some() && (is_last || maybe_assoc) { | ||
self.lint_if_path_starts_with_module( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should do this above as well, yes? After There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Discussed here @Manishearth and I figured that we couldn't find other ways to get resolve to trigger those use cases, so we'll fix them up later if they come up and otherwise we'll move forward w/ this. |
||
node_id, | ||
path, | ||
path_span, | ||
second_binding, | ||
); | ||
return PathResult::NonModule(PathResolution::with_unresolved_segments( | ||
def, path.len() - i - 1 | ||
)); | ||
|
@@ -3349,33 +3361,6 @@ impl<'a> Resolver<'a> { | |
format!("Not a module `{}`", ident), | ||
is_last); | ||
} | ||
|
||
if let Some(id) = node_id { | ||
if i == 1 && self.session.features_untracked().crate_in_paths | ||
&& !self.session.rust_2018() { | ||
let prev_name = path[0].name; | ||
if prev_name == keywords::Extern.name() || | ||
prev_name == keywords::CrateRoot.name() { | ||
let mut is_crate = false; | ||
if let NameBindingKind::Import { directive: d, .. } = binding.kind { | ||
if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass { | ||
is_crate = true; | ||
} | ||
} | ||
|
||
if !is_crate { | ||
let diag = lint::builtin::BuiltinLintDiagnostics | ||
::AbsPathWithModule(path_span); | ||
self.session.buffer_lint_with_diagnostic( | ||
lint::builtin::ABSOLUTE_PATH_STARTING_WITH_MODULE, | ||
id, path_span, | ||
"Absolute paths must start with `self`, `super`, \ | ||
`crate`, or an external crate name in the 2018 edition", | ||
diag); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
Err(Undetermined) => return PathResult::Indeterminate, | ||
Err(Determined) => { | ||
|
@@ -3408,9 +3393,77 @@ impl<'a> Resolver<'a> { | |
} | ||
} | ||
|
||
self.lint_if_path_starts_with_module(node_id, path, path_span, second_binding); | ||
|
||
PathResult::Module(module.unwrap_or(self.graph_root)) | ||
} | ||
|
||
fn lint_if_path_starts_with_module(&self, | ||
id: Option<NodeId>, | ||
path: &[Ident], | ||
path_span: Span, | ||
second_binding: Option<&NameBinding>) { | ||
let id = match id { | ||
Some(id) => id, | ||
None => return, | ||
}; | ||
|
||
let first_name = match path.get(0) { | ||
Some(ident) => ident.name, | ||
None => return, | ||
}; | ||
|
||
// We're only interested in `use` paths which should start with | ||
// `{{root}}` or `extern` currently. | ||
if first_name != keywords::Extern.name() && first_name != keywords::CrateRoot.name() { | ||
return | ||
} | ||
|
||
match path.get(1) { | ||
// If this import looks like `crate::...` it's already good | ||
Some(name) if name.name == keywords::Crate.name() => return, | ||
// Otherwise go below to see if it's an extern crate | ||
Some(_) => {} | ||
// If the path has length one (and it's `CrateRoot` most likely) | ||
// then we don't know whether we're gonna be importing a crate or an | ||
// item in our crate. Defer this lint to elsewhere | ||
None => return, | ||
} | ||
|
||
// If the first element of our path was actually resolved to an | ||
// `ExternCrate` (also used for `crate::...`) then no need to issue a | ||
// warning, this looks all good! | ||
if let Some(binding) = second_binding { | ||
if let NameBindingKind::Import { directive: d, .. } = binding.kind { | ||
if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass { | ||
return | ||
} | ||
} | ||
} | ||
|
||
self.lint_path_starts_with_module(id, path_span); | ||
} | ||
|
||
fn lint_path_starts_with_module(&self, id: NodeId, span: Span) { | ||
// In the 2018 edition this lint is a hard error, so nothing to do | ||
if self.session.rust_2018() { | ||
return | ||
} | ||
// In the 2015 edition there's no use in emitting lints unless the | ||
// crate's already enabled the feature that we're going to suggest | ||
if !self.session.features_untracked().crate_in_paths { | ||
return | ||
} | ||
let diag = lint::builtin::BuiltinLintDiagnostics | ||
::AbsPathWithModule(span); | ||
self.session.buffer_lint_with_diagnostic( | ||
lint::builtin::ABSOLUTE_PATH_NOT_STARTING_WITH_CRATE, | ||
id, span, | ||
"absolute paths must start with `self`, `super`, \ | ||
`crate`, or an external crate name in the 2018 edition", | ||
diag); | ||
} | ||
|
||
// Resolve a local definition, potentially adjusting for closures. | ||
fn adjust_local_def(&mut self, | ||
ns: Namespace, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -640,6 +640,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { | |
fn finalize_import(&mut self, directive: &'b ImportDirective<'b>) -> Option<(Span, String)> { | ||
self.current_module = directive.parent; | ||
let ImportDirective { ref module_path, span, .. } = *directive; | ||
let mut warn_if_binding_comes_from_local_crate = false; | ||
|
||
// FIXME: Last path segment is treated specially in import resolution, so extern crate | ||
// mode for absolute paths needs some special support for single-segment imports. | ||
|
@@ -653,6 +654,9 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { | |
return Some((directive.span, | ||
"cannot glob-import all possible crates".to_string())); | ||
} | ||
GlobImport { .. } if self.session.features_untracked().extern_absolute_paths => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is something that was accidentally omitted from the lint entirely before, where Note that there's still a "bug" in the lint for "remove |
||
self.lint_path_starts_with_module(directive.id, span); | ||
} | ||
SingleImport { source, target, .. } => { | ||
let crate_root = if source.name == keywords::Crate.name() && | ||
module_path[0].name != keywords::Extern.name() { | ||
|
@@ -676,6 +680,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { | |
self.populate_module_if_necessary(crate_root); | ||
Some(crate_root) | ||
} else { | ||
warn_if_binding_comes_from_local_crate = true; | ||
None | ||
}; | ||
|
||
|
@@ -870,6 +875,26 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { | |
} | ||
} | ||
|
||
if warn_if_binding_comes_from_local_crate { | ||
let mut warned = false; | ||
self.per_ns(|this, ns| { | ||
let binding = match result[ns].get().ok() { | ||
Some(b) => b, | ||
None => return | ||
}; | ||
if let NameBindingKind::Import { directive: d, .. } = binding.kind { | ||
if let ImportDirectiveSubclass::ExternCrate(..) = d.subclass { | ||
return | ||
} | ||
} | ||
if warned { | ||
return | ||
} | ||
warned = true; | ||
this.lint_path_starts_with_module(directive.id, span); | ||
}); | ||
} | ||
|
||
// Record what this import resolves to for later uses in documentation, | ||
// this may resolve to either a value or a type, but for documentation | ||
// purposes it's good enough to just favor one over the other. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
pub fn foo() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT | ||
// file at the top-level directory of this distribution and at | ||
// http://rust-lang.org/COPYRIGHT. | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
// aux-build:edition-lint-paths.rs | ||
// run-rustfix | ||
|
||
#![feature(rust_2018_preview)] | ||
#![deny(absolute_path_not_starting_with_crate)] | ||
#![allow(unused)] | ||
|
||
extern crate edition_lint_paths; | ||
|
||
pub mod foo { | ||
use edition_lint_paths; | ||
use crate::bar::Bar; | ||
//~^ ERROR absolute | ||
//~| WARN this was previously accepted | ||
use super::bar::Bar2; | ||
use crate::bar::Bar3; | ||
|
||
use crate::bar; | ||
//~^ ERROR absolute | ||
//~| WARN this was previously accepted | ||
use crate::{bar as something_else}; | ||
|
||
use {crate::Bar as SomethingElse, crate::main}; | ||
//~^ ERROR absolute | ||
//~| WARN this was previously accepted | ||
//~| ERROR absolute | ||
//~| WARN this was previously accepted | ||
|
||
use crate::{Bar as SomethingElse2, main as another_main}; | ||
|
||
pub fn test() { | ||
} | ||
} | ||
|
||
use crate::bar::Bar; | ||
//~^ ERROR absolute | ||
//~| WARN this was previously accepted | ||
|
||
pub mod bar { | ||
use edition_lint_paths as foo; | ||
pub struct Bar; | ||
pub type Bar2 = Bar; | ||
pub type Bar3 = Bar; | ||
} | ||
|
||
mod baz { | ||
use crate::*; | ||
//~^ ERROR absolute | ||
//~| WARN this was previously accepted | ||
} | ||
|
||
fn main() { | ||
let x = crate::bar::Bar; | ||
//~^ ERROR absolute | ||
//~| WARN this was previously accepted | ||
let x = bar::Bar; | ||
let x = ::crate::bar::Bar; | ||
let x = self::bar::Bar; | ||
foo::test(); | ||
|
||
{ | ||
use edition_lint_paths as bar; | ||
edition_lint_paths::foo(); | ||
bar::foo(); | ||
::edition_lint_paths::foo(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,70 @@ | ||
error: Absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:16:9 | ||
error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:22:9 | ||
| | ||
LL | use ::bar::Bar; | ||
| ^^^^^^^^^^ help: use `crate`: `crate::bar::Bar` | ||
| | ||
note: lint level defined here | ||
--> $DIR/edition-lint-paths.rs:12:9 | ||
--> $DIR/edition-lint-paths.rs:15:9 | ||
| | ||
LL | #![deny(absolute_path_starting_with_module)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
LL | #![deny(absolute_path_not_starting_with_crate)] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! | ||
= note: for more information, see issue TBD | ||
|
||
error: Absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:24:5 | ||
error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:28:9 | ||
| | ||
LL | use bar; | ||
| ^^^ help: use `crate`: `crate::bar` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! | ||
= note: for more information, see issue TBD | ||
|
||
error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:33:10 | ||
| | ||
LL | use {Bar as SomethingElse, main}; | ||
| ^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::Bar as SomethingElse` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! | ||
= note: for more information, see issue TBD | ||
|
||
error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:33:32 | ||
| | ||
LL | use {Bar as SomethingElse, main}; | ||
| ^^^^ help: use `crate`: `crate::main` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! | ||
= note: for more information, see issue TBD | ||
|
||
error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:45:5 | ||
| | ||
LL | use bar::Bar; | ||
| ^^^^^^^^ help: use `crate`: `crate::bar::Bar` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! | ||
= note: for more information, see issue TBD | ||
|
||
error: Absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:35:13 | ||
error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:57:9 | ||
| | ||
LL | use *; | ||
| ^ help: use `crate`: `crate::*` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! | ||
= note: for more information, see issue TBD | ||
|
||
error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition | ||
--> $DIR/edition-lint-paths.rs:63:13 | ||
| | ||
LL | let x = ::bar::Bar; | ||
| ^^^^^^^^^^ help: use `crate`: `crate::bar::Bar` | ||
| | ||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition! | ||
= note: for more information, see issue TBD | ||
|
||
error: aborting due to 3 previous errors | ||
error: aborting due to 7 previous errors | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The renaming would need to be backported or the old name put into the deprecated lint list