From a49b73656880f8d2379dad52088195add197be2c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 5 Jun 2023 17:08:27 +0000 Subject: [PATCH] Lint now-unnecessary associated type bounds --- compiler/rustc_hir_analysis/messages.ftl | 5 ++++ .../rustc_hir_analysis/src/astconv/mod.rs | 24 ++++++++++++++--- compiler/rustc_hir_analysis/src/errors.rs | 10 ++++++- compiler/rustc_lint_defs/src/builtin.rs | 26 ++++++++++++++++++ .../assoc_type_bounds_sized_unnecessary.rs | 14 ++++++++++ ...assoc_type_bounds_sized_unnecessary.stderr | 27 +++++++++++++++++++ 6 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.rs create mode 100644 tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index ad26c495c02..0738961d6ce 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -288,6 +288,11 @@ hir_analysis_unrecognized_intrinsic_function = unrecognized intrinsic function: `{$name}` .label = unrecognized intrinsic +hir_analysis_unused_associated_type_bounds = + unnecessary associated type bound for not object safe associated type + .note = this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`. + .suggestion = remove this bound + hir_analysis_value_of_associated_struct_already_specified = the value of the associated type `{$item_name}` (from trait `{$def_path}`) is already specified .label = re-bound here diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 6291d124ab4..1c906da28c0 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -29,6 +29,7 @@ use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin}; use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; use rustc_infer::traits::ObligationCause; +use rustc_lint_defs::builtin::UNUSED_ASSOCIATED_TYPE_BOUNDS; use rustc_middle::middle::stability::AllowUnstable; use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef}; use rustc_middle::ty::DynKind; @@ -929,6 +930,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { fn conv_object_ty_poly_trait_ref( &self, span: Span, + hir_id: hir::HirId, hir_trait_bounds: &[hir::PolyTraitRef<'_>], lifetime: &hir::Lifetime, borrowed: bool, @@ -1125,9 +1127,18 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // So every `Projection` clause is an `Assoc = Foo` bound. `associated_types` contains all associated // types's `DefId`, so the following loop removes all the `DefIds` of the associated types that have a // corresponding `Projection` clause - for (projection_bound, _) in &projection_bounds { + for (projection_bound, span) in &projection_bounds { for def_ids in associated_types.values_mut() { - def_ids.remove(&projection_bound.projection_def_id()); + let def_id = projection_bound.projection_def_id(); + def_ids.remove(&def_id); + if tcx.generics_require_sized_self(def_id) { + tcx.emit_spanned_lint( + UNUSED_ASSOCIATED_TYPE_BOUNDS, + hir_id, + *span, + crate::errors::UnusedAssociatedTypeBounds { span: *span }, + ); + } } } @@ -2812,7 +2823,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { TraitObjectSyntax::DynStar => ty::DynStar, }; - self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr) + self.conv_object_ty_poly_trait_ref( + ast_ty.span, + ast_ty.hir_id, + bounds, + lifetime, + borrowed, + repr, + ) } hir::TyKind::Path(hir::QPath::Resolved(maybe_qself, path)) => { debug!(?maybe_qself, ?path); diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index cb840592edd..205e26d0eda 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -5,7 +5,7 @@ use rustc_errors::{ error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler, IntoDiagnostic, MultiSpan, }; -use rustc_macros::{Diagnostic, Subdiagnostic}; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, print::TraitRefPrintOnlyTraitPath, Ty}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -900,3 +900,11 @@ pub(crate) enum LateBoundInApit { param_span: Span, }, } + +#[derive(LintDiagnostic)] +#[diag(hir_analysis_unused_associated_type_bounds)] +#[note] +pub struct UnusedAssociatedTypeBounds { + #[suggestion(code = "")] + pub span: Span, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ef82a6c17ee..d2d0d58784e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3468,6 +3468,32 @@ declare_lint! { report_in_external_macro } +declare_lint! { + /// The `unused_associated_type_bounds` lint is emitted when an + /// associated type bound is added to a trait object, but the associated + /// type has a `where Self: Sized` bound, and is thus unavailable on the + /// trait object anyway. + /// + /// ### Example + /// + /// ```rust + /// trait Foo { + /// type Bar where Self: Sized; + /// } + /// type Mop = dyn Foo; + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Just like methods with `Self: Sized` bounds are unavailable on trait + /// objects, associated types can be removed from the trait object. + pub UNUSED_ASSOCIATED_TYPE_BOUNDS, + Warn, + "detects unused `Foo = Bar` bounds in `dyn Trait`" +} + declare_lint! { /// The `unused_doc_comments` lint detects doc comments that aren't used /// by `rustdoc`. diff --git a/tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.rs b/tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.rs new file mode 100644 index 00000000000..564081e4ef6 --- /dev/null +++ b/tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.rs @@ -0,0 +1,14 @@ +// check-pass + +trait Foo { + type Bar + where + Self: Sized; +} + +fn foo(_: &dyn Foo) {} +//~^ WARN: unnecessary associated type bound for not object safe associated type +//~| WARN: unnecessary associated type bound for not object safe associated type +//~| WARN: unnecessary associated type bound for not object safe associated type + +fn main() {} diff --git a/tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.stderr b/tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.stderr new file mode 100644 index 00000000000..d0a4179fe3e --- /dev/null +++ b/tests/ui/object-safety/assoc_type_bounds_sized_unnecessary.stderr @@ -0,0 +1,27 @@ +warning: unnecessary associated type bound for not object safe associated type + --> $DIR/assoc_type_bounds_sized_unnecessary.rs:9:20 + | +LL | fn foo(_: &dyn Foo) {} + | ^^^^^^^^ help: remove this bound + | + = note: this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`. + = note: `#[warn(unused_associated_type_bounds)]` on by default + +warning: unnecessary associated type bound for not object safe associated type + --> $DIR/assoc_type_bounds_sized_unnecessary.rs:9:20 + | +LL | fn foo(_: &dyn Foo) {} + | ^^^^^^^^ help: remove this bound + | + = note: this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`. + +warning: unnecessary associated type bound for not object safe associated type + --> $DIR/assoc_type_bounds_sized_unnecessary.rs:9:20 + | +LL | fn foo(_: &dyn Foo) {} + | ^^^^^^^^ help: remove this bound + | + = note: this associated type has a `where Self: Sized` bound. Thus, while the associated type can be specified, it cannot be used in any way, because trait objects are not `Sized`. + +warning: 3 warnings emitted +