Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support extern in paths #47156

Merged
merged 2 commits into from
Jan 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/doc/unstable-book/src/language-features/crate_in_paths.md
Original file line number Diff line number Diff line change
@@ -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();
}
```
Original file line number Diff line number Diff line change
@@ -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");
}
```
40 changes: 40 additions & 0 deletions src/doc/unstable-book/src/language-features/extern_in_paths.md
Original file line number Diff line number Diff line change
@@ -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");
}
```
26 changes: 16 additions & 10 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
}
}
}

Expand All @@ -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() {
Expand Down
18 changes: 10 additions & 8 deletions src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -604,26 +604,28 @@ 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: \
`use crate as name;`".to_string()));
} 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 =
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! (
Expand Down Expand Up @@ -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");
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)?));
}
Expand Down
1 change: 1 addition & 0 deletions src/libsyntax/parse/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
}
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/auxiliary/xcrate.rs
Original file line number Diff line number Diff line change
@@ -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;
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-1.rs
Original file line number Diff line number Diff line change
@@ -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() {}
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-2.rs
Original file line number Diff line number Diff line change
@@ -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`
}
15 changes: 15 additions & 0 deletions src/test/compile-fail/rfc-2126-extern-in-paths/non-existent-3.rs
Original file line number Diff line number Diff line change
@@ -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() {}
Loading