diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 138947e8a873b..58440a526ee20 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -1525,6 +1525,9 @@ impl LintPass for Stability {
 declare_lint!(pub UNUSED_IMPORTS, Warn,
               "imports that are never used")
 
+declare_lint!(pub UNUSED_EXTERN_CRATE, Allow,
+              "extern crates that are never used")
+
 declare_lint!(pub UNNECESSARY_QUALIFICATION, Allow,
               "detects unnecessarily qualified names")
 
@@ -1569,6 +1572,7 @@ impl LintPass for HardwiredLints {
     fn get_lints(&self) -> LintArray {
         lint_array!(
             UNUSED_IMPORTS,
+            UNUSED_EXTERN_CRATE,
             UNNECESSARY_QUALIFICATION,
             UNRECOGNIZED_LINT,
             UNUSED_VARIABLE,
diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs
index 26ed5cbfb2cc1..a464de49b4df5 100644
--- a/src/librustc/lint/context.rs
+++ b/src/librustc/lint/context.rs
@@ -201,7 +201,7 @@ impl LintStore {
 
         add_lint_group!(sess, "unused",
                         UNUSED_IMPORTS, UNUSED_VARIABLE, DEAD_ASSIGNMENT, DEAD_CODE,
-                        UNUSED_MUT, UNREACHABLE_CODE)
+                        UNUSED_MUT, UNREACHABLE_CODE, UNUSED_EXTERN_CRATE)
 
         // We have one lint pass defined in this module.
         self.register_pass(sess, false, box GatherNodeLevels as LintPassObject);
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 854b8b9ba7711..b20f230bc3c6e 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -21,7 +21,7 @@ use middle::subst::{ParamSpace, FnSpace, TypeSpace};
 use middle::ty::{ExplicitSelfCategory, StaticExplicitSelfCategory};
 use util::nodemap::{NodeMap, DefIdSet, FnvHashMap};
 
-use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate};
+use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate, CrateNum};
 use syntax::ast::{DeclItem, DefId, Expr, ExprAgain, ExprBreak, ExprField};
 use syntax::ast::{ExprFnBlock, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall};
 use syntax::ast::{ExprPath, ExprProc, ExprStruct, ExprUnboxedFn, FnDecl};
@@ -899,6 +899,7 @@ struct Resolver<'a> {
     emit_errors: bool,
 
     used_imports: HashSet<(NodeId, Namespace)>,
+    used_crates: HashSet<CrateNum>,
 }
 
 struct BuildReducedGraphVisitor<'a, 'b:'a> {
@@ -987,6 +988,7 @@ impl<'a> Resolver<'a> {
             export_map2: RefCell::new(NodeMap::new()),
             trait_map: NodeMap::new(),
             used_imports: HashSet::new(),
+            used_crates: HashSet::new(),
             external_exports: DefIdSet::new(),
             last_private: NodeMap::new(),
 
@@ -2453,7 +2455,14 @@ impl<'a> Resolver<'a> {
                                     debug!("(resolving single import) found \
                                             import in ns {:?}", namespace);
                                     let id = import_resolution.id(namespace);
+                                    // track used imports and extern crates as well
                                     this.used_imports.insert((id, namespace));
+                                    match target_module.def_id.get() {
+                                        Some(DefId{krate: kid, ..}) => {
+                                            this.used_crates.insert(kid);
+                                        },
+                                        _ => {}
+                                    }
                                     return BoundResult(target_module, bindings);
                                 }
                             }
@@ -2496,6 +2505,11 @@ impl<'a> Resolver<'a> {
                     Some(module) => {
                         debug!("(resolving single import) found external \
                                 module");
+                        // track the module as used.
+                        match module.def_id.get() {
+                            Some(DefId{krate: kid, ..}) => { self.used_crates.insert(kid); },
+                            _ => {}
+                        }
                         let name_bindings =
                             Rc::new(Resolver::create_name_bindings_from_module(
                                 module));
@@ -3030,6 +3044,14 @@ impl<'a> Resolver<'a> {
                                         (_, _) => {
                                             search_module = module_def.clone();
 
+                                            // track extern crates for unused_extern_crate lint
+                                            match module_def.def_id.get() {
+                                                Some(did) => {
+                                                    self.used_crates.insert(did.krate);
+                                                }
+                                                _ => {}
+                                            }
+
                                             // Keep track of the closest
                                             // private module used when
                                             // resolving this import chain.
@@ -3213,7 +3235,12 @@ impl<'a> Resolver<'a> {
                     Some(target) => {
                         debug!("(resolving item in lexical scope) using \
                                 import resolution");
+                        // track used imports and extern crates as well
                         self.used_imports.insert((import_resolution.id(namespace), namespace));
+                        match target.target_module.def_id.get() {
+                            Some(DefId{krate: kid, ..}) => { self.used_crates.insert(kid); },
+                            _ => {}
+                        }
                         return Success((target, false));
                     }
                 }
@@ -3492,7 +3519,12 @@ impl<'a> Resolver<'a> {
                     Some(target) => {
                         debug!("(resolving name in module) resolved to \
                                 import");
+                        // track used imports and extern crates as well
                         self.used_imports.insert((import_resolution.id(namespace), namespace));
+                        match target.target_module.def_id.get() {
+                            Some(DefId{krate: kid, ..}) => { self.used_crates.insert(kid); },
+                            _ => {}
+                        }
                         return Success((target, true));
                     }
                 }
@@ -5061,7 +5093,14 @@ impl<'a> Resolver<'a> {
                             Some(def) => {
                                 // Found it.
                                 let id = import_resolution.id(namespace);
+                                // track imports and extern crates as well
                                 self.used_imports.insert((id, namespace));
+                                match target.target_module.def_id.get() {
+                                    Some(DefId{krate: kid, ..}) => {
+                                        self.used_crates.insert(kid);
+                                    },
+                                    _ => {}
+                                }
                                 return ImportNameDefinition(def, LastMod(AllPublic));
                             }
                             None => {
@@ -5085,6 +5124,8 @@ impl<'a> Resolver<'a> {
                     match module.def_id.get() {
                         None => {} // Continue.
                         Some(def_id) => {
+                            // track used crates
+                            self.used_crates.insert(def_id.krate);
                             let lp = if module.is_public {LastMod(AllPublic)} else {
                                 LastMod(DependsOn(def_id))
                             };
@@ -5168,6 +5209,10 @@ impl<'a> Resolver<'a> {
             },
             _ => (),
         }
+        match containing_module.def_id.get() {
+            Some(DefId{krate: kid, ..}) => { self.used_crates.insert(kid); },
+            _ => {}
+        }
         return Some(def);
     }
 
@@ -5787,6 +5832,10 @@ impl<'a> Resolver<'a> {
                 if self.trait_item_map.borrow().contains_key(&(name, did)) {
                     add_trait_info(&mut found_traits, did, name);
                     self.used_imports.insert((import.type_id, TypeNS));
+                    match target.target_module.def_id.get() {
+                        Some(DefId{krate: kid, ..}) => { self.used_crates.insert(kid); },
+                        _ => {}
+                    }
                 }
             }
 
@@ -5859,10 +5908,22 @@ impl<'a> Resolver<'a> {
         if vi.span == DUMMY_SP { return }
 
         match vi.node {
-            ViewItemExternCrate(..) => {} // ignore
+            ViewItemExternCrate(_, _, id) => {
+                match self.session.cstore.find_extern_mod_stmt_cnum(id)
+                {
+                    Some(crate_num) => if !self.used_crates.contains(&crate_num) {
+                    self.session.add_lint(lint::builtin::UNUSED_EXTERN_CRATE,
+                                          id,
+                                          vi.span,
+                                          "unused extern crate".to_string());
+                    },
+                    _ => {}
+                }
+            },
             ViewItemUse(ref p) => {
                 match p.node {
                     ViewPathSimple(_, _, id) => self.finalize_import(id, p.span),
+
                     ViewPathList(_, ref list, _) => {
                         for i in list.iter() {
                             self.finalize_import(i.node.id(), i.span);
diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs
index 5db6c72975587..e48f9df75648f 100644
--- a/src/librustc_back/lib.rs
+++ b/src/librustc_back/lib.rs
@@ -36,8 +36,6 @@
 #[phase(plugin, link)]
 extern crate log;
 extern crate syntax;
-extern crate libc;
-extern crate flate;
 extern crate serialize;
 
 pub mod abi;
diff --git a/src/libsyntax/diagnostic.rs b/src/libsyntax/diagnostic.rs
index c026a1c97c139..78d3d86b29692 100644
--- a/src/libsyntax/diagnostic.rs
+++ b/src/libsyntax/diagnostic.rs
@@ -8,8 +8,6 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-extern crate libc;
-
 use codemap::{Pos, Span};
 use codemap;
 use diagnostics;
diff --git a/src/test/compile-fail/lint-unused-extern-crate.rs b/src/test/compile-fail/lint-unused-extern-crate.rs
new file mode 100644
index 0000000000000..a4dfdbdaeae01
--- /dev/null
+++ b/src/test/compile-fail/lint-unused-extern-crate.rs
@@ -0,0 +1,32 @@
+// Copyright 2014 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.
+
+#![feature(globs)]
+#![deny(unused_extern_crate)]
+#![allow(unused_variable)]
+
+extern crate libc; //~ ERROR: unused extern crate
+
+extern crate "collections" as collecs; // no error, it is used
+
+extern crate rand; // no error, the use marks it as used
+                   // even if imported objects aren't used
+
+extern crate time; // no error, the use * marks it as used
+
+#[allow(unused_imports)]
+use rand::isaac::IsaacRng;
+
+use time::*;
+
+fn main() {
+    let x: collecs::vec::Vec<uint> = Vec::new();
+    let y = now();
+}