From ef121f28d8684d32306becb7577b4e36d7c0ff08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Mon, 10 Jun 2024 03:38:09 +0200 Subject: [PATCH 1/9] Suggesting an available assoc item is always maybe-incorrect --- compiler/rustc_hir_analysis/src/errors.rs | 8 +++++--- .../rustc_hir_analysis/src/hir_ty_lowering/errors.rs | 12 +----------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index c83788928a978..d1f9ad9d58b7a 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -126,12 +126,14 @@ pub enum AssocItemNotFoundSugg<'a> { assoc_kind: &'static str, suggested_name: Symbol, }, - #[suggestion(hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}")] + #[suggestion( + hir_analysis_assoc_item_not_found_other_sugg, + code = "{suggested_name}", + applicability = "maybe-incorrect" + )] Other { #[primary_span] span: Span, - #[applicability] - applicability: Applicability, ty_param_name: &'a str, assoc_kind: &'static str, suggested_name: Symbol, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 8ff6ced8b3983..40675a1edba7a 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -269,20 +269,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } // If we still couldn't find any associated item, and only one associated item exists, - // suggests using it. + // suggest using it. if let [candidate_name] = all_candidate_names.as_slice() { - // This should still compile, except on `#![feature(associated_type_defaults)]` - // where it could suggests `type A = Self::A`, thus recursing infinitely. - let applicability = - if assoc_kind == ty::AssocKind::Type && tcx.features().associated_type_defaults { - Applicability::Unspecified - } else { - Applicability::MaybeIncorrect - }; - err.sugg = Some(errors::AssocItemNotFoundSugg::Other { span: assoc_name.span, - applicability, ty_param_name, assoc_kind: assoc_kind_str, suggested_name: *candidate_name, From 898448ca5872aed82920dbb8342096e25e50aa5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Fri, 14 Jun 2024 12:28:23 +0200 Subject: [PATCH 2/9] HIR ty lowering: Refactor the way the projectee ("QSelf") gets passed to diagnostics --- compiler/rustc_hir_analysis/messages.ftl | 6 +- compiler/rustc_hir_analysis/src/errors.rs | 6 +- .../src/hir_ty_lowering/bounds.rs | 9 +-- .../src/hir_ty_lowering/errors.rs | 59 +++++++++------ .../src/hir_ty_lowering/mod.rs | 75 ++++++++++--------- 5 files changed, 86 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index cc404daa51f59..c209bf20c437c 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -1,4 +1,4 @@ -hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$ty_param_name}` +hir_analysis_ambiguous_assoc_item = ambiguous associated {$assoc_kind} `{$assoc_name}` in bounds of `{$qself}` .label = ambiguous associated {$assoc_kind} `{$assoc_name}` hir_analysis_ambiguous_lifetime_bound = @@ -12,14 +12,14 @@ hir_analysis_assoc_item_is_private = {$kind} `{$name}` is private .label = private {$kind} .defined_here_label = the {$kind} is defined here -hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$ty_param_name}` +hir_analysis_assoc_item_not_found = associated {$assoc_kind} `{$assoc_name}` not found for `{$qself}` hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identically_named -> [true] an *[false] a similarly named } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found -hir_analysis_assoc_item_not_found_other_sugg = `{$ty_param_name}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index d1f9ad9d58b7a..8e8b3b3d32aa9 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -22,7 +22,7 @@ pub struct AmbiguousAssocItem<'a> { pub span: Span, pub assoc_kind: &'static str, pub assoc_name: Ident, - pub ty_param_name: &'a str, + pub qself: &'a str, } #[derive(Diagnostic)] @@ -75,7 +75,7 @@ pub struct AssocItemNotFound<'a> { pub span: Span, pub assoc_name: Ident, pub assoc_kind: &'static str, - pub ty_param_name: &'a str, + pub qself: &'a str, #[subdiagnostic] pub label: Option<AssocItemNotFoundLabel<'a>>, #[subdiagnostic] @@ -134,7 +134,7 @@ pub enum AssocItemNotFoundSugg<'a> { Other { #[primary_span] span: Span, - ty_param_name: &'a str, + qself: &'a str, assoc_kind: &'static str, suggested_name: Symbol, }, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs index 6f9c481650b21..214eb6b2f06d5 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs @@ -6,7 +6,6 @@ use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::bug; -use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TyCtxt}; use rustc_span::symbol::Ident; use rustc_span::{ErrorGuaranteed, Span, Symbol}; @@ -16,9 +15,8 @@ use smallvec::SmallVec; use crate::bounds::Bounds; use crate::errors; -use crate::hir_ty_lowering::{HirTyLowerer, OnlySelfBounds, PredicateFilter}; - -use super::RegionInferReason; +use crate::hir_ty_lowering::HirTyLowerer; +use crate::hir_ty_lowering::{AssocItemQSelf, OnlySelfBounds, PredicateFilter, RegionInferReason}; impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// Add a `Sized` bound to the `bounds` if appropriate. @@ -288,8 +286,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { // one that does define it. self.probe_single_bound_for_assoc_item( || traits::supertraits(tcx, trait_ref), - trait_ref.skip_binder().print_only_trait_name(), - None, + AssocItemQSelf::Trait(trait_ref.def_id()), assoc_kind, constraint.ident, path_span, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index 40675a1edba7a..ca741e4fbb6eb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -3,7 +3,7 @@ use crate::errors::{ ParenthesizedFnTraitExpansion, TraitObjectDeclaredWithNoTraits, }; use crate::fluent_generated as fluent; -use crate::hir_ty_lowering::HirTyLowerer; +use crate::hir_ty_lowering::{AssocItemQSelf, HirTyLowerer}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::sorted_map::SortedMap; use rustc_data_structures::unord::UnordMap; @@ -11,9 +11,9 @@ use rustc_errors::MultiSpan; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, Diag, ErrorGuaranteed, }; +use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{self as hir, Node}; +use rustc_hir::def_id::DefId; use rustc_middle::bug; use rustc_middle::query::Key; use rustc_middle::ty::print::{PrintPolyTraitRefExt as _, PrintTraitRefExt as _}; @@ -116,8 +116,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { pub(super) fn complain_about_assoc_item_not_found<I>( &self, all_candidates: impl Fn() -> I, - ty_param_name: &str, - ty_param_def_id: Option<LocalDefId>, + qself: AssocItemQSelf, assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, @@ -139,7 +138,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); } - let assoc_kind_str = super::assoc_kind_str(assoc_kind); + let assoc_kind_str = assoc_kind_str(assoc_kind); + let qself_str = qself.to_string(tcx); // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a // valid span, so we point at the whole path segment instead. @@ -149,7 +149,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: if is_dummy { span } else { assoc_name.span }, assoc_name, assoc_kind: assoc_kind_str, - ty_param_name, + qself: &qself_str, label: None, sugg: None, }; @@ -219,19 +219,28 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { suggested_name, identically_named: suggested_name == assoc_name.name, }); - let hir = tcx.hir(); - if let Some(def_id) = ty_param_def_id - && let parent = hir.get_parent_item(tcx.local_def_id_to_hir_id(def_id)) - && let Some(generics) = hir.get_generics(parent.def_id) + if let AssocItemQSelf::TyParam(ty_param_def_id) = qself + // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're + // inside an opaque type while we're interested in the overarching type alias (TAIT). + // FIXME: However, for trait aliases, this incorrectly returns the enclosing module... + && let item_def_id = + tcx.hir().get_parent_item(tcx.local_def_id_to_hir_id(ty_param_def_id)) + // FIXME: ...which obviously won't have any generics. + && let Some(generics) = tcx.hir().get_generics(item_def_id.def_id) { - if generics.bounds_for_param(def_id).flat_map(|pred| pred.bounds.iter()).any( - |b| match b { + // FIXME: Suggest adding supertrait bounds if we have a `Self` type param. + // FIXME(trait_alias): Suggest adding `Self: Trait` to + // `trait Alias = where Self::Proj:;` with `trait Trait { type Proj; }`. + if generics + .bounds_for_param(ty_param_def_id) + .flat_map(|pred| pred.bounds.iter()) + .any(|b| match b { hir::GenericBound::Trait(t, ..) => { t.trait_ref.trait_def_id() == Some(best_trait) } _ => false, - }, - ) { + }) + { // The type param already has a bound for `trait_name`, we just need to // change the associated item. err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTrait { @@ -247,7 +256,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { tcx, generics, &mut err, - &ty_param_name, + &qself_str, &trait_name, None, None, @@ -273,7 +282,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let [candidate_name] = all_candidate_names.as_slice() { err.sugg = Some(errors::AssocItemNotFoundSugg::Other { span: assoc_name.span, - ty_param_name, + qself: &qself_str, assoc_kind: assoc_kind_str, suggested_name: *candidate_name, }); @@ -339,10 +348,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.dcx().emit_err(errors::AssocKindMismatch { span, - expected: super::assoc_kind_str(expected), - got: super::assoc_kind_str(got), + expected: assoc_kind_str(expected), + got: assoc_kind_str(got), expected_because_label, - assoc_kind: super::assoc_kind_str(assoc_item.kind), + assoc_kind: assoc_kind_str(assoc_item.kind), def_span: tcx.def_span(assoc_item.def_id), bound_on_assoc_const_label, wrap_in_braces_sugg, @@ -736,7 +745,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) { let grandparent = tcx.parent_hir_node(tcx.parent_hir_id(bound.trait_ref.hir_ref_id)); in_expr_or_pat = match grandparent { - Node::Expr(_) | Node::Pat(_) => true, + hir::Node::Expr(_) | hir::Node::Pat(_) => true, _ => false, }; match bound.trait_ref.path.segments { @@ -1602,3 +1611,11 @@ fn generics_args_err_extend<'a>( _ => {} } } + +pub(super) fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { + match kind { + ty::AssocKind::Fn => "function", + ty::AssocKind::Const => "constant", + ty::AssocKind::Type => "type", + } +} diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index d6eb1a66902fc..8d9055076a3bb 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -55,7 +55,6 @@ use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::wf::object_region_bounds; use rustc_trait_selection::traits::{self, ObligationCtxt}; -use std::fmt::Display; use std::slice; /// A path segment that is semantically allowed to have generic arguments. @@ -193,6 +192,25 @@ pub trait HirTyLowerer<'tcx> { } } +/// The "qualified self" of an associated item path. +/// +/// For diagnostic purposes only. +enum AssocItemQSelf { + Trait(DefId), + TyParam(LocalDefId), + SelfTyAlias, +} + +impl AssocItemQSelf { + fn to_string(&self, tcx: TyCtxt<'_>) -> String { + match *self { + Self::Trait(def_id) => tcx.def_path_str(def_id), + Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(), + Self::SelfTyAlias => kw::SelfUpper.to_string(), + } + } +} + /// New-typed boolean indicating whether explicit late-bound lifetimes /// are present in a set of generic arguments. /// @@ -811,19 +829,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let predicates = &self.probe_ty_param_bounds(span, ty_param_def_id, assoc_name).predicates; debug!("predicates={:#?}", predicates); - let param_name = tcx.hir().ty_param_name(ty_param_def_id); self.probe_single_bound_for_assoc_item( || { - traits::transitive_bounds_that_define_assoc_item( - tcx, - predicates - .iter() - .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))), - assoc_name, - ) + let trait_refs = predicates + .iter() + .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); + traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) }, - param_name, - Some(ty_param_def_id), + AssocItemQSelf::TyParam(ty_param_def_id), ty::AssocKind::Type, assoc_name, span, @@ -835,12 +848,11 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { /// /// This fails if there is no such bound in the list of candidates or if there are multiple /// candidates in which case it reports ambiguity. - #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, constraint), ret)] + #[instrument(level = "debug", skip(self, all_candidates, qself, constraint), ret)] fn probe_single_bound_for_assoc_item<I>( &self, all_candidates: impl Fn() -> I, - ty_param_name: impl Display, - ty_param_def_id: Option<LocalDefId>, + qself: AssocItemQSelf, assoc_kind: ty::AssocKind, assoc_name: Ident, span: Span, @@ -858,8 +870,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let Some(bound) = matching_candidates.next() else { let reported = self.complain_about_assoc_item_not_found( all_candidates, - &ty_param_name.to_string(), - ty_param_def_id, + qself, assoc_kind, assoc_name, span, @@ -872,13 +883,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if let Some(bound2) = matching_candidates.next() { debug!(?bound2); - let assoc_kind_str = assoc_kind_str(assoc_kind); - let ty_param_name = &ty_param_name.to_string(); + let assoc_kind_str = errors::assoc_kind_str(assoc_kind); + let qself_str = qself.to_string(tcx); let mut err = self.dcx().create_err(crate::errors::AmbiguousAssocItem { span, assoc_kind: assoc_kind_str, assoc_name, - ty_param_name, + qself: &qself_str, }); // Provide a more specific error code index entry for equality bindings. err.code( @@ -929,7 +940,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { err.span_suggestion_verbose( span.with_hi(assoc_name.span.lo()), "use fully-qualified syntax to disambiguate", - format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()), + format!("<{qself_str} as {}>::", bound.print_only_trait_path()), Applicability::MaybeIncorrect, ); } @@ -943,7 +954,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { if !where_bounds.is_empty() { err.help(format!( "consider introducing a new type parameter `T` and adding `where` constraints:\ - \n where\n T: {ty_param_name},\n{}", + \n where\n T: {qself_str},\n{}", where_bounds.join(",\n"), )); } @@ -997,11 +1008,6 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { let tcx = self.tcx(); let assoc_ident = assoc_segment.ident; - let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { - path.res - } else { - Res::Err - }; // Check if we have an enum variant or an inherent associated type. let mut variant_resolution = None; @@ -1038,6 +1044,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { } } + let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = &qself.kind { + path.res + } else { + Res::Err + }; + // Find the type of the associated item, and the trait where the associated // item is declared. let bound = match (&qself_ty.kind(), qself_res) { @@ -1056,8 +1068,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Binder::dummy(trait_ref.instantiate_identity()), ) }, - kw::SelfUpper, - None, + AssocItemQSelf::SelfTyAlias, ty::AssocKind::Type, assoc_ident, span, @@ -2522,11 +2533,3 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Some(r) } } - -fn assoc_kind_str(kind: ty::AssocKind) -> &'static str { - match kind { - ty::AssocKind::Fn => "function", - ty::AssocKind::Const => "constant", - ty::AssocKind::Type => "type", - } -} From 3c8b1085127b64de64cb6d880b59b55c1d6fc8e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Fri, 14 Jun 2024 12:38:21 +0200 Subject: [PATCH 3/9] Inside eager ty aliases on unresolved assoc tys suggest fully qualifying instead of restricting their self ty --- compiler/rustc_hir_analysis/messages.ftl | 7 ++- compiler/rustc_hir_analysis/src/errors.rs | 16 ++++++ .../src/hir_ty_lowering/errors.rs | 55 ++++++++++++------- .../src/hir_ty_lowering/mod.rs | 8 ++- tests/ui/resolve/issue-55673.stderr | 2 +- ...solved-assoc-ty-suggest-trait.eager.stderr | 25 +++++++++ ...esolved-assoc-ty-suggest-trait.lazy.stderr | 29 ++++++++++ .../unresolved-assoc-ty-suggest-trait.rs | 20 +++++++ 8 files changed, 138 insertions(+), 24 deletions(-) create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr create mode 100644 tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index c209bf20c437c..367f6e17e7fe6 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -20,8 +20,13 @@ hir_analysis_assoc_item_not_found_found_in_other_trait_label = there is {$identi } associated {$assoc_kind} `{$suggested_name}` in the trait `{$trait_name}` hir_analysis_assoc_item_not_found_label = associated {$assoc_kind} `{$assoc_name}` not found hir_analysis_assoc_item_not_found_other_sugg = `{$qself}` has the following associated {$assoc_kind} +hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg = + consider fully qualifying{$identically_named -> + [true] {""} + *[false] {" "}and renaming + } the associated {$assoc_kind} hir_analysis_assoc_item_not_found_similar_in_other_trait_sugg = change the associated {$assoc_kind} name to use `{$suggested_name}` from `{$trait_name}` -hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = and also change the associated {$assoc_kind} name +hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg = ...and changing the associated {$assoc_kind} name hir_analysis_assoc_item_not_found_similar_sugg = there is an associated {$assoc_kind} with a similar name hir_analysis_assoc_kind_mismatch = expected {$expected}, found {$got} diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 8e8b3b3d32aa9..90861ba1ff840 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -126,6 +126,22 @@ pub enum AssocItemNotFoundSugg<'a> { assoc_kind: &'static str, suggested_name: Symbol, }, + #[multipart_suggestion( + hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg, + style = "verbose", + applicability = "maybe-incorrect" + )] + SimilarInOtherTraitQPath { + #[suggestion_part(code = "<")] + lo: Span, + #[suggestion_part(code = " as {trait_}>")] + mi: Span, + #[suggestion_part(code = "{suggested_name}")] + hi: Option<Span>, + trait_: &'a str, + suggested_name: Symbol, + identically_named: bool, + }, #[suggestion( hir_analysis_assoc_item_not_found_other_sugg, code = "{suggested_name}", diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index ca741e4fbb6eb..fd2bbabe779de 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -219,7 +219,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { suggested_name, identically_named: suggested_name == assoc_name.name, }); - if let AssocItemQSelf::TyParam(ty_param_def_id) = qself + if let AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span) = qself // Not using `self.item_def_id()` here as that would yield the opaque type itself if we're // inside an opaque type while we're interested in the overarching type alias (TAIT). // FIXME: However, for trait aliases, this incorrectly returns the enclosing module... @@ -251,27 +251,44 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return self.dcx().emit_err(err); } - let mut err = self.dcx().create_err(err); - if suggest_constraining_type_param( - tcx, - generics, - &mut err, - &qself_str, - &trait_name, - None, - None, - ) && suggested_name != assoc_name.name + let identically_named = suggested_name == assoc_name.name; + + if let DefKind::TyAlias = tcx.def_kind(item_def_id) + && !tcx.type_alias_is_lazy(item_def_id) { - // We suggested constraining a type parameter, but the associated item on it - // was also not an exact match, so we also suggest changing it. - err.span_suggestion_verbose( - assoc_name.span, - fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + err.sugg = Some(errors::AssocItemNotFoundSugg::SimilarInOtherTraitQPath { + lo: ty_param_span.shrink_to_lo(), + mi: ty_param_span.shrink_to_hi(), + hi: (!identically_named).then_some(assoc_name.span), + // FIXME(fmease): Use a full trait ref here (with placeholders). + trait_: &trait_name, + identically_named, suggested_name, - Applicability::MaybeIncorrect, - ); + }); + } else { + let mut err = self.dcx().create_err(err); + if suggest_constraining_type_param( + tcx, + generics, + &mut err, + &qself_str, + // FIXME(fmease): Use a full trait ref here (with placeholders). + &trait_name, + None, + None, + ) && !identically_named + { + // We suggested constraining a type parameter, but the associated item on it + // was also not an exact match, so we also suggest changing it. + err.span_suggestion_verbose( + assoc_name.span, + fluent::hir_analysis_assoc_item_not_found_similar_in_other_trait_with_bound_sugg, + suggested_name, + Applicability::MaybeIncorrect, + ); + } + return err.emit(); } - return err.emit(); } return self.dcx().emit_err(err); } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 8d9055076a3bb..ce298641e6060 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -197,7 +197,7 @@ pub trait HirTyLowerer<'tcx> { /// For diagnostic purposes only. enum AssocItemQSelf { Trait(DefId), - TyParam(LocalDefId), + TyParam(LocalDefId, Span), SelfTyAlias, } @@ -205,7 +205,7 @@ impl AssocItemQSelf { fn to_string(&self, tcx: TyCtxt<'_>) -> String { match *self { Self::Trait(def_id) => tcx.def_path_str(def_id), - Self::TyParam(def_id) => tcx.hir().ty_param_name(def_id).to_string(), + Self::TyParam(def_id, _) => tcx.hir().ty_param_name(def_id).to_string(), Self::SelfTyAlias => kw::SelfUpper.to_string(), } } @@ -820,6 +820,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { fn probe_single_ty_param_bound_for_assoc_ty( &self, ty_param_def_id: LocalDefId, + ty_param_span: Span, assoc_name: Ident, span: Span, ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> { @@ -836,7 +837,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .filter_map(|(p, _)| Some(p.as_trait_clause()?.map_bound(|t| t.trait_ref))); traits::transitive_bounds_that_define_assoc_item(tcx, trait_refs, assoc_name) }, - AssocItemQSelf::TyParam(ty_param_def_id), + AssocItemQSelf::TyParam(ty_param_def_id, ty_param_span), ty::AssocKind::Type, assoc_name, span, @@ -1080,6 +1081,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did), ) => self.probe_single_ty_param_bound_for_assoc_ty( param_did.expect_local(), + qself.span, assoc_ident, span, )?, diff --git a/tests/ui/resolve/issue-55673.stderr b/tests/ui/resolve/issue-55673.stderr index ffc3252230ab8..4069b35a99848 100644 --- a/tests/ui/resolve/issue-55673.stderr +++ b/tests/ui/resolve/issue-55673.stderr @@ -19,7 +19,7 @@ help: consider further restricting type parameter `T` | LL | T::Baa: std::fmt::Debug, T: Foo | ~~~~~~~~ -help: and also change the associated type name +help: ...and changing the associated type name | LL | T::Bar: std::fmt::Debug, | ~~~ diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr new file mode 100644 index 0000000000000..8e6490c548d1c --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr @@ -0,0 +1,25 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + | +LL | type AssocOf<T> = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying the associated type + | +LL | type AssocOf<T> = <T as Trait>::Assoc; + | + +++++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + | +LL | type AssokOf<T> = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider fully qualifying and renaming the associated type + | +LL | type AssokOf<T> = <T as Trait>::Assoc; + | + +++++++++ ~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr new file mode 100644 index 0000000000000..051f181cb6fe4 --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr @@ -0,0 +1,29 @@ +error[E0220]: associated type `Assoc` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + | +LL | type AssocOf<T> = T::Assoc; + | ^^^^^ there is an associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssocOf<T: Trait> = T::Assoc; + | +++++++ + +error[E0220]: associated type `Assok` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + | +LL | type AssokOf<T> = T::Assok; + | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` + | +help: consider restricting type parameter `T` + | +LL | type AssokOf<T: Trait> = T::Assok; + | +++++++ +help: ...and changing the associated type name + | +LL | type AssokOf<T> = T::Assoc; + | ~~~~~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs new file mode 100644 index 0000000000000..75e4eb496b414 --- /dev/null +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs @@ -0,0 +1,20 @@ +// Ensure that we don't suggest *type alias bounds* for **eager** type aliases. +// issue: rust-lang/rust#125789 + +//@ revisions: eager lazy +#![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))] + +// FIXME(fmease): Suggest a full trait ref (with placeholders) instead of just a trait name. + +trait Trait<T> { type Assoc; } + +type AssocOf<T> = T::Assoc; //~ ERROR associated type `Assoc` not found for `T` +//[eager]~^ HELP consider fully qualifying the associated type +//[lazy]~| HELP consider restricting type parameter `T` + +type AssokOf<T> = T::Assok; //~ ERROR associated type `Assok` not found for `T` +//[eager]~^ HELP consider fully qualifying and renaming the associated type +//[lazy]~| HELP consider restricting type parameter `T` +//[lazy]~| HELP and changing the associated type name + +fn main() {} From 02a2f0272736c65b51996b0c6e7cbd991ac21d5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Sat, 15 Jun 2024 11:08:12 +0200 Subject: [PATCH 4/9] Suggest full trait ref (with placeholders) on unresolved assoc tys --- compiler/rustc_hir_analysis/src/errors.rs | 9 ++++--- .../src/hir_ty_lowering/errors.rs | 25 +++++++++++-------- ...solved-assoc-ty-suggest-trait.eager.stderr | 17 ++++++++++--- ...esolved-assoc-ty-suggest-trait.lazy.stderr | 17 ++++++++++--- .../unresolved-assoc-ty-suggest-trait.rs | 12 ++++++--- 5 files changed, 57 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 90861ba1ff840..c364a56163106 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -128,19 +128,20 @@ pub enum AssocItemNotFoundSugg<'a> { }, #[multipart_suggestion( hir_analysis_assoc_item_not_found_similar_in_other_trait_qpath_sugg, - style = "verbose", - applicability = "maybe-incorrect" + style = "verbose" )] SimilarInOtherTraitQPath { #[suggestion_part(code = "<")] lo: Span, - #[suggestion_part(code = " as {trait_}>")] + #[suggestion_part(code = " as {trait_ref}>")] mi: Span, #[suggestion_part(code = "{suggested_name}")] hi: Option<Span>, - trait_: &'a str, + trait_ref: String, suggested_name: Symbol, identically_named: bool, + #[applicability] + applicability: Applicability, }, #[suggestion( hir_analysis_assoc_item_not_found_other_sugg, diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs index fd2bbabe779de..56f508a2d43e1 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs @@ -251,6 +251,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { return self.dcx().emit_err(err); } + let trait_args = &ty::GenericArgs::identity_for_item(tcx, best_trait)[1..]; + let mut trait_ref = trait_name.clone(); + let applicability = if let [arg, args @ ..] = trait_args { + use std::fmt::Write; + write!(trait_ref, "</* {arg}").unwrap(); + args.iter().try_for_each(|arg| write!(trait_ref, ", {arg}")).unwrap(); + trait_ref += " */>"; + Applicability::HasPlaceholders + } else { + Applicability::MaybeIncorrect + }; + let identically_named = suggested_name == assoc_name.name; if let DefKind::TyAlias = tcx.def_kind(item_def_id) @@ -260,22 +272,15 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { lo: ty_param_span.shrink_to_lo(), mi: ty_param_span.shrink_to_hi(), hi: (!identically_named).then_some(assoc_name.span), - // FIXME(fmease): Use a full trait ref here (with placeholders). - trait_: &trait_name, + trait_ref, identically_named, suggested_name, + applicability, }); } else { let mut err = self.dcx().create_err(err); if suggest_constraining_type_param( - tcx, - generics, - &mut err, - &qself_str, - // FIXME(fmease): Use a full trait ref here (with placeholders). - &trait_name, - None, - None, + tcx, generics, &mut err, &qself_str, &trait_ref, None, None, ) && !identically_named { // We suggested constraining a type parameter, but the associated item on it diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr index 8e6490c548d1c..e891ff10fdaa9 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.eager.stderr @@ -1,5 +1,5 @@ error[E0220]: associated type `Assoc` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22 | LL | type AssocOf<T> = T::Assoc; | ^^^^^ there is an associated type `Assoc` in the trait `Trait` @@ -10,7 +10,7 @@ LL | type AssocOf<T> = <T as Trait>::Assoc; | + +++++++++ error[E0220]: associated type `Assok` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22 | LL | type AssokOf<T> = T::Assok; | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` @@ -20,6 +20,17 @@ help: consider fully qualifying and renaming the associated type LL | type AssokOf<T> = <T as Trait>::Assoc; | + +++++++++ ~~~~~ -error: aborting due to 2 previous errors +error[E0220]: associated type `Proj` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 + | +LL | type ProjOf<T> = T::Proj; + | ^^^^ there is an associated type `Proj` in the trait `Parametrized` + | +help: consider fully qualifying the associated type + | +LL | type ProjOf<T> = <T as Parametrized</* 'a, T, N */>>::Proj; + | + ++++++++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr index 051f181cb6fe4..96179a7b48466 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.lazy.stderr @@ -1,5 +1,5 @@ error[E0220]: associated type `Assoc` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:11:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:9:22 | LL | type AssocOf<T> = T::Assoc; | ^^^^^ there is an associated type `Assoc` in the trait `Trait` @@ -10,7 +10,7 @@ LL | type AssocOf<T: Trait> = T::Assoc; | +++++++ error[E0220]: associated type `Assok` not found for `T` - --> $DIR/unresolved-assoc-ty-suggest-trait.rs:15:22 + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:13:22 | LL | type AssokOf<T> = T::Assok; | ^^^^^ there is a similarly named associated type `Assoc` in the trait `Trait` @@ -24,6 +24,17 @@ help: ...and changing the associated type name LL | type AssokOf<T> = T::Assoc; | ~~~~~ -error: aborting due to 2 previous errors +error[E0220]: associated type `Proj` not found for `T` + --> $DIR/unresolved-assoc-ty-suggest-trait.rs:22:21 + | +LL | type ProjOf<T> = T::Proj; + | ^^^^ there is an associated type `Proj` in the trait `Parametrized` + | +help: consider restricting type parameter `T` + | +LL | type ProjOf<T: Parametrized</* 'a, T, N */>> = T::Proj; + | ++++++++++++++++++++++++++++++ + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs index 75e4eb496b414..2c8d448f3083e 100644 --- a/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs +++ b/tests/ui/type-alias/unresolved-assoc-ty-suggest-trait.rs @@ -4,9 +4,7 @@ //@ revisions: eager lazy #![cfg_attr(lazy, feature(lazy_type_alias), allow(incomplete_features))] -// FIXME(fmease): Suggest a full trait ref (with placeholders) instead of just a trait name. - -trait Trait<T> { type Assoc; } +trait Trait { type Assoc; } type AssocOf<T> = T::Assoc; //~ ERROR associated type `Assoc` not found for `T` //[eager]~^ HELP consider fully qualifying the associated type @@ -17,4 +15,12 @@ type AssokOf<T> = T::Assok; //~ ERROR associated type `Assok` not found for `T` //[lazy]~| HELP consider restricting type parameter `T` //[lazy]~| HELP and changing the associated type name +trait Parametrized<'a, T, const N: usize> { + type Proj; +} + +type ProjOf<T> = T::Proj; //~ ERROR associated type `Proj` not found for `T` +//[eager]~^ HELP consider fully qualifying the associated type +//[lazy]~| HELP consider restricting type parameter `T` + fn main() {} From 63a54d93be33bb1c0357c48532df778f6f2a416b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Fri, 14 Jun 2024 15:30:11 +0200 Subject: [PATCH 5/9] Don't suppress lint type_alias_bounds for ty aliases containing inherent assoc tys --- compiler/rustc_lint/src/builtin.rs | 28 +++++++----------- compiler/rustc_type_ir/src/visit.rs | 4 --- .../type-alias-bounds-are-enforced.rs | 20 ------------- .../type-alias-bounds.rs | 29 +++++++++++++++++++ .../type-alias-bounds.stderr | 15 ++++++++++ 5 files changed, 55 insertions(+), 41 deletions(-) delete mode 100644 tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds.rs create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds.stderr diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 9ebada0fff377..40b336cd5ea0c 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1425,30 +1425,27 @@ impl TypeAliasBounds { impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(hir_ty, type_alias_generics) = &item.kind else { return }; + let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return }; - // Bounds of lazy type aliases and TAITs are respected. - if cx.tcx.type_alias_is_lazy(item.owner_id) { + // There must not be a where clause. + if generics.predicates.is_empty() { return; } - let ty = cx.tcx.type_of(item.owner_id).skip_binder(); - if ty.has_inherent_projections() { - // Bounds of type aliases that contain opaque types or inherent projections are - // respected. E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`, `type X = - // Type::Inherent;`. + // Bounds of lazy type aliases and TAITs are respected. + if cx.tcx.type_alias_is_lazy(item.owner_id) { return; } - // There must not be a where clause - if type_alias_generics.predicates.is_empty() { - return; - } + // NOTE(inherent_associated_types): While we currently do take some bounds in type + // aliases into consideration during IAT *selection*, we don't perform full use+def + // site wfchecking for such type aliases. Therefore TAB should still trigger. + // See also `tests/ui/associated-inherent-types/type-alias-bounds.rs`. let mut where_spans = Vec::new(); let mut inline_spans = Vec::new(); let mut inline_sugg = Vec::new(); - for p in type_alias_generics.predicates { + for p in generics.predicates { let span = p.span(); if p.in_where_clause() { where_spans.push(span); @@ -1469,10 +1466,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, where_spans, - BuiltinTypeAliasWhereClause { - suggestion: type_alias_generics.where_clause_span, - sub, - }, + BuiltinTypeAliasWhereClause { suggestion: generics.where_clause_span, sub }, ); } diff --git a/compiler/rustc_type_ir/src/visit.rs b/compiler/rustc_type_ir/src/visit.rs index 6ec38b78fc22a..d5e114b2175c8 100644 --- a/compiler/rustc_type_ir/src/visit.rs +++ b/compiler/rustc_type_ir/src/visit.rs @@ -241,10 +241,6 @@ pub trait TypeVisitableExt<I: Interner>: TypeVisitable<I> { self.has_type_flags(TypeFlags::HAS_ALIAS) } - fn has_inherent_projections(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_INHERENT) - } - fn has_opaque_types(&self) -> bool { self.has_type_flags(TypeFlags::HAS_TY_OPAQUE) } diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs deleted file mode 100644 index 5ac7e1e58b8b2..0000000000000 --- a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ compile-flags: --crate-type=lib -//@ check-pass - -#![feature(inherent_associated_types)] -#![allow(incomplete_features)] - -// Bounds on the self type play a major role in the resolution of inherent associated types (*). -// As a result of that, if a type alias contains any then its bounds have to be respected and the -// lint `type_alias_bounds` should not fire. - -#![deny(type_alias_bounds)] - -pub type Alias<T: Bound> = (Source<T>::Assoc,); - -pub struct Source<T>(T); -pub trait Bound {} - -impl<T: Bound> Source<T> { - pub type Assoc = (); -} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.rs b/tests/ui/associated-inherent-types/type-alias-bounds.rs new file mode 100644 index 0000000000000..c27bcbed43ae0 --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds.rs @@ -0,0 +1,29 @@ +//@ compile-flags: --crate-type=lib +//@ check-pass + +#![feature(inherent_associated_types)] +#![allow(incomplete_features)] + +// FIXME(inherent_associated_types): +// While we currently do take some clauses of the ParamEnv into consideration +// when performing IAT selection, we do not perform full well-formedness checking +// for (eager) type alias definition and usage sites. +// +// Therefore it's *correct* for lint `type_alias_bounds` to fire here despite the +// fact that removing `Bound` from `T` in `Alias` would lead to an error! +// +// Obviously, the present situation isn't ideal and we should fix it in one way +// or another. Either we somehow delay IAT selection until after HIR ty lowering +// to avoid the need to specify any bounds inside (eager) type aliases or we +// force the overarching type alias to be *lazy* (similar to TAITs) which would +// automatically lead to full wfchecking and lint TAB getting suppressed. + +pub type Alias<T: Bound> = (Source<T>::Assoc,); +//~^ WARN bounds on generic parameters are not enforced in type aliases + +pub struct Source<T>(T); +pub trait Bound {} + +impl<T: Bound> Source<T> { + pub type Assoc = (); +} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.stderr b/tests/ui/associated-inherent-types/type-alias-bounds.stderr new file mode 100644 index 0000000000000..c5d0e9e5041df --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds.stderr @@ -0,0 +1,15 @@ +warning: bounds on generic parameters are not enforced in type aliases + --> $DIR/type-alias-bounds.rs:21:19 + | +LL | pub type Alias<T: Bound> = (Source<T>::Assoc,); + | ^^^^^ + | + = note: `#[warn(type_alias_bounds)]` on by default +help: the bound will not be checked when the type alias is used, and should be removed + | +LL - pub type Alias<T: Bound> = (Source<T>::Assoc,); +LL + pub type Alias<T> = (Source<T>::Assoc,); + | + +warning: 1 warning emitted + From a8b3dfd25336dc12f17da23a0ed7004a4ebee234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Fri, 14 Jun 2024 16:18:32 +0200 Subject: [PATCH 6/9] Suppress lint type_alias_bounds for ty aliases containing const projections under GCE --- compiler/rustc_lint/src/builtin.rs | 10 +++ .../type-alias-bounds.neg.stderr | 63 ++++++++++++++++ .../generic_const_exprs/type-alias-bounds.rs | 71 +++++++++++++++++++ 3 files changed, 144 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr create mode 100644 tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 40b336cd5ea0c..9055500760778 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1437,6 +1437,16 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { return; } + + // FIXME(generic_const_exprs): Revisit this before stabilization. + // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. + let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); + if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) + && cx.tcx.features().generic_const_exprs + { + return; + } + // NOTE(inherent_associated_types): While we currently do take some bounds in type // aliases into consideration during IAT *selection*, we don't perform full use+def // site wfchecking for such type aliases. Therefore TAB should still trigger. diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr new file mode 100644 index 0000000000000..fa12dd14753f9 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.neg.stderr @@ -0,0 +1,63 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:23:12 + | +LL | let _: AliasConstUnused<String>; + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `ct_unused_0::AliasConstUnused` + --> $DIR/type-alias-bounds.rs:20:30 + | +LL | type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>); + | ^^^^ required by this bound in `AliasConstUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:31:12 + | +LL | let _: AliasConstUnused; + | ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `ct_unused_1::AliasConstUnused` + --> $DIR/type-alias-bounds.rs:29:41 + | +LL | type AliasConstUnused where String: Copy = I32<{ 0; 0 }>; + | ^^^^ required by this bound in `AliasConstUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:39:12 + | +LL | let _: AliasFnUnused<String>; + | ^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasFnUnused` + --> $DIR/type-alias-bounds.rs:36:27 + | +LL | type AliasFnUnused<T: Copy> = (T, I32<{ code() }>); + | ^^^^ required by this bound in `AliasFnUnused` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:57:12 + | +LL | let _: AliasAssocConstUsed<String>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasAssocConstUsed` + --> $DIR/type-alias-bounds.rs:55:41 + | +LL | type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>; + | ^^^^ required by this bound in `AliasAssocConstUsed` + +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/type-alias-bounds.rs:65:12 + | +LL | let _: AliasFnUsed<String>; + | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + | +note: required by a bound in `AliasFnUsed` + --> $DIR/type-alias-bounds.rs:62:33 + | +LL | type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>; + | ^^^^ required by this bound in `AliasFnUsed` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs new file mode 100644 index 0000000000000..f16e646129c65 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs @@ -0,0 +1,71 @@ +//@ revisions: pos neg +//@[pos] check-pass + +#![feature(generic_const_exprs)] +#![feature(trivial_bounds)] // only used in test case `ct_unused_1` +#![allow(incomplete_features)] + +// FIXME(generic_const_exprs): Revisit this before stabilization. +// Check that we don't emit the lint `type_alias_bounds` for (eager) type aliases +// whose RHS contains a const projection (aka uneval'ed const). +// Since anon consts inherit the parent generics and predicates and we effectively +// check them before and after instantiaton for well-formedness, the type alias +// bounds are in every sense "enforced". +// Note that the test cases whose name ends in "unused" just demonstrate that this +// holds even if the const projections don't "visibly" capture any generics and/or +// predicates. +#![deny(type_alias_bounds)] + +fn ct_unused_0() { + type AliasConstUnused<T: Copy> = (T, I32<{ DATA }>); + const DATA: i32 = 0; + #[cfg(neg)] + let _: AliasConstUnused<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn ct_unused_1() { + #[allow(trivial_bounds)] + type AliasConstUnused where String: Copy = I32<{ 0; 0 }>; + #[cfg(neg)] + let _: AliasConstUnused; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn fn_unused() { + type AliasFnUnused<T: Copy> = (T, I32<{ code() }>); + const fn code() -> i32 { 0 } + #[cfg(neg)] + let _: AliasFnUnused<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +trait Trait { + type Proj; + const DATA: i32; +} + +impl Trait for String { + type Proj = i32; + const DATA: i32 = 0; +} + +// Regression test for issue #94398. +fn assoc_ct_used() { + type AliasAssocConstUsed<T: Trait + Copy> = I32<{ T::DATA }>; + #[cfg(neg)] + let _: AliasAssocConstUsed<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +fn fn_used() { + type AliasFnUsed<T: Trait + Copy> = I32<{ code::<T>() }>; + const fn code<T: Trait>() -> i32 { T::DATA } + #[cfg(neg)] + let _: AliasFnUsed<String>; + //[neg]~^ ERROR the trait bound `String: Copy` is not satisfied +} + +struct I32<const N: i32>; + +fn main() {} From fdf8f024ad71c6e9c46867fb31b74df0fcaaf3f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Sat, 15 Jun 2024 21:34:44 +0200 Subject: [PATCH 7/9] Improve the impl and diag output of lint type_alias_bounds --- compiler/rustc_lint/messages.ftl | 19 +- compiler/rustc_lint/src/builtin.rs | 92 ++++---- compiler/rustc_lint/src/lints.rs | 148 +++++++------ .../type-alias-bounds.rs | 2 +- .../type-alias-bounds.stderr | 15 +- .../associated-type-bounds/type-alias.stderr | 204 +++++++++--------- tests/ui/privacy/private-in-public-warn.rs | 4 +- .../ui/privacy/private-in-public-warn.stderr | 34 +-- .../trivial-bounds-inconsistent.stderr | 17 +- ...67690-type-alias-bound-diagnostic-crash.rs | 2 +- ...0-type-alias-bound-diagnostic-crash.stderr | 15 +- tests/ui/type/type-alias-bounds.rs | 21 +- tests/ui/type/type-alias-bounds.stderr | 163 +++++++------- 13 files changed, 401 insertions(+), 335 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 7d7b97e2eb1a3..ce99c8686baaf 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -139,13 +139,18 @@ lint_builtin_special_module_name_used_main = found module declaration for main.r lint_builtin_trivial_bounds = {$predicate_kind_name} bound {$predicate} does not depend on any type or lifetime parameters -lint_builtin_type_alias_bounds_help = use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases - -lint_builtin_type_alias_generic_bounds = bounds on generic parameters are not enforced in type aliases - .suggestion = the bound will not be checked when the type alias is used, and should be removed - -lint_builtin_type_alias_where_clause = where clauses are not enforced in type aliases - .suggestion = the clause will not be checked when the type alias is used, and should be removed +lint_builtin_type_alias_bounds_enable_feat_help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +lint_builtin_type_alias_bounds_label = will not be checked at usage sites of the type alias +lint_builtin_type_alias_bounds_limitation_note = this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information +lint_builtin_type_alias_bounds_param_bounds = bounds on generic parameters in type aliases are not enforced + .suggestion = remove {$count -> + [one] this bound + *[other] these bounds + } +lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg = fully qualify this associated type +lint_builtin_type_alias_bounds_where_clause = where clauses on type aliases are not enforced + .suggestion = remove this where clause lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 9055500760778..a8c8c71927aaf 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -31,12 +31,12 @@ use crate::{ BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents, BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, - BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, - BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, - BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, - BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, - BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, - BuiltinWhileTrue, InvalidAsmLabel, SuggestChangingAssocTypes, + BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, + BuiltinTypeAliasParamBoundsSuggestion, BuiltinUngatedAsyncFnTrackCaller, + BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, + BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, + BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, + TypeAliasBoundsQualifyAssocTysSugg, }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; @@ -1406,23 +1406,6 @@ declare_lint_pass!( TypeAliasBounds => [TYPE_ALIAS_BOUNDS] ); -impl TypeAliasBounds { - pub(crate) fn is_type_variable_assoc(qpath: &hir::QPath<'_>) -> bool { - match *qpath { - hir::QPath::TypeRelative(ty, _) => { - // If this is a type variable, we found a `T::Assoc`. - match ty.kind { - hir::TyKind::Path(hir::QPath::Resolved(None, path)) => { - matches!(path.res, Res::Def(DefKind::TyParam, _)) - } - _ => false, - } - } - hir::QPath::Resolved(..) | hir::QPath::LangItem(..) => false, - } - } -} - impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return }; @@ -1437,7 +1420,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { return; } - // FIXME(generic_const_exprs): Revisit this before stabilization. // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); @@ -1455,6 +1437,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut where_spans = Vec::new(); let mut inline_spans = Vec::new(); let mut inline_sugg = Vec::new(); + let mut affects_object_lifetime_defaults = false; + for p in generics.predicates { let span = p.span(); if p.in_where_clause() { @@ -1465,31 +1449,61 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { } inline_sugg.push((span, String::new())); } + + // FIXME(fmease): Move this into a "diagnostic decorator" for increased laziness + // Bounds of the form `T: 'a` where `T` is a type param of + // the type alias affect object lifetime defaults. + if !affects_object_lifetime_defaults + && let hir::WherePredicate::BoundPredicate(pred) = p + && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) + && pred.bound_generic_params.is_empty() + && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = pred.bounded_ty.kind + && let Res::Def(DefKind::TyParam, _) = path.res + { + affects_object_lifetime_defaults = true; + } } - let mut suggested_changing_assoc_types = false; - if !where_spans.is_empty() { - let sub = (!suggested_changing_assoc_types).then(|| { - suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty: hir_ty } - }); + // FIXME(fmease): Add a disclaimer (in the form of a multi-span note) that the removal of + // type-param-outlives-bounds affects OLDs and explicit object lifetime + // bounds might be required [...]. + // FIXME(fmease): The applicability should also depend on the outcome of the HIR walker + // inside of `TypeAliasBoundsQualifyAssocTysSugg`: Whether it found a + // shorthand projection or not. + let applicability = if affects_object_lifetime_defaults { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + + let mut qualify_assoc_tys_sugg = Some(TypeAliasBoundsQualifyAssocTysSugg { ty: hir_ty }); + let enable_feat_help = cx.tcx.sess.is_nightly_build().then_some(()); + + if let [.., label_sp] = *where_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, where_spans, - BuiltinTypeAliasWhereClause { suggestion: generics.where_clause_span, sub }, + BuiltinTypeAliasBounds::WhereClause { + label: label_sp, + enable_feat_help, + suggestion: (generics.where_clause_span, applicability), + qualify_assoc_tys_sugg: qualify_assoc_tys_sugg.take(), + }, ); } - - if !inline_spans.is_empty() { - let suggestion = BuiltinTypeAliasGenericBoundsSuggestion { suggestions: inline_sugg }; - let sub = (!suggested_changing_assoc_types).then(|| { - suggested_changing_assoc_types = true; - SuggestChangingAssocTypes { ty: hir_ty } - }); + if let [.., label_sp] = *inline_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, inline_spans, - BuiltinTypeAliasGenericBounds { suggestion, sub }, + BuiltinTypeAliasBounds::ParamBounds { + label: label_sp, + enable_feat_help, + suggestion: BuiltinTypeAliasParamBoundsSuggestion { + suggestions: inline_sugg, + applicability, + }, + qualify_assoc_tys_sugg, + }, ); } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 6c5f366727f95..6cf8b9330fc40 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -9,7 +9,7 @@ use rustc_errors::{ ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, }; -use rustc_hir::{def::Namespace, def_id::DefId}; +use rustc_hir::{self as hir, def::Namespace, def_id::DefId}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{ inhabitedness::InhabitedPredicate, Clause, PolyExistentialTraitRef, Ty, TyCtxt, @@ -22,9 +22,7 @@ use rustc_span::{ Span, Symbol, }; -use crate::{ - builtin::InitError, builtin::TypeAliasBounds, errors::OverruledAttributeSub, LateContext, -}; +use crate::{builtin::InitError, errors::OverruledAttributeSub, LateContext}; // array_into_iter.rs #[derive(LintDiagnostic)] @@ -263,84 +261,104 @@ pub struct BuiltinUnreachablePub<'a> { pub help: Option<()>, } -pub struct SuggestChangingAssocTypes<'a, 'b> { - pub ty: &'a rustc_hir::Ty<'b>, +#[derive(LintDiagnostic)] +#[diag(lint_macro_expr_fragment_specifier_2024_migration)] +pub struct MacroExprFragment2024 { + #[suggestion(code = "expr_2021", applicability = "machine-applicable")] + pub suggestion: Span, +} + +#[derive(LintDiagnostic)] +pub enum BuiltinTypeAliasBounds<'a, 'hir> { + #[diag(lint_builtin_type_alias_bounds_where_clause)] + #[note(lint_builtin_type_alias_bounds_limitation_note)] + WhereClause { + #[label(lint_builtin_type_alias_bounds_label)] + label: Span, + #[help(lint_builtin_type_alias_bounds_enable_feat_help)] + enable_feat_help: Option<()>, + #[suggestion(code = "")] + suggestion: (Span, Applicability), + #[subdiagnostic] + qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>, + }, + #[diag(lint_builtin_type_alias_bounds_param_bounds)] + #[note(lint_builtin_type_alias_bounds_limitation_note)] + ParamBounds { + #[label(lint_builtin_type_alias_bounds_label)] + label: Span, + #[help(lint_builtin_type_alias_bounds_enable_feat_help)] + enable_feat_help: Option<()>, + #[subdiagnostic] + suggestion: BuiltinTypeAliasParamBoundsSuggestion, + #[subdiagnostic] + qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>, + }, +} + +pub struct BuiltinTypeAliasParamBoundsSuggestion { + pub suggestions: Vec<(Span, String)>, + pub applicability: Applicability, } -impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> { +impl Subdiagnostic for BuiltinTypeAliasParamBoundsSuggestion { fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( self, diag: &mut Diag<'_, G>, _f: &F, ) { - // Access to associates types should use `<T as Bound>::Assoc`, which does not need a - // bound. Let's see if this type does that. - - // We use a HIR visitor to walk the type. - use rustc_hir::intravisit::{self, Visitor}; - struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> { - err: &'a mut Diag<'b, G>, - } - impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> { - fn visit_qpath( - &mut self, - qpath: &rustc_hir::QPath<'_>, - id: rustc_hir::HirId, - span: Span, - ) { - if TypeAliasBounds::is_type_variable_assoc(qpath) { - self.err.span_help(span, fluent::lint_builtin_type_alias_bounds_help); - } - intravisit::walk_qpath(self, qpath, id) - } - } - - // Let's go for a walk! - let mut visitor = WalkAssocTypes { err: diag }; - visitor.visit_ty(self.ty); + diag.arg("count", self.suggestions.len()); + diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, self.applicability); } } -#[derive(LintDiagnostic)] -#[diag(lint_builtin_type_alias_where_clause)] -pub struct BuiltinTypeAliasWhereClause<'a, 'b> { - #[suggestion(code = "", applicability = "machine-applicable")] - pub suggestion: Span, - #[subdiagnostic] - pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>, -} - -#[derive(LintDiagnostic)] -#[diag(lint_builtin_type_alias_generic_bounds)] -pub struct BuiltinTypeAliasGenericBounds<'a, 'b> { - #[subdiagnostic] - pub suggestion: BuiltinTypeAliasGenericBoundsSuggestion, - #[subdiagnostic] - pub sub: Option<SuggestChangingAssocTypes<'a, 'b>>, -} - -#[derive(LintDiagnostic)] -#[diag(lint_macro_expr_fragment_specifier_2024_migration)] -pub struct MacroExprFragment2024 { - #[suggestion(code = "expr_2021", applicability = "machine-applicable")] - pub suggestion: Span, +pub struct TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { + pub ty: &'a hir::Ty<'hir>, } -pub struct BuiltinTypeAliasGenericBoundsSuggestion { - pub suggestions: Vec<(Span, String)>, -} - -impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { +impl<'a, 'hir> Subdiagnostic for TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( self, diag: &mut Diag<'_, G>, _f: &F, ) { - diag.multipart_suggestion( - fluent::lint_suggestion, - self.suggestions, - Applicability::MachineApplicable, - ); + // We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to + // avoid doing throwaway work in case the lint ends up getting suppressed. + + use hir::intravisit::Visitor; + struct ProbeShorthandAssocTys<'a, 'b, G: EmissionGuarantee> { + diag: &'a mut Diag<'b, G>, + } + impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for ProbeShorthandAssocTys<'a, 'b, G> { + fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) { + // Look for "type-parameter shorthand-associated-types". I.e., paths of the + // form `T::Assoc` with `T` type param. These are reliant on trait bounds. + // Suggest fully qualifying them via `<T as /* Trait */>::Assoc`. + // + // Instead of attempting to figure out the necessary trait ref, just use a + // placeholder. Since we don't record type-dependent resolutions for non-body + // items like type aliases, we can't simply deduce the corresp. trait from + // the HIR path alone without rerunning parts of HIR ty lowering here + // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible. + // + // (We could employ some simple heuristics but that's likely not worth it). + if let hir::QPath::TypeRelative(qself, _) = qpath + && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind + && let hir::def::Res::Def(hir::def::DefKind::TyParam, _) = path.res + { + self.diag.multipart_suggestion( + fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, + vec![ + (qself.span.shrink_to_lo(), "<".into()), + (qself.span.shrink_to_hi(), " as /* Trait */>".into()), + ], + Applicability::HasPlaceholders, + ); + } + hir::intravisit::walk_qpath(self, qpath, id) + } + } + ProbeShorthandAssocTys { diag }.visit_ty(self.ty); } } diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.rs b/tests/ui/associated-inherent-types/type-alias-bounds.rs index c27bcbed43ae0..61641a83994ab 100644 --- a/tests/ui/associated-inherent-types/type-alias-bounds.rs +++ b/tests/ui/associated-inherent-types/type-alias-bounds.rs @@ -19,7 +19,7 @@ // automatically lead to full wfchecking and lint TAB getting suppressed. pub type Alias<T: Bound> = (Source<T>::Assoc,); -//~^ WARN bounds on generic parameters are not enforced in type aliases +//~^ WARN bounds on generic parameters in type aliases are not enforced pub struct Source<T>(T); pub trait Bound {} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds.stderr b/tests/ui/associated-inherent-types/type-alias-bounds.stderr index c5d0e9e5041df..c56dd498f7708 100644 --- a/tests/ui/associated-inherent-types/type-alias-bounds.stderr +++ b/tests/ui/associated-inherent-types/type-alias-bounds.stderr @@ -1,15 +1,16 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:21:19 | LL | pub type Alias<T: Bound> = (Source<T>::Assoc,); - | ^^^^^ + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias<T: Bound> = (Source<T>::Assoc,); -LL + pub type Alias<T> = (Source<T>::Assoc,); - | warning: 1 warning emitted diff --git a/tests/ui/associated-type-bounds/type-alias.stderr b/tests/ui/associated-type-bounds/type-alias.stderr index 072c471467c77..d59952b4a146a 100644 --- a/tests/ui/associated-type-bounds/type-alias.stderr +++ b/tests/ui/associated-type-bounds/type-alias.stderr @@ -1,147 +1,159 @@ -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:3:25 | LL | type _TaWhere1<T> where T: Iterator<Item: Copy> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | + | ------^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere1<T> where T: Iterator<Item: Copy> = T; -LL + type _TaWhere1<T> = T; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:4:25 | LL | type _TaWhere2<T> where T: Iterator<Item: 'static> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere2<T> where T: Iterator<Item: 'static> = T; -LL + type _TaWhere2<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:5:25 | LL | type _TaWhere3<T> where T: Iterator<Item: 'static> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere3<T> where T: Iterator<Item: 'static> = T; -LL + type _TaWhere3<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:6:25 | LL | type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere4<T> where T: Iterator<Item: 'static + Copy + Send> = T; -LL + type _TaWhere4<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:7:25 | LL | type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere5<T> where T: Iterator<Item: for<'a> Into<&'a u8>> = T; -LL + type _TaWhere5<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias.rs:8:25 | LL | type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type _TaWhere6<T> where T: Iterator<Item: Iterator<Item: Copy>> = T; -LL + type _TaWhere6<T> = T; - | + | ------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:10:20 | LL | type _TaInline1<T: Iterator<Item: Copy>> = T; - | ^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline1<T: Iterator<Item: Copy>> = T; -LL + type _TaInline1<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:11:20 | LL | type _TaInline2<T: Iterator<Item: 'static>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline2<T: Iterator<Item: 'static>> = T; -LL + type _TaInline2<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:12:20 | LL | type _TaInline3<T: Iterator<Item: 'static>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline3<T: Iterator<Item: 'static>> = T; -LL + type _TaInline3<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:13:20 | LL | type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline4<T: Iterator<Item: 'static + Copy + Send>> = T; -LL + type _TaInline4<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:14:20 | LL | type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline5<T: Iterator<Item: for<'a> Into<&'a u8>>> = T; -LL + type _TaInline5<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias.rs:15:20 | LL | type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type _TaInline6<T: Iterator<Item: Iterator<Item: Copy>>> = T; -LL + type _TaInline6<T> = T; - | + | --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics warning: 12 warnings emitted diff --git a/tests/ui/privacy/private-in-public-warn.rs b/tests/ui/privacy/private-in-public-warn.rs index 99d318e36be3d..746b98fbd078e 100644 --- a/tests/ui/privacy/private-in-public-warn.rs +++ b/tests/ui/privacy/private-in-public-warn.rs @@ -39,7 +39,7 @@ mod traits { pub trait PubTr {} pub type Alias<T: PrivTr> = T; //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Alias` - //~^ WARNING bounds on generic parameters are not enforced in type aliases + //~^ WARNING bounds on generic parameters in type aliases are not enforced pub trait Tr1: PrivTr {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr1` pub trait Tr2<T: PrivTr> {} //~ ERROR trait `traits::PrivTr` is more private than the item `traits::Tr2` pub trait Tr3 { @@ -58,7 +58,7 @@ mod traits_where { pub type Alias<T> where T: PrivTr = T; //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Alias` - //~| WARNING where clauses are not enforced in type aliases + //~| WARNING where clauses on type aliases are not enforced pub trait Tr2<T> where T: PrivTr {} //~^ ERROR trait `traits_where::PrivTr` is more private than the item `traits_where::Tr2` pub trait Tr3 { diff --git a/tests/ui/privacy/private-in-public-warn.stderr b/tests/ui/privacy/private-in-public-warn.stderr index ac7e5547de914..3f7b8c281e706 100644 --- a/tests/ui/privacy/private-in-public-warn.stderr +++ b/tests/ui/privacy/private-in-public-warn.stderr @@ -395,30 +395,32 @@ note: but type `Priv2` is only usable at visibility `pub(self)` LL | struct Priv2; | ^^^^^^^^^^^^ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/private-in-public-warn.rs:41:23 | LL | pub type Alias<T: PrivTr> = T; - | ^^^^^^ - | + | --^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias<T: PrivTr> = T; -LL + pub type Alias<T> = T; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/private-in-public-warn.rs:59:29 | LL | pub type Alias<T> where T: PrivTr = T; - | ^^^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - pub type Alias<T> where T: PrivTr = T; -LL + pub type Alias<T> = T; - | + | ------^^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics error: aborting due to 34 previous errors; 2 warnings emitted diff --git a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr index d66e468873ba4..0eae68bfcf0f8 100644 --- a/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr +++ b/tests/ui/trivial-bounds/trivial-bounds-inconsistent.stderr @@ -24,18 +24,19 @@ warning: trait bound i32: Foo does not depend on any type or lifetime parameters LL | union U where i32: Foo { f: i32 } | ^^^ -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/trivial-bounds-inconsistent.rs:22:14 | LL | type Y where i32: Foo = (); - | ^^^^^^^^ - | + | ------^^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type Y where i32: Foo = (); -LL + type Y = (); - | warning: trait bound i32: Foo does not depend on any type or lifetime parameters --> $DIR/trivial-bounds-inconsistent.rs:22:19 diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs index 5ee3c027f4049..52e0887175d44 100644 --- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs +++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.rs @@ -3,6 +3,6 @@ //@ check-pass pub type T<P: Send + Send + Send> = P; -//~^ WARN bounds on generic parameters are not enforced in type aliases +//~^ WARN bounds on generic parameters in type aliases are not enforced fn main() {} diff --git a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr index 125ffbbb41757..9fd0fe4913b47 100644 --- a/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr +++ b/tests/ui/type/issue-67690-type-alias-bound-diagnostic-crash.stderr @@ -1,15 +1,16 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/issue-67690-type-alias-bound-diagnostic-crash.rs:5:15 | LL | pub type T<P: Send + Send + Send> = P; - | ^^^^ ^^^^ ^^^^ + | --^^^^---^^^^---^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - pub type T<P: Send + Send + Send> = P; -LL + pub type T<P> = P; - | warning: 1 warning emitted diff --git a/tests/ui/type/type-alias-bounds.rs b/tests/ui/type/type-alias-bounds.rs index 6d63c0c7e1bda..37c073fe1f95d 100644 --- a/tests/ui/type/type-alias-bounds.rs +++ b/tests/ui/type/type-alias-bounds.rs @@ -6,15 +6,15 @@ use std::rc::Rc; type SVec<T: Send + Send> = Vec<T>; -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type S2Vec<T> where T: Send = Vec<T>; -//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] +//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds] type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); -//~^ WARN bounds on generic parameters are not enforced in type aliases [type_alias_bounds] +//~^ WARN bounds on generic parameters in type aliases are not enforced [type_alias_bounds] type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); -//~^ WARN where clauses are not enforced in type aliases [type_alias_bounds] +//~^ WARN where clauses on type aliases are not enforced [type_alias_bounds] static STATIC: u32 = 0; @@ -42,10 +42,11 @@ fn foo<'a>(y: &'a i32) { struct Sendable<T: Send>(T); type MySendable<T> = Sendable<T>; // no error here! -// However, bounds *are* taken into account when accessing associated types +// Bounds on type params do enable shorthand type alias paths. +// However, that doesn't actually mean that they are properly enforced. trait Bound { type Assoc; } -type T1<U: Bound> = U::Assoc; //~ WARN not enforced in type aliases -type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases +type T1<U: Bound> = U::Assoc; //~ WARN are not enforced +type T2<U> where U: Bound = U::Assoc; //~ WARN are not enforced // This errors: // `type T3<U> = U::Assoc;` @@ -53,7 +54,7 @@ type T2<U> where U: Bound = U::Assoc; //~ WARN not enforced in type aliases type T4<U> = <U as Bound>::Assoc; // Make sure the help about associated types is not shown incorrectly -type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN not enforced in type aliases -type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN not enforced in type aliases +type T5<U: Bound> = <U as Bound>::Assoc; //~ WARN are not enforced +type T6<U: Bound> = ::std::vec::Vec<U>; //~ WARN are not enforced fn main() {} diff --git a/tests/ui/type/type-alias-bounds.stderr b/tests/ui/type/type-alias-bounds.stderr index 92e573393c90e..15c0090106655 100644 --- a/tests/ui/type/type-alias-bounds.stderr +++ b/tests/ui/type/type-alias-bounds.stderr @@ -1,121 +1,132 @@ -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:8:14 | LL | type SVec<T: Send + Send> = Vec<T>; - | ^^^^ ^^^^ - | + | --^^^^---^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics = note: `#[warn(type_alias_bounds)]` on by default -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type SVec<T: Send + Send> = Vec<T>; -LL + type SVec<T> = Vec<T>; - | -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias-bounds.rs:10:21 | LL | type S2Vec<T> where T: Send = Vec<T>; - | ^^^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type S2Vec<T> where T: Send = Vec<T>; -LL + type S2Vec<T> = Vec<T>; - | + | ------^^^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:12:19 | LL | type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); - | ^^ ^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type VVec<'b, 'a: 'b + 'b> = (&'b u32, Vec<&'a i32>); -LL + type VVec<'b, 'a> = (&'b u32, Vec<&'a i32>); - | + | --^^---^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases +warning: bounds on generic parameters in type aliases are not enforced --> $DIR/type-alias-bounds.rs:14:18 | LL | type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); - | ^^ ^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type WVec<'b, T: 'b + 'b> = (&'b u32, Vec<T>); -LL + type WVec<'b, T> = (&'b u32, Vec<T>); - | + | --^^---^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: where clauses are not enforced in type aliases +warning: where clauses on type aliases are not enforced --> $DIR/type-alias-bounds.rs:16:25 | LL | type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); - | ^^^^^ ^^^^^ - | -help: the clause will not be checked when the type alias is used, and should be removed - | -LL - type W2Vec<'b, T> where T: 'b, T: 'b = (&'b u32, Vec<T>); -LL + type W2Vec<'b, T> = (&'b u32, Vec<T>); - | + | ------^^^^^--^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this where clause + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:47:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:48:12 | LL | type T1<U: Bound> = U::Assoc; - | ^^^^^ + | ^^^^^ will not be checked at usage sites of the type alias | -help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases - --> $DIR/type-alias-bounds.rs:47:21 - | -LL | type T1<U: Bound> = U::Assoc; - | ^^^^^^^^ -help: the bound will not be checked when the type alias is used, and should be removed + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +help: remove this bound | LL - type T1<U: Bound> = U::Assoc; LL + type T1<U> = U::Assoc; | +help: fully qualify this associated type + | +LL | type T1<U: Bound> = <U as /* Trait */>::Assoc; + | + +++++++++++++++ -warning: where clauses are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:48:18 +warning: where clauses on type aliases are not enforced + --> $DIR/type-alias-bounds.rs:49:18 | LL | type T2<U> where U: Bound = U::Assoc; - | ^^^^^^^^ + | ^^^^^^^^ will not be checked at usage sites of the type alias | -help: use fully disambiguated paths (i.e., `<T as Trait>::Assoc`) to refer to associated types in type aliases - --> $DIR/type-alias-bounds.rs:48:29 - | -LL | type T2<U> where U: Bound = U::Assoc; - | ^^^^^^^^ -help: the clause will not be checked when the type alias is used, and should be removed + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics +help: remove this where clause | LL - type T2<U> where U: Bound = U::Assoc; LL + type T2<U> = U::Assoc; | +help: fully qualify this associated type + | +LL | type T2<U> where U: Bound = <U as /* Trait */>::Assoc; + | + +++++++++++++++ -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:56:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:57:12 | LL | type T5<U: Bound> = <U as Bound>::Assoc; - | ^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type T5<U: Bound> = <U as Bound>::Assoc; -LL + type T5<U> = <U as Bound>::Assoc; - | + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics -warning: bounds on generic parameters are not enforced in type aliases - --> $DIR/type-alias-bounds.rs:57:12 +warning: bounds on generic parameters in type aliases are not enforced + --> $DIR/type-alias-bounds.rs:58:12 | LL | type T6<U: Bound> = ::std::vec::Vec<U>; - | ^^^^^ - | -help: the bound will not be checked when the type alias is used, and should be removed - | -LL - type T6<U: Bound> = ::std::vec::Vec<U>; -LL + type T6<U> = ::std::vec::Vec<U>; - | + | --^^^^^ + | | | + | | will not be checked at usage sites of the type alias + | help: remove this bound + | + = note: this is a known limitation of the type checker that may be lifted in a future edition. + see issue #112792 <https://github.com/rust-lang/rust/issues/112792> for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable the desired semantics warning: 9 warnings emitted From d67b61637e96e6e72b5a676b2f02d1a01b3a94a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Sun, 16 Jun 2024 12:22:12 +0200 Subject: [PATCH 8/9] Make lint type_alias_bounds's removal sugg maybe-incorrect if the RHS contains shorthand assoc tys --- compiler/rustc_lint/src/builtin.rs | 91 +++++++++--------- compiler/rustc_lint/src/lints.rs | 147 ++++++++++++----------------- 2 files changed, 109 insertions(+), 129 deletions(-) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index a8c8c71927aaf..4e1e19e522e9d 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -32,11 +32,10 @@ use crate::{ BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc, BuiltinMutablesTransmutes, BuiltinNoMangleGeneric, BuiltinNonShorthandFieldPatterns, BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasBounds, - BuiltinTypeAliasParamBoundsSuggestion, BuiltinUngatedAsyncFnTrackCaller, - BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, - BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, - BuiltinUnusedDocCommentSub, BuiltinWhileTrue, InvalidAsmLabel, - TypeAliasBoundsQualifyAssocTysSugg, + BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, + BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, + BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, + BuiltinWhileTrue, InvalidAsmLabel, }, EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, }; @@ -1406,9 +1405,23 @@ declare_lint_pass!( TypeAliasBounds => [TYPE_ALIAS_BOUNDS] ); +impl TypeAliasBounds { + pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool { + // Bounds of the form `T: 'a` with `T` type param affect object lifetime defaults. + if let hir::WherePredicate::BoundPredicate(pred) = pred + && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) + && pred.bound_generic_params.is_empty() // indeed, even if absent from the RHS + && pred.bounded_ty.as_generic_param().is_some() + { + return true; + } + false + } +} + impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - let hir::ItemKind::TyAlias(hir_ty, generics) = &item.kind else { return }; + let hir::ItemKind::TyAlias(hir_ty, generics) = item.kind else { return }; // There must not be a where clause. if generics.predicates.is_empty() { @@ -1437,7 +1450,6 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { let mut where_spans = Vec::new(); let mut inline_spans = Vec::new(); let mut inline_sugg = Vec::new(); - let mut affects_object_lifetime_defaults = false; for p in generics.predicates { let span = p.span(); @@ -1449,45 +1461,22 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { } inline_sugg.push((span, String::new())); } - - // FIXME(fmease): Move this into a "diagnostic decorator" for increased laziness - // Bounds of the form `T: 'a` where `T` is a type param of - // the type alias affect object lifetime defaults. - if !affects_object_lifetime_defaults - && let hir::WherePredicate::BoundPredicate(pred) = p - && pred.bounds.iter().any(|bound| matches!(bound, hir::GenericBound::Outlives(_))) - && pred.bound_generic_params.is_empty() - && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = pred.bounded_ty.kind - && let Res::Def(DefKind::TyParam, _) = path.res - { - affects_object_lifetime_defaults = true; - } } - // FIXME(fmease): Add a disclaimer (in the form of a multi-span note) that the removal of - // type-param-outlives-bounds affects OLDs and explicit object lifetime - // bounds might be required [...]. - // FIXME(fmease): The applicability should also depend on the outcome of the HIR walker - // inside of `TypeAliasBoundsQualifyAssocTysSugg`: Whether it found a - // shorthand projection or not. - let applicability = if affects_object_lifetime_defaults { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }; - - let mut qualify_assoc_tys_sugg = Some(TypeAliasBoundsQualifyAssocTysSugg { ty: hir_ty }); - let enable_feat_help = cx.tcx.sess.is_nightly_build().then_some(()); + let mut ty = Some(hir_ty); + let enable_feat_help = cx.tcx.sess.is_nightly_build(); if let [.., label_sp] = *where_spans { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, where_spans, - BuiltinTypeAliasBounds::WhereClause { + BuiltinTypeAliasBounds { + in_where_clause: true, label: label_sp, enable_feat_help, - suggestion: (generics.where_clause_span, applicability), - qualify_assoc_tys_sugg: qualify_assoc_tys_sugg.take(), + suggestions: vec![(generics.where_clause_span, String::new())], + preds: generics.predicates, + ty: ty.take(), }, ); } @@ -1495,20 +1484,36 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { cx.emit_span_lint( TYPE_ALIAS_BOUNDS, inline_spans, - BuiltinTypeAliasBounds::ParamBounds { + BuiltinTypeAliasBounds { + in_where_clause: false, label: label_sp, enable_feat_help, - suggestion: BuiltinTypeAliasParamBoundsSuggestion { - suggestions: inline_sugg, - applicability, - }, - qualify_assoc_tys_sugg, + suggestions: inline_sugg, + preds: generics.predicates, + ty, }, ); } } } +pub(crate) struct ShorthandAssocTyCollector { + pub(crate) qselves: Vec<Span>, +} + +impl hir::intravisit::Visitor<'_> for ShorthandAssocTyCollector { + fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) { + // Look for "type-parameter shorthand-associated-types". I.e., paths of the + // form `T::Assoc` with `T` type param. These are reliant on trait bounds. + if let hir::QPath::TypeRelative(qself, _) = qpath + && qself.as_generic_param().is_some() + { + self.qselves.push(qself.span); + } + hir::intravisit::walk_qpath(self, qpath, id) + } +} + declare_lint! { /// The `trivial_bounds` lint detects trait bounds that don't depend on /// any type parameters. diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 6cf8b9330fc40..b833d56c9bc95 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2,8 +2,10 @@ #![allow(rustc::untranslatable_diagnostic)] use std::num::NonZero; -use crate::errors::RequestedLevel; +use crate::builtin::{InitError, ShorthandAssocTyCollector, TypeAliasBounds}; +use crate::errors::{OverruledAttributeSub, RequestedLevel}; use crate::fluent_generated as fluent; +use crate::LateContext; use rustc_errors::{ codes::*, Applicability, Diag, DiagArgValue, DiagMessage, DiagStyledString, ElidedLifetimeInPathSubdiag, EmissionGuarantee, LintDiagnostic, MultiSpan, SubdiagMessageOp, @@ -22,8 +24,6 @@ use rustc_span::{ Span, Symbol, }; -use crate::{builtin::InitError, errors::OverruledAttributeSub, LateContext}; - // array_into_iter.rs #[derive(LintDiagnostic)] #[diag(lint_shadowed_into_iter)] @@ -268,97 +268,72 @@ pub struct MacroExprFragment2024 { pub suggestion: Span, } -#[derive(LintDiagnostic)] -pub enum BuiltinTypeAliasBounds<'a, 'hir> { - #[diag(lint_builtin_type_alias_bounds_where_clause)] - #[note(lint_builtin_type_alias_bounds_limitation_note)] - WhereClause { - #[label(lint_builtin_type_alias_bounds_label)] - label: Span, - #[help(lint_builtin_type_alias_bounds_enable_feat_help)] - enable_feat_help: Option<()>, - #[suggestion(code = "")] - suggestion: (Span, Applicability), - #[subdiagnostic] - qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>, - }, - #[diag(lint_builtin_type_alias_bounds_param_bounds)] - #[note(lint_builtin_type_alias_bounds_limitation_note)] - ParamBounds { - #[label(lint_builtin_type_alias_bounds_label)] - label: Span, - #[help(lint_builtin_type_alias_bounds_enable_feat_help)] - enable_feat_help: Option<()>, - #[subdiagnostic] - suggestion: BuiltinTypeAliasParamBoundsSuggestion, - #[subdiagnostic] - qualify_assoc_tys_sugg: Option<TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir>>, - }, -} - -pub struct BuiltinTypeAliasParamBoundsSuggestion { +pub struct BuiltinTypeAliasBounds<'a, 'hir> { + pub in_where_clause: bool, + pub label: Span, + pub enable_feat_help: bool, pub suggestions: Vec<(Span, String)>, - pub applicability: Applicability, + pub preds: &'hir [hir::WherePredicate<'hir>], + pub ty: Option<&'a hir::Ty<'hir>>, } -impl Subdiagnostic for BuiltinTypeAliasParamBoundsSuggestion { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { - diag.arg("count", self.suggestions.len()); - diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, self.applicability); - } -} - -pub struct TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { - pub ty: &'a hir::Ty<'hir>, -} +impl<'a> LintDiagnostic<'a, ()> for BuiltinTypeAliasBounds<'_, '_> { + fn decorate_lint<'b>(self, diag: &'b mut Diag<'a, ()>) { + diag.primary_message(if self.in_where_clause { + fluent::lint_builtin_type_alias_bounds_where_clause + } else { + fluent::lint_builtin_type_alias_bounds_param_bounds + }); + diag.span_label(self.label, fluent::lint_builtin_type_alias_bounds_label); + diag.note(fluent::lint_builtin_type_alias_bounds_limitation_note); + if self.enable_feat_help { + diag.help(fluent::lint_builtin_type_alias_bounds_enable_feat_help); + } -impl<'a, 'hir> Subdiagnostic for TypeAliasBoundsQualifyAssocTysSugg<'a, 'hir> { - fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( - self, - diag: &mut Diag<'_, G>, - _f: &F, - ) { // We perform the walk in here instead of in `<TypeAliasBounds as LateLintPass>` to // avoid doing throwaway work in case the lint ends up getting suppressed. - - use hir::intravisit::Visitor; - struct ProbeShorthandAssocTys<'a, 'b, G: EmissionGuarantee> { - diag: &'a mut Diag<'b, G>, + let mut collector = ShorthandAssocTyCollector { qselves: Vec::new() }; + if let Some(ty) = self.ty { + hir::intravisit::Visitor::visit_ty(&mut collector, ty); } - impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for ProbeShorthandAssocTys<'a, 'b, G> { - fn visit_qpath(&mut self, qpath: &hir::QPath<'_>, id: hir::HirId, _: Span) { - // Look for "type-parameter shorthand-associated-types". I.e., paths of the - // form `T::Assoc` with `T` type param. These are reliant on trait bounds. - // Suggest fully qualifying them via `<T as /* Trait */>::Assoc`. - // - // Instead of attempting to figure out the necessary trait ref, just use a - // placeholder. Since we don't record type-dependent resolutions for non-body - // items like type aliases, we can't simply deduce the corresp. trait from - // the HIR path alone without rerunning parts of HIR ty lowering here - // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible. - // - // (We could employ some simple heuristics but that's likely not worth it). - if let hir::QPath::TypeRelative(qself, _) = qpath - && let hir::TyKind::Path(hir::QPath::Resolved(None, path)) = qself.kind - && let hir::def::Res::Def(hir::def::DefKind::TyParam, _) = path.res - { - self.diag.multipart_suggestion( - fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, - vec![ - (qself.span.shrink_to_lo(), "<".into()), - (qself.span.shrink_to_hi(), " as /* Trait */>".into()), - ], - Applicability::HasPlaceholders, - ); - } - hir::intravisit::walk_qpath(self, qpath, id) - } + + let affect_object_lifetime_defaults = self + .preds + .iter() + .filter(|pred| pred.in_where_clause() == self.in_where_clause) + .any(|pred| TypeAliasBounds::affects_object_lifetime_defaults(pred)); + + // If there are any shorthand assoc tys, then the bounds can't be removed automatically. + // The user first needs to fully qualify the assoc tys. + let applicability = if !collector.qselves.is_empty() || affect_object_lifetime_defaults { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + + diag.arg("count", self.suggestions.len()); + diag.multipart_suggestion(fluent::lint_suggestion, self.suggestions, applicability); + + // Suggest fully qualifying paths of the form `T::Assoc` with `T` type param via + // `<T as /* Trait */>::Assoc` to remove their reliance on any type param bounds. + // + // Instead of attempting to figure out the necessary trait ref, just use a + // placeholder. Since we don't record type-dependent resolutions for non-body + // items like type aliases, we can't simply deduce the corresp. trait from + // the HIR path alone without rerunning parts of HIR ty lowering here + // (namely `probe_single_ty_param_bound_for_assoc_ty`) which is infeasible. + // + // (We could employ some simple heuristics but that's likely not worth it). + for qself in collector.qselves { + diag.multipart_suggestion( + fluent::lint_builtin_type_alias_bounds_qualify_assoc_tys_sugg, + vec![ + (qself.shrink_to_lo(), "<".into()), + (qself.shrink_to_hi(), " as /* Trait */>".into()), + ], + Applicability::HasPlaceholders, + ); } - ProbeShorthandAssocTys { diag }.visit_ty(self.ty); } } From 5859dff7429436498aed7fc7efed88a73b3eaf4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= <me@fmease.dev> Date: Sun, 16 Jun 2024 15:53:01 +0200 Subject: [PATCH 9/9] Update the description of lint type_alias_bounds --- compiler/rustc_lint/src/builtin.rs | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 4e1e19e522e9d..ab0b47d48e5b5 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1390,20 +1390,32 @@ declare_lint! { /// /// ### Explanation /// - /// The trait bounds in a type alias are currently ignored, and should not - /// be included to avoid confusion. This was previously allowed - /// unintentionally; this may become a hard error in the future. + /// Trait and lifetime bounds on generic parameters and in where clauses of + /// type aliases are not checked at usage sites of the type alias. Moreover, + /// they are not thoroughly checked for correctness at their definition site + /// either similar to the aliased type. + /// + /// This is a known limitation of the type checker that may be lifted in a + /// future edition. Permitting such bounds in light of this was unintentional. + /// + /// While these bounds may have secondary effects such as enabling the use of + /// "shorthand" associated type paths[^1] and affecting the default trait + /// object lifetime[^2] of trait object types passed to the type alias, this + /// should not have been allowed until the aforementioned restrictions of the + /// type checker have been lifted. + /// + /// Using such bounds is highly discouraged as they are actively misleading. + /// + /// [^1]: I.e., paths of the form `T::Assoc` where `T` is a type parameter + /// bounded by trait `Trait` which defines an associated type called `Assoc` + /// as opposed to a fully qualified path of the form `<T as Trait>::Assoc`. + /// [^2]: <https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes> TYPE_ALIAS_BOUNDS, Warn, "bounds in type aliases are not enforced" } -declare_lint_pass!( - /// Lint for trait and lifetime bounds in type aliases being mostly ignored. - /// They are relevant when using associated types, but otherwise neither checked - /// at definition site nor enforced at use site. - TypeAliasBounds => [TYPE_ALIAS_BOUNDS] -); +declare_lint_pass!(TypeAliasBounds => [TYPE_ALIAS_BOUNDS]); impl TypeAliasBounds { pub(crate) fn affects_object_lifetime_defaults(pred: &hir::WherePredicate<'_>) -> bool {