From 42d0b363c172e81808f0138fcc2d46b9222b90ef Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 22 May 2018 19:01:09 -0400 Subject: [PATCH] handle fully qualified paths properly when linting fixes #50970 --- src/librustc_resolve/lib.rs | 56 ++++++++++++++++--- .../edition-lint-fully-qualified-paths.fixed | 37 ++++++++++++ .../edition-lint-fully-qualified-paths.rs | 37 ++++++++++++ .../edition-lint-fully-qualified-paths.stderr | 25 +++++++++ .../edition-lint-nested-empty-paths.fixed | 40 +++++++++++++ .../edition-lint-nested-empty-paths.rs | 40 +++++++++++++ .../edition-lint-nested-empty-paths.stderr | 34 +++++++++++ 7 files changed, 261 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/rust-2018/edition-lint-fully-qualified-paths.fixed create mode 100644 src/test/ui/rust-2018/edition-lint-fully-qualified-paths.rs create mode 100644 src/test/ui/rust-2018/edition-lint-fully-qualified-paths.stderr create mode 100644 src/test/ui/rust-2018/edition-lint-nested-empty-paths.fixed create mode 100644 src/test/ui/rust-2018/edition-lint-nested-empty-paths.rs create mode 100644 src/test/ui/rust-2018/edition-lint-nested-empty-paths.stderr 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 @@ fn resolve_item(&mut self, item: &Item) { 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 @@ fn resolve_item(&mut self, item: &Item) { } } - 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 @@ fn resolve_use_tree(&mut self, id: NodeId, use_tree: &ast::UseTree, prefix: &Pat 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 @@ fn resolve_qpath(&mut self, 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 `::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 `::B::C` is a trait item. + + // Make sure `A::B` in `::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 @@ fn resolve_qpath(&mut self, &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 @@ fn resolve_qpath(&mut self, 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 @@ fn lint_if_path_starts_with_module( 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 `::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 or the MIT license +// , 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 _: ::Bar = (); + //~^ ERROR absolute paths must start with + //~| this was previously accepted + + let _: ::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 or the MIT license +// , 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 _: ::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 _: ::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 or the MIT license +// , 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 or the MIT license +// , 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 + -- GitLab