]> git.lizzy.rs Git - rust.git/commitdiff
Rollup merge of #33044 - petrochenkov:prefix, r=eddyb
authorManish Goregaokar <manishsmail@gmail.com>
Sun, 17 Apr 2016 12:20:35 +0000 (17:50 +0530)
committerManish Goregaokar <manishsmail@gmail.com>
Sun, 17 Apr 2016 12:20:35 +0000 (17:50 +0530)
syntax: Parse import prefixes as paths

Fixes https://github.com/rust-lang/rust/issues/10415

r? @eddyb
(This partially intersects with https://github.com/rust-lang/rust/pull/33041)

src/librustc_resolve/build_reduced_graph.rs
src/libsyntax/parse/parser.rs
src/test/compile-fail/import-prefix-macro-1.rs [new file with mode: 0644]
src/test/compile-fail/import-prefix-macro-2.rs [new file with mode: 0644]
src/test/compile-fail/import-ty-params.rs [new file with mode: 0644]
src/test/compile-fail/self_type_keyword-2.rs [new file with mode: 0644]
src/test/compile-fail/self_type_keyword.rs
src/test/parse-fail/use-ends-with-mod-sep.rs
src/test/run-pass/import-prefix-macro.rs [new file with mode: 0644]

index 2bec7725b76b72c1e3c8e4003ec19c77a73750bf..15ad049cbe3d454dfc32ffb114320c3d4d8395d4 100644 (file)
@@ -28,9 +28,9 @@
 use rustc::hir::def_id::{CRATE_DEF_INDEX, DefId};
 use rustc::ty::{self, VariantKind};
 
-use syntax::ast::Name;
+use syntax::ast::{Name, NodeId};
 use syntax::attr::AttrMetaMethods;
-use syntax::parse::token::{special_idents, SELF_KEYWORD_NAME, SUPER_KEYWORD_NAME};
+use syntax::parse::token::keywords;
 use syntax::codemap::{Span, DUMMY_SP};
 
 use rustc::hir;
@@ -100,6 +100,37 @@ fn is_item(statement: &hir::Stmt) -> bool {
         block.stmts.iter().any(is_item)
     }
 
+    fn sanity_check_import(&self, view_path: &hir::ViewPath, id: NodeId) {
+        let path = match view_path.node {
+            ViewPathSimple(_, ref path) |
+            ViewPathGlob (ref path) |
+            ViewPathList(ref path, _) => path
+        };
+
+        // Check for type parameters
+        let found_param = path.segments.iter().any(|segment| {
+            !segment.parameters.types().is_empty() ||
+            !segment.parameters.lifetimes().is_empty() ||
+            !segment.parameters.bindings().is_empty()
+        });
+        if found_param {
+            self.session.span_err(path.span,
+                                  "type or lifetime parameter is found in import path");
+        }
+
+        // Checking for special identifiers in path
+        // prevent `self` or `super` at beginning of global path
+        if path.global && path.segments.len() > 0 {
+            let first = path.segments[0].identifier.name;
+            if first == keywords::Super.to_name() || first == keywords::SelfValue.to_name() {
+                self.session.add_lint(
+                    lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH, id, path.span,
+                    format!("expected identifier, found keyword `{}`", first)
+                );
+            }
+        }
+    }
+
     /// Constructs the reduced graph for one item.
     fn build_reduced_graph_for_item(&mut self, item: &Item, parent_ref: &mut Module<'b>) {
         let parent = *parent_ref;
@@ -114,10 +145,8 @@ fn build_reduced_graph_for_item(&mut self, item: &Item, parent_ref: &mut Module<
                 // 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 is_global;
                 let module_path: Vec<Name> = match view_path.node {
                     ViewPathSimple(_, ref full_path) => {
-                        is_global = full_path.global;
                         full_path.segments
                                  .split_last()
                                  .unwrap()
@@ -129,7 +158,6 @@ fn build_reduced_graph_for_item(&mut self, item: &Item, parent_ref: &mut Module<
 
                     ViewPathGlob(ref module_ident_path) |
                     ViewPathList(ref module_ident_path, _) => {
-                        is_global = module_ident_path.global;
                         module_ident_path.segments
                                          .iter()
                                          .map(|seg| seg.identifier.name)
@@ -137,22 +165,10 @@ fn build_reduced_graph_for_item(&mut self, item: &Item, parent_ref: &mut Module<
                     }
                 };
 
-                // Checking for special identifiers in path
-                // prevent `self` or `super` at beginning of global path
-                if is_global && (module_path.first() == Some(&SELF_KEYWORD_NAME) ||
-                                 module_path.first() == Some(&SUPER_KEYWORD_NAME)) {
-                    self.session.add_lint(
-                        lint::builtin::SUPER_OR_SELF_IN_GLOBAL_PATH,
-                        item.id,
-                        item.span,
-                        format!("expected identifier, found keyword `{}`",
-                                module_path.first().unwrap().as_str()));
-                }
+                self.sanity_check_import(view_path, item.id);
 
                 // Build up the import directives.
-                let is_prelude = item.attrs.iter().any(|attr| {
-                    attr.name() == special_idents::prelude_import.name.as_str()
-                });
+                let is_prelude = item.attrs.iter().any(|attr| attr.name() == "prelude_import");
 
                 match view_path.node {
                     ViewPathSimple(binding, ref full_path) => {
index d0047e743c30399cac0a0c6e3c348aa2371043d5..f3d3bbd9f99052160c6f685d6160f896b2fb820f 100644 (file)
@@ -81,6 +81,8 @@
 pub enum PathParsingMode {
     /// A path with no type parameters; e.g. `foo::bar::Baz`
     NoTypesAllowed,
+    /// Same as `NoTypesAllowed`, but may end with `::{` or `::*`, which are left unparsed
+    ImportPrefix,
     /// A path with a lifetime and type parameters, with no double colons
     /// before the type parameters; e.g. `foo::bar<'a>::Baz<T>`
     LifetimeAndTypesWithoutColons,
@@ -591,20 +593,6 @@ pub fn parse_ident_or_self_type(&mut self) -> PResult<'a, ast::Ident> {
         }
     }
 
-    pub fn parse_path_list_item(&mut self) -> PResult<'a, ast::PathListItem> {
-        let lo = self.span.lo;
-        let node = if self.eat_keyword(keywords::SelfValue) {
-            let rename = self.parse_rename()?;
-            ast::PathListItemKind::Mod { id: ast::DUMMY_NODE_ID, rename: rename }
-        } else {
-            let ident = self.parse_ident()?;
-            let rename = self.parse_rename()?;
-            ast::PathListItemKind::Ident { name: ident, rename: rename, id: ast::DUMMY_NODE_ID }
-        };
-        let hi = self.last_span.hi;
-        Ok(spanned(lo, hi, node))
-    }
-
     /// Check if the next token is `tok`, and return `true` if so.
     ///
     /// This method will automatically add `tok` to `expected_tokens` if `tok` is not
@@ -1763,8 +1751,8 @@ pub fn parse_qualified_path(&mut self, mode: PathParsingMode)
             LifetimeAndTypesWithColons => {
                 self.parse_path_segments_with_colons()?
             }
-            NoTypesAllowed => {
-                self.parse_path_segments_without_types()?
+            NoTypesAllowed | ImportPrefix => {
+                self.parse_path_segments_without_types(mode == ImportPrefix)?
             }
         };
         path.segments.extend(segments);
@@ -1801,8 +1789,8 @@ pub fn parse_path(&mut self, mode: PathParsingMode) -> PResult<'a, ast::Path> {
             LifetimeAndTypesWithColons => {
                 self.parse_path_segments_with_colons()?
             }
-            NoTypesAllowed => {
-                self.parse_path_segments_without_types()?
+            NoTypesAllowed | ImportPrefix => {
+                self.parse_path_segments_without_types(mode == ImportPrefix)?
             }
         };
 
@@ -1920,7 +1908,8 @@ pub fn parse_path_segments_with_colons(&mut self) -> PResult<'a, Vec<ast::PathSe
 
     /// Examples:
     /// - `a::b::c`
-    pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec<ast::PathSegment>> {
+    pub fn parse_path_segments_without_types(&mut self, import_prefix: bool)
+                                             -> PResult<'a, Vec<ast::PathSegment>> {
         let mut segments = Vec::new();
         loop {
             // First, parse an identifier.
@@ -1932,9 +1921,11 @@ pub fn parse_path_segments_without_types(&mut self) -> PResult<'a, Vec<ast::Path
                 parameters: ast::PathParameters::none()
             });
 
-            // If we do not see a `::`, stop.
-            if !self.eat(&token::ModSep) {
+            // If we do not see a `::` or see `::{`/`::*`, stop.
+            if !self.check(&token::ModSep) || import_prefix && self.is_import_coupler() {
                 return Ok(segments);
+            } else {
+                self.bump();
             }
         }
     }
@@ -6127,106 +6118,67 @@ pub fn parse_item(&mut self) -> PResult<'a, Option<P<Item>>> {
         self.parse_item_(attrs, true, false)
     }
 
+    fn parse_path_list_items(&mut self) -> PResult<'a, Vec<ast::PathListItem>> {
+        self.parse_unspanned_seq(&token::OpenDelim(token::Brace),
+                                 &token::CloseDelim(token::Brace),
+                                 SeqSep::trailing_allowed(token::Comma), |this| {
+            let lo = this.span.lo;
+            let node = if this.eat_keyword(keywords::SelfValue) {
+                let rename = this.parse_rename()?;
+                ast::PathListItemKind::Mod { id: ast::DUMMY_NODE_ID, rename: rename }
+            } else {
+                let ident = this.parse_ident()?;
+                let rename = this.parse_rename()?;
+                ast::PathListItemKind::Ident { name: ident, rename: rename, id: ast::DUMMY_NODE_ID }
+            };
+            let hi = this.last_span.hi;
+            Ok(spanned(lo, hi, node))
+        })
+    }
+
+    /// `::{` or `::*`
+    fn is_import_coupler(&mut self) -> bool {
+        self.check(&token::ModSep) &&
+            self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace) ||
+                                   *t == token::BinOp(token::Star))
+    }
 
-    /// Matches view_path : MOD? non_global_path as IDENT
-    /// | MOD? non_global_path MOD_SEP LBRACE RBRACE
-    /// | MOD? non_global_path MOD_SEP LBRACE ident_seq RBRACE
-    /// | MOD? non_global_path MOD_SEP STAR
-    /// | MOD? non_global_path
+    /// Matches ViewPath:
+    /// MOD_SEP? non_global_path
+    /// MOD_SEP? non_global_path as IDENT
+    /// MOD_SEP? non_global_path MOD_SEP STAR
+    /// MOD_SEP? non_global_path MOD_SEP LBRACE item_seq RBRACE
+    /// MOD_SEP? LBRACE item_seq RBRACE
     fn parse_view_path(&mut self) -> PResult<'a, P<ViewPath>> {
         let lo = self.span.lo;
-
-        // Allow a leading :: because the paths are absolute either way.
-        // This occurs with "use $crate::..." in macros.
-        let is_global = self.eat(&token::ModSep);
-
-        if self.check(&token::OpenDelim(token::Brace)) {
-            // use {foo,bar}
-            let idents = self.parse_unspanned_seq(
-                &token::OpenDelim(token::Brace),
-                &token::CloseDelim(token::Brace),
-                SeqSep::trailing_allowed(token::Comma),
-                |p| p.parse_path_list_item())?;
-            let path = ast::Path {
+        if self.check(&token::OpenDelim(token::Brace)) || self.is_import_coupler() {
+            // `{foo, bar}` or `::{foo, bar}`
+            let prefix = ast::Path {
+                global: self.eat(&token::ModSep),
+                segments: Vec::new(),
                 span: mk_sp(lo, self.span.hi),
-                global: is_global,
-                segments: Vec::new()
             };
-            return Ok(P(spanned(lo, self.span.hi, ViewPathList(path, idents))));
-        }
-
-        let first_ident = self.parse_ident()?;
-        let mut path = vec!(first_ident);
-        if let token::ModSep = self.token {
-            // foo::bar or foo::{a,b,c} or foo::*
-            while self.check(&token::ModSep) {
+            let items = self.parse_path_list_items()?;
+            Ok(P(spanned(lo, self.span.hi, ViewPathList(prefix, items))))
+        } else {
+            let prefix = self.parse_path(ImportPrefix)?;
+            if self.is_import_coupler() {
+                // `foo::bar::{a, b}` or `foo::bar::*`
                 self.bump();
-
-                match self.token {
-                  token::Ident(..) => {
-                    let ident = self.parse_ident()?;
-                    path.push(ident);
-                  }
-
-                  // foo::bar::{a,b,c}
-                  token::OpenDelim(token::Brace) => {
-                    let idents = self.parse_unspanned_seq(
-                        &token::OpenDelim(token::Brace),
-                        &token::CloseDelim(token::Brace),
-                        SeqSep::trailing_allowed(token::Comma),
-                        |p| p.parse_path_list_item()
-                    )?;
-                    let path = ast::Path {
-                        span: mk_sp(lo, self.span.hi),
-                        global: is_global,
-                        segments: path.into_iter().map(|identifier| {
-                            ast::PathSegment {
-                                identifier: identifier,
-                                parameters: ast::PathParameters::none(),
-                            }
-                        }).collect()
-                    };
-                    return Ok(P(spanned(lo, self.span.hi, ViewPathList(path, idents))));
-                  }
-
-                  // foo::bar::*
-                  token::BinOp(token::Star) => {
+                if self.check(&token::BinOp(token::Star)) {
                     self.bump();
-                    let path = ast::Path {
-                        span: mk_sp(lo, self.span.hi),
-                        global: is_global,
-                        segments: path.into_iter().map(|identifier| {
-                            ast::PathSegment {
-                                identifier: identifier,
-                                parameters: ast::PathParameters::none(),
-                            }
-                        }).collect()
-                    };
-                    return Ok(P(spanned(lo, self.span.hi, ViewPathGlob(path))));
-                  }
-
-                  // fall-through for case foo::bar::;
-                  token::Semi => {
-                    self.span_err(self.span, "expected identifier or `{` or `*`, found `;`");
-                  }
-
-                  _ => break
+                    Ok(P(spanned(lo, self.span.hi, ViewPathGlob(prefix))))
+                } else {
+                    let items = self.parse_path_list_items()?;
+                    Ok(P(spanned(lo, self.span.hi, ViewPathList(prefix, items))))
                 }
+            } else {
+                // `foo::bar` or `foo::bar as baz`
+                let rename = self.parse_rename()?.
+                                  unwrap_or(prefix.segments.last().unwrap().identifier);
+                Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename, prefix))))
             }
         }
-        let mut rename_to = path[path.len() - 1];
-        let path = ast::Path {
-            span: mk_sp(lo, self.last_span.hi),
-            global: is_global,
-            segments: path.into_iter().map(|identifier| {
-                ast::PathSegment {
-                    identifier: identifier,
-                    parameters: ast::PathParameters::none(),
-                }
-            }).collect()
-        };
-        rename_to = self.parse_rename()?.unwrap_or(rename_to);
-        Ok(P(spanned(lo, self.last_span.hi, ViewPathSimple(rename_to, path))))
     }
 
     fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
diff --git a/src/test/compile-fail/import-prefix-macro-1.rs b/src/test/compile-fail/import-prefix-macro-1.rs
new file mode 100644 (file)
index 0000000..beb15a1
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2016 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.
+
+mod a {
+    pub mod b {
+        pub mod c {
+            pub struct S;
+            pub struct Z;
+        }
+    }
+}
+
+macro_rules! import {
+    ($p: path) => (use $p {S, Z}); //~ERROR expected one of `::`, `;`, or `as`, found `{`
+}
+
+import! { a::b::c }
+
+fn main() {}
diff --git a/src/test/compile-fail/import-prefix-macro-2.rs b/src/test/compile-fail/import-prefix-macro-2.rs
new file mode 100644 (file)
index 0000000..56c6273
--- /dev/null
@@ -0,0 +1,26 @@
+// Copyright 2016 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.
+
+mod a {
+    pub mod b {
+        pub mod c {
+            pub struct S;
+            pub struct Z;
+        }
+    }
+}
+
+macro_rules! import {
+    ($p: path) => (use ::$p {S, Z}); //~ERROR  expected identifier, found `a::b::c`
+}
+
+import! { a::b::c }
+
+fn main() {}
diff --git a/src/test/compile-fail/import-ty-params.rs b/src/test/compile-fail/import-ty-params.rs
new file mode 100644 (file)
index 0000000..66d4d6d
--- /dev/null
@@ -0,0 +1,25 @@
+// Copyright 2016 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.
+
+mod a {
+    pub mod b {
+        pub mod c {
+            pub struct S<T>(T);
+        }
+    }
+}
+
+macro_rules! import {
+    ($p: path) => (use $p;);
+}
+
+import! { a::b::c::S<u8> } //~ERROR type or lifetime parameter is found in import path
+
+fn main() {}
diff --git a/src/test/compile-fail/self_type_keyword-2.rs b/src/test/compile-fail/self_type_keyword-2.rs
new file mode 100644 (file)
index 0000000..613f54e
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2015 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.
+
+use self::Self as Foo; //~ ERROR unresolved import `self::Self`
+
+pub fn main() {}
index 62966737874070b81e930905d51e87885e39fbdf..b28f48bb1056e2f6e09588c2280582f7662cb5b7 100644 (file)
@@ -39,9 +39,6 @@ pub fn main() {
     }
 }
 
-use self::Self as Foo;
-//~^ ERROR expected identifier, found keyword `Self`
-
 use std::option::Option as Self;
 //~^ ERROR expected identifier, found keyword `Self`
 
index 143886e23377f346044264e49ac5da3b05d36f58..e6a10d43e2994331be2f33b408a42193de398e9a 100644 (file)
@@ -10,4 +10,4 @@
 
 // compile-flags: -Z parse-only
 
-use std::any::; //~ ERROR expected identifier or `{` or `*`, found `;`
+use std::any::; //~ ERROR expected identifier, found `;`
diff --git a/src/test/run-pass/import-prefix-macro.rs b/src/test/run-pass/import-prefix-macro.rs
new file mode 100644 (file)
index 0000000..cfe4ff7
--- /dev/null
@@ -0,0 +1,35 @@
+// Copyright 2016 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.
+
+mod a {
+    pub mod b {
+        pub mod c {
+            pub struct S;
+            pub struct Z;
+        }
+        pub struct W;
+    }
+}
+
+macro_rules! import {
+    (1 $p: path) => (use $p;);
+    (2 $p: path) => (use $p::{Z};);
+    (3 $p: path) => (use $p::*;);
+}
+
+import! { 1 a::b::c::S }
+import! { 2 a::b::c }
+import! { 3 a::b }
+
+fn main() {
+    let s = S;
+    let z = Z;
+    let w = W;
+}