auto merge of #4803 : alexcrichton/rust/fix-unused-imports, r=graydon

The first commit message has most of the comments, but this pull request basically fixes a lot of issues surrounding the `unused_imports` warning/deny attribute.

Before this patch there were these problems:

1. Unused imports from `prelude.rs` were warned about with dummy spans, leading to a large number of confusing warnings.
2. Unused imports from `intrinsic.rs` were warned about with the file `<intrinsic>` which couldn't be forced to go away
3. Methods used from imported traites (like `io::WriterUtil`) resulted in an unused warning of the import even though it was used.
4. If one `use` statement imported N modules, M of which weren't used, M warning statements were issued.
5. If a glob import statement was used, each public export of the target module which wasn't used had a warning issued.

This patch deals with all these cases by doing:

1. Ignore unused imports from `prelude.rs` (indicated by a dummy span of 0)
2. Ignore unused imports from `intrinsic.rs` (test on the imported module name, is there a better way?)
3. Track when imported modules are used as candidates for methods, and just assume they're used. This may not end up being the actual case, but in theory not warning about an unused thing is worse than warning about a used thing.
4. Only issue one warning statement
5. Only issue one warning statement.

This is the first time I've edited the compiler itself, and I tried to keep up with the style around, but I may have missed something here or there...
This commit is contained in:
bors 2013-02-07 15:20:16 -08:00
commit 2bc9655bc1
2 changed files with 82 additions and 28 deletions

View File

@ -392,17 +392,19 @@ pub struct ImportResolution {
/// The type that this `use` directive names, if there is one.
mut type_target: Option<Target>,
mut used: bool,
/// There exists one state per import statement
state: @mut ImportState,
}
pub fn ImportResolution(privacy: Privacy, span: span) -> ImportResolution {
pub fn ImportResolution(privacy: Privacy, span: span,
state: @mut ImportState) -> ImportResolution {
ImportResolution {
privacy: privacy,
span: span,
outstanding_references: 0,
value_target: None,
type_target: None,
used: false
state: state,
}
}
@ -415,6 +417,15 @@ pub impl ImportResolution {
}
}
pub struct ImportState {
used: bool,
warned: bool
}
pub fn ImportState() -> ImportState {
ImportState{ used: false, warned: false }
}
/// The link from a module up to its nearest parent node.
pub enum ParentLink {
NoParentLink,
@ -1415,6 +1426,7 @@ pub impl Resolver {
// Build up the import directives.
let module_ = self.get_module_from_parent(parent);
let state = @mut ImportState();
match view_path.node {
view_path_simple(binding, full_path, ns, _) => {
let ns = match ns {
@ -1430,7 +1442,8 @@ pub impl Resolver {
module_,
module_path,
subclass,
view_path.span);
view_path.span,
state);
}
view_path_list(_, ref source_idents, _) => {
for (*source_idents).each |source_ident| {
@ -1442,7 +1455,8 @@ pub impl Resolver {
module_,
module_path,
subclass,
view_path.span);
view_path.span,
state);
}
}
view_path_glob(_, _) => {
@ -1450,7 +1464,8 @@ pub impl Resolver {
module_,
module_path,
@GlobImport,
view_path.span);
view_path.span,
state);
}
}
}
@ -1573,7 +1588,8 @@ pub impl Resolver {
// avoid creating cycles in the
// module graph.
let resolution = @ImportResolution(Public, dummy_sp());
let resolution = @ImportResolution(Public, dummy_sp(),
@mut ImportState());
resolution.outstanding_references = 0;
match existing_module.parent_link {
@ -1826,7 +1842,8 @@ pub impl Resolver {
module_: @Module,
module_path: @DVec<ident>,
subclass: @ImportDirectiveSubclass,
span: span) {
span: span,
state: @mut ImportState) {
let directive = @ImportDirective(privacy, module_path,
subclass, span);
module_.imports.push(directive);
@ -1850,7 +1867,14 @@ pub impl Resolver {
}
None => {
debug!("(building import directive) creating new");
let resolution = @ImportResolution(privacy, span);
let resolution = @ImportResolution(privacy, span,
state);
let name = self.idents_to_str(module_path.get());
// Don't warn about unused intrinsics because they're
// automatically appended to all files
if name == ~"intrinsic::rusti" {
resolution.state.warned = true;
}
resolution.outstanding_references = 1;
module_.import_resolutions.insert(target, resolution);
}
@ -2183,7 +2207,7 @@ pub impl Resolver {
return UnboundResult;
}
Some(target) => {
import_resolution.used = true;
import_resolution.state.used = true;
return BoundResult(target.target_module,
target.bindings);
}
@ -2352,7 +2376,7 @@ pub impl Resolver {
module_result = UnboundResult;
}
Some(target) => {
import_resolution.used = true;
import_resolution.state.used = true;
module_result = BoundResult
(target.target_module,
target.bindings);
@ -2419,6 +2443,7 @@ pub impl Resolver {
// everything it can to the list of import resolutions of the module
// node.
debug!("(resolving glob import) resolving %? glob import", privacy);
let state = @mut ImportState();
// We must bail out if the node has unresolved imports of any kind
// (including globs).
@ -2445,7 +2470,8 @@ pub impl Resolver {
// Simple: just copy the old import resolution.
let new_import_resolution =
@ImportResolution(privacy,
target_import_resolution.span);
target_import_resolution.span,
state);
new_import_resolution.value_target =
copy target_import_resolution.value_target;
new_import_resolution.type_target =
@ -2486,7 +2512,8 @@ pub impl Resolver {
match module_.import_resolutions.find(&ident) {
None => {
// Create a new import resolution from this child.
dest_import_resolution = @ImportResolution(privacy, span);
dest_import_resolution = @ImportResolution(privacy, span,
state);
module_.import_resolutions.insert
(ident, dest_import_resolution);
}
@ -2713,7 +2740,7 @@ pub impl Resolver {
namespace);
}
Some(target) => {
import_resolution.used = true;
import_resolution.state.used = true;
return Success(copy target);
}
}
@ -2962,7 +2989,7 @@ pub impl Resolver {
Some(target) => {
debug!("(resolving name in module) resolved to \
import");
import_resolution.used = true;
import_resolution.state.used = true;
return Success(copy target);
}
}
@ -4560,7 +4587,7 @@ pub impl Resolver {
namespace)) {
(Some(def), Some(Public)) => {
// Found it.
import_resolution.used = true;
import_resolution.state.used = true;
return ImportNameDefinition(def);
}
(Some(_), _) | (None, _) => {
@ -5034,9 +5061,13 @@ pub impl Resolver {
Some(def) => {
match def {
def_ty(trait_def_id) => {
self.
let added = self.
add_trait_info_if_containing_method(
found_traits, trait_def_id, name);
if added {
import_resolution.state.used =
true;
}
}
_ => {
// Continue.
@ -5069,7 +5100,7 @@ pub impl Resolver {
fn add_trait_info_if_containing_method(found_traits: @DVec<def_id>,
trait_def_id: def_id,
name: ident) {
name: ident) -> bool {
debug!("(adding trait info if containing method) trying trait %d:%d \
for method '%s'",
@ -5085,9 +5116,10 @@ pub impl Resolver {
trait_def_id.node,
self.session.str_of(name));
(*found_traits).push(trait_def_id);
true
}
Some(_) | None => {
// Continue.
false
}
}
}
@ -5204,7 +5236,13 @@ pub impl Resolver {
fn check_for_unused_imports_in_module(module_: @Module) {
for module_.import_resolutions.each_value_ref |&import_resolution| {
if !import_resolution.used {
// Ignore dummy spans for things like automatically injected
// imports for the prelude, and also don't warn about the same
// import statement being unused more than once.
if !import_resolution.state.used &&
!import_resolution.state.warned &&
import_resolution.span != dummy_sp() {
import_resolution.state.warned = true;
match self.unused_import_lint_level {
warn => {
self.session.span_warn(import_resolution.span,

View File

@ -8,23 +8,39 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// error-pattern:unused import
// compile-flags:-W unused-imports
// compile-flags: -D unused-imports
use cal = bar::c::cc;
use core::either::Right; //~ ERROR unused import
use core::util::*; // shouldn't get errors for not using
// everything imported
// Should only get one error instead of two errors here
use core::option::{Some, None}; //~ ERROR unused import
use core::io::ReaderUtil; //~ ERROR unused import
// Be sure that if we just bring some methods into scope that they're also
// counted as being used.
use core::io::WriterUtil;
mod foo {
pub type point = {x: int, y: int};
pub type square = {p: point, h: uint, w: uint};
pub struct Point{x: int, y: int}
pub struct Square{p: Point, h: uint, w: uint}
}
mod bar {
pub mod c {
use foo::point;
use foo::square;
pub fn cc(p: point) -> str { return 2 * (p.x + p.y); }
use foo::Point;
use foo::Square; //~ ERROR unused import
pub fn cc(p: Point) -> int { return 2 * (p.x + p.y); }
}
}
fn main() {
cal({x:3, y:9});
cal(foo::Point{x:3, y:9});
let a = 3;
ignore(a);
io::stdout().write_str(~"a");
}