about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/unstable-book/src/language-features/crate_in_paths.md54
-rw-r--r--src/doc/unstable-book/src/language-features/extern_absolute_paths.md43
-rw-r--r--src/doc/unstable-book/src/language-features/extern_in_paths.md40
-rw-r--r--src/librustc_resolve/lib.rs26
-rw-r--r--src/librustc_resolve/resolve_imports.rs18
-rw-r--r--src/libsyntax/feature_gate.rs6
-rw-r--r--src/libsyntax/parse/parser.rs15
-rw-r--r--src/libsyntax/parse/token.rs1
-rw-r--r--src/test/compile-fail/keyword-extern-as-identifier.rs (renamed from src/test/parse-fail/keyword-extern-as-identifier.rs)6
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs15
-rw-r--r--src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs23
-rw-r--r--src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs31
-rw-r--r--src/test/ui/feature-gate-extern_in_paths.rs15
-rw-r--r--src/test/ui/feature-gate-extern_in_paths.stderr10
17 files changed, 322 insertions, 26 deletions
diff --git a/src/doc/unstable-book/src/language-features/crate_in_paths.md b/src/doc/unstable-book/src/language-features/crate_in_paths.md
new file mode 100644
index 00000000000..f1656993e87
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/crate_in_paths.md
@@ -0,0 +1,54 @@
+# `crate_in_paths`
+
+The tracking issue for this feature is: [#44660]
+
+[#44660]: https://github.com/rust-lang/rust/issues/44660
+
+------------------------
+
+The `crate_in_paths` feature allows to explicitly refer to the crate root in absolute paths
+using keyword `crate`.
+
+`crate` can be used *only* in absolute paths, i.e. either in `::crate::a::b::c` form or in `use`
+items where the starting `::` is added implicitly.  
+Paths like `crate::a::b::c` are not accepted currently.
+
+This feature is required in `feature(extern_absolute_paths)` mode to refer to any absolute path
+in the local crate (absolute paths refer to extern crates by default in that mode), but can be
+used without `feature(extern_absolute_paths)` as well.
+
+```rust
+#![feature(crate_in_paths)]
+
+// Imports, `::` is added implicitly
+use crate::m::f;
+use crate as root;
+
+mod m {
+    pub fn f() -> u8 { 1 }
+    pub fn g() -> u8 { 2 }
+    pub fn h() -> u8 { 3 }
+
+    // OK, visibilities implicitly add starting `::` as well, like imports
+    pub(in crate::m) struct S;
+}
+
+mod n
+{
+    use crate::m::f;
+    use crate as root;
+    pub fn check() {
+        assert_eq!(f(), 1);
+        // `::` is required in non-import paths
+        assert_eq!(::crate::m::g(), 2);
+        assert_eq!(root::m::h(), 3);
+    }
+}
+
+fn main() {
+    assert_eq!(f(), 1);
+    assert_eq!(::crate::m::g(), 2);
+    assert_eq!(root::m::h(), 3);
+    n::check();
+}
+```
diff --git a/src/doc/unstable-book/src/language-features/extern_absolute_paths.md b/src/doc/unstable-book/src/language-features/extern_absolute_paths.md
new file mode 100644
index 00000000000..f45c5053e8d
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/extern_absolute_paths.md
@@ -0,0 +1,43 @@
+# `extern_absolute_paths`
+
+The tracking issue for this feature is: [#44660]
+
+[#44660]: https://github.com/rust-lang/rust/issues/44660
+
+------------------------
+
+The `extern_absolute_paths` feature enables mode allowing to refer to names from other crates
+"inline", without introducing `extern crate` items, using absolute paths like `::my_crate::a::b`.
+
+`::my_crate::a::b` will resolve to path `a::b` in crate `my_crate`.
+
+`feature(crate_in_paths)` can be used in `feature(extern_absolute_paths)` mode for referring
+to absolute paths in the local crate (`::crate::a::b`).
+
+`feature(extern_in_paths)` provides the same effect by using keyword `extern` to refer to
+paths from other crates (`extern::my_crate::a::b`).
+
+```rust,ignore
+#![feature(extern_absolute_paths)]
+
+// Suppose we have a dependency crate `xcrate` available through `Cargo.toml`, or `--extern`
+// options, or standard Rust distribution, or some other means.
+
+use xcrate::Z;
+
+fn f() {
+    use xcrate;
+    use xcrate as ycrate;
+    let s = xcrate::S;
+    assert_eq!(format!("{:?}", s), "S");
+    let z = ycrate::Z;
+    assert_eq!(format!("{:?}", z), "Z");
+}
+
+fn main() {
+    let s = ::xcrate::S;
+    assert_eq!(format!("{:?}", s), "S");
+    let z = Z;
+    assert_eq!(format!("{:?}", z), "Z");
+}
+```
diff --git a/src/doc/unstable-book/src/language-features/extern_in_paths.md b/src/doc/unstable-book/src/language-features/extern_in_paths.md
new file mode 100644
index 00000000000..3ae6cc29df0
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/extern_in_paths.md
@@ -0,0 +1,40 @@
+# `extern_in_paths`
+
+The tracking issue for this feature is: [#44660]
+
+[#44660]: https://github.com/rust-lang/rust/issues/44660
+
+------------------------
+
+The `extern_in_paths` feature allows to refer to names from other crates "inline", without
+introducing `extern crate` items, using keyword `extern`.
+
+For example, `extern::my_crat::a::b` will resolve to path `a::b` in crate `my_crate`.
+
+`feature(extern_absolute_paths)` mode provides the same effect by resolving absolute paths like
+`::my_crate::a::b` to paths from extern crates by default.
+
+```rust,ignore
+#![feature(extern_in_paths)]
+
+// Suppose we have a dependency crate `xcrate` available through `Cargo.toml`, or `--extern`
+// options, or standard Rust distribution, or some other means.
+
+use extern::xcrate::Z;
+
+fn f() {
+    use extern::xcrate;
+    use extern::xcrate as ycrate;
+    let s = xcrate::S;
+    assert_eq!(format!("{:?}", s), "S");
+    let z = ycrate::Z;
+    assert_eq!(format!("{:?}", z), "Z");
+}
+
+fn main() {
+    let s = extern::xcrate::S;
+    assert_eq!(format!("{:?}", s), "S");
+    let z = Z;
+    assert_eq!(format!("{:?}", z), "Z");
+}
+```
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 9f00bf97deb..34e32dc9b64 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2982,6 +2982,8 @@ impl<'a> Resolver<'a> {
                     let msg = "There are too many initial `super`s.".to_string();
                     return PathResult::Failed(ident.span, msg, false);
                 }
+            } else if i == 0 && ns == TypeNS && name == keywords::Extern.name() {
+                continue;
             }
             allow_super = false;
 
@@ -2996,16 +2998,19 @@ impl<'a> Resolver<'a> {
                     // `$crate::a::b`
                     module = Some(self.resolve_crate_root(ident.node.ctxt));
                     continue
-                } else if i == 1 && self.session.features.borrow().extern_absolute_paths &&
-                                    path[0].node.name == keywords::CrateRoot.name() &&
-                                    !token::Ident(ident.node).is_path_segment_keyword() {
-                    // `::extern_crate::a::b`
-                    let crate_id = self.crate_loader.resolve_crate_from_path(name, ident.span);
-                    let crate_root =
-                        self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
-                    self.populate_module_if_necessary(crate_root);
-                    module = Some(crate_root);
-                    continue
+                } else if i == 1 && !token::Ident(ident.node).is_path_segment_keyword() {
+                    let prev_name = path[0].node.name;
+                    if prev_name == keywords::Extern.name() ||
+                       prev_name == keywords::CrateRoot.name() &&
+                       self.session.features.borrow().extern_absolute_paths {
+                        // `::extern_crate::a::b`
+                        let crate_id = self.crate_loader.resolve_crate_from_path(name, ident.span);
+                        let crate_root =
+                            self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX });
+                        self.populate_module_if_necessary(crate_root);
+                        module = Some(crate_root);
+                        continue
+                    }
                 }
             }
 
@@ -3015,6 +3020,7 @@ impl<'a> Resolver<'a> {
                name == keywords::SelfValue.name() && i != 0 ||
                name == keywords::SelfType.name() && i != 0 ||
                name == keywords::Super.name() && i != 0 ||
+               name == keywords::Extern.name() && i != 0 ||
                name == keywords::Crate.name() && i != 1 &&
                     path[0].node.name != keywords::CrateRoot.name() {
                 let name_str = if name == keywords::CrateRoot.name() {
diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs
index e249ecfca58..98cbb660908 100644
--- a/src/librustc_resolve/resolve_imports.rs
+++ b/src/librustc_resolve/resolve_imports.rs
@@ -604,17 +604,20 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
         self.current_module = directive.parent;
         let ImportDirective { ref module_path, span, .. } = *directive;
 
-        // Extern crate mode for absolute paths needs some
-        // special support for single-segment imports.
-        let extern_absolute_paths = self.session.features.borrow().extern_absolute_paths;
-        if module_path.len() == 1 && module_path[0].node.name == keywords::CrateRoot.name() {
+        // FIXME: Last path segment is treated specially in import resolution, so extern crate
+        // mode for absolute paths needs some special support for single-segment imports.
+        if module_path.len() == 1 && (module_path[0].node.name == keywords::CrateRoot.name() ||
+                                      module_path[0].node.name == keywords::Extern.name()) {
+            let is_extern = module_path[0].node.name == keywords::Extern.name() ||
+                            self.session.features.borrow().extern_absolute_paths;
             match directive.subclass {
-                GlobImport { .. } if extern_absolute_paths => {
+                GlobImport { .. } if is_extern => {
                     return Some((directive.span,
                                  "cannot glob-import all possible crates".to_string()));
                 }
                 SingleImport { source, target, .. } => {
-                    let crate_root = if source.name == keywords::Crate.name() {
+                    let crate_root = if source.name == keywords::Crate.name() &&
+                                        module_path[0].node.name != keywords::Extern.name() {
                         if target.name == keywords::Crate.name() {
                             return Some((directive.span,
                                          "crate root imports need to be explicitly named: \
@@ -622,8 +625,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
                         } else {
                             Some(self.resolve_crate_root(source.ctxt.modern()))
                         }
-                    } else if extern_absolute_paths &&
-                              !token::Ident(source).is_path_segment_keyword() {
+                    } else if is_extern && !token::Ident(source).is_path_segment_keyword() {
                         let crate_id =
                             self.crate_loader.resolve_crate_from_path(source.name, directive.span);
                         let crate_root =
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index e5ef9393e7b..08eec0f9117 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -450,6 +450,9 @@ declare_features! (
 
     // Allows use of the :lifetime macro fragment specifier
     (active, macro_lifetime_matcher, "1.24.0", Some(46895)),
+
+    // `extern` in paths
+    (active, extern_in_paths, "1.23.0", Some(44660)),
 );
 
 declare_features! (
@@ -1790,6 +1793,9 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             if segment.identifier.name == keywords::Crate.name() {
                 gate_feature_post!(&self, crate_in_paths, segment.span,
                                    "`crate` in paths is experimental");
+            } else if segment.identifier.name == keywords::Extern.name() {
+                gate_feature_post!(&self, extern_in_paths, segment.span,
+                                   "`extern` in paths is experimental");
             }
         }
 
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index 812e3c4967a..e7c648d5e37 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -1387,7 +1387,7 @@ impl<'a> Parser<'a> {
                 None
             };
             (ident, TraitItemKind::Const(ty, default), ast::Generics::default())
-        } else if self.token.is_path_start() {
+        } else if self.token.is_path_start() && !self.is_extern_non_path() {
             // trait item macro.
             // code copied from parse_macro_use_or_failure... abstraction!
             let prev_span = self.prev_span;
@@ -4037,6 +4037,10 @@ impl<'a> Parser<'a> {
         self.token.is_keyword(keywords::Crate) && self.look_ahead(1, |t| t != &token::ModSep)
     }
 
+    fn is_extern_non_path(&self) -> bool {
+        self.token.is_keyword(keywords::Extern) && self.look_ahead(1, |t| t != &token::ModSep)
+    }
+
     fn eat_auto_trait(&mut self) -> bool {
         if self.token.is_keyword(keywords::Auto)
             && self.look_ahead(1, |t| t.is_keyword(keywords::Trait))
@@ -4152,10 +4156,12 @@ impl<'a> Parser<'a> {
         // like a path (1 token), but it fact not a path.
         // `union::b::c` - path, `union U { ... }` - not a path.
         // `crate::b::c` - path, `crate struct S;` - not a path.
+        // `extern::b::c` - path, `extern crate c;` - not a path.
         } else if self.token.is_path_start() &&
                   !self.token.is_qpath_start() &&
                   !self.is_union_item() &&
-                  !self.is_crate_vis() {
+                  !self.is_crate_vis() &&
+                  !self.is_extern_non_path() {
             let pth = self.parse_path(PathStyle::Expr)?;
 
             if !self.eat(&token::Not) {
@@ -5236,7 +5242,7 @@ impl<'a> Parser<'a> {
                          -> PResult<'a, (Ident, Vec<ast::Attribute>, ast::Generics,
                              ast::ImplItemKind)> {
         // code copied from parse_macro_use_or_failure... abstraction!
-        if self.token.is_path_start() {
+        if self.token.is_path_start() && !self.is_extern_non_path() {
             // Method macro.
 
             let prev_span = self.prev_span;
@@ -6238,7 +6244,8 @@ impl<'a> Parser<'a> {
             return Ok(Some(item));
         }
 
-        if self.eat_keyword(keywords::Extern) {
+        if self.check_keyword(keywords::Extern) && self.is_extern_non_path() {
+            self.bump(); // `extern`
             if self.eat_keyword(keywords::Crate) {
                 return Ok(Some(self.parse_item_extern_crate(lo, visibility, attrs)?));
             }
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index bd4f7f9853d..2be93c07d5a 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -359,6 +359,7 @@ impl Token {
             Some(id) => id.name == keywords::Super.name() ||
                         id.name == keywords::SelfValue.name() ||
                         id.name == keywords::SelfType.name() ||
+                        id.name == keywords::Extern.name() ||
                         id.name == keywords::Crate.name() ||
                         id.name == keywords::DollarCrate.name(),
             None => false,
diff --git a/src/test/parse-fail/keyword-extern-as-identifier.rs b/src/test/compile-fail/keyword-extern-as-identifier.rs
index 3bbe24ed56c..e5927d09b41 100644
--- a/src/test/parse-fail/keyword-extern-as-identifier.rs
+++ b/src/test/compile-fail/keyword-extern-as-identifier.rs
@@ -8,10 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-// compile-flags: -Z parse-only
-
-// This file was auto-generated using 'src/etc/generate-keyword-tests.py extern'
+#![feature(extern_in_paths)]
 
 fn main() {
-    let extern = "foo"; //~ error: expected pattern, found keyword `extern`
+    let extern = 0; //~ ERROR expected unit struct/variant or constant, found module `extern`
 }
diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs
new file mode 100644
index 00000000000..c3da4a51872
--- /dev/null
+++ b/src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 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.
+
+#[derive(Debug)]
+pub struct S;
+
+#[derive(Debug)]
+pub struct Z;
diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs
new file mode 100644
index 00000000000..7eba02ed444
--- /dev/null
+++ b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 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(extern_in_paths)]
+
+use extern::xcrate::S; //~ ERROR can't find crate for `xcrate`
+
+fn main() {}
diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs
new file mode 100644
index 00000000000..4d09a05253e
--- /dev/null
+++ b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 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(extern_in_paths)]
+
+fn main() {
+    let s = extern::xcrate::S; //~ ERROR can't find crate for `xcrate`
+}
diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs
new file mode 100644
index 00000000000..402d294b2e3
--- /dev/null
+++ b/src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 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(extern_in_paths)]
+
+use extern::ycrate; //~ ERROR can't find crate for `ycrate`
+
+fn main() {}
diff --git a/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs
new file mode 100644
index 00000000000..ebc42aa9d44
--- /dev/null
+++ b/src/test/compile-fail/rfc-2126-extern-in-paths/single-segment.rs
@@ -0,0 +1,23 @@
+// Copyright 2017 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.
+
+// aux-build:xcrate.rs
+
+#![feature(extern_in_paths)]
+
+use extern; //~ ERROR unresolved import `extern`
+            //~^ NOTE no `extern` in the root
+use extern::*; //~ ERROR unresolved import `extern::*`
+               //~^ NOTE cannot glob-import all possible crates
+
+fn main() {
+    let s = extern::xcrate; //~ ERROR expected value, found module `extern::xcrate`
+                            //~^ NOTE not a value
+}
diff --git a/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs b/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs
new file mode 100644
index 00000000000..52b52b23c87
--- /dev/null
+++ b/src/test/run-pass/rfc-2126-extern-absolute-paths/extern.rs
@@ -0,0 +1,31 @@
+// Copyright 2017 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.
+
+// aux-build:xcrate.rs
+
+#![feature(extern_in_paths)]
+
+use extern::xcrate::Z;
+
+fn f() {
+    use extern::xcrate;
+    use extern::xcrate as ycrate;
+    let s = xcrate::S;
+    assert_eq!(format!("{:?}", s), "S");
+    let z = ycrate::Z;
+    assert_eq!(format!("{:?}", z), "Z");
+}
+
+fn main() {
+    let s = extern::xcrate::S;
+    assert_eq!(format!("{:?}", s), "S");
+    let z = Z;
+    assert_eq!(format!("{:?}", z), "Z");
+}
diff --git a/src/test/ui/feature-gate-extern_in_paths.rs b/src/test/ui/feature-gate-extern_in_paths.rs
new file mode 100644
index 00000000000..3c01fcf6833
--- /dev/null
+++ b/src/test/ui/feature-gate-extern_in_paths.rs
@@ -0,0 +1,15 @@
+// Copyright 2017 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.
+
+struct S;
+
+fn main() {
+    let _ = extern::std::vec::Vec::new(); //~ ERROR `extern` in paths is experimental
+}
diff --git a/src/test/ui/feature-gate-extern_in_paths.stderr b/src/test/ui/feature-gate-extern_in_paths.stderr
new file mode 100644
index 00000000000..ac68e79e1ca
--- /dev/null
+++ b/src/test/ui/feature-gate-extern_in_paths.stderr
@@ -0,0 +1,10 @@
+error: `extern` in paths is experimental (see issue #44660)
+  --> $DIR/feature-gate-extern_in_paths.rs:14:13
+   |
+14 |     let _ = extern::std::vec::Vec::new(); //~ ERROR `extern` in paths is experimental
+   |             ^^^^^^
+   |
+   = help: add #![feature(extern_in_paths)] to the crate attributes to enable
+
+error: aborting due to previous error
+