diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index f08aa2eb49f72..35c0dcf2c5659 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -106,7 +106,7 @@ pub trait IntoEarlyLint { fn into_early_lint(self, id: LintId) -> EarlyLint; } -impl<'a> IntoEarlyLint for (Span, &'a str) { +impl<'a, S: Into> IntoEarlyLint for (S, &'a str) { fn into_early_lint(self, id: LintId) -> EarlyLint { let (span, msg) = self; let mut diagnostic = Diagnostic::new(errors::Level::Warning, msg); @@ -530,7 +530,10 @@ pub trait LintContext: Sized { }) } - fn lookup_and_emit(&self, lint: &'static Lint, span: Option, msg: &str) { + fn lookup_and_emit>(&self, + lint: &'static Lint, + span: Option, + msg: &str) { let (level, src) = match self.level_src(lint) { None => return, Some(pair) => pair, @@ -553,7 +556,7 @@ pub trait LintContext: Sized { } /// Emit a lint at the appropriate level, for a particular span. - fn span_lint(&self, lint: &'static Lint, span: Span, msg: &str) { + fn span_lint>(&self, lint: &'static Lint, span: S, msg: &str) { self.lookup_and_emit(lint, Some(span), msg); } @@ -601,7 +604,7 @@ pub trait LintContext: Sized { /// Emit a lint at the appropriate level, with no associated span. fn lint(&self, lint: &'static Lint, msg: &str) { - self.lookup_and_emit(lint, None, msg); + self.lookup_and_emit(lint, None as Option, msg); } /// Merge the lints specified by any lint attributes into the diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index b4dadbf7961fb..f258b4c518209 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -258,14 +258,15 @@ impl Session { pub fn unimpl(&self, msg: &str) -> ! { self.diagnostic().unimpl(msg) } - pub fn add_lint(&self, - lint: &'static lint::Lint, - id: ast::NodeId, - sp: Span, - msg: String) + pub fn add_lint>(&self, + lint: &'static lint::Lint, + id: ast::NodeId, + sp: S, + msg: String) { self.add_lint_diagnostic(lint, id, (sp, &msg[..])) } + pub fn add_lint_diagnostic(&self, lint: &'static lint::Lint, id: ast::NodeId, diff --git a/src/librustc_resolve/check_unused.rs b/src/librustc_resolve/check_unused.rs index e1ea40809da07..36e05a433414e 100644 --- a/src/librustc_resolve/check_unused.rs +++ b/src/librustc_resolve/check_unused.rs @@ -25,13 +25,16 @@ use Resolver; use Namespace::{TypeNS, ValueNS}; use rustc::lint; +use rustc::util::nodemap::NodeMap; use syntax::ast::{self, ViewPathGlob, ViewPathList, ViewPathSimple}; use syntax::visit::{self, Visitor}; -use syntax_pos::{Span, DUMMY_SP}; +use syntax_pos::{Span, MultiSpan, DUMMY_SP}; struct UnusedImportCheckVisitor<'a, 'b: 'a> { resolver: &'a mut Resolver<'b>, + /// All the (so far) unused imports, grouped path list + unused_imports: NodeMap>, } // Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver. @@ -52,23 +55,21 @@ impl<'a, 'b> DerefMut for UnusedImportCheckVisitor<'a, 'b> { impl<'a, 'b> UnusedImportCheckVisitor<'a, 'b> { // We have information about whether `use` (import) directives are actually // used now. If an import is not used at all, we signal a lint error. - fn check_import(&mut self, id: ast::NodeId, span: Span) { + fn check_import(&mut self, item_id: ast::NodeId, id: ast::NodeId, span: Span) { if !self.used_imports.contains(&(id, TypeNS)) && !self.used_imports.contains(&(id, ValueNS)) { if self.maybe_unused_trait_imports.contains(&id) { // Check later. return; } - let msg = if let Ok(snippet) = self.session.codemap().span_to_snippet(span) { - format!("unused import: `{}`", snippet) - } else { - "unused import".to_string() - }; - self.session.add_lint(lint::builtin::UNUSED_IMPORTS, id, span, msg); + self.unused_imports.entry(item_id).or_insert_with(NodeMap).insert(id, span); } else { // This trait import is definitely used, in a way other than // method resolution. self.maybe_unused_trait_imports.remove(&id); + if let Some(i) = self.unused_imports.get_mut(&item_id) { + i.remove(&id); + } } } } @@ -98,16 +99,16 @@ impl<'a, 'b> Visitor for UnusedImportCheckVisitor<'a, 'b> { ast::ItemKind::Use(ref p) => { match p.node { ViewPathSimple(..) => { - self.check_import(item.id, p.span) + self.check_import(item.id, item.id, p.span) } ViewPathList(_, ref list) => { for i in list { - self.check_import(i.node.id, i.span); + self.check_import(item.id, i.node.id, i.span); } } ViewPathGlob(_) => { - self.check_import(item.id, p.span) + self.check_import(item.id, item.id, p.span); } } } @@ -117,6 +118,35 @@ impl<'a, 'b> Visitor for UnusedImportCheckVisitor<'a, 'b> { } pub fn check_crate(resolver: &mut Resolver, krate: &ast::Crate) { - let mut visitor = UnusedImportCheckVisitor { resolver: resolver }; + let mut visitor = UnusedImportCheckVisitor { + resolver: resolver, + unused_imports: NodeMap(), + }; visit::walk_crate(&mut visitor, krate); + + for (id, spans) in &visitor.unused_imports { + let len = spans.len(); + let mut spans = spans.values().map(|s| *s).collect::>(); + spans.sort(); + let ms = MultiSpan::from_spans(spans.clone()); + let mut span_snippets = spans.iter() + .filter_map(|s| { + match visitor.session.codemap().span_to_snippet(*s) { + Ok(s) => Some(format!("`{}`", s)), + _ => None, + } + }).collect::>(); + span_snippets.sort(); + let msg = format!("unused import{}{}", + if len > 1 { "s" } else { "" }, + if span_snippets.len() > 0 { + format!(": {}", span_snippets.join(", ")) + } else { + String::new() + }); + visitor.session.add_lint(lint::builtin::UNUSED_IMPORTS, + *id, + ms, + msg); + } } diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index d99850332c36e..d6e45aa0b9f0c 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -52,7 +52,7 @@ pub type FileName = String; /// able to use many of the functions on spans in codemap and you cannot assume /// that the length of the span = hi - lo; there may be space in the BytePos /// range between files. -#[derive(Clone, Copy, Hash, PartialEq, Eq)] +#[derive(Clone, Copy, Hash, PartialEq, Eq, Ord, PartialOrd)] pub struct Span { pub lo: BytePos, pub hi: BytePos, @@ -67,7 +67,7 @@ pub struct Span { /// the error, and would be rendered with `^^^`. /// - they can have a *label*. In this case, the label is written next /// to the mark in the snippet when we render. -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct MultiSpan { primary_spans: Vec, span_labels: Vec<(Span, String)>, @@ -254,7 +254,7 @@ impl From for MultiSpan { } } -#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy)] +#[derive(PartialEq, Eq, Clone, Debug, Hash, RustcEncodable, RustcDecodable, Copy, Ord, PartialOrd)] pub struct ExpnId(pub u32); pub const NO_EXPANSION: ExpnId = ExpnId(!0); diff --git a/src/test/compile-fail/lint-unused-imports.rs b/src/test/compile-fail/lint-unused-imports.rs index 3f91c3e1e5c79..5b1c04946a40b 100644 --- a/src/test/compile-fail/lint-unused-imports.rs +++ b/src/test/compile-fail/lint-unused-imports.rs @@ -17,8 +17,9 @@ use std::mem::*; // shouldn't get errors for not using // everything imported // Should get errors for both 'Some' and 'None' -use std::option::Option::{Some, None}; //~ ERROR unused import: `Some` - //~^ ERROR unused import: `None` +use std::option::Option::{Some, None}; +//~^ ERROR unused imports: `None`, `Some` +//~| ERROR unused imports: `None`, `Some` use test::A; //~ ERROR unused import: `test::A` // Be sure that if we just bring some methods into scope that they're also diff --git a/src/test/ui/span/multispan-import-lint.rs b/src/test/ui/span/multispan-import-lint.rs new file mode 100644 index 0000000000000..43b6cd8f85f82 --- /dev/null +++ b/src/test/ui/span/multispan-import-lint.rs @@ -0,0 +1,15 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::cmp::{Eq, Ord, min, PartialEq, PartialOrd}; + +fn main() { + let _ = min(1, 2); +} diff --git a/src/test/ui/span/multispan-import-lint.stderr b/src/test/ui/span/multispan-import-lint.stderr new file mode 100644 index 0000000000000..b581584eee7e2 --- /dev/null +++ b/src/test/ui/span/multispan-import-lint.stderr @@ -0,0 +1,6 @@ +warning: unused imports: `Eq`, `Ord`, `PartialEq`, `PartialOrd`, #[warn(unused_imports)] on by default + --> $DIR/multispan-import-lint.rs:11:16 + | +11 | use std::cmp::{Eq, Ord, min, PartialEq, PartialOrd}; + | ^^ ^^^ ^^^^^^^^^ ^^^^^^^^^^ +