diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 40adc6bcb122f..3136aeaf8ca1f 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -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; @@ -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) => { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index b30fc38fcbcdf..46513a5740aed 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -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() @@ -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"); diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 8207057fd0aae..42c31a6e47eaa 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -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), @@ -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 { @@ -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) diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index bcbabd700946a..d72253e5a8a48 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -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)); diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 99dff4edaade7..ad9d58651207d 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -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 } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 5c7450baa2f7d..036c941499048 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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; @@ -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! ( @@ -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, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index c1819307928ba..0b03429ea2e52 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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)) @@ -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) { @@ -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)); } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index f83343bf9afa5..ff87f146c0a71 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -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, } diff --git a/src/test/compile-fail/dollar-crate-is-keyword-2.rs b/src/test/compile-fail/dollar-crate-is-keyword-2.rs index ac96279d61467..87a2903803581 100644 --- a/src/test/compile-fail/dollar-crate-is-keyword-2.rs +++ b/src/test/compile-fail/dollar-crate-is-keyword-2.rs @@ -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 } } diff --git a/src/test/compile-fail/rfc-2126-crate-paths/crate-path-non-absolute.rs b/src/test/compile-fail/rfc-2126-crate-paths/crate-path-non-absolute.rs new file mode 100644 index 0000000000000..75c2a5f5bc477 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-crate-paths/crate-path-non-absolute.rs @@ -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 or the MIT license +// , 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() {} diff --git a/src/test/compile-fail/rfc-2126-crate-paths/crate-visibility-ambiguity.rs b/src/test/compile-fail/rfc-2126-crate-paths/crate-visibility-ambiguity.rs new file mode 100644 index 0000000000000..8c5a971c2f756 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-crate-paths/crate-visibility-ambiguity.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 or the MIT license +// , 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) +} diff --git a/src/test/compile-fail/rfc-2126-crate-paths/feature-gate-crate_in_paths.rs b/src/test/compile-fail/rfc-2126-crate-paths/feature-gate-crate_in_paths.rs new file mode 100644 index 0000000000000..830ec5959b702 --- /dev/null +++ b/src/test/compile-fail/rfc-2126-crate-paths/feature-gate-crate_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 or the MIT license +// , 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 +} diff --git a/src/test/parse-fail/keyword-crate-as-identifier.rs b/src/test/compile-fail/rfc-2126-crate-paths/keyword-crate-as-identifier.rs similarity index 72% rename from src/test/parse-fail/keyword-crate-as-identifier.rs rename to src/test/compile-fail/rfc-2126-crate-paths/keyword-crate-as-identifier.rs index 8a914ca7b178c..2c94f7b0f59df 100644 --- a/src/test/parse-fail/keyword-crate-as-identifier.rs +++ b/src/test/compile-fail/rfc-2126-crate-paths/keyword-crate-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 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 } diff --git a/src/test/compile-fail/super-at-top-level.rs b/src/test/compile-fail/super-at-top-level.rs index 4db673e2006f5..c607711c44f37 100644 --- a/src/test/compile-fail/super-at-top-level.rs +++ b/src/test/compile-fail/super-at-top-level.rs @@ -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() { } diff --git a/src/test/compile-fail/use-super-global-path.rs b/src/test/compile-fail/use-super-global-path.rs index 4162e037cf32e..fc1a72f6f2b90 100644 --- a/src/test/compile-fail/use-super-global-path.rs +++ b/src/test/compile-fail/use-super-global-path.rs @@ -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 } } diff --git a/src/test/run-pass/rfc-2126-crate-paths/crate-path-absolute.rs b/src/test/run-pass/rfc-2126-crate-paths/crate-path-absolute.rs new file mode 100644 index 0000000000000..172c34e79d233 --- /dev/null +++ b/src/test/run-pass/rfc-2126-crate-paths/crate-path-absolute.rs @@ -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 or the MIT license +// , 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(); +}