use super::{
- EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation,
- SelectionContext,
+ DerivedObligationCause, EvaluationResult, ImplDerivedObligationCause, Obligation,
+ ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext,
};
use crate::autoderef::Autoderef;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder,
- ErrorGuaranteed, Style,
+ ErrorGuaranteed, MultiSpan, Style,
};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::intravisit::Visitor;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node};
+use rustc_middle::hir::map;
use rustc_middle::ty::{
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
- Infer, InferTy, ToPredicate, Ty, TyCtxt, TypeFoldable,
+ GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, ToPredicate, Ty, TyCtxt,
+ TypeFoldable,
};
use rustc_middle::ty::{TypeAndMut, TypeckResults};
use rustc_session::Limit;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
-use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span, DUMMY_SP};
+use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span, DUMMY_SP};
use rustc_target::spec::abi;
use std::fmt;
Upvar(Span),
}
+// This type provides a uniform interface to retrieve data on generators, whether it originated from
+// the local crate being compiled or from a foreign crate.
+#[derive(Debug)]
+pub enum GeneratorData<'tcx, 'a> {
+ Local(&'a TypeckResults<'tcx>),
+ Foreign(&'tcx GeneratorDiagnosticData<'tcx>),
+}
+
+impl<'tcx, 'a> GeneratorData<'tcx, 'a> {
+ // Try to get information about variables captured by the generator that matches a type we are
+ // looking for with `ty_matches` function. We uses it to find upvar which causes a failure to
+ // meet an obligation
+ fn try_get_upvar_span<F>(
+ &self,
+ infer_context: &InferCtxt<'a, 'tcx>,
+ generator_did: DefId,
+ ty_matches: F,
+ ) -> Option<GeneratorInteriorOrUpvar>
+ where
+ F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,
+ {
+ match self {
+ GeneratorData::Local(typeck_results) => {
+ infer_context.tcx.upvars_mentioned(generator_did).and_then(|upvars| {
+ upvars.iter().find_map(|(upvar_id, upvar)| {
+ let upvar_ty = typeck_results.node_type(*upvar_id);
+ let upvar_ty = infer_context.resolve_vars_if_possible(upvar_ty);
+ if ty_matches(ty::Binder::dummy(upvar_ty)) {
+ Some(GeneratorInteriorOrUpvar::Upvar(upvar.span))
+ } else {
+ None
+ }
+ })
+ })
+ }
+ GeneratorData::Foreign(_) => None,
+ }
+ }
+
+ // Try to get the span of a type being awaited on that matches the type we are looking with the
+ // `ty_matches` function. We uses it to find awaited type which causes a failure to meet an
+ // obligation
+ fn get_from_await_ty<F>(
+ &self,
+ visitor: AwaitsVisitor,
+ hir: map::Map<'tcx>,
+ ty_matches: F,
+ ) -> Option<Span>
+ where
+ F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,
+ {
+ match self {
+ GeneratorData::Local(typeck_results) => visitor
+ .awaits
+ .into_iter()
+ .map(|id| hir.expect_expr(id))
+ .find(|await_expr| {
+ ty_matches(ty::Binder::dummy(typeck_results.expr_ty_adjusted(&await_expr)))
+ })
+ .map(|expr| expr.span),
+ GeneratorData::Foreign(generator_diagnostic_data) => visitor
+ .awaits
+ .into_iter()
+ .map(|id| hir.expect_expr(id))
+ .find(|await_expr| {
+ ty_matches(ty::Binder::dummy(
+ generator_diagnostic_data
+ .adjustments
+ .get(&await_expr.hir_id.local_id)
+ .map_or::<&[ty::adjustment::Adjustment<'tcx>], _>(&[], |a| &a[..])
+ .last()
+ .map_or_else::<Ty<'tcx>, _, _>(
+ || {
+ generator_diagnostic_data
+ .nodes_types
+ .get(&await_expr.hir_id.local_id)
+ .cloned()
+ .unwrap_or_else(|| {
+ bug!(
+ "node_type: no type for node `{}`",
+ ty::tls::with(|tcx| tcx
+ .hir()
+ .node_to_string(await_expr.hir_id))
+ )
+ })
+ },
+ |adj| adj.target,
+ ),
+ ))
+ })
+ .map(|expr| expr.span),
+ }
+ }
+
+ /// Get the type, expression, span and optional scope span of all types
+ /// that are live across the yield of this generator
+ fn get_generator_interior_types(
+ &self,
+ ) -> ty::Binder<'tcx, &Vec<GeneratorInteriorTypeCause<'tcx>>> {
+ match self {
+ GeneratorData::Local(typeck_result) => typeck_result.generator_interior_types.as_ref(),
+ GeneratorData::Foreign(generator_diagnostic_data) => {
+ generator_diagnostic_data.generator_interior_types.as_ref()
+ }
+ }
+ }
+
+ // Used to get the source of the data, note we don't have as much information for generators
+ // originated from foreign crates
+ fn is_foreign(&self) -> bool {
+ match self {
+ GeneratorData::Local(_) => false,
+ GeneratorData::Foreign(_) => true,
+ }
+ }
+}
+
// This trait is public to expose the diagnostics methods to clippy.
pub trait InferCtxtExt<'tcx> {
fn suggest_restricting_param_bound(
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- );
+ ) -> bool;
fn get_closure_name(&self, def_id: DefId, err: &mut Diagnostic, msg: &str) -> Option<String>;
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- );
+ ) -> bool;
fn suggest_add_reference_to_arg(
&self,
has_custom_message: bool,
) -> bool;
+ fn suggest_borrowing_for_object_cast(
+ &self,
+ err: &mut Diagnostic,
+ obligation: &PredicateObligation<'tcx>,
+ self_ty: Ty<'tcx>,
+ object_ty: Ty<'tcx>,
+ );
+
fn suggest_remove_reference(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- );
+ ) -> bool;
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic);
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- );
+ ) -> bool;
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span>;
err: &mut Diagnostic,
interior_or_upvar_span: GeneratorInteriorOrUpvar,
interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
- inner_generator_body: Option<&hir::Body<'tcx>>,
+ is_async: bool,
outer_generator: Option<DefId>,
trait_pred: ty::TraitPredicate<'tcx>,
target_ty: Ty<'tcx>,
err: &mut Diagnostic,
trait_ref: &ty::PolyTraitRef<'tcx>,
);
+
+ fn suggest_derive(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut Diagnostic,
+ trait_pred: ty::PolyTraitPredicate<'tcx>,
+ );
}
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
(
- generics.where_clause.tail_span_for_suggestion(),
- format!(
- "{} {}",
- if !generics.where_clause.predicates.is_empty() { "," } else { " where" },
- pred,
- ),
+ generics.tail_span_for_predicate_suggestion(),
+ format!("{} {}", if generics.has_where_clause { "," } else { " where" }, pred,),
)
}
// - ^^^^^^^^^ GenericBounds
// |
// &Ident
- let span = generics.where_clause.span_for_predicates_or_empty_place();
+ let span = generics.span_for_predicates_or_empty_place();
if span.from_expansion() || span.desugaring_kind().is_some() {
return;
}
let pred = trait_pred.to_predicate(tcx).to_string();
let pred = pred.replace(&impl_trait_str, &type_param_name);
let mut sugg = vec![
- // Find the last of the generic parameters contained within the span of
- // the generics
- match generics
- .params
- .iter()
- .map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
- .filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
- .max_by_key(|span| span.hi())
- {
- // `fn foo(t: impl Trait)`
- // ^ suggest `<T: Trait>` here
- None => (generics.span, format!("<{}>", type_param)),
- // `fn foo<A>(t: impl Trait)`
- // ^^^ suggest `<A, T: Trait>` here
- Some(span) => (span, format!(", {}", type_param)),
+ if let Some(span) = generics.span_for_param_suggestion() {
+ (span, format!(", {}", type_param))
+ } else {
+ (generics.span, format!("<{}>", type_param))
},
// `fn foo(t: impl Trait)`
// ^ suggest `where <T as Trait>::A: Bound`
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- ) {
+ ) -> bool {
// It only make sense when suggesting dereferences for arguments
- let code = if let ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } =
- obligation.cause.code()
- {
- parent_code.clone()
- } else {
- return;
+ let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else {
+ return false;
};
let param_env = obligation.param_env;
let body_id = obligation.cause.body_id;
let span = obligation.cause.span;
- let real_trait_pred = match &*code {
- ObligationCauseCode::ImplDerivedObligation(cause) => cause.derived.parent_trait_pred,
- ObligationCauseCode::DerivedObligation(cause)
- | ObligationCauseCode::BuiltinDerivedObligation(cause) => cause.parent_trait_pred,
- _ => trait_pred,
- };
- let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
- return;
- };
+ let mut real_trait_pred = trait_pred;
+ let mut code = obligation.cause.code();
+ loop {
+ match &code {
+ ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => {
+ code = &parent_code;
+ }
+ ObligationCauseCode::ImplDerivedObligation(box ImplDerivedObligationCause {
+ derived: DerivedObligationCause { parent_code, parent_trait_pred },
+ ..
+ })
+ | ObligationCauseCode::BuiltinDerivedObligation(DerivedObligationCause {
+ parent_code,
+ parent_trait_pred,
+ })
+ | ObligationCauseCode::DerivedObligation(DerivedObligationCause {
+ parent_code,
+ parent_trait_pred,
+ }) => {
+ code = &parent_code;
+ real_trait_pred = *parent_trait_pred;
+ }
+ _ => break,
+ };
+ let Some(real_ty) = real_trait_pred.self_ty().no_bound_vars() else {
+ continue;
+ };
- if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
- let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
- if let Some(steps) = autoderef.find_map(|(ty, steps)| {
- // Re-add the `&`
- let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
- let obligation =
- self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
- Some(steps).filter(|_| self.predicate_may_hold(&obligation))
- }) {
- if steps > 0 {
- if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
- // Don't care about `&mut` because `DerefMut` is used less
- // often and user will not expect autoderef happens.
- if src.starts_with('&') && !src.starts_with("&mut ") {
- let derefs = "*".repeat(steps);
- err.span_suggestion(
- span,
- "consider adding dereference here",
- format!("&{}{}", derefs, &src[1..]),
- Applicability::MachineApplicable,
- );
+ if let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() {
+ let mut autoderef = Autoderef::new(self, param_env, body_id, span, base_ty, span);
+ if let Some(steps) = autoderef.find_map(|(ty, steps)| {
+ // Re-add the `&`
+ let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
+ let obligation =
+ self.mk_trait_obligation_with_new_self_ty(param_env, real_trait_pred, ty);
+ Some(steps).filter(|_| self.predicate_may_hold(&obligation))
+ }) {
+ if steps > 0 {
+ if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
+ // Don't care about `&mut` because `DerefMut` is used less
+ // often and user will not expect autoderef happens.
+ if src.starts_with('&') && !src.starts_with("&mut ") {
+ let derefs = "*".repeat(steps);
+ err.span_suggestion(
+ span,
+ "consider dereferencing here",
+ format!("&{}{}", derefs, &src[1..]),
+ Applicability::MachineApplicable,
+ );
+ return true;
+ }
}
}
+ } else if real_trait_pred != trait_pred {
+ // This branch addresses #87437.
+ let obligation = self.mk_trait_obligation_with_new_self_ty(
+ param_env,
+ real_trait_pred,
+ base_ty,
+ );
+ if self.predicate_may_hold(&obligation) {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ "consider dereferencing here",
+ "*".to_string(),
+ Applicability::MachineApplicable,
+ );
+ return true;
+ }
}
}
}
+ false
}
/// Given a closure's `DefId`, return the given name of the closure.
Some(format!("{}", name))
}
_ => {
- err.note(&msg);
+ err.note(msg);
None
}
}
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- ) {
+ ) -> bool {
let Some(self_ty) = trait_pred.self_ty().no_bound_vars() else {
- return;
+ return false;
};
let (def_id, output_ty, callable) = match *self_ty.kind() {
ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"),
ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"),
- _ => return,
+ _ => return false,
};
let msg = format!("use parentheses to call the {}", callable);
// `mk_trait_obligation_with_new_self_ty` only works for types with no escaping bound
// variables, so bail out if we have any.
let Some(output_ty) = output_ty.no_bound_vars() else {
- return;
+ return false;
};
let new_obligation =
Ok(
EvaluationResult::EvaluatedToOk
| EvaluationResult::EvaluatedToOkModuloRegions
+ | EvaluationResult::EvaluatedToOkModuloOpaqueTypes
| EvaluationResult::EvaluatedToAmbig,
) => {}
- _ => return,
+ _ => return false,
}
let hir = self.tcx.hir();
// Get the name of the callable and the arguments to be used in the suggestion.
})) => {
err.span_label(*span, "consider calling this closure");
let Some(name) = self.get_closure_name(def_id, err, &msg) else {
- return;
+ return false;
};
let args = decl.inputs.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
let sugg = format!("({})", args);
let sugg = format!("({})", args);
(format!("{}{}", ident, sugg), sugg)
}
- _ => return,
+ _ => return false,
};
if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. })
{
} else {
err.help(&format!("{}: `{}`", msg, snippet));
}
+ true
}
fn suggest_add_reference_to_arg(
if has_custom_message {
err.note(&msg);
} else {
- err.message = vec![(msg, Style::NoStyle)];
+ err.message =
+ vec![(rustc_errors::DiagnosticMessage::Str(msg), Style::NoStyle)];
}
if snippet.starts_with('&') {
// This is already a literal borrow and the obligation is failing
}
}
+ // Suggest borrowing the type
+ fn suggest_borrowing_for_object_cast(
+ &self,
+ err: &mut Diagnostic,
+ obligation: &PredicateObligation<'tcx>,
+ self_ty: Ty<'tcx>,
+ object_ty: Ty<'tcx>,
+ ) {
+ let ty::Dynamic(predicates, _) = object_ty.kind() else { return; };
+ let self_ref_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, self_ty);
+
+ for predicate in predicates.iter() {
+ if !self.predicate_must_hold_modulo_regions(
+ &obligation.with(predicate.with_self_ty(self.tcx, self_ref_ty)),
+ ) {
+ return;
+ }
+ }
+
+ err.span_suggestion(
+ obligation.cause.span.shrink_to_lo(),
+ &format!(
+ "consider borrowing the value, since `&{self_ty}` can be coerced into `{object_ty}`"
+ ),
+ "&".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
/// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,
/// suggest removing these references until we reach a type that implements the trait.
fn suggest_remove_reference(
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- ) {
+ ) -> bool {
let span = obligation.cause.span;
+ let mut suggested = false;
if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let refs_number =
snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
// Do not suggest removal of borrow from type arguments.
- return;
+ return false;
}
let Some(mut suggested_ty) = trait_pred.self_ty().no_bound_vars() else {
- return;
+ return false;
};
for refs_remaining in 0..refs_number {
String::new(),
Applicability::MachineApplicable,
);
+ suggested = true;
break;
}
}
}
+ suggested
}
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
self.in_progress_typeck_results.map(|t| t.borrow())
&& let ty = typeck_results.expr_ty_adjusted(base)
&& let ty::FnDef(def_id, _substs) = ty.kind()
- && let Some(hir::Node::Item(hir::Item { span, ident, .. })) =
+ && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) =
hir.get_if_local(*def_id)
{
- err.span_suggestion_verbose(
- span.shrink_to_lo(),
- &format!(
- "alternatively, consider making `fn {}` asynchronous",
- ident
- ),
- "async ".to_string(),
- Applicability::MaybeIncorrect,
+ let msg = format!(
+ "alternatively, consider making `fn {}` asynchronous",
+ ident
);
+ if vis_span.is_empty() {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ &msg,
+ "async ".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_suggestion_verbose(
+ vis_span.shrink_to_hi(),
+ &msg,
+ " async".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
}
}
}
err: &mut Diagnostic,
span: Span,
trait_pred: ty::PolyTraitPredicate<'tcx>,
- ) {
+ ) -> bool {
let hir = self.tcx.hir();
let parent_node = hir.get_parent_node(obligation.cause.body_id);
let node = hir.find(parent_node);
if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node
- && let body = hir.body(*body_id)
- && let hir::ExprKind::Block(blk, _) = &body.value.kind
+ && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind
&& sig.decl.output.span().overlaps(span)
&& blk.expr.is_none()
- && *trait_pred.self_ty().skip_binder().kind() == ty::Tuple(ty::List::empty())
- // FIXME(estebank): When encountering a method with a trait
- // bound not satisfied in the return type with a body that has
- // no return, suggest removal of semicolon on last statement.
- // Once that is added, close #54771.
+ && trait_pred.self_ty().skip_binder().is_unit()
&& let Some(stmt) = blk.stmts.last()
- && let hir::StmtKind::Semi(_) = stmt.kind
+ && let hir::StmtKind::Semi(expr) = stmt.kind
+ // Only suggest this if the expression behind the semicolon implements the predicate
+ && let Some(typeck_results) = self.in_progress_typeck_results
+ && let Some(ty) = typeck_results.borrow().expr_ty_opt(expr)
+ && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred, ty))
{
- let sp = self.tcx.sess.source_map().end_point(stmt.span);
- err.span_label(sp, "consider removing this semicolon");
+ err.span_label(
+ expr.span,
+ &format!(
+ "this expression has type `{}`, which implements `{}`",
+ ty,
+ trait_pred.print_modifiers_and_trait_path()
+ )
+ );
+ err.span_suggestion(
+ self.tcx.sess.source_map().end_point(stmt.span),
+ "remove this semicolon",
+ String::new(),
+ Applicability::MachineApplicable
+ );
+ return true;
}
+ false
}
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
}
let hir = self.tcx.hir();
- let parent_node = hir.get_parent_node(obligation.cause.body_id);
- let node = hir.find(parent_node);
+ let fn_hir_id = hir.get_parent_node(obligation.cause.body_id);
+ let node = hir.find(fn_hir_id);
let Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn(sig, _, body_id),
..
visitor.visit_body(&body);
let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap();
+ let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; };
- let mut ret_types = visitor
+ let ret_types = visitor
.returns
.iter()
- .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id))
- .map(|ty| self.resolve_vars_if_possible(ty));
+ .filter_map(|expr| Some((expr.span, typeck_results.node_type_opt(expr.hir_id)?)))
+ .map(|(expr_span, ty)| (expr_span, self.resolve_vars_if_possible(ty)));
let (last_ty, all_returns_have_same_type, only_never_return) = ret_types.clone().fold(
(None, true, true),
|(last_ty, mut same, only_never_return): (std::option::Option<Ty<'_>>, bool, bool),
- ty| {
+ (_, ty)| {
let ty = self.resolve_vars_if_possible(ty);
same &=
!matches!(ty.kind(), ty::Error(_))
(Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never))
},
);
- let all_returns_conform_to_trait =
- if let Some(ty_ret_ty) = typeck_results.node_type_opt(ret_ty.hir_id) {
- match ty_ret_ty.kind() {
- ty::Dynamic(predicates, _) => {
- let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id);
- let param_env = ty::ParamEnv::empty();
- only_never_return
- || ret_types.all(|returned_ty| {
- predicates.iter().all(|predicate| {
- let pred = predicate.with_self_ty(self.tcx, returned_ty);
- let obl = Obligation::new(cause.clone(), param_env, pred);
- self.predicate_may_hold(&obl)
- })
+ let mut spans_and_needs_box = vec![];
+
+ match liberated_sig.output().kind() {
+ ty::Dynamic(predicates, _) => {
+ let cause = ObligationCause::misc(ret_ty.span, fn_hir_id);
+ let param_env = ty::ParamEnv::empty();
+
+ if !only_never_return {
+ for (expr_span, return_ty) in ret_types {
+ let self_ty_satisfies_dyn_predicates = |self_ty| {
+ predicates.iter().all(|predicate| {
+ let pred = predicate.with_self_ty(self.tcx, self_ty);
+ let obl = Obligation::new(cause.clone(), param_env, pred);
+ self.predicate_may_hold(&obl)
})
+ };
+
+ if let ty::Adt(def, substs) = return_ty.kind()
+ && def.is_box()
+ && self_ty_satisfies_dyn_predicates(substs.type_at(0))
+ {
+ spans_and_needs_box.push((expr_span, false));
+ } else if self_ty_satisfies_dyn_predicates(return_ty) {
+ spans_and_needs_box.push((expr_span, true));
+ } else {
+ return false;
+ }
}
- _ => false,
}
- } else {
- true
- };
+ }
+ _ => return false,
+ };
let sm = self.tcx.sess.source_map();
- let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = (
- // Verify that we're dealing with a return `dyn Trait`
- ret_ty.span.overlaps(span),
- &ret_ty.kind,
- sm.span_to_snippet(ret_ty.span),
- // If any of the return types does not conform to the trait, then we can't
- // suggest `impl Trait` nor trait objects: it is a type mismatch error.
- all_returns_conform_to_trait,
- ) else {
+ if !ret_ty.span.overlaps(span) {
return false;
+ }
+ let snippet = if let hir::TyKind::TraitObject(..) = ret_ty.kind {
+ if let Ok(snippet) = sm.span_to_snippet(ret_ty.span) {
+ snippet
+ } else {
+ return false;
+ }
+ } else {
+ // Substitute the type, so we can print a fixup given `type Alias = dyn Trait`
+ let name = liberated_sig.output().to_string();
+ let name =
+ name.strip_prefix('(').and_then(|name| name.strip_suffix(')')).unwrap_or(&name);
+ if !name.starts_with("dyn ") {
+ return false;
+ }
+ name.to_owned()
};
+
err.code(error_code!(E0746));
err.set_primary_message("return type cannot have an unboxed trait object");
err.children.clear();
let trait_obj_msg = "for information on trait objects, see \
<https://doc.rust-lang.org/book/ch17-02-trait-objects.html\
#using-trait-objects-that-allow-for-values-of-different-types>";
+
let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn");
let trait_obj = if has_dyn { &snippet[4..] } else { &snippet };
if only_never_return {
} else {
if is_object_safe {
// Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
- // Get all the return values and collect their span and suggestion.
- let mut suggestions: Vec<_> = visitor
- .returns
- .iter()
- .flat_map(|expr| {
- [
- (expr.span.shrink_to_lo(), "Box::new(".to_string()),
- (expr.span.shrink_to_hi(), ")".to_string()),
- ]
- .into_iter()
- })
- .collect();
- if !suggestions.is_empty() {
- // Add the suggestion for the return type.
- suggestions.push((ret_ty.span, format!("Box<dyn {}>", trait_obj)));
- err.multipart_suggestion(
- "return a boxed trait object instead",
- suggestions,
- Applicability::MaybeIncorrect,
- );
+ err.multipart_suggestion(
+ "return a boxed trait object instead",
+ vec![
+ (ret_ty.span.shrink_to_lo(), "Box<".to_string()),
+ (span.shrink_to_hi(), ">".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ for (span, needs_box) in spans_and_needs_box {
+ if needs_box {
+ err.multipart_suggestion(
+ "... and box this value",
+ vec![
+ (span.shrink_to_lo(), "Box::new(".to_string()),
+ (span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
}
} else {
// This is currently not possible to trigger because E0038 takes precedence, but
.map(|def_id| hir.local_def_id_to_hir_id(def_id))
.and_then(|hir_id| hir.maybe_body_owned_by(hir_id))
.map(|body_id| hir.body(body_id));
+ let is_async = match generator_did.as_local() {
+ Some(_) => generator_body
+ .and_then(|body| body.generator_kind())
+ .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..)))
+ .unwrap_or(false),
+ None => self
+ .tcx
+ .generator_kind(generator_did)
+ .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..)))
+ .unwrap_or(false),
+ };
let mut visitor = AwaitsVisitor::default();
if let Some(body) = generator_body {
visitor.visit_body(body);
// type-checking; otherwise, get them by performing a query. This is needed to avoid
// cycles. If we can't use resolved types because the generator comes from another crate,
// we still provide a targeted error but without all the relevant spans.
- let query_typeck_results;
- let typeck_results: Option<&TypeckResults<'tcx>> = match &in_progress_typeck_results {
- Some(t) if t.hir_owner.to_def_id() == generator_did_root => Some(&t),
+ let generator_data: Option<GeneratorData<'tcx, '_>> = match &in_progress_typeck_results {
+ Some(t) if t.hir_owner.to_def_id() == generator_did_root => {
+ Some(GeneratorData::Local(&t))
+ }
_ if generator_did.is_local() => {
- query_typeck_results = self.tcx.typeck(generator_did.expect_local());
- Some(&query_typeck_results)
+ Some(GeneratorData::Local(self.tcx.typeck(generator_did.expect_local())))
}
- _ => None, // Do not ICE on closure typeck (#66868).
+ _ => self
+ .tcx
+ .generator_diagnostic_data(generator_did)
+ .as_ref()
+ .map(|generator_diag_data| GeneratorData::Foreign(generator_diag_data)),
};
- if let Some(typeck_results) = typeck_results {
- if let Some(upvars) = self.tcx.upvars_mentioned(generator_did) {
- interior_or_upvar_span = upvars.iter().find_map(|(upvar_id, upvar)| {
- let upvar_ty = typeck_results.node_type(*upvar_id);
- let upvar_ty = self.resolve_vars_if_possible(upvar_ty);
- if ty_matches(ty::Binder::dummy(upvar_ty)) {
- Some(GeneratorInteriorOrUpvar::Upvar(upvar.span))
- } else {
- None
- }
- });
- };
+
+ if let Some(generator_data) = generator_data.as_ref() {
+ interior_or_upvar_span =
+ generator_data.try_get_upvar_span(&self, generator_did, ty_matches);
// The generator interior types share the same binders
if let Some(cause) =
- typeck_results.generator_interior_types.as_ref().skip_binder().iter().find(
+ generator_data.get_generator_interior_types().skip_binder().iter().find(
|ty::GeneratorInteriorTypeCause { ty, .. }| {
- ty_matches(typeck_results.generator_interior_types.rebind(*ty))
+ ty_matches(generator_data.get_generator_interior_types().rebind(*ty))
},
)
{
- // Check to see if any awaited expressions have the target type.
- let from_awaited_ty = visitor
- .awaits
- .into_iter()
- .map(|id| hir.expect_expr(id))
- .find(|await_expr| {
- ty_matches(ty::Binder::dummy(typeck_results.expr_ty_adjusted(&await_expr)))
- })
- .map(|expr| expr.span);
+ let from_awaited_ty = generator_data.get_from_await_ty(visitor, hir, ty_matches);
let ty::GeneratorInteriorTypeCause { span, scope_span, yield_span, expr, .. } =
cause;
interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(*span));
interior_extra_info = Some((*scope_span, *yield_span, *expr, from_awaited_ty));
- };
- } else {
- interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span));
+ }
+
+ if interior_or_upvar_span.is_none() && generator_data.is_foreign() {
+ interior_or_upvar_span = Some(GeneratorInteriorOrUpvar::Interior(span));
+ }
}
if let Some(interior_or_upvar_span) = interior_or_upvar_span {
+ let typeck_results = generator_data.and_then(|generator_data| match generator_data {
+ GeneratorData::Local(typeck_results) => Some(typeck_results),
+ GeneratorData::Foreign(_) => None,
+ });
self.note_obligation_cause_for_async_await(
err,
interior_or_upvar_span,
interior_extra_info,
- generator_body,
+ is_async,
outer_generator,
trait_ref,
target_ty,
err: &mut Diagnostic,
interior_or_upvar_span: GeneratorInteriorOrUpvar,
interior_extra_info: Option<(Option<Span>, Span, Option<hir::HirId>, Option<Span>)>,
- inner_generator_body: Option<&hir::Body<'tcx>>,
+ is_async: bool,
outer_generator: Option<DefId>,
trait_pred: ty::TraitPredicate<'tcx>,
target_ty: Ty<'tcx>,
) {
let source_map = self.tcx.sess.source_map();
- let is_async = inner_generator_body
- .and_then(|body| body.generator_kind())
- .map(|generator_kind| matches!(generator_kind, hir::GeneratorKind::Async(..)))
- .unwrap_or(false);
let (await_or_yield, an_await_or_yield) =
if is_async { ("await", "an await") } else { ("yield", "a yield") };
let future_or_generator = if is_async { "future" } else { "generator" };
GeneratorKind::Async(AsyncGeneratorKind::Fn) => self
.tcx
.parent(generator_did)
- .and_then(|parent_did| parent_did.as_local())
+ .as_local()
.map(|parent_did| hir.local_def_id_to_hir_id(parent_did))
.and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
.map(|name| {
ObligationCauseCode::BindingObligation(item_def_id, span) => {
let item_name = tcx.def_path_str(item_def_id);
let mut multispan = MultiSpan::from(span);
- if let Some(ident) = tcx.opt_item_name(item_def_id) {
+ if let Some(ident) = tcx.opt_item_ident(item_def_id) {
let sm = tcx.sess.source_map();
let same_line =
match (sm.lookup_line(ident.span.hi()), sm.lookup_line(span.lo())) {
}
ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
err.note(
- "the `Copy` trait is required because the repeated element will be copied",
+ "the `Copy` trait is required because this value will be copied for each element of the array",
);
if is_const_fn {
if !is_upvar_tys_infer_tuple {
let msg = format!("required because it appears within the type `{}`", ty);
match ty.kind() {
- ty::Adt(def, _) => match self.tcx.opt_item_name(def.did()) {
+ ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) {
Some(ident) => err.span_note(ident.span, &msg),
None => err.note(&msg),
},
);
let sp = self
.tcx
- .opt_item_name(trait_item_def_id)
+ .opt_item_ident(trait_item_def_id)
.map(|i| i.span)
.unwrap_or_else(|| self.tcx.def_span(trait_item_def_id));
let mut assoc_span: MultiSpan = sp.into();
if let Some(ident) = self
.tcx
.opt_associated_item(trait_item_def_id)
- .and_then(|i| self.tcx.opt_item_name(i.container.id()))
+ .and_then(|i| self.tcx.opt_item_ident(i.container.id()))
{
- assoc_span.push_span_label(ident.span, "in this trait".into());
+ assoc_span.push_span_label(ident.span, "in this trait");
}
err.span_note(assoc_span, &msg);
}
if let Some(ident) = self
.tcx
.opt_associated_item(trait_item_def_id)
- .and_then(|i| self.tcx.opt_item_name(i.container.id()))
+ .and_then(|i| self.tcx.opt_item_ident(i.container.id()))
{
- assoc_span.push_span_label(ident.span, "in this trait".into());
+ assoc_span.push_span_label(ident.span, "in this trait");
}
err.span_note(assoc_span, &msg);
}
_ => {}
}
}
+
+ fn suggest_derive(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut Diagnostic,
+ trait_pred: ty::PolyTraitPredicate<'tcx>,
+ ) {
+ let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else {
+ return;
+ };
+ let (adt, substs) = match trait_pred.skip_binder().self_ty().kind() {
+ ty::Adt(adt, substs) if adt.did().is_local() => (adt, substs),
+ _ => return,
+ };
+ let can_derive = {
+ let is_derivable_trait = match diagnostic_name {
+ sym::Default => !adt.is_enum(),
+ sym::PartialEq | sym::PartialOrd => {
+ let rhs_ty = trait_pred.skip_binder().trait_ref.substs.type_at(1);
+ trait_pred.skip_binder().self_ty() == rhs_ty
+ }
+ sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true,
+ _ => false,
+ };
+ is_derivable_trait &&
+ // Ensure all fields impl the trait.
+ adt.all_fields().all(|field| {
+ let field_ty = field.ty(self.tcx, substs);
+ let trait_substs = match diagnostic_name {
+ sym::PartialEq | sym::PartialOrd => {
+ self.tcx.mk_substs_trait(field_ty, &[field_ty.into()])
+ }
+ _ => self.tcx.mk_substs_trait(field_ty, &[]),
+ };
+ let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate {
+ trait_ref: ty::TraitRef {
+ substs: trait_substs,
+ ..trait_pred.skip_binder().trait_ref
+ },
+ ..*tr
+ });
+ let field_obl = Obligation::new(
+ obligation.cause.clone(),
+ obligation.param_env,
+ trait_pred.to_predicate(self.tcx),
+ );
+ self.predicate_must_hold_modulo_regions(&field_obl)
+ })
+ };
+ if can_derive {
+ err.span_suggestion_verbose(
+ self.tcx.def_span(adt.did()).shrink_to_lo(),
+ &format!(
+ "consider annotating `{}` with `#[derive({})]`",
+ trait_pred.skip_binder().self_ty(),
+ diagnostic_name,
+ ),
+ format!("#[derive({})]\n", diagnostic_name),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
}
/// Collect all the returned expressions within the input expression.
Applicability::MaybeIncorrect,
);
if is_object_safe {
- err.span_suggestion(
- ret_ty,
+ err.multipart_suggestion(
&format!(
"use a boxed trait object if all return paths implement trait `{}`",
trait_obj,
),
- format!("Box<dyn {}>", trait_obj),
+ vec![
+ (ret_ty.shrink_to_lo(), "Box<".to_string()),
+ (ret_ty.shrink_to_hi(), ">".to_string()),
+ ],
Applicability::MaybeIncorrect,
);
}