diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 2966e9ec9d9f1..0fa41cb484fc9 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -40,7 +40,7 @@ use syntax::ext::base::{MacroKind, SyntaxExtension};
 use syntax::ext::base::Determinacy::Undetermined;
 use syntax::ext::hygiene::Mark;
 use syntax::ext::tt::macro_rules;
-use syntax::feature_gate::is_builtin_attr;
+use syntax::feature_gate::{is_builtin_attr, emit_feature_err, GateIssue};
 use syntax::parse::token::{self, Token};
 use syntax::std_inject::injected_crate_name;
 use syntax::symbol::keywords;
@@ -344,9 +344,23 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
             }
 
             ItemKind::ExternCrate(orig_name) => {
-                let crate_id = self.crate_loader.process_extern_crate(item, &self.definitions);
-                let module =
-                    self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
+                let module = if orig_name.is_none() && ident.name == keywords::SelfValue.name() {
+                    self.session
+                        .struct_span_err(item.span, "`extern crate self;` requires renaming")
+                        .span_suggestion(item.span, "try", "extern crate self as name;".into())
+                        .emit();
+                    return;
+                } else if orig_name == Some(keywords::SelfValue.name()) {
+                    if !self.session.features_untracked().extern_crate_self {
+                        emit_feature_err(&self.session.parse_sess, "extern_crate_self", item.span,
+                                         GateIssue::Language, "`extern crate self` is unstable");
+                    }
+                    self.graph_root
+                } else {
+                    let crate_id = self.crate_loader.process_extern_crate(item, &self.definitions);
+                    self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX })
+                };
+
                 self.populate_module_if_necessary(module);
                 if injected_crate_name().map_or(false, |name| ident.name == name) {
                     self.injected_crate = Some(module);
@@ -768,6 +782,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
                     span_err!(self.session, item.span, E0468,
                         "an `extern crate` loading macros must be at the crate root");
                 }
+                if let ItemKind::ExternCrate(Some(orig_name)) = item.node {
+                    if orig_name == keywords::SelfValue.name() {
+                        self.session.span_err(attr.span,
+                            "`macro_use` is not supported on `extern crate self`");
+                    }
+                }
                 let ill_formed = |span| span_err!(self.session, span, E0466, "bad macro import");
                 match attr.meta() {
                     Some(meta) => match meta.node {
diff --git a/src/librustc_typeck/check_unused.rs b/src/librustc_typeck/check_unused.rs
index 103331894ff1e..9c1860fb82ee4 100644
--- a/src/librustc_typeck/check_unused.rs
+++ b/src/librustc_typeck/check_unused.rs
@@ -113,11 +113,12 @@ fn unused_crates_lint<'tcx>(tcx: TyCtxt<'_, 'tcx, 'tcx>) {
             true
         })
         .filter(|&&(def_id, _)| {
-            let cnum = tcx.extern_mod_stmt_cnum(def_id).unwrap();
-            !tcx.is_compiler_builtins(cnum)
-                && !tcx.is_panic_runtime(cnum)
-                && !tcx.has_global_allocator(cnum)
-                && !tcx.has_panic_handler(cnum)
+            tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| {
+                !tcx.is_compiler_builtins(cnum) &&
+                !tcx.is_panic_runtime(cnum) &&
+                !tcx.has_global_allocator(cnum) &&
+                !tcx.has_panic_handler(cnum)
+            })
         })
         .cloned()
         .collect();
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 2402de5a816b7..d0c4d1c7dce0a 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -492,6 +492,9 @@ declare_features! (
 
     // `reason = ` in lint attributes and `expect` lint attribute
     (active, lint_reasons, "1.31.0", Some(54503), None),
+
+    // `extern crate self as foo;` puts local crate root into extern prelude under name `foo`.
+    (active, extern_crate_self, "1.31.0", Some(54658), None),
 );
 
 declare_features! (
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index f13bb7df0b444..33715f206dedf 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -6783,7 +6783,11 @@ impl<'a> Parser<'a> {
         let error_msg = "crate name using dashes are not valid in `extern crate` statements";
         let suggestion_msg = "if the original crate name uses dashes you need to use underscores \
                               in the code";
-        let mut ident = self.parse_ident()?;
+        let mut ident = if self.token.is_keyword(keywords::SelfValue) {
+            self.parse_path_segment_ident()
+        } else {
+            self.parse_ident()
+        }?;
         let mut idents = vec![];
         let mut replacement = vec![];
         let mut fixed_crate_name = false;
diff --git a/src/test/ui/feature-gates/feature-gate-extern_crate_self.rs b/src/test/ui/feature-gates/feature-gate-extern_crate_self.rs
new file mode 100644
index 0000000000000..2161932c2f6aa
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-extern_crate_self.rs
@@ -0,0 +1,3 @@
+extern crate self as foo; //~ ERROR `extern crate self` is unstable
+
+fn main() {}
diff --git a/src/test/ui/feature-gates/feature-gate-extern_crate_self.stderr b/src/test/ui/feature-gates/feature-gate-extern_crate_self.stderr
new file mode 100644
index 0000000000000..61cc68477830d
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-extern_crate_self.stderr
@@ -0,0 +1,11 @@
+error[E0658]: `extern crate self` is unstable (see issue #54658)
+  --> $DIR/feature-gate-extern_crate_self.rs:1:1
+   |
+LL | extern crate self as foo; //~ ERROR `extern crate self` is unstable
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: add #![feature(extern_crate_self)] to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/imports/extern-crate-self-fail.rs b/src/test/ui/imports/extern-crate-self-fail.rs
new file mode 100644
index 0000000000000..eab7b7032aa07
--- /dev/null
+++ b/src/test/ui/imports/extern-crate-self-fail.rs
@@ -0,0 +1,8 @@
+#![feature(extern_crate_self)]
+
+extern crate self; //~ ERROR `extern crate self;` requires renaming
+
+#[macro_use] //~ ERROR `macro_use` is not supported on `extern crate self`
+extern crate self as foo;
+
+fn main() {}
diff --git a/src/test/ui/imports/extern-crate-self-fail.stderr b/src/test/ui/imports/extern-crate-self-fail.stderr
new file mode 100644
index 0000000000000..0ca0d89eaf08e
--- /dev/null
+++ b/src/test/ui/imports/extern-crate-self-fail.stderr
@@ -0,0 +1,14 @@
+error: `extern crate self;` requires renaming
+  --> $DIR/extern-crate-self-fail.rs:3:1
+   |
+LL | extern crate self; //~ ERROR `extern crate self;` requires renaming
+   | ^^^^^^^^^^^^^^^^^^ help: try: `extern crate self as name;`
+
+error: `macro_use` is not supported on `extern crate self`
+  --> $DIR/extern-crate-self-fail.rs:5:1
+   |
+LL | #[macro_use] //~ ERROR `macro_use` is not supported on `extern crate self`
+   | ^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/imports/extern-crate-self-pass.rs b/src/test/ui/imports/extern-crate-self-pass.rs
new file mode 100644
index 0000000000000..bf255bb6b8194
--- /dev/null
+++ b/src/test/ui/imports/extern-crate-self-pass.rs
@@ -0,0 +1,15 @@
+// compile-pass
+
+#![feature(extern_crate_self)]
+
+extern crate self as foo;
+
+struct S;
+
+mod m {
+    fn check() {
+        foo::S; // OK
+    }
+}
+
+fn main() {}