]>
Commit | Line | Data |
---|---|---|
29967ef6 | 1 | use super::FnCtxt; |
29967ef6 | 2 | |
2b03887a FG |
3 | use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; |
4 | use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; | |
04454e1e | 5 | use rustc_errors::{Applicability, Diagnostic, MultiSpan}; |
29967ef6 XL |
6 | use rustc_hir as hir; |
7 | use rustc_hir::def::{CtorOf, DefKind}; | |
8 | use rustc_hir::lang_items::LangItem; | |
5099ac24 | 9 | use rustc_hir::{ |
923072b8 | 10 | Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, |
5099ac24 | 11 | }; |
2b03887a | 12 | use rustc_hir_analysis::astconv::AstConv; |
5099ac24 | 13 | use rustc_infer::infer::{self, TyCtxtInferExt}; |
064997fb | 14 | use rustc_infer::traits::{self, StatementAsExpression}; |
6a06907d | 15 | use rustc_middle::lint::in_external_macro; |
2b03887a FG |
16 | use rustc_middle::ty::{self, Binder, IsSuggestable, ToPredicate, Ty}; |
17 | use rustc_session::errors::ExprParenthesesNeeded; | |
923072b8 | 18 | use rustc_span::symbol::sym; |
04454e1e | 19 | use rustc_span::Span; |
f2b60f7d | 20 | use rustc_trait_selection::infer::InferCtxtExt; |
2b03887a | 21 | use rustc_trait_selection::traits::error_reporting::DefIdOrName; |
064997fb | 22 | use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; |
29967ef6 XL |
23 | |
24 | impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | |
5e7ed085 | 25 | pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { |
29967ef6 XL |
26 | err.span_suggestion_short( |
27 | span.shrink_to_hi(), | |
28 | "consider using a semicolon here", | |
923072b8 | 29 | ";", |
29967ef6 XL |
30 | Applicability::MachineApplicable, |
31 | ); | |
32 | } | |
33 | ||
34 | /// On implicit return expressions with mismatched types, provides the following suggestions: | |
35 | /// | |
36 | /// - Points out the method's return type as the reason for the expected type. | |
37 | /// - Possible missing semicolon. | |
38 | /// - Possible missing return type if the return type is the default, and not `fn main()`. | |
39 | pub fn suggest_mismatched_types_on_tail( | |
40 | &self, | |
5e7ed085 | 41 | err: &mut Diagnostic, |
29967ef6 XL |
42 | expr: &'tcx hir::Expr<'tcx>, |
43 | expected: Ty<'tcx>, | |
44 | found: Ty<'tcx>, | |
29967ef6 XL |
45 | blk_id: hir::HirId, |
46 | ) -> bool { | |
47 | let expr = expr.peel_drop_temps(); | |
923072b8 | 48 | self.suggest_missing_semicolon(err, expr, expected, false); |
29967ef6 XL |
49 | let mut pointing_at_return_type = false; |
50 | if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { | |
cdc7bbd5 | 51 | let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap(); |
136023e0 XL |
52 | pointing_at_return_type = self.suggest_missing_return_type( |
53 | err, | |
54 | &fn_decl, | |
55 | expected, | |
56 | found, | |
57 | can_suggest, | |
58 | fn_id, | |
59 | ); | |
cdc7bbd5 XL |
60 | self.suggest_missing_break_or_return_expr( |
61 | err, expr, &fn_decl, expected, found, blk_id, fn_id, | |
62 | ); | |
29967ef6 XL |
63 | } |
64 | pointing_at_return_type | |
65 | } | |
66 | ||
f2b60f7d | 67 | /// When encountering an fn-like type, try accessing the output of the type |
2b03887a | 68 | /// and suggesting calling it if it satisfies a predicate (i.e. if the |
f2b60f7d | 69 | /// output has a method or a field): |
04454e1e | 70 | /// ```compile_fail,E0308 |
29967ef6 XL |
71 | /// fn foo(x: usize) -> usize { x } |
72 | /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` | |
73 | /// ``` | |
f2b60f7d | 74 | pub(crate) fn suggest_fn_call( |
29967ef6 | 75 | &self, |
5e7ed085 | 76 | err: &mut Diagnostic, |
29967ef6 | 77 | expr: &hir::Expr<'_>, |
29967ef6 | 78 | found: Ty<'tcx>, |
f2b60f7d | 79 | can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, |
29967ef6 | 80 | ) -> bool { |
f2b60f7d FG |
81 | let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found) |
82 | else { return false; }; | |
83 | if can_satisfy(output) { | |
84 | let (sugg_call, mut applicability) = match inputs.len() { | |
923072b8 FG |
85 | 0 => ("".to_string(), Applicability::MachineApplicable), |
86 | 1..=4 => ( | |
f2b60f7d FG |
87 | inputs |
88 | .iter() | |
89 | .map(|ty| { | |
90 | if ty.is_suggestable(self.tcx, false) { | |
91 | format!("/* {ty} */") | |
92 | } else { | |
2b03887a | 93 | "/* value */".to_string() |
f2b60f7d FG |
94 | } |
95 | }) | |
96 | .collect::<Vec<_>>() | |
97 | .join(", "), | |
98 | Applicability::HasPlaceholders, | |
923072b8 | 99 | ), |
f2b60f7d | 100 | _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), |
29967ef6 | 101 | }; |
923072b8 | 102 | |
f2b60f7d FG |
103 | let msg = match def_id_or_name { |
104 | DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { | |
2b03887a FG |
105 | DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), |
106 | DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), | |
f2b60f7d FG |
107 | kind => format!("call this {}", kind.descr(def_id)), |
108 | }, | |
109 | DefIdOrName::Name(name) => format!("call this {name}"), | |
923072b8 FG |
110 | }; |
111 | ||
112 | let sugg = match expr.kind { | |
113 | hir::ExprKind::Call(..) | |
114 | | hir::ExprKind::Path(..) | |
115 | | hir::ExprKind::Index(..) | |
116 | | hir::ExprKind::Lit(..) => { | |
117 | vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] | |
29967ef6 | 118 | } |
923072b8 FG |
119 | hir::ExprKind::Closure { .. } => { |
120 | // Might be `{ expr } || { bool }` | |
121 | applicability = Applicability::MaybeIncorrect; | |
122 | vec![ | |
123 | (expr.span.shrink_to_lo(), "(".to_string()), | |
124 | (expr.span.shrink_to_hi(), format!(")({sugg_call})")), | |
125 | ] | |
29967ef6 | 126 | } |
923072b8 FG |
127 | _ => { |
128 | vec![ | |
129 | (expr.span.shrink_to_lo(), "(".to_string()), | |
130 | (expr.span.shrink_to_hi(), format!(")({sugg_call})")), | |
131 | ] | |
29967ef6 | 132 | } |
923072b8 FG |
133 | }; |
134 | ||
135 | err.multipart_suggestion_verbose( | |
136 | format!("use parentheses to {msg}"), | |
137 | sugg, | |
29967ef6 XL |
138 | applicability, |
139 | ); | |
140 | return true; | |
141 | } | |
142 | false | |
143 | } | |
144 | ||
f2b60f7d FG |
145 | /// Extracts information about a callable type for diagnostics. This is a |
146 | /// heuristic -- it doesn't necessarily mean that a type is always callable, | |
147 | /// because the callable type must also be well-formed to be called. | |
148 | pub(in super::super) fn extract_callable_info( | |
149 | &self, | |
150 | expr: &Expr<'_>, | |
151 | found: Ty<'tcx>, | |
152 | ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { | |
153 | // Autoderef is useful here because sometimes we box callables, etc. | |
154 | let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { | |
155 | match *found.kind() { | |
156 | ty::FnPtr(fn_sig) => | |
157 | Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())), | |
158 | ty::FnDef(def_id, _) => { | |
159 | let fn_sig = found.fn_sig(self.tcx); | |
160 | Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) | |
161 | } | |
162 | ty::Closure(def_id, substs) => { | |
163 | let fn_sig = substs.as_closure().sig(); | |
164 | Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..]))) | |
165 | } | |
166 | ty::Opaque(def_id, substs) => { | |
167 | self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { | |
168 | if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() | |
169 | && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() | |
170 | // args tuple will always be substs[1] | |
171 | && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() | |
172 | { | |
173 | Some(( | |
174 | DefIdOrName::DefId(def_id), | |
175 | pred.kind().rebind(proj.term.ty().unwrap()), | |
176 | pred.kind().rebind(args.as_slice()), | |
177 | )) | |
178 | } else { | |
179 | None | |
180 | } | |
181 | }) | |
182 | } | |
183 | ty::Dynamic(data, _, ty::Dyn) => { | |
184 | data.iter().find_map(|pred| { | |
185 | if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() | |
186 | && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() | |
187 | // for existential projection, substs are shifted over by 1 | |
188 | && let ty::Tuple(args) = proj.substs.type_at(0).kind() | |
189 | { | |
190 | Some(( | |
191 | DefIdOrName::Name("trait object"), | |
192 | pred.rebind(proj.term.ty().unwrap()), | |
193 | pred.rebind(args.as_slice()), | |
194 | )) | |
195 | } else { | |
196 | None | |
197 | } | |
198 | }) | |
199 | } | |
200 | ty::Param(param) => { | |
201 | let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id; | |
202 | self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| { | |
203 | if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() | |
204 | && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() | |
205 | && proj.projection_ty.self_ty() == found | |
206 | // args tuple will always be substs[1] | |
207 | && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() | |
208 | { | |
209 | Some(( | |
210 | DefIdOrName::DefId(def_id), | |
211 | pred.kind().rebind(proj.term.ty().unwrap()), | |
212 | pred.kind().rebind(args.as_slice()), | |
213 | )) | |
214 | } else { | |
215 | None | |
216 | } | |
217 | }) | |
218 | } | |
219 | _ => None, | |
220 | } | |
221 | }) else { return None; }; | |
222 | ||
223 | let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); | |
224 | let inputs = inputs | |
225 | .skip_binder() | |
226 | .iter() | |
227 | .map(|ty| { | |
228 | self.replace_bound_vars_with_fresh_vars( | |
229 | expr.span, | |
230 | infer::FnCall, | |
231 | inputs.rebind(*ty), | |
232 | ) | |
233 | }) | |
234 | .collect(); | |
235 | ||
236 | // We don't want to register any extra obligations, which should be | |
237 | // implied by wf, but also because that would possibly result in | |
238 | // erroneous errors later on. | |
239 | let infer::InferOk { value: output, obligations: _ } = | |
240 | self.normalize_associated_types_in_as_infer_ok(expr.span, output); | |
241 | ||
242 | if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } | |
243 | } | |
244 | ||
245 | pub fn suggest_two_fn_call( | |
246 | &self, | |
247 | err: &mut Diagnostic, | |
248 | lhs_expr: &'tcx hir::Expr<'tcx>, | |
249 | lhs_ty: Ty<'tcx>, | |
250 | rhs_expr: &'tcx hir::Expr<'tcx>, | |
251 | rhs_ty: Ty<'tcx>, | |
252 | can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, | |
253 | ) -> bool { | |
254 | let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty) | |
255 | else { return false; }; | |
256 | let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty) | |
257 | else { return false; }; | |
258 | ||
259 | if can_satisfy(lhs_output_ty, rhs_output_ty) { | |
260 | let mut sugg = vec![]; | |
261 | let mut applicability = Applicability::MachineApplicable; | |
262 | ||
263 | for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] { | |
264 | let (sugg_call, this_applicability) = match inputs.len() { | |
265 | 0 => ("".to_string(), Applicability::MachineApplicable), | |
266 | 1..=4 => ( | |
267 | inputs | |
268 | .iter() | |
269 | .map(|ty| { | |
270 | if ty.is_suggestable(self.tcx, false) { | |
271 | format!("/* {ty} */") | |
272 | } else { | |
273 | "/* value */".to_string() | |
274 | } | |
275 | }) | |
276 | .collect::<Vec<_>>() | |
277 | .join(", "), | |
278 | Applicability::HasPlaceholders, | |
279 | ), | |
280 | _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), | |
281 | }; | |
282 | ||
283 | applicability = applicability.max(this_applicability); | |
284 | ||
285 | match expr.kind { | |
286 | hir::ExprKind::Call(..) | |
287 | | hir::ExprKind::Path(..) | |
288 | | hir::ExprKind::Index(..) | |
289 | | hir::ExprKind::Lit(..) => { | |
290 | sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]); | |
291 | } | |
292 | hir::ExprKind::Closure { .. } => { | |
293 | // Might be `{ expr } || { bool }` | |
294 | applicability = Applicability::MaybeIncorrect; | |
295 | sugg.extend([ | |
296 | (expr.span.shrink_to_lo(), "(".to_string()), | |
297 | (expr.span.shrink_to_hi(), format!(")({sugg_call})")), | |
298 | ]); | |
299 | } | |
300 | _ => { | |
301 | sugg.extend([ | |
302 | (expr.span.shrink_to_lo(), "(".to_string()), | |
303 | (expr.span.shrink_to_hi(), format!(")({sugg_call})")), | |
304 | ]); | |
305 | } | |
306 | } | |
307 | } | |
308 | ||
309 | err.multipart_suggestion_verbose( | |
310 | format!("use parentheses to call these"), | |
311 | sugg, | |
312 | applicability, | |
313 | ); | |
314 | ||
315 | true | |
316 | } else { | |
317 | false | |
318 | } | |
319 | } | |
320 | ||
29967ef6 XL |
321 | pub fn suggest_deref_ref_or_into( |
322 | &self, | |
5e7ed085 | 323 | err: &mut Diagnostic, |
5099ac24 | 324 | expr: &hir::Expr<'tcx>, |
29967ef6 XL |
325 | expected: Ty<'tcx>, |
326 | found: Ty<'tcx>, | |
327 | expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, | |
2b03887a | 328 | ) -> bool { |
cdc7bbd5 | 329 | let expr = expr.peel_blocks(); |
2b03887a | 330 | if let Some((sp, msg, suggestion, applicability, verbose, annotation)) = |
94222f64 XL |
331 | self.check_ref(expr, found, expected) |
332 | { | |
333 | if verbose { | |
5e7ed085 | 334 | err.span_suggestion_verbose(sp, &msg, suggestion, applicability); |
94222f64 | 335 | } else { |
5e7ed085 | 336 | err.span_suggestion(sp, &msg, suggestion, applicability); |
94222f64 | 337 | } |
2b03887a FG |
338 | if annotation { |
339 | let suggest_annotation = match expr.peel_drop_temps().kind { | |
340 | hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, _) => "&", | |
341 | hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Mut, _) => "&mut ", | |
342 | _ => return true, | |
343 | }; | |
344 | let mut tuple_indexes = Vec::new(); | |
345 | let mut expr_id = expr.hir_id; | |
346 | for (parent_id, node) in self.tcx.hir().parent_iter(expr.hir_id) { | |
347 | match node { | |
348 | Node::Expr(&Expr { kind: ExprKind::Tup(subs), .. }) => { | |
349 | tuple_indexes.push( | |
350 | subs.iter() | |
351 | .enumerate() | |
352 | .find(|(_, sub_expr)| sub_expr.hir_id == expr_id) | |
353 | .unwrap() | |
354 | .0, | |
355 | ); | |
356 | expr_id = parent_id; | |
357 | } | |
358 | Node::Local(local) => { | |
359 | if let Some(mut ty) = local.ty { | |
360 | while let Some(index) = tuple_indexes.pop() { | |
361 | match ty.kind { | |
362 | TyKind::Tup(tys) => ty = &tys[index], | |
363 | _ => return true, | |
364 | } | |
365 | } | |
366 | let annotation_span = ty.span; | |
367 | err.span_suggestion( | |
368 | annotation_span.with_hi(annotation_span.lo()), | |
369 | format!("alternatively, consider changing the type annotation"), | |
370 | suggest_annotation, | |
371 | Applicability::MaybeIncorrect, | |
372 | ); | |
373 | } | |
374 | break; | |
375 | } | |
376 | _ => break, | |
377 | } | |
378 | } | |
379 | } | |
380 | return true; | |
381 | } else if self.suggest_else_fn_with_closure(err, expr, found, expected) { | |
382 | return true; | |
f2b60f7d FG |
383 | } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) |
384 | && let ty::FnDef(def_id, ..) = &found.kind() | |
385 | && let Some(sp) = self.tcx.hir().span_if_local(*def_id) | |
29967ef6 | 386 | { |
f2b60f7d | 387 | err.span_label(sp, format!("{found} defined here")); |
2b03887a FG |
388 | return true; |
389 | } else if self.check_for_cast(err, expr, found, expected, expected_ty_expr) { | |
390 | return true; | |
391 | } else { | |
29967ef6 | 392 | let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); |
3c0e092e | 393 | if !methods.is_empty() { |
064997fb FG |
394 | let mut suggestions = methods.iter() |
395 | .filter_map(|conversion_method| { | |
396 | let receiver_method_ident = expr.method_ident(); | |
397 | if let Some(method_ident) = receiver_method_ident | |
398 | && method_ident.name == conversion_method.name | |
399 | { | |
400 | return None // do not suggest code that is already there (#53348) | |
401 | } | |
402 | ||
403 | let method_call_list = [sym::to_vec, sym::to_string]; | |
404 | let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind | |
405 | && receiver_method.ident.name == sym::clone | |
406 | && method_call_list.contains(&conversion_method.name) | |
407 | // If receiver is `.clone()` and found type has one of those methods, | |
408 | // we guess that the user wants to convert from a slice type (`&[]` or `&str`) | |
409 | // to an owned type (`Vec` or `String`). These conversions clone internally, | |
410 | // so we remove the user's `clone` call. | |
411 | { | |
412 | vec![( | |
413 | receiver_method.ident.span, | |
414 | conversion_method.name.to_string() | |
415 | )] | |
416 | } else if expr.precedence().order() | |
417 | < ExprPrecedence::MethodCall.order() | |
418 | { | |
419 | vec![ | |
420 | (expr.span.shrink_to_lo(), "(".to_string()), | |
421 | (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), | |
422 | ] | |
423 | } else { | |
424 | vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] | |
425 | }; | |
426 | let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); | |
427 | if let Some(name) = struct_pat_shorthand_field { | |
428 | sugg.insert( | |
429 | 0, | |
430 | (expr.span.shrink_to_lo(), format!("{}: ", name)), | |
431 | ); | |
432 | } | |
433 | Some(sugg) | |
434 | }) | |
435 | .peekable(); | |
436 | if suggestions.peek().is_some() { | |
437 | err.multipart_suggestions( | |
438 | "try using a conversion method", | |
439 | suggestions, | |
440 | Applicability::MaybeIncorrect, | |
441 | ); | |
2b03887a | 442 | return true; |
3c0e092e | 443 | } |
064997fb FG |
444 | } else if let ty::Adt(found_adt, found_substs) = found.kind() |
445 | && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) | |
446 | && let ty::Adt(expected_adt, expected_substs) = expected.kind() | |
447 | && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) | |
448 | && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind() | |
449 | && inner_ty.is_str() | |
3c0e092e | 450 | { |
064997fb FG |
451 | let ty = found_substs.type_at(0); |
452 | let mut peeled = ty; | |
453 | let mut ref_cnt = 0; | |
454 | while let ty::Ref(_, inner, _) = peeled.kind() { | |
455 | peeled = *inner; | |
456 | ref_cnt += 1; | |
457 | } | |
458 | if let ty::Adt(adt, _) = peeled.kind() | |
459 | && self.tcx.is_diagnostic_item(sym::String, adt.did()) | |
460 | { | |
461 | err.span_suggestion_verbose( | |
462 | expr.span.shrink_to_hi(), | |
463 | "try converting the passed type into a `&str`", | |
464 | format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), | |
465 | Applicability::MaybeIncorrect, | |
466 | ); | |
2b03887a | 467 | return true; |
29967ef6 XL |
468 | } |
469 | } | |
470 | } | |
2b03887a FG |
471 | |
472 | false | |
29967ef6 XL |
473 | } |
474 | ||
475 | /// When encountering the expected boxed value allocated in the stack, suggest allocating it | |
476 | /// in the heap by calling `Box::new()`. | |
477 | pub(in super::super) fn suggest_boxing_when_appropriate( | |
478 | &self, | |
5e7ed085 | 479 | err: &mut Diagnostic, |
29967ef6 XL |
480 | expr: &hir::Expr<'_>, |
481 | expected: Ty<'tcx>, | |
482 | found: Ty<'tcx>, | |
2b03887a | 483 | ) -> bool { |
29967ef6 XL |
484 | if self.tcx.hir().is_inside_const_context(expr.hir_id) { |
485 | // Do not suggest `Box::new` in const context. | |
2b03887a | 486 | return false; |
29967ef6 XL |
487 | } |
488 | if !expected.is_box() || found.is_box() { | |
2b03887a | 489 | return false; |
29967ef6 XL |
490 | } |
491 | let boxed_found = self.tcx.mk_box(found); | |
94222f64 XL |
492 | if self.can_coerce(boxed_found, expected) { |
493 | err.multipart_suggestion( | |
29967ef6 | 494 | "store this in the heap by calling `Box::new`", |
94222f64 XL |
495 | vec![ |
496 | (expr.span.shrink_to_lo(), "Box::new(".to_string()), | |
497 | (expr.span.shrink_to_hi(), ")".to_string()), | |
498 | ], | |
29967ef6 XL |
499 | Applicability::MachineApplicable, |
500 | ); | |
501 | err.note( | |
502 | "for more on the distinction between the stack and the heap, read \ | |
503 | https://doc.rust-lang.org/book/ch15-01-box.html, \ | |
504 | https://doc.rust-lang.org/rust-by-example/std/box.html, and \ | |
505 | https://doc.rust-lang.org/std/boxed/index.html", | |
506 | ); | |
2b03887a FG |
507 | true |
508 | } else { | |
509 | false | |
29967ef6 XL |
510 | } |
511 | } | |
512 | ||
5869c6ff XL |
513 | /// When encountering a closure that captures variables, where a FnPtr is expected, |
514 | /// suggest a non-capturing closure | |
515 | pub(in super::super) fn suggest_no_capture_closure( | |
516 | &self, | |
5e7ed085 | 517 | err: &mut Diagnostic, |
5869c6ff XL |
518 | expected: Ty<'tcx>, |
519 | found: Ty<'tcx>, | |
2b03887a | 520 | ) -> bool { |
5869c6ff XL |
521 | if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { |
522 | if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { | |
523 | // Report upto four upvars being captured to reduce the amount error messages | |
524 | // reported back to the user. | |
525 | let spans_and_labels = upvars | |
526 | .iter() | |
527 | .take(4) | |
528 | .map(|(var_hir_id, upvar)| { | |
529 | let var_name = self.tcx.hir().name(*var_hir_id).to_string(); | |
530 | let msg = format!("`{}` captured here", var_name); | |
531 | (upvar.span, msg) | |
532 | }) | |
533 | .collect::<Vec<_>>(); | |
534 | ||
535 | let mut multi_span: MultiSpan = | |
536 | spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into(); | |
537 | for (sp, label) in spans_and_labels { | |
538 | multi_span.push_span_label(sp, label); | |
539 | } | |
c295e0f8 XL |
540 | err.span_note( |
541 | multi_span, | |
542 | "closures can only be coerced to `fn` types if they do not capture any variables" | |
543 | ); | |
2b03887a | 544 | return true; |
5869c6ff XL |
545 | } |
546 | } | |
2b03887a | 547 | false |
5869c6ff XL |
548 | } |
549 | ||
29967ef6 | 550 | /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. |
c295e0f8 | 551 | #[instrument(skip(self, err))] |
29967ef6 XL |
552 | pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( |
553 | &self, | |
5e7ed085 | 554 | err: &mut Diagnostic, |
29967ef6 XL |
555 | expr: &hir::Expr<'_>, |
556 | expected: Ty<'tcx>, | |
557 | found: Ty<'tcx>, | |
558 | ) -> bool { | |
559 | // Handle #68197. | |
560 | ||
561 | if self.tcx.hir().is_inside_const_context(expr.hir_id) { | |
562 | // Do not suggest `Box::new` in const context. | |
563 | return false; | |
564 | } | |
565 | let pin_did = self.tcx.lang_items().pin_type(); | |
c295e0f8 XL |
566 | // This guards the `unwrap` and `mk_box` below. |
567 | if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() { | |
568 | return false; | |
29967ef6 | 569 | } |
c295e0f8 XL |
570 | let box_found = self.tcx.mk_box(found); |
571 | let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap(); | |
572 | let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap(); | |
573 | match expected.kind() { | |
5e7ed085 | 574 | ty::Adt(def, _) if Some(def.did()) == pin_did => { |
c295e0f8 XL |
575 | if self.can_coerce(pin_box_found, expected) { |
576 | debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected); | |
577 | match found.kind() { | |
578 | ty::Adt(def, _) if def.is_box() => { | |
579 | err.help("use `Box::pin`"); | |
580 | } | |
581 | _ => { | |
582 | err.multipart_suggestion( | |
583 | "you need to pin and box this expression", | |
584 | vec![ | |
585 | (expr.span.shrink_to_lo(), "Box::pin(".to_string()), | |
586 | (expr.span.shrink_to_hi(), ")".to_string()), | |
587 | ], | |
588 | Applicability::MaybeIncorrect, | |
589 | ); | |
590 | } | |
591 | } | |
592 | true | |
593 | } else if self.can_coerce(pin_found, expected) { | |
594 | match found.kind() { | |
595 | ty::Adt(def, _) if def.is_box() => { | |
596 | err.help("use `Box::pin`"); | |
597 | true | |
598 | } | |
599 | _ => false, | |
600 | } | |
601 | } else { | |
602 | false | |
29967ef6 | 603 | } |
c295e0f8 XL |
604 | } |
605 | ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => { | |
606 | // Check if the parent expression is a call to Pin::new. If it | |
607 | // is and we were expecting a Box, ergo Pin<Box<expected>>, we | |
608 | // can suggest Box::pin. | |
609 | let parent = self.tcx.hir().get_parent_node(expr.hir_id); | |
5e7ed085 FG |
610 | let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else { |
611 | return false; | |
c295e0f8 XL |
612 | }; |
613 | match fn_name.kind { | |
614 | ExprKind::Path(QPath::TypeRelative( | |
615 | hir::Ty { | |
616 | kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })), | |
617 | .. | |
618 | }, | |
619 | method, | |
620 | )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => { | |
621 | err.span_suggestion( | |
622 | fn_name.span, | |
623 | "use `Box::pin` to pin and box this expression", | |
923072b8 | 624 | "Box::pin", |
c295e0f8 XL |
625 | Applicability::MachineApplicable, |
626 | ); | |
627 | true | |
628 | } | |
629 | _ => false, | |
29967ef6 XL |
630 | } |
631 | } | |
c295e0f8 | 632 | _ => false, |
29967ef6 XL |
633 | } |
634 | } | |
635 | ||
636 | /// A common error is to forget to add a semicolon at the end of a block, e.g., | |
637 | /// | |
04454e1e FG |
638 | /// ```compile_fail,E0308 |
639 | /// # fn bar_that_returns_u32() -> u32 { 4 } | |
29967ef6 XL |
640 | /// fn foo() { |
641 | /// bar_that_returns_u32() | |
642 | /// } | |
643 | /// ``` | |
644 | /// | |
645 | /// This routine checks if the return expression in a block would make sense on its own as a | |
646 | /// statement and the return type has been left as default or has been specified as `()`. If so, | |
647 | /// it suggests adding a semicolon. | |
923072b8 FG |
648 | /// |
649 | /// If the expression is the expression of a closure without block (`|| expr`), a | |
650 | /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`. | |
651 | pub fn suggest_missing_semicolon( | |
29967ef6 | 652 | &self, |
5e7ed085 | 653 | err: &mut Diagnostic, |
29967ef6 XL |
654 | expression: &'tcx hir::Expr<'tcx>, |
655 | expected: Ty<'tcx>, | |
923072b8 | 656 | needs_block: bool, |
29967ef6 XL |
657 | ) { |
658 | if expected.is_unit() { | |
659 | // `BlockTailExpression` only relevant if the tail expr would be | |
660 | // useful on its own. | |
661 | match expression.kind { | |
662 | ExprKind::Call(..) | |
663 | | ExprKind::MethodCall(..) | |
664 | | ExprKind::Loop(..) | |
5869c6ff | 665 | | ExprKind::If(..) |
29967ef6 | 666 | | ExprKind::Match(..) |
6a06907d | 667 | | ExprKind::Block(..) |
923072b8 FG |
668 | if expression.can_have_side_effects() |
669 | // If the expression is from an external macro, then do not suggest | |
670 | // adding a semicolon, because there's nowhere to put it. | |
671 | // See issue #81943. | |
672 | && !in_external_macro(self.tcx.sess, expression.span) => | |
6a06907d | 673 | { |
923072b8 FG |
674 | if needs_block { |
675 | err.multipart_suggestion( | |
676 | "consider using a semicolon here", | |
677 | vec![ | |
678 | (expression.span.shrink_to_lo(), "{ ".to_owned()), | |
679 | (expression.span.shrink_to_hi(), "; }".to_owned()), | |
680 | ], | |
681 | Applicability::MachineApplicable, | |
682 | ); | |
683 | } else { | |
684 | err.span_suggestion( | |
685 | expression.span.shrink_to_hi(), | |
686 | "consider using a semicolon here", | |
687 | ";", | |
688 | Applicability::MachineApplicable, | |
689 | ); | |
690 | } | |
29967ef6 XL |
691 | } |
692 | _ => (), | |
693 | } | |
694 | } | |
695 | } | |
696 | ||
697 | /// A possible error is to forget to add a return type that is needed: | |
698 | /// | |
04454e1e FG |
699 | /// ```compile_fail,E0308 |
700 | /// # fn bar_that_returns_u32() -> u32 { 4 } | |
29967ef6 XL |
701 | /// fn foo() { |
702 | /// bar_that_returns_u32() | |
703 | /// } | |
704 | /// ``` | |
705 | /// | |
706 | /// This routine checks if the return type is left as default, the method is not part of an | |
707 | /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return | |
708 | /// type. | |
709 | pub(in super::super) fn suggest_missing_return_type( | |
710 | &self, | |
5e7ed085 | 711 | err: &mut Diagnostic, |
29967ef6 XL |
712 | fn_decl: &hir::FnDecl<'_>, |
713 | expected: Ty<'tcx>, | |
714 | found: Ty<'tcx>, | |
715 | can_suggest: bool, | |
136023e0 | 716 | fn_id: hir::HirId, |
29967ef6 | 717 | ) -> bool { |
5e7ed085 FG |
718 | let found = |
719 | self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); | |
29967ef6 XL |
720 | // Only suggest changing the return type for methods that |
721 | // haven't set a return type at all (and aren't `fn main()` or an impl). | |
f2b60f7d FG |
722 | match &fn_decl.output { |
723 | &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => { | |
29967ef6 | 724 | // `fn main()` must return `()`, do not suggest changing return type |
04454e1e | 725 | err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span }); |
f2b60f7d FG |
726 | return true; |
727 | } | |
728 | &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { | |
729 | if found.is_suggestable(self.tcx, false) { | |
730 | err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); | |
731 | return true; | |
732 | } else if let ty::Closure(_, substs) = found.kind() | |
733 | // FIXME(compiler-errors): Get better at printing binders... | |
734 | && let closure = substs.as_closure() | |
735 | && closure.sig().is_suggestable(self.tcx, false) | |
736 | { | |
737 | err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() }); | |
738 | return true; | |
739 | } else { | |
740 | // FIXME: if `found` could be `impl Iterator` we should suggest that. | |
741 | err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); | |
742 | return true | |
743 | } | |
29967ef6 | 744 | } |
f2b60f7d | 745 | &hir::FnRetTy::Return(ref ty) => { |
29967ef6 XL |
746 | // Only point to return type if the expected type is the return type, as if they |
747 | // are not, the expectation must have been caused by something else. | |
748 | debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); | |
04454e1e | 749 | let span = ty.span; |
6a06907d | 750 | let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); |
29967ef6 XL |
751 | debug!("suggest_missing_return_type: return type {:?}", ty); |
752 | debug!("suggest_missing_return_type: expected type {:?}", ty); | |
136023e0 | 753 | let bound_vars = self.tcx.late_bound_vars(fn_id); |
c295e0f8 | 754 | let ty = Binder::bind_with_vars(ty, bound_vars); |
04454e1e | 755 | let ty = self.normalize_associated_types_in(span, ty); |
c295e0f8 | 756 | let ty = self.tcx.erase_late_bound_regions(ty); |
136023e0 | 757 | if self.can_coerce(expected, ty) { |
04454e1e | 758 | err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); |
5099ac24 | 759 | self.try_suggest_return_impl_trait(err, expected, ty, fn_id); |
29967ef6 XL |
760 | return true; |
761 | } | |
29967ef6 | 762 | } |
f2b60f7d | 763 | _ => {} |
29967ef6 | 764 | } |
f2b60f7d | 765 | false |
29967ef6 XL |
766 | } |
767 | ||
5099ac24 FG |
768 | /// check whether the return type is a generic type with a trait bound |
769 | /// only suggest this if the generic param is not present in the arguments | |
770 | /// if this is true, hint them towards changing the return type to `impl Trait` | |
04454e1e | 771 | /// ```compile_fail,E0308 |
5099ac24 FG |
772 | /// fn cant_name_it<T: Fn() -> u32>() -> T { |
773 | /// || 3 | |
774 | /// } | |
775 | /// ``` | |
776 | fn try_suggest_return_impl_trait( | |
777 | &self, | |
5e7ed085 | 778 | err: &mut Diagnostic, |
5099ac24 FG |
779 | expected: Ty<'tcx>, |
780 | found: Ty<'tcx>, | |
781 | fn_id: hir::HirId, | |
782 | ) { | |
783 | // Only apply the suggestion if: | |
784 | // - the return type is a generic parameter | |
785 | // - the generic param is not used as a fn param | |
786 | // - the generic param has at least one bound | |
787 | // - the generic param doesn't appear in any other bounds where it's not the Self type | |
788 | // Suggest: | |
789 | // - Changing the return type to be `impl <all bounds>` | |
790 | ||
791 | debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found); | |
792 | ||
793 | let ty::Param(expected_ty_as_param) = expected.kind() else { return }; | |
794 | ||
795 | let fn_node = self.tcx.hir().find(fn_id); | |
796 | ||
797 | let Some(hir::Node::Item(hir::Item { | |
798 | kind: | |
799 | hir::ItemKind::Fn( | |
800 | hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. }, | |
04454e1e | 801 | hir::Generics { params, predicates, .. }, |
5099ac24 FG |
802 | _body_id, |
803 | ), | |
804 | .. | |
805 | })) = fn_node else { return }; | |
806 | ||
04454e1e FG |
807 | if params.get(expected_ty_as_param.index as usize).is_none() { |
808 | return; | |
809 | }; | |
5099ac24 FG |
810 | |
811 | // get all where BoundPredicates here, because they are used in to cases below | |
04454e1e | 812 | let where_predicates = predicates |
5099ac24 FG |
813 | .iter() |
814 | .filter_map(|p| match p { | |
815 | WherePredicate::BoundPredicate(hir::WhereBoundPredicate { | |
816 | bounds, | |
817 | bounded_ty, | |
818 | .. | |
819 | }) => { | |
820 | // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) | |
821 | let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty); | |
822 | Some((ty, bounds)) | |
823 | } | |
824 | _ => None, | |
825 | }) | |
826 | .map(|(ty, bounds)| match ty.kind() { | |
827 | ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)), | |
828 | // check whether there is any predicate that contains our `T`, like `Option<T>: Send` | |
829 | _ => match ty.contains(expected) { | |
830 | true => Err(()), | |
831 | false => Ok(None), | |
832 | }, | |
833 | }) | |
834 | .collect::<Result<Vec<_>, _>>(); | |
835 | ||
5e7ed085 | 836 | let Ok(where_predicates) = where_predicates else { return }; |
5099ac24 FG |
837 | |
838 | // now get all predicates in the same types as the where bounds, so we can chain them | |
839 | let predicates_from_where = | |
04454e1e | 840 | where_predicates.iter().flatten().flat_map(|bounds| bounds.iter()); |
5099ac24 FG |
841 | |
842 | // extract all bounds from the source code using their spans | |
04454e1e | 843 | let all_matching_bounds_strs = predicates_from_where |
5099ac24 FG |
844 | .filter_map(|bound| match bound { |
845 | GenericBound::Trait(_, _) => { | |
846 | self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() | |
847 | } | |
848 | _ => None, | |
849 | }) | |
850 | .collect::<Vec<String>>(); | |
851 | ||
852 | if all_matching_bounds_strs.len() == 0 { | |
853 | return; | |
854 | } | |
855 | ||
856 | let all_bounds_str = all_matching_bounds_strs.join(" + "); | |
857 | ||
858 | let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { | |
859 | let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param); | |
860 | matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) | |
861 | }); | |
862 | ||
863 | if ty_param_used_in_fn_params { | |
864 | return; | |
865 | } | |
866 | ||
867 | err.span_suggestion( | |
868 | fn_return.span(), | |
869 | "consider using an impl return type", | |
870 | format!("impl {}", all_bounds_str), | |
871 | Applicability::MaybeIncorrect, | |
872 | ); | |
873 | } | |
874 | ||
cdc7bbd5 | 875 | pub(in super::super) fn suggest_missing_break_or_return_expr( |
6a06907d | 876 | &self, |
5e7ed085 | 877 | err: &mut Diagnostic, |
6a06907d XL |
878 | expr: &'tcx hir::Expr<'tcx>, |
879 | fn_decl: &hir::FnDecl<'_>, | |
880 | expected: Ty<'tcx>, | |
881 | found: Ty<'tcx>, | |
cdc7bbd5 XL |
882 | id: hir::HirId, |
883 | fn_id: hir::HirId, | |
6a06907d XL |
884 | ) { |
885 | if !expected.is_unit() { | |
886 | return; | |
887 | } | |
888 | let found = self.resolve_vars_with_obligations(found); | |
cdc7bbd5 XL |
889 | |
890 | let in_loop = self.is_loop(id) | |
891 | || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id)); | |
892 | ||
893 | let in_local_statement = self.is_local_statement(id) | |
894 | || self | |
895 | .tcx | |
896 | .hir() | |
897 | .parent_iter(id) | |
898 | .any(|(parent_id, _)| self.is_local_statement(parent_id)); | |
899 | ||
900 | if in_loop && in_local_statement { | |
901 | err.multipart_suggestion( | |
902 | "you might have meant to break the loop with this value", | |
903 | vec![ | |
904 | (expr.span.shrink_to_lo(), "break ".to_string()), | |
905 | (expr.span.shrink_to_hi(), ";".to_string()), | |
906 | ], | |
907 | Applicability::MaybeIncorrect, | |
908 | ); | |
909 | return; | |
910 | } | |
911 | ||
6a06907d XL |
912 | if let hir::FnRetTy::Return(ty) = fn_decl.output { |
913 | let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); | |
cdc7bbd5 XL |
914 | let bound_vars = self.tcx.late_bound_vars(fn_id); |
915 | let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); | |
6a06907d | 916 | let ty = self.normalize_associated_types_in(expr.span, ty); |
5099ac24 | 917 | let ty = match self.tcx.asyncness(fn_id.owner) { |
2b03887a FG |
918 | hir::IsAsync::Async => { |
919 | let infcx = self.tcx.infer_ctxt().build(); | |
920 | infcx | |
921 | .get_impl_future_output_ty(ty) | |
922 | .unwrap_or_else(|| { | |
5099ac24 FG |
923 | span_bug!( |
924 | fn_decl.output.span(), | |
925 | "failed to get output type of async function" | |
926 | ) | |
927 | }) | |
2b03887a FG |
928 | .skip_binder() |
929 | } | |
5099ac24 FG |
930 | hir::IsAsync::NotAsync => ty, |
931 | }; | |
6a06907d XL |
932 | if self.can_coerce(found, ty) { |
933 | err.multipart_suggestion( | |
934 | "you might have meant to return this value", | |
935 | vec![ | |
936 | (expr.span.shrink_to_lo(), "return ".to_string()), | |
937 | (expr.span.shrink_to_hi(), ";".to_string()), | |
938 | ], | |
939 | Applicability::MaybeIncorrect, | |
940 | ); | |
941 | } | |
942 | } | |
943 | } | |
944 | ||
29967ef6 XL |
945 | pub(in super::super) fn suggest_missing_parentheses( |
946 | &self, | |
5e7ed085 | 947 | err: &mut Diagnostic, |
29967ef6 | 948 | expr: &hir::Expr<'_>, |
2b03887a | 949 | ) -> bool { |
29967ef6 XL |
950 | let sp = self.tcx.sess.source_map().start_point(expr.span); |
951 | if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { | |
952 | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` | |
2b03887a FG |
953 | err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); |
954 | true | |
955 | } else { | |
956 | false | |
29967ef6 XL |
957 | } |
958 | } | |
cdc7bbd5 | 959 | |
5e7ed085 FG |
960 | /// Given an expression type mismatch, peel any `&` expressions until we get to |
961 | /// a block expression, and then suggest replacing the braces with square braces | |
962 | /// if it was possibly mistaken array syntax. | |
963 | pub(crate) fn suggest_block_to_brackets_peeling_refs( | |
964 | &self, | |
965 | diag: &mut Diagnostic, | |
966 | mut expr: &hir::Expr<'_>, | |
967 | mut expr_ty: Ty<'tcx>, | |
968 | mut expected_ty: Ty<'tcx>, | |
2b03887a | 969 | ) -> bool { |
5e7ed085 FG |
970 | loop { |
971 | match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { | |
972 | ( | |
973 | hir::ExprKind::AddrOf(_, _, inner_expr), | |
974 | ty::Ref(_, inner_expr_ty, _), | |
975 | ty::Ref(_, inner_expected_ty, _), | |
976 | ) => { | |
977 | expr = *inner_expr; | |
978 | expr_ty = *inner_expr_ty; | |
979 | expected_ty = *inner_expected_ty; | |
980 | } | |
981 | (hir::ExprKind::Block(blk, _), _, _) => { | |
982 | self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); | |
2b03887a | 983 | break true; |
5e7ed085 | 984 | } |
2b03887a | 985 | _ => break false, |
5e7ed085 FG |
986 | } |
987 | } | |
988 | } | |
989 | ||
f2b60f7d FG |
990 | pub(crate) fn suggest_copied_or_cloned( |
991 | &self, | |
992 | diag: &mut Diagnostic, | |
993 | expr: &hir::Expr<'_>, | |
994 | expr_ty: Ty<'tcx>, | |
995 | expected_ty: Ty<'tcx>, | |
2b03887a FG |
996 | ) -> bool { |
997 | let ty::Adt(adt_def, substs) = expr_ty.kind() else { return false; }; | |
998 | let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return false; }; | |
f2b60f7d | 999 | if adt_def != expected_adt_def { |
2b03887a | 1000 | return false; |
f2b60f7d FG |
1001 | } |
1002 | ||
1003 | let mut suggest_copied_or_cloned = || { | |
1004 | let expr_inner_ty = substs.type_at(0); | |
1005 | let expected_inner_ty = expected_substs.type_at(0); | |
1006 | if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() | |
1007 | && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok() | |
1008 | { | |
1009 | let def_path = self.tcx.def_path_str(adt_def.did()); | |
1010 | if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) { | |
1011 | diag.span_suggestion_verbose( | |
1012 | expr.span.shrink_to_hi(), | |
1013 | format!( | |
1014 | "use `{def_path}::copied` to copy the value inside the `{def_path}`" | |
1015 | ), | |
1016 | ".copied()", | |
1017 | Applicability::MachineApplicable, | |
1018 | ); | |
2b03887a | 1019 | return true; |
f2b60f7d FG |
1020 | } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() |
1021 | && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( | |
1022 | self, | |
1023 | self.param_env, | |
1024 | *ty, | |
1025 | clone_did, | |
1026 | expr.span | |
1027 | ) | |
1028 | { | |
1029 | diag.span_suggestion_verbose( | |
1030 | expr.span.shrink_to_hi(), | |
1031 | format!( | |
1032 | "use `{def_path}::cloned` to clone the value inside the `{def_path}`" | |
1033 | ), | |
1034 | ".cloned()", | |
1035 | Applicability::MachineApplicable, | |
1036 | ); | |
2b03887a | 1037 | return true; |
f2b60f7d FG |
1038 | } |
1039 | } | |
2b03887a | 1040 | false |
f2b60f7d FG |
1041 | }; |
1042 | ||
1043 | if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) | |
1044 | && adt_def.did() == result_did | |
1045 | // Check that the error types are equal | |
1046 | && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() | |
1047 | { | |
2b03887a | 1048 | return suggest_copied_or_cloned(); |
f2b60f7d FG |
1049 | } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) |
1050 | && adt_def.did() == option_did | |
1051 | { | |
2b03887a | 1052 | return suggest_copied_or_cloned(); |
f2b60f7d | 1053 | } |
2b03887a FG |
1054 | |
1055 | false | |
1056 | } | |
1057 | ||
1058 | pub(crate) fn suggest_into( | |
1059 | &self, | |
1060 | diag: &mut Diagnostic, | |
1061 | expr: &hir::Expr<'_>, | |
1062 | expr_ty: Ty<'tcx>, | |
1063 | expected_ty: Ty<'tcx>, | |
1064 | ) -> bool { | |
1065 | let expr = expr.peel_blocks(); | |
1066 | ||
1067 | // We have better suggestions for scalar interconversions... | |
1068 | if expr_ty.is_scalar() && expected_ty.is_scalar() { | |
1069 | return false; | |
1070 | } | |
1071 | ||
1072 | // Don't suggest turning a block into another type (e.g. `{}.into()`) | |
1073 | if matches!(expr.kind, hir::ExprKind::Block(..)) { | |
1074 | return false; | |
1075 | } | |
1076 | ||
1077 | // We'll later suggest `.as_ref` when noting the type error, | |
1078 | // so skip if we will suggest that instead. | |
1079 | if self.err_ctxt().should_suggest_as_ref(expected_ty, expr_ty).is_some() { | |
1080 | return false; | |
1081 | } | |
1082 | ||
1083 | if let Some(into_def_id) = self.tcx.get_diagnostic_item(sym::Into) | |
1084 | && self.predicate_must_hold_modulo_regions(&traits::Obligation::new( | |
1085 | self.misc(expr.span), | |
1086 | self.param_env, | |
1087 | ty::Binder::dummy(ty::TraitRef { | |
1088 | def_id: into_def_id, | |
1089 | substs: self.tcx.mk_substs_trait(expr_ty, &[expected_ty.into()]), | |
1090 | }) | |
1091 | .to_poly_trait_predicate() | |
1092 | .to_predicate(self.tcx), | |
1093 | )) | |
1094 | { | |
1095 | let sugg = if expr.precedence().order() >= PREC_POSTFIX { | |
1096 | vec![(expr.span.shrink_to_hi(), ".into()".to_owned())] | |
1097 | } else { | |
1098 | vec![(expr.span.shrink_to_lo(), "(".to_owned()), (expr.span.shrink_to_hi(), ").into()".to_owned())] | |
1099 | }; | |
1100 | diag.multipart_suggestion( | |
1101 | format!("call `Into::into` on this expression to convert `{expr_ty}` into `{expected_ty}`"), | |
1102 | sugg, | |
1103 | Applicability::MaybeIncorrect | |
1104 | ); | |
1105 | return true; | |
1106 | } | |
1107 | ||
1108 | false | |
f2b60f7d FG |
1109 | } |
1110 | ||
5e7ed085 FG |
1111 | /// Suggest wrapping the block in square brackets instead of curly braces |
1112 | /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. | |
1113 | pub(crate) fn suggest_block_to_brackets( | |
1114 | &self, | |
1115 | diag: &mut Diagnostic, | |
1116 | blk: &hir::Block<'_>, | |
1117 | blk_ty: Ty<'tcx>, | |
1118 | expected_ty: Ty<'tcx>, | |
1119 | ) { | |
1120 | if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() { | |
1121 | if self.can_coerce(blk_ty, *elem_ty) | |
1122 | && blk.stmts.is_empty() | |
1123 | && blk.rules == hir::BlockCheckMode::DefaultBlock | |
1124 | { | |
1125 | let source_map = self.tcx.sess.source_map(); | |
1126 | if let Ok(snippet) = source_map.span_to_snippet(blk.span) { | |
1127 | if snippet.starts_with('{') && snippet.ends_with('}') { | |
1128 | diag.multipart_suggestion_verbose( | |
1129 | "to create an array, use square brackets instead of curly braces", | |
1130 | vec![ | |
1131 | ( | |
1132 | blk.span | |
1133 | .shrink_to_lo() | |
1134 | .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), | |
1135 | "[".to_string(), | |
1136 | ), | |
1137 | ( | |
1138 | blk.span | |
1139 | .shrink_to_hi() | |
1140 | .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), | |
1141 | "]".to_string(), | |
1142 | ), | |
1143 | ], | |
1144 | Applicability::MachineApplicable, | |
1145 | ); | |
1146 | } | |
1147 | } | |
1148 | } | |
1149 | } | |
1150 | } | |
1151 | ||
cdc7bbd5 XL |
1152 | fn is_loop(&self, id: hir::HirId) -> bool { |
1153 | let node = self.tcx.hir().get(id); | |
1154 | matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) | |
1155 | } | |
1156 | ||
1157 | fn is_local_statement(&self, id: hir::HirId) -> bool { | |
1158 | let node = self.tcx.hir().get(id); | |
1159 | matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. })) | |
1160 | } | |
04454e1e FG |
1161 | |
1162 | /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`, | |
1163 | /// which is a side-effect of autoref. | |
1164 | pub(crate) fn note_type_is_not_clone( | |
1165 | &self, | |
1166 | diag: &mut Diagnostic, | |
1167 | expected_ty: Ty<'tcx>, | |
1168 | found_ty: Ty<'tcx>, | |
1169 | expr: &hir::Expr<'_>, | |
1170 | ) { | |
f2b60f7d | 1171 | let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; }; |
04454e1e FG |
1172 | let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; |
1173 | let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return }; | |
1174 | let results = self.typeck_results.borrow(); | |
1175 | // First, look for a `Clone::clone` call | |
1176 | if segment.ident.name == sym::clone | |
1177 | && results.type_dependent_def_id(expr.hir_id).map_or( | |
1178 | false, | |
1179 | |did| { | |
064997fb FG |
1180 | let assoc_item = self.tcx.associated_item(did); |
1181 | assoc_item.container == ty::AssocItemContainer::TraitContainer | |
1182 | && assoc_item.container_id(self.tcx) == clone_trait_did | |
04454e1e FG |
1183 | }, |
1184 | ) | |
1185 | // If that clone call hasn't already dereferenced the self type (i.e. don't give this | |
1186 | // diagnostic in cases where we have `(&&T).clone()` and we expect `T`). | |
1187 | && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) | |
1188 | // Check that we're in fact trying to clone into the expected type | |
1189 | && self.can_coerce(*pointee_ty, expected_ty) | |
1190 | // And the expected type doesn't implement `Clone` | |
1191 | && !self.predicate_must_hold_considering_regions(&traits::Obligation { | |
1192 | cause: traits::ObligationCause::dummy(), | |
1193 | param_env: self.param_env, | |
1194 | recursion_depth: 0, | |
1195 | predicate: ty::Binder::dummy(ty::TraitRef { | |
1196 | def_id: clone_trait_did, | |
1197 | substs: self.tcx.mk_substs([expected_ty.into()].iter()), | |
1198 | }) | |
1199 | .without_const() | |
1200 | .to_predicate(self.tcx), | |
1201 | }) | |
1202 | { | |
1203 | diag.span_note( | |
1204 | callee_expr.span, | |
1205 | &format!( | |
1206 | "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead" | |
1207 | ), | |
1208 | ); | |
1209 | } | |
1210 | } | |
064997fb FG |
1211 | |
1212 | /// A common error is to add an extra semicolon: | |
1213 | /// | |
1214 | /// ```compile_fail,E0308 | |
1215 | /// fn foo() -> usize { | |
1216 | /// 22; | |
1217 | /// } | |
1218 | /// ``` | |
1219 | /// | |
1220 | /// This routine checks if the final statement in a block is an | |
1221 | /// expression with an explicit semicolon whose type is compatible | |
1222 | /// with `expected_ty`. If so, it suggests removing the semicolon. | |
1223 | pub(crate) fn consider_removing_semicolon( | |
1224 | &self, | |
1225 | blk: &'tcx hir::Block<'tcx>, | |
1226 | expected_ty: Ty<'tcx>, | |
1227 | err: &mut Diagnostic, | |
1228 | ) -> bool { | |
2b03887a | 1229 | if let Some((span_semi, boxed)) = self.err_ctxt().could_remove_semicolon(blk, expected_ty) { |
064997fb FG |
1230 | if let StatementAsExpression::NeedsBoxing = boxed { |
1231 | err.span_suggestion_verbose( | |
1232 | span_semi, | |
1233 | "consider removing this semicolon and boxing the expression", | |
1234 | "", | |
1235 | Applicability::HasPlaceholders, | |
1236 | ); | |
1237 | } else { | |
1238 | err.span_suggestion_short( | |
1239 | span_semi, | |
2b03887a | 1240 | "remove this semicolon to return this value", |
064997fb FG |
1241 | "", |
1242 | Applicability::MachineApplicable, | |
1243 | ); | |
1244 | } | |
1245 | true | |
1246 | } else { | |
1247 | false | |
1248 | } | |
1249 | } | |
29967ef6 | 1250 | } |