};
use rustc_ast as ast;
-use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported};
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
-use rustc_hir::{ExprKind, GenericArg, ItemKind, Node, QPath};
+use rustc_hir::{ExprKind, GenericArg, Node, QPath};
use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282;
-use rustc_infer::infer::{self, InferOk, InferResult};
+use rustc_infer::infer::{InferOk, InferResult};
use rustc_middle::ty::adjustment::{
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
};
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
-use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::{
- self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
-};
+use rustc_trait_selection::traits::{self, ObligationCauseCode, TraitEngine, TraitEngineExt};
use std::collections::hash_map::Entry;
-use std::iter;
use std::mem::replace;
use std::slice;
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
-
/// Produces warning on the given node, if the current point in the
/// function is unreachable, and there hasn't been another warning.
pub(super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) {
}
}
- pub(super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) {
- err.span_suggestion_short(
- span.shrink_to_hi(),
- "consider using a semicolon here",
- ";".to_string(),
- Applicability::MachineApplicable,
- );
- }
-
pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>) {
// Don't do all the complex logic below for `DeclItem`.
match stmt.kind {
})
}
- /// On implicit return expressions with mismatched types, provides the following suggestions:
- ///
- /// - Points out the method's return type as the reason for the expected type.
- /// - Possible missing semicolon.
- /// - Possible missing return type if the return type is the default, and not `fn main()`.
- pub fn suggest_mismatched_types_on_tail(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- cause_span: Span,
- blk_id: hir::HirId,
- ) -> bool {
- let expr = expr.peel_drop_temps();
- self.suggest_missing_semicolon(err, expr, expected, cause_span);
- let mut pointing_at_return_type = false;
- if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
- pointing_at_return_type =
- self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
- }
- pointing_at_return_type
- }
-
- /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
- /// the ctor would successfully solve the type mismatch and if so, suggest it:
- /// ```
- /// fn foo(x: usize) -> usize { x }
- /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
- /// ```
- fn suggest_fn_call(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) -> bool {
- let hir = self.tcx.hir();
- let (def_id, sig) = match *found.kind() {
- ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
- ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
- _ => return false,
- };
-
- let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0;
- let sig = self.normalize_associated_types_in(expr.span, &sig);
- if self.can_coerce(sig.output(), expected) {
- let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
- (String::new(), Applicability::MachineApplicable)
- } else {
- ("...".to_string(), Applicability::HasPlaceholders)
- };
- let mut msg = "call this function";
- match hir.get_if_local(def_id) {
- Some(
- Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
- | Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(_, body_id), ..
- })
- | Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
- ..
- }),
- ) => {
- let body = hir.body(*body_id);
- sugg_call = body
- .params
- .iter()
- .map(|param| match ¶m.pat.kind {
- hir::PatKind::Binding(_, _, ident, None)
- if ident.name != kw::SelfLower =>
- {
- ident.to_string()
- }
- _ => "_".to_string(),
- })
- .collect::<Vec<_>>()
- .join(", ");
- }
- Some(Node::Expr(hir::Expr {
- kind: ExprKind::Closure(_, _, body_id, _, _),
- span: full_closure_span,
- ..
- })) => {
- if *full_closure_span == expr.span {
- return false;
- }
- msg = "call this closure";
- let body = hir.body(*body_id);
- sugg_call = body
- .params
- .iter()
- .map(|param| match ¶m.pat.kind {
- hir::PatKind::Binding(_, _, ident, None)
- if ident.name != kw::SelfLower =>
- {
- ident.to_string()
- }
- _ => "_".to_string(),
- })
- .collect::<Vec<_>>()
- .join(", ");
- }
- Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
- sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
- match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
- Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
- msg = "instantiate this tuple variant";
- }
- Some(DefKind::Ctor(CtorOf::Struct, _)) => {
- msg = "instantiate this tuple struct";
- }
- _ => {}
- }
- }
- Some(Node::ForeignItem(hir::ForeignItem {
- kind: hir::ForeignItemKind::Fn(_, idents, _),
- ..
- })) => {
- sugg_call = idents
- .iter()
- .map(|ident| {
- if ident.name != kw::SelfLower {
- ident.to_string()
- } else {
- "_".to_string()
- }
- })
- .collect::<Vec<_>>()
- .join(", ")
- }
- Some(Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
- ..
- })) => {
- sugg_call = idents
- .iter()
- .map(|ident| {
- if ident.name != kw::SelfLower {
- ident.to_string()
- } else {
- "_".to_string()
- }
- })
- .collect::<Vec<_>>()
- .join(", ")
- }
- _ => {}
- }
- err.span_suggestion_verbose(
- expr.span.shrink_to_hi(),
- &format!("use parentheses to {}", msg),
- format!("({})", sugg_call),
- applicability,
- );
- return true;
- }
- false
- }
-
- pub fn suggest_deref_ref_or_into(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
- ) {
- if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
- err.span_suggestion(sp, msg, suggestion, applicability);
- } else if let (ty::FnDef(def_id, ..), true) =
- (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
- {
- if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
- let sp = self.sess().source_map().guess_head_span(sp);
- err.span_label(sp, &format!("{} defined here", found));
- }
- } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
- let is_struct_pat_shorthand_field =
- self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
- let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
- if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
- let mut suggestions = iter::repeat(&expr_text)
- .zip(methods.iter())
- .filter_map(|(receiver, method)| {
- let method_call = format!(".{}()", method.ident);
- if receiver.ends_with(&method_call) {
- None // do not suggest code that is already there (#53348)
- } else {
- let method_call_list = [".to_vec()", ".to_string()"];
- let sugg = if receiver.ends_with(".clone()")
- && method_call_list.contains(&method_call.as_str())
- {
- let max_len = receiver.rfind('.').unwrap();
- format!("{}{}", &receiver[..max_len], method_call)
- } else {
- if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
- format!("({}){}", receiver, method_call)
- } else {
- format!("{}{}", receiver, method_call)
- }
- };
- Some(if is_struct_pat_shorthand_field {
- format!("{}: {}", receiver, sugg)
- } else {
- sugg
- })
- }
- })
- .peekable();
- if suggestions.peek().is_some() {
- err.span_suggestions(
- expr.span,
- "try using a conversion method",
- suggestions,
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
-
- /// When encountering the expected boxed value allocated in the stack, suggest allocating it
- /// in the heap by calling `Box::new()`.
- pub(super) fn suggest_boxing_when_appropriate(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return;
- }
- if !expected.is_box() || found.is_box() {
- return;
- }
- let boxed_found = self.tcx.mk_box(found);
- if let (true, Ok(snippet)) = (
- self.can_coerce(boxed_found, expected),
- self.sess().source_map().span_to_snippet(expr.span),
- ) {
- err.span_suggestion(
- expr.span,
- "store this in the heap by calling `Box::new`",
- format!("Box::new({})", snippet),
- Applicability::MachineApplicable,
- );
- err.note(
- "for more on the distinction between the stack and the heap, read \
- https://doc.rust-lang.org/book/ch15-01-box.html, \
- https://doc.rust-lang.org/rust-by-example/std/box.html, and \
- https://doc.rust-lang.org/std/boxed/index.html",
- );
- }
- }
-
pub(super) fn note_internal_mutation_in_method(
&self,
err: &mut DiagnosticBuilder<'_>,
}
}
- /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
- pub(super) fn suggest_calling_boxed_future_when_appropriate(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) -> bool {
- // Handle #68197.
-
- if self.tcx.hir().is_inside_const_context(expr.hir_id) {
- // Do not suggest `Box::new` in const context.
- return false;
- }
- let pin_did = self.tcx.lang_items().pin_type();
- match expected.kind() {
- ty::Adt(def, _) if Some(def.did) != pin_did => return false,
- // This guards the `unwrap` and `mk_box` below.
- _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
- _ => {}
- }
- let boxed_found = self.tcx.mk_box(found);
- let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
- if let (true, Ok(snippet)) = (
- self.can_coerce(new_found, expected),
- self.sess().source_map().span_to_snippet(expr.span),
- ) {
- match found.kind() {
- ty::Adt(def, _) if def.is_box() => {
- err.help("use `Box::pin`");
- }
- _ => {
- err.span_suggestion(
- expr.span,
- "you need to pin and box this expression",
- format!("Box::pin({})", snippet),
- Applicability::MachineApplicable,
- );
- }
- }
- true
- } else {
- false
- }
- }
-
- /// A common error is to forget to add a semicolon at the end of a block, e.g.,
- ///
- /// ```
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return expression in a block would make sense on its own as a
- /// statement and the return type has been left as default or has been specified as `()`. If so,
- /// it suggests adding a semicolon.
- fn suggest_missing_semicolon(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expression: &'tcx hir::Expr<'tcx>,
- expected: Ty<'tcx>,
- cause_span: Span,
- ) {
- if expected.is_unit() {
- // `BlockTailExpression` only relevant if the tail expr would be
- // useful on its own.
- match expression.kind {
- ExprKind::Call(..)
- | ExprKind::MethodCall(..)
- | ExprKind::Loop(..)
- | ExprKind::Match(..)
- | ExprKind::Block(..) => {
- err.span_suggestion(
- cause_span.shrink_to_hi(),
- "try adding a semicolon",
- ";".to_string(),
- Applicability::MachineApplicable,
- );
- }
- _ => (),
- }
- }
- }
-
- /// A possible error is to forget to add a return type that is needed:
- ///
- /// ```
- /// fn foo() {
- /// bar_that_returns_u32()
- /// }
- /// ```
- ///
- /// This routine checks if the return type is left as default, the method is not part of an
- /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
- /// type.
- pub(super) fn suggest_missing_return_type(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- fn_decl: &hir::FnDecl<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- can_suggest: bool,
- ) -> bool {
- // Only suggest changing the return type for methods that
- // haven't set a return type at all (and aren't `fn main()` or an impl).
- match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
- (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
- err.span_suggestion(
- span,
- "try adding a return type",
- format!("-> {} ", self.resolve_vars_with_obligations(found)),
- Applicability::MachineApplicable,
- );
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
- err.span_label(span, "possibly return type missing here?");
- true
- }
- (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
- // `fn main()` must return `()`, do not suggest changing return type
- err.span_label(span, "expected `()` because of default return type");
- true
- }
- // expectation was caused by something else, not the default return
- (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
- (&hir::FnRetTy::Return(ref ty), _, _, _) => {
- // Only point to return type if the expected type is the return type, as if they
- // are not, the expectation must have been caused by something else.
- debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
- let sp = ty.span;
- let ty = AstConv::ast_ty_to_ty(self, ty);
- debug!("suggest_missing_return_type: return type {:?}", ty);
- debug!("suggest_missing_return_type: expected type {:?}", ty);
- if ty.kind() == expected.kind() {
- err.span_label(sp, format!("expected `{}` because of return type", expected));
- return true;
- }
- false
- }
- }
- }
-
- /// A possible error is to forget to add `.await` when using futures:
- ///
- /// ```
- /// async fn make_u32() -> u32 {
- /// 22
- /// }
- ///
- /// fn take_u32(x: u32) {}
- ///
- /// async fn foo() {
- /// let x = make_u32();
- /// take_u32(x);
- /// }
- /// ```
- ///
- /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
- /// expected type. If this is the case, and we are inside of an async body, it suggests adding
- /// `.await` to the tail of the expression.
- pub(super) fn suggest_missing_await(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- expected: Ty<'tcx>,
- found: Ty<'tcx>,
- ) {
- debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
- // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
- // body isn't `async`.
- let item_id = self.tcx().hir().get_parent_node(self.body_id);
- if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
- let body = self.tcx().hir().body(body_id);
- if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
- let sp = expr.span;
- // Check for `Future` implementations by constructing a predicate to
- // prove: `<T as Future>::Output == U`
- let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
- let item_def_id = self
- .tcx
- .associated_items(future_trait)
- .in_definition_order()
- .next()
- .unwrap()
- .def_id;
- // `<T as Future>::Output`
- let projection_ty = ty::ProjectionTy {
- // `T`
- substs: self
- .tcx
- .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
- // `Future::Output`
- item_def_id,
- };
-
- let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
- projection_ty,
- ty: expected,
- })
- .potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
- let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
-
- debug!("suggest_missing_await: trying obligation {:?}", obligation);
-
- if self.infcx.predicate_may_hold(&obligation) {
- debug!("suggest_missing_await: obligation held: {:?}", obligation);
- if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
- err.span_suggestion(
- sp,
- "consider using `.await` here",
- format!("{}.await", code),
- Applicability::MaybeIncorrect,
- );
- } else {
- debug!("suggest_missing_await: no snippet for {:?}", sp);
- }
- } else {
- debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
- }
- }
- }
- }
-
- pub(super) fn suggest_missing_parentheses(
- &self,
- err: &mut DiagnosticBuilder<'_>,
- expr: &hir::Expr<'_>,
- ) {
- let sp = self.tcx.sess.source_map().start_point(expr.span);
- if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
- // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
- self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
- }
- }
-
pub(super) fn note_need_for_fn_pointer(
&self,
err: &mut DiagnosticBuilder<'_>,
--- /dev/null
+use super::FnCtxt;
+use crate::astconv::AstConv;
+
+use rustc_ast::util::parser::ExprPrecedence;
+use rustc_span::{self, Span};
+use rustc_trait_selection::traits;
+
+use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{ExprKind, ItemKind, Node};
+use rustc_infer::infer;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::symbol::kw;
+use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
+
+use std::iter;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ pub(super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut DiagnosticBuilder<'_>) {
+ err.span_suggestion_short(
+ span.shrink_to_hi(),
+ "consider using a semicolon here",
+ ";".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ /// On implicit return expressions with mismatched types, provides the following suggestions:
+ ///
+ /// - Points out the method's return type as the reason for the expected type.
+ /// - Possible missing semicolon.
+ /// - Possible missing return type if the return type is the default, and not `fn main()`.
+ pub fn suggest_mismatched_types_on_tail(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &'tcx hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ cause_span: Span,
+ blk_id: hir::HirId,
+ ) -> bool {
+ let expr = expr.peel_drop_temps();
+ self.suggest_missing_semicolon(err, expr, expected, cause_span);
+ let mut pointing_at_return_type = false;
+ if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) {
+ pointing_at_return_type =
+ self.suggest_missing_return_type(err, &fn_decl, expected, found, can_suggest);
+ }
+ pointing_at_return_type
+ }
+
+ /// When encountering an fn-like ctor that needs to unify with a value, check whether calling
+ /// the ctor would successfully solve the type mismatch and if so, suggest it:
+ /// ```
+ /// fn foo(x: usize) -> usize { x }
+ /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
+ /// ```
+ fn suggest_fn_call(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) -> bool {
+ let hir = self.tcx.hir();
+ let (def_id, sig) = match *found.kind() {
+ ty::FnDef(def_id, _) => (def_id, found.fn_sig(self.tcx)),
+ ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig()),
+ _ => return false,
+ };
+
+ let sig = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, &sig).0;
+ let sig = self.normalize_associated_types_in(expr.span, &sig);
+ if self.can_coerce(sig.output(), expected) {
+ let (mut sugg_call, applicability) = if sig.inputs().is_empty() {
+ (String::new(), Applicability::MachineApplicable)
+ } else {
+ ("...".to_string(), Applicability::HasPlaceholders)
+ };
+ let mut msg = "call this function";
+ match hir.get_if_local(def_id) {
+ Some(
+ Node::Item(hir::Item { kind: ItemKind::Fn(.., body_id), .. })
+ | Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(_, body_id), ..
+ })
+ | Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Provided(body_id)),
+ ..
+ }),
+ ) => {
+ let body = hir.body(*body_id);
+ sugg_call = body
+ .params
+ .iter()
+ .map(|param| match ¶m.pat.kind {
+ hir::PatKind::Binding(_, _, ident, None)
+ if ident.name != kw::SelfLower =>
+ {
+ ident.to_string()
+ }
+ _ => "_".to_string(),
+ })
+ .collect::<Vec<_>>()
+ .join(", ");
+ }
+ Some(Node::Expr(hir::Expr {
+ kind: ExprKind::Closure(_, _, body_id, _, _),
+ span: full_closure_span,
+ ..
+ })) => {
+ if *full_closure_span == expr.span {
+ return false;
+ }
+ msg = "call this closure";
+ let body = hir.body(*body_id);
+ sugg_call = body
+ .params
+ .iter()
+ .map(|param| match ¶m.pat.kind {
+ hir::PatKind::Binding(_, _, ident, None)
+ if ident.name != kw::SelfLower =>
+ {
+ ident.to_string()
+ }
+ _ => "_".to_string(),
+ })
+ .collect::<Vec<_>>()
+ .join(", ");
+ }
+ Some(Node::Ctor(hir::VariantData::Tuple(fields, _))) => {
+ sugg_call = fields.iter().map(|_| "_").collect::<Vec<_>>().join(", ");
+ match def_id.as_local().map(|def_id| hir.def_kind(def_id)) {
+ Some(DefKind::Ctor(hir::def::CtorOf::Variant, _)) => {
+ msg = "instantiate this tuple variant";
+ }
+ Some(DefKind::Ctor(CtorOf::Struct, _)) => {
+ msg = "instantiate this tuple struct";
+ }
+ _ => {}
+ }
+ }
+ Some(Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Fn(_, idents, _),
+ ..
+ })) => {
+ sugg_call = idents
+ .iter()
+ .map(|ident| {
+ if ident.name != kw::SelfLower {
+ ident.to_string()
+ } else {
+ "_".to_string()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+ Some(Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(.., hir::TraitFn::Required(idents)),
+ ..
+ })) => {
+ sugg_call = idents
+ .iter()
+ .map(|ident| {
+ if ident.name != kw::SelfLower {
+ ident.to_string()
+ } else {
+ "_".to_string()
+ }
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ }
+ _ => {}
+ }
+ err.span_suggestion_verbose(
+ expr.span.shrink_to_hi(),
+ &format!("use parentheses to {}", msg),
+ format!("({})", sugg_call),
+ applicability,
+ );
+ return true;
+ }
+ false
+ }
+
+ pub fn suggest_deref_ref_or_into(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>,
+ ) {
+ if let Some((sp, msg, suggestion, applicability)) = self.check_ref(expr, found, expected) {
+ err.span_suggestion(sp, msg, suggestion, applicability);
+ } else if let (ty::FnDef(def_id, ..), true) =
+ (&found.kind(), self.suggest_fn_call(err, expr, expected, found))
+ {
+ if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
+ let sp = self.sess().source_map().guess_head_span(sp);
+ err.span_label(sp, &format!("{} defined here", found));
+ }
+ } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
+ let is_struct_pat_shorthand_field =
+ self.is_hir_id_from_struct_pattern_shorthand_field(expr.hir_id, expr.span);
+ let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
+ if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) {
+ let mut suggestions = iter::repeat(&expr_text)
+ .zip(methods.iter())
+ .filter_map(|(receiver, method)| {
+ let method_call = format!(".{}()", method.ident);
+ if receiver.ends_with(&method_call) {
+ None // do not suggest code that is already there (#53348)
+ } else {
+ let method_call_list = [".to_vec()", ".to_string()"];
+ let sugg = if receiver.ends_with(".clone()")
+ && method_call_list.contains(&method_call.as_str())
+ {
+ let max_len = receiver.rfind('.').unwrap();
+ format!("{}{}", &receiver[..max_len], method_call)
+ } else {
+ if expr.precedence().order() < ExprPrecedence::MethodCall.order() {
+ format!("({}){}", receiver, method_call)
+ } else {
+ format!("{}{}", receiver, method_call)
+ }
+ };
+ Some(if is_struct_pat_shorthand_field {
+ format!("{}: {}", receiver, sugg)
+ } else {
+ sugg
+ })
+ }
+ })
+ .peekable();
+ if suggestions.peek().is_some() {
+ err.span_suggestions(
+ expr.span,
+ "try using a conversion method",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
+
+ /// When encountering the expected boxed value allocated in the stack, suggest allocating it
+ /// in the heap by calling `Box::new()`.
+ pub(super) fn suggest_boxing_when_appropriate(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ if self.tcx.hir().is_inside_const_context(expr.hir_id) {
+ // Do not suggest `Box::new` in const context.
+ return;
+ }
+ if !expected.is_box() || found.is_box() {
+ return;
+ }
+ let boxed_found = self.tcx.mk_box(found);
+ if let (true, Ok(snippet)) = (
+ self.can_coerce(boxed_found, expected),
+ self.sess().source_map().span_to_snippet(expr.span),
+ ) {
+ err.span_suggestion(
+ expr.span,
+ "store this in the heap by calling `Box::new`",
+ format!("Box::new({})", snippet),
+ Applicability::MachineApplicable,
+ );
+ err.note(
+ "for more on the distinction between the stack and the heap, read \
+ https://doc.rust-lang.org/book/ch15-01-box.html, \
+ https://doc.rust-lang.org/rust-by-example/std/box.html, and \
+ https://doc.rust-lang.org/std/boxed/index.html",
+ );
+ }
+ }
+
+ /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
+ pub(super) fn suggest_calling_boxed_future_when_appropriate(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) -> bool {
+ // Handle #68197.
+
+ if self.tcx.hir().is_inside_const_context(expr.hir_id) {
+ // Do not suggest `Box::new` in const context.
+ return false;
+ }
+ let pin_did = self.tcx.lang_items().pin_type();
+ match expected.kind() {
+ ty::Adt(def, _) if Some(def.did) != pin_did => return false,
+ // This guards the `unwrap` and `mk_box` below.
+ _ if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() => return false,
+ _ => {}
+ }
+ let boxed_found = self.tcx.mk_box(found);
+ let new_found = self.tcx.mk_lang_item(boxed_found, LangItem::Pin).unwrap();
+ if let (true, Ok(snippet)) = (
+ self.can_coerce(new_found, expected),
+ self.sess().source_map().span_to_snippet(expr.span),
+ ) {
+ match found.kind() {
+ ty::Adt(def, _) if def.is_box() => {
+ err.help("use `Box::pin`");
+ }
+ _ => {
+ err.span_suggestion(
+ expr.span,
+ "you need to pin and box this expression",
+ format!("Box::pin({})", snippet),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ true
+ } else {
+ false
+ }
+ }
+
+ /// A common error is to forget to add a semicolon at the end of a block, e.g.,
+ ///
+ /// ```
+ /// fn foo() {
+ /// bar_that_returns_u32()
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the return expression in a block would make sense on its own as a
+ /// statement and the return type has been left as default or has been specified as `()`. If so,
+ /// it suggests adding a semicolon.
+ fn suggest_missing_semicolon(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expression: &'tcx hir::Expr<'tcx>,
+ expected: Ty<'tcx>,
+ cause_span: Span,
+ ) {
+ if expected.is_unit() {
+ // `BlockTailExpression` only relevant if the tail expr would be
+ // useful on its own.
+ match expression.kind {
+ ExprKind::Call(..)
+ | ExprKind::MethodCall(..)
+ | ExprKind::Loop(..)
+ | ExprKind::Match(..)
+ | ExprKind::Block(..) => {
+ err.span_suggestion(
+ cause_span.shrink_to_hi(),
+ "try adding a semicolon",
+ ";".to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ _ => (),
+ }
+ }
+ }
+
+ /// A possible error is to forget to add a return type that is needed:
+ ///
+ /// ```
+ /// fn foo() {
+ /// bar_that_returns_u32()
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the return type is left as default, the method is not part of an
+ /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return
+ /// type.
+ pub(super) fn suggest_missing_return_type(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ fn_decl: &hir::FnDecl<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ can_suggest: bool,
+ ) -> bool {
+ // Only suggest changing the return type for methods that
+ // haven't set a return type at all (and aren't `fn main()` or an impl).
+ match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+ (&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
+ err.span_suggestion(
+ span,
+ "try adding a return type",
+ format!("-> {} ", self.resolve_vars_with_obligations(found)),
+ Applicability::MachineApplicable,
+ );
+ true
+ }
+ (&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
+ err.span_label(span, "possibly return type missing here?");
+ true
+ }
+ (&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
+ // `fn main()` must return `()`, do not suggest changing return type
+ err.span_label(span, "expected `()` because of default return type");
+ true
+ }
+ // expectation was caused by something else, not the default return
+ (&hir::FnRetTy::DefaultReturn(_), _, _, false) => false,
+ (&hir::FnRetTy::Return(ref ty), _, _, _) => {
+ // Only point to return type if the expected type is the return type, as if they
+ // are not, the expectation must have been caused by something else.
+ debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
+ let sp = ty.span;
+ let ty = AstConv::ast_ty_to_ty(self, ty);
+ debug!("suggest_missing_return_type: return type {:?}", ty);
+ debug!("suggest_missing_return_type: expected type {:?}", ty);
+ if ty.kind() == expected.kind() {
+ err.span_label(sp, format!("expected `{}` because of return type", expected));
+ return true;
+ }
+ false
+ }
+ }
+ }
+
+ /// A possible error is to forget to add `.await` when using futures:
+ ///
+ /// ```
+ /// async fn make_u32() -> u32 {
+ /// 22
+ /// }
+ ///
+ /// fn take_u32(x: u32) {}
+ ///
+ /// async fn foo() {
+ /// let x = make_u32();
+ /// take_u32(x);
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+ /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+ /// `.await` to the tail of the expression.
+ pub(super) fn suggest_missing_await(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ expected: Ty<'tcx>,
+ found: Ty<'tcx>,
+ ) {
+ debug!("suggest_missing_await: expr={:?} expected={:?}, found={:?}", expr, expected, found);
+ // `.await` is not permitted outside of `async` bodies, so don't bother to suggest if the
+ // body isn't `async`.
+ let item_id = self.tcx().hir().get_parent_node(self.body_id);
+ if let Some(body_id) = self.tcx().hir().maybe_body_owned_by(item_id) {
+ let body = self.tcx().hir().body(body_id);
+ if let Some(hir::GeneratorKind::Async(_)) = body.generator_kind {
+ let sp = expr.span;
+ // Check for `Future` implementations by constructing a predicate to
+ // prove: `<T as Future>::Output == U`
+ let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(sp));
+ let item_def_id = self
+ .tcx
+ .associated_items(future_trait)
+ .in_definition_order()
+ .next()
+ .unwrap()
+ .def_id;
+ // `<T as Future>::Output`
+ let projection_ty = ty::ProjectionTy {
+ // `T`
+ substs: self
+ .tcx
+ .mk_substs_trait(found, self.fresh_substs_for_item(sp, item_def_id)),
+ // `Future::Output`
+ item_def_id,
+ };
+
+ let predicate = ty::PredicateAtom::Projection(ty::ProjectionPredicate {
+ projection_ty,
+ ty: expected,
+ })
+ .potentially_quantified(self.tcx, ty::PredicateKind::ForAll);
+ let obligation = traits::Obligation::new(self.misc(sp), self.param_env, predicate);
+
+ debug!("suggest_missing_await: trying obligation {:?}", obligation);
+
+ if self.infcx.predicate_may_hold(&obligation) {
+ debug!("suggest_missing_await: obligation held: {:?}", obligation);
+ if let Ok(code) = self.sess().source_map().span_to_snippet(sp) {
+ err.span_suggestion(
+ sp,
+ "consider using `.await` here",
+ format!("{}.await", code),
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ debug!("suggest_missing_await: no snippet for {:?}", sp);
+ }
+ } else {
+ debug!("suggest_missing_await: obligation did not hold: {:?}", obligation)
+ }
+ }
+ }
+ }
+
+ pub(super) fn suggest_missing_parentheses(
+ &self,
+ err: &mut DiagnosticBuilder<'_>,
+ expr: &hir::Expr<'_>,
+ ) {
+ let sp = self.tcx.sess.source_map().start_point(expr.span);
+ if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) {
+ // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
+ self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp, None);
+ }
+ }
+}