about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2018-05-22 19:01:09 -0400
committerNiko Matsakis <niko@alum.mit.edu>2018-05-23 05:51:03 -0400
commit42d0b363c172e81808f0138fcc2d46b9222b90ef (patch)
tree51d4bea09ec318e80c5e1872ceb75997ee6ece0d
parentbe2bb8084c77eca43e89e9c844c0596e7db4d863 (diff)
downloadrust-42d0b363c172e81808f0138fcc2d46b9222b90ef.tar.gz
rust-42d0b363c172e81808f0138fcc2d46b9222b90ef.zip
handle fully qualified paths properly when linting
fixes #50970
-rw-r--r--src/librustc_resolve/lib.rs56
-rw-r--r--src/test/ui/rust-2018/edition-lint-fully-qualified-paths.fixed37
-rw-r--r--src/test/ui/rust-2018/edition-lint-fully-qualified-paths.rs37
-rw-r--r--src/test/ui/rust-2018/edition-lint-fully-qualified-paths.stderr25
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed40
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs40
-rw-r--r--src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr34
7 files changed, 261 insertions, 8 deletions
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index acd3e159524..e0f02e93e4e 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -2222,7 +2222,7 @@ impl<'a> Resolver<'a> {
                     segments: use_tree.prefix.make_root().into_iter().collect(),
                     span: use_tree.span,
                 };
-                self.resolve_use_tree(item.id, use_tree, &path);
+                self.resolve_use_tree(item.id, use_tree.span, item.id, use_tree, &path);
             }
 
             ItemKind::ExternCrate(_) | ItemKind::MacroDef(..) | ItemKind::GlobalAsm(_) => {
@@ -2233,7 +2233,18 @@ impl<'a> Resolver<'a> {
         }
     }
 
-    fn resolve_use_tree(&mut self, id: NodeId, use_tree: &ast::UseTree, prefix: &Path) {
+    /// For the most part, use trees are desugared into `ImportDirective` instances
+    /// when building the reduced graph (see `build_reduced_graph_for_use_tree`). But
+    /// there is one special case we handle here: an empty nested import like
+    /// `a::{b::{}}`, which desugares into...no import directives.
+    fn resolve_use_tree(
+        &mut self,
+        root_id: NodeId,
+        root_span: Span,
+        id: NodeId,
+        use_tree: &ast::UseTree,
+        prefix: &Path,
+    ) {
         match use_tree.kind {
             ast::UseTreeKind::Nested(ref items) => {
                 let path = Path {
@@ -2252,11 +2263,11 @@ impl<'a> Resolver<'a> {
                         None,
                         &path,
                         PathSource::ImportPrefix,
-                        CrateLint::SimplePath(id), // TODO seems wrong
+                        CrateLint::UsePath { root_id, root_span },
                     );
                 } else {
                     for &(ref tree, nested_id) in items {
-                        self.resolve_use_tree(nested_id, tree, &path);
+                        self.resolve_use_tree(root_id, root_span, nested_id, tree, &path);
                     }
                 }
             }
@@ -3188,12 +3199,28 @@ impl<'a> Resolver<'a> {
 
         if let Some(qself) = qself {
             if qself.position == 0 {
-                // FIXME: Create some fake resolution that can't possibly be a type.
+                // This is a case like `<T>::B`, where there is no
+                // trait to resolve.  In that case, we leave the `B`
+                // segment to be resolved by type-check.
                 return Some(PathResolution::with_unresolved_segments(
                     Def::Mod(DefId::local(CRATE_DEF_INDEX)), path.len()
                 ));
             }
-            // Make sure `A::B` in `<T as A>::B::C` is a trait item.
+
+            // Make sure `A::B` in `<T as A::B>::C` is a trait item.
+            //
+            // Currently, `path` names the full item (`A::B::C`, in
+            // our example).  so we extract the prefix of that that is
+            // the trait (the slice upto and including
+            // `qself.position`). And then we recursively resolve that,
+            // but with `qself` set to `None`.
+            //
+            // However, setting `qself` to none (but not changing the
+            // span) loses the information about where this path
+            // *actually* appears, so for the purposes of the crate
+            // lint we pass along information that this is the trait
+            // name from a fully qualified path, and this also
+            // contains the full span (the `CrateLint::QPathTrait`).
             let ns = if qself.position + 1 == path.len() { ns } else { TypeNS };
             let res = self.smart_resolve_path_fragment(
                 id,
@@ -3201,8 +3228,15 @@ impl<'a> Resolver<'a> {
                 &path[..qself.position + 1],
                 span,
                 PathSource::TraitItem(ns),
-                crate_lint, // TODO wrong
+                CrateLint::QPathTrait {
+                    qpath_id: id,
+                    qpath_span: qself.path_span,
+                },
             );
+
+            // The remaining segments (the `C` in our example) will
+            // have to be resolved by type-check, since that requires doing
+            // trait resolution.
             return Some(PathResolution::with_unresolved_segments(
                 res.base_def(), res.unresolved_segments() + path.len() - qself.position - 1
             ));
@@ -3213,7 +3247,7 @@ impl<'a> Resolver<'a> {
             Some(ns),
             true,
             span,
-            CrateLint::SimplePath(id),
+            crate_lint,
         ) {
             PathResult::NonModule(path_res) => path_res,
             PathResult::Module(module) if !module.is_normal() => {
@@ -3468,6 +3502,7 @@ impl<'a> Resolver<'a> {
             CrateLint::No => return,
             CrateLint::SimplePath(id) => (id, path_span),
             CrateLint::UsePath { root_id, root_span } => (root_id, root_span),
+            CrateLint::QPathTrait { qpath_id, qpath_span } => (qpath_id, qpath_span),
         };
 
         let first_name = match path.get(0) {
@@ -4536,6 +4571,11 @@ enum CrateLint {
     /// have nested things like `use a::{b, c}`, we care about the
     /// `use a` part.
     UsePath { root_id: NodeId, root_span: Span },
+
+    /// This is the "trait item" from a fully qualified path. For example,
+    /// we might be resolving  `X::Y::Z` from a path like `<T as X::Y>::Z`.
+    /// The `path_span` is the span of the to the trait itself (`X::Y`).
+    QPathTrait { qpath_id: NodeId, qpath_span: Span },
 }
 
 __build_diagnostic_array! { librustc_resolve, DIAGNOSTICS }
diff --git a/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.fixed b/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.fixed
new file mode 100644
index 00000000000..717abba6390
--- /dev/null
+++ b/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.fixed
@@ -0,0 +1,37 @@
+// Copyright 2018 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.
+
+// run-rustfix
+
+#![feature(rust_2018_preview)]
+#![deny(absolute_path_not_starting_with_crate)]
+
+mod foo {
+    crate trait Foo {
+        type Bar;
+    }
+
+    crate struct Baz { }
+
+    impl Foo for Baz {
+        type Bar = ();
+    }
+}
+
+
+fn main() {
+    let _: <foo::Baz as crate::foo::Foo>::Bar = ();
+    //~^ ERROR absolute paths must start with
+    //~| this was previously accepted
+
+    let _: <crate::foo::Baz as foo::Foo>::Bar = ();
+    //~^ ERROR absolute paths must start with
+    //~| this was previously accepted
+}
diff --git a/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.rs b/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.rs
new file mode 100644
index 00000000000..eaa09b1f751
--- /dev/null
+++ b/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.rs
@@ -0,0 +1,37 @@
+// Copyright 2018 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.
+
+// run-rustfix
+
+#![feature(rust_2018_preview)]
+#![deny(absolute_path_not_starting_with_crate)]
+
+mod foo {
+    crate trait Foo {
+        type Bar;
+    }
+
+    crate struct Baz { }
+
+    impl Foo for Baz {
+        type Bar = ();
+    }
+}
+
+
+fn main() {
+    let _: <foo::Baz as ::foo::Foo>::Bar = ();
+    //~^ ERROR absolute paths must start with
+    //~| this was previously accepted
+
+    let _: <::foo::Baz as foo::Foo>::Bar = ();
+    //~^ ERROR absolute paths must start with
+    //~| this was previously accepted
+}
diff --git a/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.stderr b/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.stderr
new file mode 100644
index 00000000000..aea920342fc
--- /dev/null
+++ b/src/test/ui/rust-2018/edition-lint-fully-qualified-paths.stderr
@@ -0,0 +1,25 @@
+error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
+  --> $DIR/edition-lint-fully-qualified-paths.rs:30:25
+   |
+LL |     let _: <foo::Baz as ::foo::Foo>::Bar = ();
+   |                         ^^^^^^^^^^ help: use `crate`: `crate::foo::Foo`
+   |
+note: lint level defined here
+  --> $DIR/edition-lint-fully-qualified-paths.rs:14:9
+   |
+LL | #![deny(absolute_path_not_starting_with_crate)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
+   = note: for more information, see issue TBD
+
+error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
+  --> $DIR/edition-lint-fully-qualified-paths.rs:34:13
+   |
+LL |     let _: <::foo::Baz as foo::Foo>::Bar = ();
+   |             ^^^^^^^^^^ help: use `crate`: `crate::foo::Baz`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
+   = note: for more information, see issue TBD
+
+error: aborting due to 2 previous errors
+
diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed
new file mode 100644
index 00000000000..1fc76fb657f
--- /dev/null
+++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed
@@ -0,0 +1,40 @@
+// Copyright 2018 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.
+
+// run-rustfix
+
+#![feature(rust_2018_preview)]
+#![deny(absolute_path_not_starting_with_crate)]
+#![allow(unused_imports)]
+#![allow(dead_code)]
+
+crate mod foo {
+    crate mod bar {
+        crate mod baz { }
+        crate mod baz1 { }
+
+        crate struct XX;
+    }
+}
+
+use crate::foo::{bar::{baz::{}}};
+//~^ ERROR absolute paths must start with
+//~| WARN this was previously accepted
+
+use crate::foo::{bar::{XX, baz::{}}};
+//~^ ERROR absolute paths must start with
+//~| WARN this was previously accepted
+
+use crate::foo::{bar::{baz::{}, baz1::{}}};
+//~^ ERROR absolute paths must start with
+//~| WARN this was previously accepted
+
+fn main() {
+}
diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs
new file mode 100644
index 00000000000..8327c62d779
--- /dev/null
+++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs
@@ -0,0 +1,40 @@
+// Copyright 2018 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.
+
+// run-rustfix
+
+#![feature(rust_2018_preview)]
+#![deny(absolute_path_not_starting_with_crate)]
+#![allow(unused_imports)]
+#![allow(dead_code)]
+
+crate mod foo {
+    crate mod bar {
+        crate mod baz { }
+        crate mod baz1 { }
+
+        crate struct XX;
+    }
+}
+
+use foo::{bar::{baz::{}}};
+//~^ ERROR absolute paths must start with
+//~| WARN this was previously accepted
+
+use foo::{bar::{XX, baz::{}}};
+//~^ ERROR absolute paths must start with
+//~| WARN this was previously accepted
+
+use foo::{bar::{baz::{}, baz1::{}}};
+//~^ ERROR absolute paths must start with
+//~| WARN this was previously accepted
+
+fn main() {
+}
diff --git a/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr
new file mode 100644
index 00000000000..82c8ef30ac3
--- /dev/null
+++ b/src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr
@@ -0,0 +1,34 @@
+error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
+  --> $DIR/edition-lint-nested-empty-paths.rs:27:5
+   |
+LL | use foo::{bar::{baz::{}}};
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}}}`
+   |
+note: lint level defined here
+  --> $DIR/edition-lint-nested-empty-paths.rs:14:9
+   |
+LL | #![deny(absolute_path_not_starting_with_crate)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
+   = note: for more information, see issue TBD
+
+error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
+  --> $DIR/edition-lint-nested-empty-paths.rs:31:5
+   |
+LL | use foo::{bar::{XX, baz::{}}};
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{XX, baz::{}}}`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
+   = note: for more information, see issue TBD
+
+error: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
+  --> $DIR/edition-lint-nested-empty-paths.rs:35:5
+   |
+LL | use foo::{bar::{baz::{}, baz1::{}}};
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `crate`: `crate::foo::{bar::{baz::{}, baz1::{}}}`
+   |
+   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
+   = note: for more information, see issue TBD
+
+error: aborting due to 3 previous errors
+