diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5f277d03c52..a9bbcbba819 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 { @@ -2247,10 +2258,16 @@ impl<'a> Resolver<'a> { if items.len() == 0 { // Resolve prefix of an import with empty braces (issue #28388). - self.smart_resolve_path(id, None, &path, PathSource::ImportPrefix); + self.smart_resolve_path_with_crate_lint( + id, + None, + &path, + PathSource::ImportPrefix, + 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); } } } @@ -2354,7 +2371,8 @@ impl<'a> Resolver<'a> { None, &path, trait_ref.path.span, - PathSource::Trait(AliasPossibility::No) + PathSource::Trait(AliasPossibility::No), + CrateLint::SimplePath(trait_ref.ref_id), ).base_def(); if def != Def::Err { new_id = Some(def.def_id()); @@ -2787,10 +2805,29 @@ impl<'a> Resolver<'a> { path: &Path, source: PathSource) -> PathResolution { + self.smart_resolve_path_with_crate_lint(id, qself, path, source, CrateLint::SimplePath(id)) + } + + /// A variant of `smart_resolve_path` where you also specify extra + /// information about where the path came from; this extra info is + /// sometimes needed for the lint that recommends rewriting + /// absoluate paths to `crate`, so that it knows how to frame the + /// suggestion. If you are just resolving a path like `foo::bar` + /// that appears...somewhere, though, then you just want + /// `CrateLint::SimplePath`, which is what `smart_resolve_path` + /// already provides. + fn smart_resolve_path_with_crate_lint( + &mut self, + id: NodeId, + qself: Option<&QSelf>, + path: &Path, + source: PathSource, + crate_lint: CrateLint + ) -> PathResolution { let segments = &path.segments.iter() .map(|seg| seg.ident) .collect::>(); - self.smart_resolve_path_fragment(id, qself, segments, path.span, source) + self.smart_resolve_path_fragment(id, qself, segments, path.span, source, crate_lint) } fn smart_resolve_path_fragment(&mut self, @@ -2798,7 +2835,8 @@ impl<'a> Resolver<'a> { qself: Option<&QSelf>, path: &[Ident], span: Span, - source: PathSource) + source: PathSource, + crate_lint: CrateLint) -> PathResolution { let ident_span = path.last().map_or(span, |ident| ident.span); let ns = source.namespace(); @@ -2999,9 +3037,16 @@ impl<'a> Resolver<'a> { err_path_resolution() }; - let resolution = match self.resolve_qpath_anywhere(id, qself, path, ns, span, - source.defer_to_typeck(), - source.global_by_default()) { + let resolution = match self.resolve_qpath_anywhere( + id, + qself, + path, + ns, + span, + source.defer_to_typeck(), + source.global_by_default(), + crate_lint, + ) { Some(resolution) if resolution.unresolved_segments() == 0 => { if is_expected(resolution.base_def()) || resolution.base_def() == Def::Err { resolution @@ -3102,14 +3147,15 @@ impl<'a> Resolver<'a> { primary_ns: Namespace, span: Span, defer_to_typeck: bool, - global_by_default: bool) + global_by_default: bool, + crate_lint: CrateLint) -> Option { let mut fin_res = None; // FIXME: can't resolve paths in macro namespace yet, macros are // processed by the little special hack below. for (i, ns) in [primary_ns, TypeNS, ValueNS, /*MacroNS*/].iter().cloned().enumerate() { if i == 0 || ns != primary_ns { - match self.resolve_qpath(id, qself, path, ns, span, global_by_default) { + match self.resolve_qpath(id, qself, path, ns, span, global_by_default, crate_lint) { // If defer_to_typeck, then resolution > no resolution, // otherwise full resolution > partial resolution > no resolution. Some(res) if res.unresolved_segments() == 0 || defer_to_typeck => @@ -3137,19 +3183,60 @@ impl<'a> Resolver<'a> { path: &[Ident], ns: Namespace, span: Span, - global_by_default: bool) + global_by_default: bool, + crate_lint: CrateLint) -> Option { + debug!( + "resolve_qpath(id={:?}, qself={:?}, path={:?}, \ + ns={:?}, span={:?}, global_by_default={:?})", + id, + qself, + path, + ns, + span, + global_by_default, + ); + 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, None, &path[..qself.position + 1], - span, PathSource::TraitItem(ns)); + let res = self.smart_resolve_path_fragment( + id, + None, + &path[..qself.position + 1], + span, + PathSource::TraitItem(ns), + 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 )); @@ -3160,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() => { @@ -3231,6 +3318,16 @@ impl<'a> Resolver<'a> { let mut allow_super = true; let mut second_binding = None; + debug!( + "resolve_path(path={:?}, opt_ns={:?}, record_used={:?}, \ + path_span={:?}, crate_lint={:?})", + path, + opt_ns, + record_used, + path_span, + crate_lint, + ); + for (i, &ident) in path.iter().enumerate() { debug!("resolve_path ident {} {:?}", i, ident); let is_last = i == path.len() - 1; @@ -3406,6 +3503,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) { @@ -4093,8 +4191,14 @@ impl<'a> Resolver<'a> { let segments = path.make_root().iter().chain(path.segments.iter()) .map(|seg| seg.ident) .collect::>(); - let def = self.smart_resolve_path_fragment(id, None, &segments, path.span, - PathSource::Visibility).base_def(); + let def = self.smart_resolve_path_fragment( + id, + None, + &segments, + path.span, + PathSource::Visibility, + CrateLint::SimplePath(id), + ).base_def(); if def == Def::Err { ty::Visibility::Public } else { @@ -4454,6 +4558,7 @@ pub enum MakeGlobMap { No, } +#[derive(Copy, Clone, Debug)] enum CrateLint { /// Do not issue the lint No, @@ -4467,6 +4572,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/libsyntax/ast.rs b/src/libsyntax/ast.rs index 0ce9763ded8..72d5323a872 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1211,6 +1211,11 @@ pub enum ExprKind { #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)] pub struct QSelf { pub ty: P, + + /// The span of `a::b::Trait` in a path like ` as + /// a::b::Trait>::AssociatedItem`; in the case where `position == + /// 0`, this is an empty span. + pub path_span: Span, pub position: usize } diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index a4f9ebcf418..6664c0a5982 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -373,6 +373,7 @@ impl<'a> AstBuilder for ExtCtxt<'a> { (ast::QSelf { ty: self_type, + path_span: path.span, position: path.segments.len() - 1 }, path) } diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 28fb95f165f..525a82b6134 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -390,9 +390,10 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { TyKind::Tup(tys) => TyKind::Tup(tys.move_map(|ty| fld.fold_ty(ty))), TyKind::Paren(ty) => TyKind::Paren(fld.fold_ty(ty)), TyKind::Path(qself, path) => { - let qself = qself.map(|QSelf { ty, position }| { + let qself = qself.map(|QSelf { ty, path_span, position }| { QSelf { ty: fld.fold_ty(ty), + path_span: fld.new_span(path_span), position, } }); @@ -1131,7 +1132,11 @@ pub fn noop_fold_pat(p: P, folder: &mut T) -> P { } PatKind::Path(opt_qself, pth) => { let opt_qself = opt_qself.map(|qself| { - QSelf { ty: folder.fold_ty(qself.ty), position: qself.position } + QSelf { + ty: folder.fold_ty(qself.ty), + path_span: folder.new_span(qself.path_span), + position: qself.position, + } }); PatKind::Path(opt_qself, folder.fold_path(pth)) } @@ -1292,9 +1297,10 @@ pub fn noop_fold_expr(Expr {id, node, span, attrs}: Expr, folder: &mu lim) } ExprKind::Path(qself, path) => { - let qself = qself.map(|QSelf { ty, position }| { + let qself = qself.map(|QSelf { ty, path_span, position }| { QSelf { ty: folder.fold_ty(ty), + path_span: folder.new_span(path_span), position, } }); diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 62d007b4240..1429d881fe9 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1715,8 +1715,11 @@ impl<'a> Parser<'a> { self.parse_path_segments(&mut segments, T::PATH_STYLE, true)?; let span = ty.span.to(self.prev_span); - let recovered = - base.to_recovered(Some(QSelf { ty, position: 0 }), ast::Path { segments, span }); + let path_span = span.to(span); // use an empty path since `position` == 0 + let recovered = base.to_recovered( + Some(QSelf { ty, path_span, position: 0 }), + ast::Path { segments, span }, + ); self.diagnostic() .struct_span_err(span, "missing angle brackets in associated item path") @@ -1905,21 +1908,32 @@ impl<'a> Parser<'a> { /// `qualified_path = ::path` /// /// # Examples + /// `::default` /// `::a` /// `::F::a` (without disambiguator) /// `::F::a::` (with disambiguator) fn parse_qpath(&mut self, style: PathStyle) -> PResult<'a, (QSelf, ast::Path)> { let lo = self.prev_span; let ty = self.parse_ty()?; - let mut path = if self.eat_keyword(keywords::As) { - self.parse_path(PathStyle::Type)? + + // `path` will contain the prefix of the path up to the `>`, + // if any (e.g., `U` in the `::*` examples + // above). `path_span` has the span of that path, or an empty + // span in the case of something like `::Bar`. + let (mut path, path_span); + if self.eat_keyword(keywords::As) { + let path_lo = self.span; + path = self.parse_path(PathStyle::Type)?; + path_span = path_lo.to(self.prev_span); } else { - ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP } - }; + path = ast::Path { segments: Vec::new(), span: syntax_pos::DUMMY_SP }; + path_span = self.span.to(self.span); + } + self.expect(&token::Gt)?; self.expect(&token::ModSep)?; - let qself = QSelf { ty, position: path.segments.len() }; + let qself = QSelf { ty, path_span, position: path.segments.len() }; self.parse_path_segments(&mut path.segments, style, true)?; Ok((qself, ast::Path { segments: path.segments, span: lo.to(self.prev_span) })) 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 +