From 32c97da0f466ceff1512d62729c246a8e3951afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Tue, 16 Feb 2021 11:02:39 -0800 Subject: [PATCH] In some limited cases, suggest `where` bounds for non-type params Partially address #81971. --- compiler/rustc_middle/src/ty/diagnostics.rs | 30 +++++++++++++++++++ .../src/traits/error_reporting/suggestions.rs | 26 ++++++++++++++-- src/test/ui/partialeq_help.stderr | 4 +++ .../deafult-associated-type-bound-2.stderr | 4 +++ src/test/ui/suggestions/suggest-change-mut.rs | 2 +- .../ui/suggestions/suggest-change-mut.stderr | 4 +++ .../ui/traits/suggest-where-clause.stderr | 8 +++++ 7 files changed, 74 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_middle/src/ty/diagnostics.rs b/compiler/rustc_middle/src/ty/diagnostics.rs index 3b7dc25b6cf50..f41bb7e6d6350 100644 --- a/compiler/rustc_middle/src/ty/diagnostics.rs +++ b/compiler/rustc_middle/src/ty/diagnostics.rs @@ -75,6 +75,36 @@ impl<'tcx> TyS<'tcx> { } } +pub fn suggest_arbitrary_trait_bound( + generics: &hir::Generics<'_>, + err: &mut DiagnosticBuilder<'_>, + param_name: &str, + constraint: &str, +) -> bool { + let param = generics.params.iter().find(|p| p.name.ident().as_str() == param_name); + match (param, param_name) { + (Some(_), "Self") => return false, + _ => {} + } + // Suggest a where clause bound for a non-type paremeter. + let (action, prefix) = if generics.where_clause.predicates.is_empty() { + ("introducing a", " where ") + } else { + ("extending the", ", ") + }; + err.span_suggestion_verbose( + generics.where_clause.tail_span_for_suggestion(), + &format!( + "consider {} `where` bound, but there might be an alternative better way to express \ + this requirement", + action, + ), + format!("{}{}: {}", prefix, param_name, constraint), + Applicability::MaybeIncorrect, + ); + true +} + /// Suggest restricting a type param with a new bound. pub fn suggest_constraining_type_param( tcx: TyCtxt<'_>, diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 9fd2f12100740..5c97791530d99 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -17,8 +17,8 @@ use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_middle::ty::{ - self, suggest_constraining_type_param, AdtKind, DefIdTree, Infer, InferTy, ToPredicate, Ty, - TyCtxt, TypeFoldable, WithConstness, + self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, + Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -334,7 +334,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let (param_ty, projection) = match self_ty.kind() { ty::Param(_) => (true, None), ty::Projection(projection) => (false, Some(projection)), - _ => return, + _ => (false, None), }; // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we @@ -453,6 +453,26 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } } + hir::Node::Item(hir::Item { + kind: + hir::ItemKind::Struct(_, generics) + | hir::ItemKind::Enum(_, generics) + | hir::ItemKind::Union(_, generics) + | hir::ItemKind::Trait(_, _, generics, ..) + | hir::ItemKind::Impl(hir::Impl { generics, .. }) + | hir::ItemKind::Fn(_, generics, _) + | hir::ItemKind::TyAlias(_, generics) + | hir::ItemKind::TraitAlias(generics, _) + | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), + .. + }) if !param_ty => { + // Missing generic type parameter bound. + let param_name = self_ty.to_string(); + let constraint = trait_ref.print_only_trait_path().to_string(); + if suggest_arbitrary_trait_bound(generics, &mut err, ¶m_name, &constraint) { + return; + } + } hir::Node::Crate(..) => return, _ => {} diff --git a/src/test/ui/partialeq_help.stderr b/src/test/ui/partialeq_help.stderr index 6decbef1af4a2..e14e17c162233 100644 --- a/src/test/ui/partialeq_help.stderr +++ b/src/test/ui/partialeq_help.stderr @@ -5,6 +5,10 @@ LL | a == b; | ^^ no implementation for `&T == T` | = help: the trait `PartialEq` is not implemented for `&T` +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +LL | fn foo(a: &T, b: T) where &T: PartialEq { + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/src/test/ui/specialization/deafult-associated-type-bound-2.stderr b/src/test/ui/specialization/deafult-associated-type-bound-2.stderr index 2bc14dbe3b2e7..0e8a774bce37f 100644 --- a/src/test/ui/specialization/deafult-associated-type-bound-2.stderr +++ b/src/test/ui/specialization/deafult-associated-type-bound-2.stderr @@ -18,6 +18,10 @@ LL | default type U = &'static B; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `&'static B == B` | = help: the trait `PartialEq` is not implemented for `&'static B` +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +LL | impl X for T where &'static B: PartialEq { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error; 1 warning emitted diff --git a/src/test/ui/suggestions/suggest-change-mut.rs b/src/test/ui/suggestions/suggest-change-mut.rs index 8b465aae66b6e..a2bc6fd09b566 100644 --- a/src/test/ui/suggestions/suggest-change-mut.rs +++ b/src/test/ui/suggestions/suggest-change-mut.rs @@ -2,7 +2,7 @@ use std::io::{BufRead, BufReader, Read, Write}; -fn issue_81421(mut stream: T) { +fn issue_81421(mut stream: T) { //~ HELP consider introducing a `where` bound let initial_message = format!("Hello world"); let mut buffer: Vec = Vec::new(); let bytes_written = stream.write_all(initial_message.as_bytes()); diff --git a/src/test/ui/suggestions/suggest-change-mut.stderr b/src/test/ui/suggestions/suggest-change-mut.stderr index cb156f7c7877a..9b8181647a0c1 100644 --- a/src/test/ui/suggestions/suggest-change-mut.stderr +++ b/src/test/ui/suggestions/suggest-change-mut.stderr @@ -9,6 +9,10 @@ help: consider removing the leading `&`-reference | LL | let mut stream_reader = BufReader::new(stream); | -- +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +LL | fn issue_81421(mut stream: T) where &T: std::io::Read { + | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing this borrow's mutability | LL | let mut stream_reader = BufReader::new(&mut stream); diff --git a/src/test/ui/traits/suggest-where-clause.stderr b/src/test/ui/traits/suggest-where-clause.stderr index b50017afa4d63..86d589ffa9e31 100644 --- a/src/test/ui/traits/suggest-where-clause.stderr +++ b/src/test/ui/traits/suggest-where-clause.stderr @@ -35,6 +35,10 @@ LL | >::from; | ^^^^^^^^^^^^^^^^^^^^^^ the trait `From` is not implemented for `u64` | = note: required by `from` +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +LL | fn check() where u64: From { + | ^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `u64: From<::Item>` is not satisfied --> $DIR/suggest-where-clause.rs:18:5 @@ -43,6 +47,10 @@ LL | ::Item>>::from; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `From<::Item>` is not implemented for `u64` | = note: required by `from` +help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement + | +LL | fn check() where u64: From<::Item> { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0277]: the trait bound `Misc<_>: From` is not satisfied --> $DIR/suggest-where-clause.rs:23:5