Skip to content

Commit

Permalink
Auto merge of #45771 - petrochenkov:crate, r=nikomatsakis
Browse files Browse the repository at this point in the history
Support `::crate` in paths

cc #45477
Fixes #45229
  • Loading branch information
bors committed Nov 21, 2017
2 parents ebda766 + 90f5cfd commit f28df20
Show file tree
Hide file tree
Showing 16 changed files with 187 additions and 49 deletions.
13 changes: 0 additions & 13 deletions src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use rustc::session::Session;
use syntax::ast::*;
use syntax::attr;
use syntax::codemap::Spanned;
use syntax::parse::token;
use syntax::symbol::keywords;
use syntax::visit::{self, Visitor};
use syntax_pos::Span;
Expand Down Expand Up @@ -182,18 +181,6 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
visit::walk_ty(self, ty)
}

fn visit_path(&mut self, path: &'a Path, _: NodeId) {
if path.segments.len() >= 2 && path.is_global() {
let ident = path.segments[1].identifier;
if token::Ident(ident).is_path_segment_keyword() {
self.err_handler()
.span_err(path.span, &format!("global paths cannot start with `{}`", ident));
}
}

visit::walk_path(self, path)
}

fn visit_item(&mut self, item: &'a Item) {
match item.node {
ItemKind::Use(ref view_path) => {
Expand Down
10 changes: 1 addition & 9 deletions src/librustc_resolve/build_reduced_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ impl<'a> Resolver<'a> {
// Extract and intern the module part of the path. For
// globs and lists, the path is found directly in the AST;
// for simple paths we have to munge the path a little.
let mut module_path: Vec<_> = match view_path.node {
let module_path: Vec<_> = match view_path.node {
ViewPathSimple(_, ref full_path) => {
full_path.segments
.split_last()
Expand All @@ -134,14 +134,6 @@ impl<'a> Resolver<'a> {
}
};

// This can be removed once warning cycle #36888 is complete.
if module_path.len() >= 2 &&
module_path[0].node.name == keywords::CrateRoot.name() &&
token::Ident(module_path[1].node).is_path_segment_keyword()
{
module_path.remove(0);
}

// Build up the import directives.
let is_prelude = attr::contains_name(&item.attrs, "prelude_import");

Expand Down
48 changes: 39 additions & 9 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2865,12 +2865,13 @@ impl<'a> Resolver<'a> {
debug!("resolve_path ident {} {:?}", i, ident);
let is_last = i == path.len() - 1;
let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS };
let name = ident.node.name;

if i == 0 && ns == TypeNS && ident.node.name == keywords::SelfValue.name() {
if i == 0 && ns == TypeNS && name == keywords::SelfValue.name() {
let mut ctxt = ident.node.ctxt.modern();
module = Some(self.resolve_self(&mut ctxt, self.current_module));
continue
} else if allow_super && ns == TypeNS && ident.node.name == keywords::Super.name() {
} else if allow_super && ns == TypeNS && name == keywords::Super.name() {
let mut ctxt = ident.node.ctxt.modern();
let self_module = match i {
0 => self.resolve_self(&mut ctxt, self.current_module),
Expand All @@ -2886,12 +2887,41 @@ impl<'a> Resolver<'a> {
}
allow_super = false;

if i == 0 && ns == TypeNS && ident.node.name == keywords::CrateRoot.name() {
module = Some(self.resolve_crate_root(ident.node.ctxt.modern()));
continue
} else if i == 0 && ns == TypeNS && ident.node.name == keywords::DollarCrate.name() {
module = Some(self.resolve_crate_root(ident.node.ctxt));
continue
if ns == TypeNS {
if (i == 0 && name == keywords::CrateRoot.name()) ||
(i == 1 && name == keywords::Crate.name() &&
path[0].node.name == keywords::CrateRoot.name()) {
// `::a::b` or `::crate::a::b`
module = Some(self.resolve_crate_root(ident.node.ctxt.modern()));
continue
} else if i == 0 && name == keywords::DollarCrate.name() {
// `$crate::a::b`
module = Some(self.resolve_crate_root(ident.node.ctxt));
continue
}
}

// Report special messages for path segment keywords in wrong positions.
if name == keywords::CrateRoot.name() && i != 0 ||
name == keywords::DollarCrate.name() && i != 0 ||
name == keywords::SelfValue.name() && i != 0 ||
name == keywords::SelfType.name() && i != 0 ||
name == keywords::Super.name() && i != 0 ||
name == keywords::Crate.name() && i != 1 &&
path[0].node.name != keywords::CrateRoot.name() {
let name_str = if name == keywords::CrateRoot.name() {
format!("crate root")
} else {
format!("`{}`", name)
};
let msg = if i == 1 && path[0].node.name == keywords::CrateRoot.name() {
format!("global paths cannot start with {}", name_str)
} else if i == 0 && name == keywords::Crate.name() {
format!("{} can only be used in absolute paths", name_str)
} else {
format!("{} in paths can only be used in start position", name_str)
};
return PathResult::Failed(ident.span, msg, false);
}

let binding = if let Some(module) = module {
Expand Down Expand Up @@ -2942,7 +2972,7 @@ impl<'a> Resolver<'a> {
let msg = if module.and_then(ModuleData::def) == self.graph_root.def() {
let is_mod = |def| match def { Def::Mod(..) => true, _ => false };
let mut candidates =
self.lookup_import_candidates(ident.node.name, TypeNS, is_mod);
self.lookup_import_candidates(name, TypeNS, is_mod);
candidates.sort_by_key(|c| (c.path.segments.len(), c.path.to_string()));
if let Some(candidate) = candidates.get(0) {
format!("Did you mean `{}`?", candidate.path)
Expand Down
10 changes: 8 additions & 2 deletions src/librustc_resolve/resolve_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,10 +606,16 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> {
let module_result = self.resolve_path(&module_path, None, true, span);
let module = match module_result {
PathResult::Module(module) => module,
PathResult::Failed(span, msg, _) => {
PathResult::Failed(span, msg, false) => {
resolve_error(self, span, ResolutionError::FailedToResolve(&msg));
return None;
}
PathResult::Failed(span, msg, true) => {
let (mut self_path, mut self_result) = (module_path.clone(), None);
if !self_path.is_empty() &&
!token::Ident(self_path[0].node).is_path_segment_keyword()
!token::Ident(self_path[0].node).is_path_segment_keyword() &&
!(self_path.len() > 1 &&
token::Ident(self_path[1].node).is_path_segment_keyword())
{
self_path[0].node.name = keywords::SelfValue.name();
self_result = Some(self.resolve_path(&self_path, None, false, span));
Expand Down
11 changes: 8 additions & 3 deletions src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,15 @@ impl Path {
}
}

// Add starting "crate root" segment to all paths except those that
// already have it or start with `self`, `super`, `Self` or `$crate`.
pub fn default_to_global(mut self) -> Path {
if !self.is_global() &&
!::parse::token::Ident(self.segments[0].identifier).is_path_segment_keyword() {
self.segments.insert(0, PathSegment::crate_root(self.span));
if !self.is_global() {
let ident = self.segments[0].identifier;
if !::parse::token::Ident(ident).is_path_segment_keyword() ||
ident.name == keywords::Crate.name() {
self.segments.insert(0, PathSegment::crate_root(self.span));
}
}
self
}
Expand Down
16 changes: 15 additions & 1 deletion src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use syntax_pos::Span;
use errors::{DiagnosticBuilder, Handler, FatalError};
use visit::{self, FnKind, Visitor};
use parse::ParseSess;
use symbol::Symbol;
use symbol::{keywords, Symbol};

use std::env;

Expand Down Expand Up @@ -420,6 +420,9 @@ declare_features! (

// #![wasm_import_memory] attribute
(active, wasm_import_memory, "1.22.0", None),

// `crate` in paths
(active, crate_in_paths, "1.23.0", Some(45477)),
);

declare_features! (
Expand Down Expand Up @@ -1634,6 +1637,17 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
visit::walk_impl_item(self, ii);
}

fn visit_path(&mut self, path: &'a ast::Path, _id: NodeId) {
for segment in &path.segments {
if segment.identifier.name == keywords::Crate.name() {
gate_feature_post!(&self, crate_in_paths, segment.span,
"`crate` in paths is experimental");
}
}

visit::walk_path(self, path);
}

fn visit_vis(&mut self, vis: &'a ast::Visibility) {
if let ast::Visibility::Crate(span, ast::CrateSugar::JustCrate) = *vis {
gate_feature_post!(&self, crate_visibility_modifier, span,
Expand Down
17 changes: 14 additions & 3 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3916,6 +3916,10 @@ impl<'a> Parser<'a> {
self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident())
}

fn is_crate_vis(&self) -> bool {
self.token.is_keyword(keywords::Crate) && 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 @@ -4026,10 +4030,15 @@ impl<'a> Parser<'a> {
node: StmtKind::Item(macro_def),
span: lo.to(self.prev_span),
}
// Starts like a simple path, but not a union item.
// Starts like a simple path, but not a union item or item with `crate` visibility.
// Our goal here is to parse an arbitrary path `a::b::c` but not something that starts
// 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.
} else if self.token.is_path_start() &&
!self.token.is_qpath_start() &&
!self.is_union_item() {
!self.is_union_item() &&
!self.is_crate_vis() {
let pth = self.parse_path(PathStyle::Expr)?;

if !self.eat(&token::Not) {
Expand Down Expand Up @@ -5399,7 +5408,9 @@ impl<'a> Parser<'a> {
pub fn parse_visibility(&mut self, can_take_tuple: bool) -> PResult<'a, Visibility> {
maybe_whole!(self, NtVis, |x| x);

if self.eat_keyword(keywords::Crate) {
self.expected_tokens.push(TokenType::Keyword(keywords::Crate));
if self.is_crate_vis() {
self.bump(); // `crate`
return Ok(Visibility::Crate(self.prev_span, CrateSugar::JustCrate));
}

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 @@ -347,6 +347,7 @@ impl Token {
Some(id) => id.name == keywords::Super.name() ||
id.name == keywords::SelfValue.name() ||
id.name == keywords::SelfType.name() ||
id.name == keywords::Crate.name() ||
id.name == keywords::DollarCrate.name(),
None => false,
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/compile-fail/dollar-crate-is-keyword-2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ mod a {}
macro_rules! m {
() => {
use a::$crate; //~ ERROR unresolved import `a::$crate`
use a::$crate::b; //~ ERROR unresolved import `a::$crate`
type A = a::$crate; //~ ERROR cannot find type `$crate` in module `a`
use a::$crate::b; //~ ERROR `$crate` in paths can only be used in start position
type A = a::$crate; //~ ERROR `$crate` in paths can only be used in start position
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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(crate_in_paths)]

struct S;

mod m {
fn f() {
let s = crate::S; //~ ERROR `crate` can only be used in absolute paths
}
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -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.

#![feature(crate_in_paths)]
#![feature(crate_visibility_modifier)]

mod m {
pub struct Z;
pub struct S1(crate (::m::Z)); // OK
pub struct S2(::crate ::m::Z); // OK
pub struct S3(crate ::m::Z); //~ ERROR `crate` can only be used in absolute paths
}

fn main() {
crate struct S; // OK (item in statement position)
}
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.

struct S;

fn main() {
let _ = ::crate::S; //~ ERROR `crate` in paths is experimental
}
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 crate'
#![feature(crate_in_paths)]

fn main() {
let crate = "foo"; //~ error: expected pattern, found keyword `crate`
let crate = 0; //~ ERROR `crate` can only be used in absolute paths
}
3 changes: 1 addition & 2 deletions src/test/compile-fail/super-at-top-level.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use super::f; //~ ERROR unresolved import `super` [E0432]
//~^ There are too many initial `super`s.
use super::f; //~ ERROR There are too many initial `super`s

fn main() {
}
2 changes: 1 addition & 1 deletion src/test/compile-fail/use-super-global-path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mod foo {

pub fn g() {
use ::super::main; //~ ERROR global paths cannot start with `super`
main();
main(); //~ ERROR cannot find function `main` in this scope
}
}

Expand Down
36 changes: 36 additions & 0 deletions src/test/run-pass/rfc-2126-crate-paths/crate-path-absolute.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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(crate_in_paths)]

use crate::m::f;

mod m {
pub fn f() -> u8 { 1 }
pub fn g() -> u8 { 2 }

// OK, visibilities are implicitly absolute like imports
pub(in crate::m) struct S;
}

mod n
{
use crate::m::f;
pub fn check() {
assert_eq!(f(), 1);
assert_eq!(::crate::m::g(), 2);
}
}

fn main() {
assert_eq!(f(), 1);
assert_eq!(::crate::m::g(), 2);
n::check();
}

0 comments on commit f28df20

Please sign in to comment.