diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 2ee1bd0dfd1ec..af78954ba1547 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -12,8 +12,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor, VisitorExt}; use rustc_hir::{ - self as hir, AmbigArg, ConstStability, DefaultBodyStability, FieldDef, Item, ItemKind, - Stability, StabilityLevel, StableSince, TraitRef, Ty, TyKind, UnstableReason, + self as hir, AmbigArg, ConstStability, DefaultBodyStability, FieldDef, HirId, Item, ItemKind, + Path, Stability, StabilityLevel, StableSince, TraitRef, Ty, TyKind, UnstableReason, UsePath, VERSION_PLACEHOLDER, Variant, find_attr, }; use rustc_middle::hir::nested_filter; @@ -739,6 +739,35 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { intravisit::walk_poly_trait_ref(self, t); } + fn visit_use(&mut self, path: &'tcx UsePath<'tcx>, hir_id: HirId) { + let res = path.res; + + // A use item can import something from two namespaces at the same time. + // For deprecation/stability we don't want to warn twice. + // This specifically happens with constructors for unit/tuple structs. + if let Some(ty_ns_res) = res.type_ns + && let Some(value_ns_res) = res.value_ns + && let Some(type_ns_did) = ty_ns_res.opt_def_id() + && let Some(value_ns_did) = value_ns_res.opt_def_id() + && let DefKind::Ctor(.., _) = self.tcx.def_kind(value_ns_did) + && self.tcx.parent(value_ns_did) == type_ns_did + { + // Only visit the value namespace path when we've detected a duplicate, + // not the type namespace path. + let UsePath { segments, res: _, span } = *path; + self.visit_path(&Path { segments, res: value_ns_res, span }, hir_id); + + // Though, visit the macro namespace if it exists, + // regardless of the checks above relating to constructors. + if let Some(res) = res.macro_ns { + self.visit_path(&Path { segments, res, span }, hir_id); + } + } else { + // if there's no duplicate, just walk as normal + intravisit::walk_use(self, path, hir_id) + } + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, id: hir::HirId) { if let Some(def_id) = path.res.opt_def_id() { let method_span = path.segments.last().map(|s| s.ident.span); diff --git a/tests/ui/deprecation/unit_and_tuple_struct.rs b/tests/ui/deprecation/unit_and_tuple_struct.rs new file mode 100644 index 0000000000000..5f1484e7431dc --- /dev/null +++ b/tests/ui/deprecation/unit_and_tuple_struct.rs @@ -0,0 +1,45 @@ +#![deny(deprecated)] + +#[deprecated] +pub mod a { + pub struct Foo; + pub struct Bar(); + pub struct Baz {} + + pub enum Enum { + VFoo, + VBar(), + VBaz {}, + } +} + + +use a::Foo; +//~^ ERROR use of deprecated unit struct `a::Foo` +use a::Bar; +//~^ ERROR use of deprecated tuple struct `a::Bar` +use a::Baz; +//~^ ERROR use of deprecated struct `a::Baz` + +use a::Enum::VFoo; +//~^ ERROR use of deprecated unit variant `a::Enum::VFoo` +use a::Enum::VBar; +//~^ ERROR use of deprecated tuple variant `a::Enum::VBar` +use a::Enum::VBaz; +//~^ ERROR use of deprecated variant `a::Enum::VBaz` + +fn main() { + a::Foo; + //~^ ERROR use of deprecated unit struct `a::Foo` + a::Bar(); + //~^ ERROR use of deprecated tuple struct `a::Bar` + a::Baz {}; + //~^ ERROR use of deprecated struct `a::Baz` + + a::Enum::VFoo; + //~^ ERROR use of deprecated unit variant `a::Enum::VFoo` + a::Enum::VBar(); + //~^ ERROR use of deprecated tuple variant `a::Enum::VBar` + a::Enum::VBaz{}; + //~^ ERROR use of deprecated variant `a::Enum::VBaz` +} diff --git a/tests/ui/deprecation/unit_and_tuple_struct.stderr b/tests/ui/deprecation/unit_and_tuple_struct.stderr new file mode 100644 index 0000000000000..4408eaefb81ef --- /dev/null +++ b/tests/ui/deprecation/unit_and_tuple_struct.stderr @@ -0,0 +1,80 @@ +error: use of deprecated unit struct `a::Foo` + --> $DIR/unit_and_tuple_struct.rs:17:8 + | +LL | use a::Foo; + | ^^^ + | +note: the lint level is defined here + --> $DIR/unit_and_tuple_struct.rs:1:9 + | +LL | #![deny(deprecated)] + | ^^^^^^^^^^ + +error: use of deprecated tuple struct `a::Bar` + --> $DIR/unit_and_tuple_struct.rs:19:8 + | +LL | use a::Bar; + | ^^^ + +error: use of deprecated struct `a::Baz` + --> $DIR/unit_and_tuple_struct.rs:21:8 + | +LL | use a::Baz; + | ^^^ + +error: use of deprecated unit variant `a::Enum::VFoo` + --> $DIR/unit_and_tuple_struct.rs:24:14 + | +LL | use a::Enum::VFoo; + | ^^^^ + +error: use of deprecated tuple variant `a::Enum::VBar` + --> $DIR/unit_and_tuple_struct.rs:26:14 + | +LL | use a::Enum::VBar; + | ^^^^ + +error: use of deprecated variant `a::Enum::VBaz` + --> $DIR/unit_and_tuple_struct.rs:28:14 + | +LL | use a::Enum::VBaz; + | ^^^^ + +error: use of deprecated unit struct `a::Foo` + --> $DIR/unit_and_tuple_struct.rs:32:6 + | +LL | a::Foo; + | ^^^ + +error: use of deprecated tuple struct `a::Bar` + --> $DIR/unit_and_tuple_struct.rs:34:6 + | +LL | a::Bar(); + | ^^^ + +error: use of deprecated struct `a::Baz` + --> $DIR/unit_and_tuple_struct.rs:36:6 + | +LL | a::Baz {}; + | ^^^ + +error: use of deprecated unit variant `a::Enum::VFoo` + --> $DIR/unit_and_tuple_struct.rs:39:12 + | +LL | a::Enum::VFoo; + | ^^^^ + +error: use of deprecated tuple variant `a::Enum::VBar` + --> $DIR/unit_and_tuple_struct.rs:41:12 + | +LL | a::Enum::VBar(); + | ^^^^ + +error: use of deprecated variant `a::Enum::VBaz` + --> $DIR/unit_and_tuple_struct.rs:43:12 + | +LL | a::Enum::VBaz{}; + | ^^^^ + +error: aborting due to 12 previous errors +