From 1599c5a82175485bdebbb231fba1cedcac742868 Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Mon, 11 Jul 2022 02:19:45 +0900 Subject: [PATCH 1/2] remove a string matching about methods --- .../src/check/fn_ctxt/suggestions.rs | 93 ++++++++++--------- src/test/ui/suggestions/issue-52820.fixed | 15 +++ src/test/ui/suggestions/issue-52820.rs | 3 + src/test/ui/suggestions/issue-52820.stderr | 10 +- src/test/ui/suggestions/issue-53692.fixed | 20 ++++ src/test/ui/suggestions/issue-53692.rs | 27 +++--- src/test/ui/suggestions/issue-53692.stderr | 28 +++--- 7 files changed, 122 insertions(+), 74 deletions(-) create mode 100644 src/test/ui/suggestions/issue-52820.fixed create mode 100644 src/test/ui/suggestions/issue-53692.fixed diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 863a981134f24..721fc8c568113 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -14,7 +14,7 @@ use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; -use rustc_span::symbol::sym; +use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; @@ -187,55 +187,62 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(self.tcx.def_span(def_id), &format!("{} defined here", found)); } } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { - let is_struct_pat_shorthand_field = - self.maybe_get_struct_pattern_shorthand_field(expr).is_some(); + let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { - if let Ok(expr_text) = self.sess().source_map().span_to_snippet(expr.span) { - let mut suggestions = iter::zip(iter::repeat(&expr_text), &methods) - .filter_map(|(receiver, method)| { - let method_call = format!(".{}()", method.name); - if receiver.ends_with(&method_call) { - None // do not suggest code that is already there (#53348) + let mut suggestions = iter::zip(iter::repeat(&expr), &methods) + .filter_map(|(receiver_expr, method)| { + let method_call = format!(".{}()", method.name); + fn method_ident(expr: &hir::Expr<'_>) -> Option { + match expr.kind { + ExprKind::MethodCall(receiver_method, ..) => Some(receiver_method.ident), + ExprKind::Unary(_, expr) | ExprKind::AddrOf(.., expr) => method_ident(expr), + _ => None + } + } + let method_ident = method_ident(&receiver_expr); + if let Some(method_ident) = method_ident + && method_ident.name == method.name + { + None // do not suggest code that is already there (#53348) + } else { + let method_call_list = [".to_vec()", ".to_string()"]; + let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = receiver_expr.kind + && receiver_method.ident.name == sym::clone + && method_call_list.contains(&method_call.as_str()) + { + vec![( + receiver_method.ident.span, + method.name.to_string() + )] } else { - let method_call_list = [".to_vec()", ".to_string()"]; - let mut sugg = if receiver.ends_with(".clone()") - && method_call_list.contains(&method_call.as_str()) + if expr.precedence().order() + < ExprPrecedence::MethodCall.order() { - let max_len = receiver.rfind('.').unwrap(); - vec![( - expr.span, - format!("{}{}", &receiver[..max_len], method_call), - )] + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!("){}", method_call)), + ] } else { - if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!("){}", method_call)), - ] - } else { - vec![(expr.span.shrink_to_hi(), method_call)] - } - }; - if is_struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", receiver)), - ); + vec![(expr.span.shrink_to_hi(), method_call)] } - Some(sugg) + }; + if let Some(name) = struct_pat_shorthand_field { + sugg.insert( + 0, + (expr.span.shrink_to_lo(), format!("{}: ", name)), + ); } - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - } + Some(sugg) + } + }) + .peekable(); + if suggestions.peek().is_some() { + err.multipart_suggestions( + "try using a conversion method", + suggestions, + Applicability::MaybeIncorrect, + ); } } else if let ty::Adt(found_adt, found_substs) = found.kind() && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) diff --git a/src/test/ui/suggestions/issue-52820.fixed b/src/test/ui/suggestions/issue-52820.fixed new file mode 100644 index 0000000000000..514690de4d045 --- /dev/null +++ b/src/test/ui/suggestions/issue-52820.fixed @@ -0,0 +1,15 @@ +// run-rustfix +#![allow(dead_code)] + +struct Bravery { + guts: String, + brains: String, +} + +fn main() { + let guts = "mettle"; + let _ = Bravery { + guts: guts.to_string(), //~ ERROR mismatched types + brains: guts.to_string(), //~ ERROR mismatched types + }; +} diff --git a/src/test/ui/suggestions/issue-52820.rs b/src/test/ui/suggestions/issue-52820.rs index 075b07f565203..17cd9224c57ec 100644 --- a/src/test/ui/suggestions/issue-52820.rs +++ b/src/test/ui/suggestions/issue-52820.rs @@ -1,3 +1,6 @@ +// run-rustfix +#![allow(dead_code)] + struct Bravery { guts: String, brains: String, diff --git a/src/test/ui/suggestions/issue-52820.stderr b/src/test/ui/suggestions/issue-52820.stderr index 7b46584450192..09269ed4eee99 100644 --- a/src/test/ui/suggestions/issue-52820.stderr +++ b/src/test/ui/suggestions/issue-52820.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-52820.rs:9:9 + --> $DIR/issue-52820.rs:12:9 | LL | guts, | ^^^^ expected struct `String`, found `&str` @@ -10,13 +10,13 @@ LL | guts: guts.to_string(), | +++++ ++++++++++++ error[E0308]: mismatched types - --> $DIR/issue-52820.rs:10:17 + --> $DIR/issue-52820.rs:13:17 | LL | brains: guts.clone(), - | ^^^^^^^^^^^^ - | | + | ^^^^^-----^^ + | | | + | | help: try using a conversion method: `to_string` | expected struct `String`, found `&str` - | help: try using a conversion method: `guts.to_string()` error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/issue-53692.fixed b/src/test/ui/suggestions/issue-53692.fixed new file mode 100644 index 0000000000000..35a677b476186 --- /dev/null +++ b/src/test/ui/suggestions/issue-53692.fixed @@ -0,0 +1,20 @@ +// run-rustfix +#![allow(unused_variables)] + +fn main() { + let items = vec![1, 2, 3]; + let ref_items: &[i32] = &items; + let items_clone: Vec = ref_items.to_vec(); + //~^ ERROR mismatched types + + // in that case no suggestion will be triggered + let items_clone_2: Vec = items.clone(); + + let s = "hi"; + let string: String = s.to_string(); + //~^ ERROR mismatched types + + // in that case no suggestion will be triggered + let s2 = "hi"; + let string_2: String = s2.to_string(); +} diff --git a/src/test/ui/suggestions/issue-53692.rs b/src/test/ui/suggestions/issue-53692.rs index 30f344e428248..6f6707be5f651 100644 --- a/src/test/ui/suggestions/issue-53692.rs +++ b/src/test/ui/suggestions/issue-53692.rs @@ -1,17 +1,20 @@ +// run-rustfix +#![allow(unused_variables)] + fn main() { - let items = vec![1, 2, 3]; - let ref_items: &[i32] = &items; - let items_clone: Vec = ref_items.clone(); -//~^ ERROR mismatched types + let items = vec![1, 2, 3]; + let ref_items: &[i32] = &items; + let items_clone: Vec = ref_items.clone(); + //~^ ERROR mismatched types - // in that case no suggestion will be triggered - let items_clone_2:Vec = items.clone(); + // in that case no suggestion will be triggered + let items_clone_2: Vec = items.clone(); - let s = "hi"; - let string: String = s.clone(); -//~^ ERROR mismatched types + let s = "hi"; + let string: String = s.clone(); + //~^ ERROR mismatched types - // in that case no suggestion will be triggered - let s2 = "hi"; - let string_2: String = s2.to_string(); + // in that case no suggestion will be triggered + let s2 = "hi"; + let string_2: String = s2.to_string(); } diff --git a/src/test/ui/suggestions/issue-53692.stderr b/src/test/ui/suggestions/issue-53692.stderr index 09c78da54bcd0..3a1b624f402f0 100644 --- a/src/test/ui/suggestions/issue-53692.stderr +++ b/src/test/ui/suggestions/issue-53692.stderr @@ -1,25 +1,25 @@ error[E0308]: mismatched types - --> $DIR/issue-53692.rs:4:37 + --> $DIR/issue-53692.rs:7:33 | -LL | let items_clone: Vec = ref_items.clone(); - | -------- ^^^^^^^^^^^^^^^^^ - | | | - | | expected struct `Vec`, found `&[i32]` - | | help: try using a conversion method: `ref_items.to_vec()` - | expected due to this +LL | let items_clone: Vec = ref_items.clone(); + | -------- ^^^^^^^^^^-----^^ + | | | | + | | | help: try using a conversion method: `to_vec` + | | expected struct `Vec`, found `&[i32]` + | expected due to this | = note: expected struct `Vec` found reference `&[i32]` error[E0308]: mismatched types - --> $DIR/issue-53692.rs:11:30 + --> $DIR/issue-53692.rs:14:26 | -LL | let string: String = s.clone(); - | ------ ^^^^^^^^^ - | | | - | | expected struct `String`, found `&str` - | | help: try using a conversion method: `s.to_string()` - | expected due to this +LL | let string: String = s.clone(); + | ------ ^^-----^^ + | | | | + | | | help: try using a conversion method: `to_string` + | | expected struct `String`, found `&str` + | expected due to this error: aborting due to 2 previous errors From 45b88aff100463534c036e3880c5f53ac75b83ae Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Fri, 15 Jul 2022 14:29:15 +0900 Subject: [PATCH 2/2] simplify `suggest_deref_ref_or_into` --- compiler/rustc_hir/src/hir.rs | 8 ++ compiler/rustc_span/src/symbol.rs | 2 + .../src/check/fn_ctxt/suggestions.rs | 82 +++++++++---------- 3 files changed, 47 insertions(+), 45 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 04f585df34cc2..b1f2b52bb2eb6 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1811,6 +1811,14 @@ impl Expr<'_> { _ => false, } } + + pub fn method_ident(&self) -> Option { + match self.kind { + ExprKind::MethodCall(receiver_method, ..) => Some(receiver_method.ident), + ExprKind::Unary(_, expr) | ExprKind::AddrOf(.., expr) => expr.method_ident(), + _ => None, + } + } } /// Checks if the specified expression is a built-in range literal. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9b6967621f1dc..37d675d6b4ddf 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1434,6 +1434,8 @@ symbols! { thumb2, thumb_mode: "thumb-mode", tmm_reg, + to_string, + to_vec, todo_macro, tool_attributes, tool_lints, diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs index 721fc8c568113..ac3bd29ea0027 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs @@ -14,12 +14,10 @@ use rustc_infer::infer::{self, TyCtxtInferExt}; use rustc_infer::traits; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; -use rustc_span::symbol::{sym, Ident}; +use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use std::iter; - impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { err.span_suggestion_short( @@ -187,54 +185,48 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err.span_label(self.tcx.def_span(def_id), &format!("{} defined here", found)); } } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { - let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); if !methods.is_empty() { - let mut suggestions = iter::zip(iter::repeat(&expr), &methods) - .filter_map(|(receiver_expr, method)| { - let method_call = format!(".{}()", method.name); - fn method_ident(expr: &hir::Expr<'_>) -> Option { - match expr.kind { - ExprKind::MethodCall(receiver_method, ..) => Some(receiver_method.ident), - ExprKind::Unary(_, expr) | ExprKind::AddrOf(.., expr) => method_ident(expr), - _ => None - } + let mut suggestions = methods.iter() + .filter_map(|conversion_method| { + let receiver_method_ident = expr.method_ident(); + if let Some(method_ident) = receiver_method_ident + && method_ident.name == conversion_method.name + { + return None // do not suggest code that is already there (#53348) } - let method_ident = method_ident(&receiver_expr); - if let Some(method_ident) = method_ident - && method_ident.name == method.name + + let method_call_list = [sym::to_vec, sym::to_string]; + let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind + && receiver_method.ident.name == sym::clone + && method_call_list.contains(&conversion_method.name) + // If receiver is `.clone()` and found type has one of those methods, + // we guess that the user wants to convert from a slice type (`&[]` or `&str`) + // to an owned type (`Vec` or `String`). These conversions clone internally, + // so we remove the user's `clone` call. { - None // do not suggest code that is already there (#53348) + vec![( + receiver_method.ident.span, + conversion_method.name.to_string() + )] + } else if expr.precedence().order() + < ExprPrecedence::MethodCall.order() + { + vec![ + (expr.span.shrink_to_lo(), "(".to_string()), + (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), + ] } else { - let method_call_list = [".to_vec()", ".to_string()"]; - let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = receiver_expr.kind - && receiver_method.ident.name == sym::clone - && method_call_list.contains(&method_call.as_str()) - { - vec![( - receiver_method.ident.span, - method.name.to_string() - )] - } else { - if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!("){}", method_call)), - ] - } else { - vec![(expr.span.shrink_to_hi(), method_call)] - } - }; - if let Some(name) = struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", name)), - ); - } - Some(sugg) + vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] + }; + let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); + if let Some(name) = struct_pat_shorthand_field { + sugg.insert( + 0, + (expr.span.shrink_to_lo(), format!("{}: ", name)), + ); } + Some(sugg) }) .peekable(); if suggestions.peek().is_some() {