@@ -25,13 +25,16 @@ use Resolver;
2525use Namespace :: { TypeNS , ValueNS } ;
2626
2727use rustc:: lint;
28+ use rustc:: util:: nodemap:: NodeMap ;
2829use syntax:: ast:: { self , ViewPathGlob , ViewPathList , ViewPathSimple } ;
2930use syntax:: visit:: { self , Visitor } ;
30- use syntax_pos:: { Span , DUMMY_SP } ;
31+ use syntax_pos:: { Span , MultiSpan , DUMMY_SP } ;
3132
3233
3334struct UnusedImportCheckVisitor < ' a , ' b : ' a > {
3435 resolver : & ' a mut Resolver < ' b > ,
36+ /// All the (so far) unused imports, grouped path list
37+ unused_imports : NodeMap < NodeMap < Span > > ,
3538}
3639
3740// Deref and DerefMut impls allow treating UnusedImportCheckVisitor as Resolver.
@@ -52,23 +55,21 @@ impl<'a, 'b> DerefMut for UnusedImportCheckVisitor<'a, 'b> {
5255impl < ' a , ' b > UnusedImportCheckVisitor < ' a , ' b > {
5356 // We have information about whether `use` (import) directives are actually
5457 // used now. If an import is not used at all, we signal a lint error.
55- fn check_import ( & mut self , id : ast:: NodeId , span : Span ) {
58+ fn check_import ( & mut self , item_id : ast :: NodeId , id : ast:: NodeId , span : Span ) {
5659 if !self . used_imports . contains ( & ( id, TypeNS ) ) &&
5760 !self . used_imports . contains ( & ( id, ValueNS ) ) {
5861 if self . maybe_unused_trait_imports . contains ( & id) {
5962 // Check later.
6063 return ;
6164 }
62- let msg = if let Ok ( snippet) = self . session . codemap ( ) . span_to_snippet ( span) {
63- format ! ( "unused import: `{}`" , snippet)
64- } else {
65- "unused import" . to_string ( )
66- } ;
67- self . session . add_lint ( lint:: builtin:: UNUSED_IMPORTS , id, span, msg) ;
65+ self . unused_imports . entry ( item_id) . or_insert_with ( NodeMap ) . insert ( id, span) ;
6866 } else {
6967 // This trait import is definitely used, in a way other than
7068 // method resolution.
7169 self . maybe_unused_trait_imports . remove ( & id) ;
70+ if let Some ( i) = self . unused_imports . get_mut ( & item_id) {
71+ i. remove ( & id) ;
72+ }
7273 }
7374 }
7475}
@@ -98,16 +99,16 @@ impl<'a, 'b> Visitor for UnusedImportCheckVisitor<'a, 'b> {
9899 ast:: ItemKind :: Use ( ref p) => {
99100 match p. node {
100101 ViewPathSimple ( ..) => {
101- self . check_import ( item. id , p. span )
102+ self . check_import ( item. id , item . id , p. span )
102103 }
103104
104105 ViewPathList ( _, ref list) => {
105106 for i in list {
106- self . check_import ( i. node . id , i. span ) ;
107+ self . check_import ( item . id , i. node . id , i. span ) ;
107108 }
108109 }
109110 ViewPathGlob ( _) => {
110- self . check_import ( item. id , p. span )
111+ self . check_import ( item. id , item . id , p. span ) ;
111112 }
112113 }
113114 }
@@ -117,6 +118,35 @@ impl<'a, 'b> Visitor for UnusedImportCheckVisitor<'a, 'b> {
117118}
118119
119120pub fn check_crate ( resolver : & mut Resolver , krate : & ast:: Crate ) {
120- let mut visitor = UnusedImportCheckVisitor { resolver : resolver } ;
121+ let mut visitor = UnusedImportCheckVisitor {
122+ resolver : resolver,
123+ unused_imports : NodeMap ( ) ,
124+ } ;
121125 visit:: walk_crate ( & mut visitor, krate) ;
126+
127+ for ( id, spans) in & visitor. unused_imports {
128+ let len = spans. len ( ) ;
129+ let mut spans = spans. values ( ) . map ( |s| * s) . collect :: < Vec < Span > > ( ) ;
130+ spans. sort ( ) ;
131+ let ms = MultiSpan :: from_spans ( spans. clone ( ) ) ;
132+ let mut span_snippets = spans. iter ( )
133+ . filter_map ( |s| {
134+ match visitor. session . codemap ( ) . span_to_snippet ( * s) {
135+ Ok ( s) => Some ( format ! ( "`{}`" , s) ) ,
136+ _ => None ,
137+ }
138+ } ) . collect :: < Vec < String > > ( ) ;
139+ span_snippets. sort ( ) ;
140+ let msg = format ! ( "unused import{}{}" ,
141+ if len > 1 { "s" } else { "" } ,
142+ if span_snippets. len( ) > 0 {
143+ format!( ": {}" , span_snippets. join( ", " ) )
144+ } else {
145+ String :: new( )
146+ } ) ;
147+ visitor. session . add_lint ( lint:: builtin:: UNUSED_IMPORTS ,
148+ * id,
149+ ms,
150+ msg) ;
151+ }
122152}
0 commit comments