From 872aa75867ea24e23dd4698e43c109fba260c561 Mon Sep 17 00:00:00 2001 From: Veera Date: Tue, 23 Jul 2024 13:46:38 -0400 Subject: [PATCH 1/2] Add Tests --- .../typo-in-repeat-expr-issue-80173.rs | 70 ++++++++++ .../typo-in-repeat-expr-issue-80173.stderr | 124 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs create mode 100644 tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs new file mode 100644 index 000000000000..ec0daa4e1ce9 --- /dev/null +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs @@ -0,0 +1,70 @@ +#[derive(Copy, Clone)] +struct Type; + +struct NewType; + +const fn get_size() -> usize { + 10 +} + +fn get_dyn_size() -> usize { + 10 +} + +fn main() { + let a = ["a", 10]; + //~^ ERROR mismatched types + //~| HELP replace comma with semicolon to create an array + + const size_b: usize = 20; + let b = [Type, size_b]; + //~^ ERROR mismatched types + //~| HELP replace comma with semicolon to create an array + + let size_c: usize = 13; + let c = [Type, size_c]; + //~^ ERROR mismatched types + + const size_d: bool = true; + let d = [Type, size_d]; + //~^ ERROR mismatched types + + let e = [String::new(), 10]; + //~^ ERROR mismatched types + //~| HELP try using a conversion method + + let f = ["f", get_size()]; + //~^ ERROR mismatched types + //~| HELP replace comma with semicolon to create an array + + let m = ["m", get_dyn_size()]; + //~^ ERROR mismatched types + + // is_vec, is_clone, is_usize_like + let g = vec![String::new(), 10]; + //~^ ERROR mismatched types + //~| HELP replace comma with semicolon to create a vector + + let dyn_size = 10; + let h = vec![Type, dyn_size]; + //~^ ERROR mismatched types + //~| HELP replace comma with semicolon to create a vector + + let i = vec![Type, get_dyn_size()]; + //~^ ERROR mismatched types + //~| HELP replace comma with semicolon to create a vector + + let k = vec!['c', 10]; + //~^ ERROR mismatched types + //~| HELP replace comma with semicolon to create a vector + + let j = vec![Type, 10_u8]; + //~^ ERROR mismatched types + + let l = vec![NewType, 10]; + //~^ ERROR mismatched types + + let byte_size: u8 = 10; + let h = vec![Type, byte_size]; + //~^ ERROR mismatched types +} diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr new file mode 100644 index 000000000000..d1bd9b590ad4 --- /dev/null +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr @@ -0,0 +1,124 @@ +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:15:19 + | +LL | let a = ["a", 10]; + | ^^ expected `&str`, found integer + | +help: replace comma with semicolon to create an array + | +LL | let a = ["a"; 10]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:20:20 + | +LL | let b = [Type, size_b]; + | ^^^^^^ expected `Type`, found `usize` + | +help: replace comma with semicolon to create an array + | +LL | let b = [Type; size_b]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:25:20 + | +LL | let c = [Type, size_c]; + | ^^^^^^ expected `Type`, found `usize` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:29:20 + | +LL | let d = [Type, size_d]; + | ^^^^^^ expected `Type`, found `bool` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:32:29 + | +LL | let e = [String::new(), 10]; + | ^^- help: try using a conversion method: `.to_string()` + | | + | expected `String`, found integer + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:36:19 + | +LL | let f = ["f", get_size()]; + | ^^^^^^^^^^ expected `&str`, found `usize` + | +help: replace comma with semicolon to create an array + | +LL | let f = ["f"; get_size()]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:40:19 + | +LL | let m = ["m", get_dyn_size()]; + | ^^^^^^^^^^^^^^ expected `&str`, found `usize` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:44:33 + | +LL | let g = vec![String::new(), 10]; + | ^^ expected `String`, found integer + | +help: replace comma with semicolon to create a vector + | +LL | let g = vec![String::new(); 10]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:49:24 + | +LL | let h = vec![Type, dyn_size]; + | ^^^^^^^^ expected `Type`, found integer + | +help: replace comma with semicolon to create a vector + | +LL | let h = vec![Type; dyn_size]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:53:24 + | +LL | let i = vec![Type, get_dyn_size()]; + | ^^^^^^^^^^^^^^ expected `Type`, found `usize` + | +help: replace comma with semicolon to create a vector + | +LL | let i = vec![Type; get_dyn_size()]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:57:23 + | +LL | let k = vec!['c', 10]; + | ^^ expected `char`, found `u8` + | +help: replace comma with semicolon to create a vector + | +LL | let k = vec!['c'; 10]; + | ~ + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:61:24 + | +LL | let j = vec![Type, 10_u8]; + | ^^^^^ expected `Type`, found `u8` + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:64:27 + | +LL | let l = vec![NewType, 10]; + | ^^ expected `NewType`, found integer + +error[E0308]: mismatched types + --> $DIR/typo-in-repeat-expr-issue-80173.rs:68:24 + | +LL | let h = vec![Type, byte_size]; + | ^^^^^^^^^ expected `Type`, found `u8` + +error: aborting due to 14 previous errors + +For more information about this error, try `rustc --explain E0308`. From 98cc3457af5655f289dd7a9de0ff8433697ea105 Mon Sep 17 00:00:00 2001 From: Veera Date: Mon, 29 Jul 2024 13:24:26 -0400 Subject: [PATCH 2/2] Suggest Semicolon in Incorrect Repeat Expressions --- compiler/rustc_hir/src/hir.rs | 14 ++- compiler/rustc_hir_typeck/messages.ftl | 2 + compiler/rustc_hir_typeck/src/demand.rs | 2 +- compiler/rustc_hir_typeck/src/errors.rs | 13 +++ .../src/fn_ctxt/suggestions.rs | 90 +++++++++++++++++-- compiler/rustc_middle/src/ty/sty.rs | 14 ++- compiler/rustc_trait_selection/src/infer.rs | 6 ++ .../typo-in-repeat-expr-issue-80173.rs | 14 +-- .../typo-in-repeat-expr-issue-80173.stderr | 14 +-- 9 files changed, 144 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 398b694ae6bd..dd8fb1f7eb6c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -7,7 +7,7 @@ use rustc_ast::token::CommentKind; use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::{ self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, - IntTy, Label, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, + IntTy, Label, LitIntType, LitKind, MetaItemInner, MetaItemLit, TraitObjectSyntax, UintTy, }; pub use rustc_ast::{ BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, @@ -2064,6 +2064,18 @@ impl Expr<'_> { } } + /// Check if expression is an integer literal that can be used + /// where `usize` is expected. + pub fn is_size_lit(&self) -> bool { + matches!( + self.kind, + ExprKind::Lit(Lit { + node: LitKind::Int(_, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::Usize)), + .. + }) + ) + } + /// If `Self.kind` is `ExprKind::DropTemps(expr)`, drill down until we get a non-`DropTemps` /// `Expr`. This is used in suggestions to ignore this `ExprKind` as it is semantically /// silent, only signaling the ownership system. By doing this, suggestions that check the diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index a93da52b2703..0f424a39840a 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -165,6 +165,8 @@ hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this ret hir_typeck_remove_semi_for_coerce_semi = the `match` is a statement because of this semicolon, consider removing it hir_typeck_remove_semi_for_coerce_suggestion = remove this semicolon +hir_typeck_replace_comma_with_semicolon = replace the comma with a semicolon to create {$descr} + hir_typeck_return_stmt_outside_of_fn_body = {$statement_kind} statement outside of function body .encl_body_label = the {$statement_kind} is part of this body... diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index e51323fc5c85..56f7a2c1150a 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -30,7 +30,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if expr_ty == expected { return; } - self.annotate_alternative_method_deref(err, expr, error); self.explain_self_literal(err, expr, expected, expr_ty); @@ -39,6 +38,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { || self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty) || self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_associated_const(err, expr, expected) + || self.suggest_semicolon_in_repeat_expr(err, expr, expr_ty) || self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr) || self.suggest_option_to_bool(err, expr, expr_ty, expected) || self.suggest_compatible_variants(err, expr, expected, expr_ty) diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index ff09583cc65a..4eed2bc12388 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -846,3 +846,16 @@ pub(crate) struct PassFnItemToVariadicFunction { pub sugg_span: Span, pub replace: String, } + +#[derive(Subdiagnostic)] +#[suggestion( + hir_typeck_replace_comma_with_semicolon, + applicability = "machine-applicable", + style = "verbose", + code = "; " +)] +pub(crate) struct ReplaceCommaWithSemicolon { + #[primary_span] + pub comma_span: Span, + pub descr: &'static str, +} diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 964ef5b2106b..c17218b611ec 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1319,14 +1319,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let span = expr.span.shrink_to_hi(); let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) { errors::OptionResultRefMismatch::Copied { span, def_path } - } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() - && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( - self, - self.param_env, - ty, - clone_did, - ) - { + } else if self.type_is_clone_modulo_regions(self.param_env, ty) { errors::OptionResultRefMismatch::Cloned { span, def_path } } else { return false; @@ -2181,6 +2174,87 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + /// Suggest replacing comma with semicolon in incorrect repeat expressions + /// like `["_", 10]` or `vec![String::new(), 10]`. + pub(crate) fn suggest_semicolon_in_repeat_expr( + &self, + err: &mut Diag<'_>, + expr: &hir::Expr<'_>, + expr_ty: Ty<'tcx>, + ) -> bool { + // Check if `expr` is contained in array of two elements + if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id) + && let hir::ExprKind::Array(elements) = array_expr.kind + && let [first, second] = &elements[..] + && second.hir_id == expr.hir_id + { + // Span between the two elements of the array + let comma_span = first.span.between(second.span); + + // Check if `expr` is a constant value of type `usize`. + // This can only detect const variable declarations and + // calls to const functions. + + // Checking this here instead of rustc_hir::hir because + // this check needs access to `self.tcx` but rustc_hir + // has no access to `TyCtxt`. + let expr_is_const_usize = expr_ty.is_usize() + && match expr.kind { + ExprKind::Path(QPath::Resolved( + None, + Path { res: Res::Def(DefKind::Const, _), .. }, + )) => true, + ExprKind::Call( + Expr { + kind: + ExprKind::Path(QPath::Resolved( + None, + Path { res: Res::Def(DefKind::Fn, fn_def_id), .. }, + )), + .. + }, + _, + ) => self.tcx.is_const_fn(*fn_def_id), + _ => false, + }; + + // Type of the first element is guaranteed to be checked + // when execution reaches here because `mismatched types` + // error occurs only when type of second element of array + // is not the same as type of first element. + let first_ty = self.typeck_results.borrow().expr_ty(first); + + // `array_expr` is from a macro `vec!["a", 10]` if + // 1. array expression's span is imported from a macro + // 2. first element of array implements `Clone` trait + // 3. second element is an integer literal or is an expression of `usize` like type + if self.tcx.sess.source_map().is_imported(array_expr.span) + && self.type_is_clone_modulo_regions(self.param_env, first_ty) + && (expr.is_size_lit() || expr_ty.is_usize_like()) + { + err.subdiagnostic(errors::ReplaceCommaWithSemicolon { + comma_span, + descr: "a vector", + }); + return true; + } + + // `array_expr` is from an array `["a", 10]` if + // 1. first element of array implements `Copy` trait + // 2. second element is an integer literal or is a const value of type `usize` + if self.type_is_copy_modulo_regions(self.param_env, first_ty) + && (expr.is_size_lit() || expr_is_const_usize) + { + err.subdiagnostic(errors::ReplaceCommaWithSemicolon { + comma_span, + descr: "an array", + }); + return true; + } + } + false + } + /// If the expected type is an enum (Issue #55250) with any variants whose /// sole field is of the found type, suggest such variants. (Issue #42764) pub(crate) fn suggest_compatible_variants( diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index 045c483d6a58..c45238b1eb0e 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -27,7 +27,7 @@ use crate::infer::canonical::Canonical; use crate::ty::InferTy::*; use crate::ty::{ self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, - Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, + Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, }; // Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here @@ -1008,6 +1008,18 @@ impl<'tcx> Ty<'tcx> { } } + /// Check if type is an `usize`. + #[inline] + pub fn is_usize(self) -> bool { + matches!(self.kind(), Uint(UintTy::Usize)) + } + + /// Check if type is an `usize` or an integral type variable. + #[inline] + pub fn is_usize_like(self) -> bool { + matches!(self.kind(), Uint(UintTy::Usize) | Infer(IntVar(_))) + } + #[inline] pub fn is_never(self) -> bool { matches!(self.kind(), Never) diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs index ee708564a804..f373706b2960 100644 --- a/compiler/rustc_trait_selection/src/infer.rs +++ b/compiler/rustc_trait_selection/src/infer.rs @@ -47,6 +47,12 @@ impl<'tcx> InferCtxt<'tcx> { traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) } + fn type_is_clone_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { + let ty = self.resolve_vars_if_possible(ty); + let clone_def_id = self.tcx.require_lang_item(LangItem::Clone, None); + traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id) + } + fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool { let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item) diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs index ec0daa4e1ce9..c76e7a1d7166 100644 --- a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs @@ -14,12 +14,12 @@ fn get_dyn_size() -> usize { fn main() { let a = ["a", 10]; //~^ ERROR mismatched types - //~| HELP replace comma with semicolon to create an array + //~| HELP replace the comma with a semicolon to create an array const size_b: usize = 20; let b = [Type, size_b]; //~^ ERROR mismatched types - //~| HELP replace comma with semicolon to create an array + //~| HELP replace the comma with a semicolon to create an array let size_c: usize = 13; let c = [Type, size_c]; @@ -35,7 +35,7 @@ fn main() { let f = ["f", get_size()]; //~^ ERROR mismatched types - //~| HELP replace comma with semicolon to create an array + //~| HELP replace the comma with a semicolon to create an array let m = ["m", get_dyn_size()]; //~^ ERROR mismatched types @@ -43,20 +43,20 @@ fn main() { // is_vec, is_clone, is_usize_like let g = vec![String::new(), 10]; //~^ ERROR mismatched types - //~| HELP replace comma with semicolon to create a vector + //~| HELP replace the comma with a semicolon to create a vector let dyn_size = 10; let h = vec![Type, dyn_size]; //~^ ERROR mismatched types - //~| HELP replace comma with semicolon to create a vector + //~| HELP replace the comma with a semicolon to create a vector let i = vec![Type, get_dyn_size()]; //~^ ERROR mismatched types - //~| HELP replace comma with semicolon to create a vector + //~| HELP replace the comma with a semicolon to create a vector let k = vec!['c', 10]; //~^ ERROR mismatched types - //~| HELP replace comma with semicolon to create a vector + //~| HELP replace the comma with a semicolon to create a vector let j = vec![Type, 10_u8]; //~^ ERROR mismatched types diff --git a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr index d1bd9b590ad4..95eddbde9e67 100644 --- a/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr +++ b/tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr @@ -4,7 +4,7 @@ error[E0308]: mismatched types LL | let a = ["a", 10]; | ^^ expected `&str`, found integer | -help: replace comma with semicolon to create an array +help: replace the comma with a semicolon to create an array | LL | let a = ["a"; 10]; | ~ @@ -15,7 +15,7 @@ error[E0308]: mismatched types LL | let b = [Type, size_b]; | ^^^^^^ expected `Type`, found `usize` | -help: replace comma with semicolon to create an array +help: replace the comma with a semicolon to create an array | LL | let b = [Type; size_b]; | ~ @@ -46,7 +46,7 @@ error[E0308]: mismatched types LL | let f = ["f", get_size()]; | ^^^^^^^^^^ expected `&str`, found `usize` | -help: replace comma with semicolon to create an array +help: replace the comma with a semicolon to create an array | LL | let f = ["f"; get_size()]; | ~ @@ -63,7 +63,7 @@ error[E0308]: mismatched types LL | let g = vec![String::new(), 10]; | ^^ expected `String`, found integer | -help: replace comma with semicolon to create a vector +help: replace the comma with a semicolon to create a vector | LL | let g = vec![String::new(); 10]; | ~ @@ -74,7 +74,7 @@ error[E0308]: mismatched types LL | let h = vec![Type, dyn_size]; | ^^^^^^^^ expected `Type`, found integer | -help: replace comma with semicolon to create a vector +help: replace the comma with a semicolon to create a vector | LL | let h = vec![Type; dyn_size]; | ~ @@ -85,7 +85,7 @@ error[E0308]: mismatched types LL | let i = vec![Type, get_dyn_size()]; | ^^^^^^^^^^^^^^ expected `Type`, found `usize` | -help: replace comma with semicolon to create a vector +help: replace the comma with a semicolon to create a vector | LL | let i = vec![Type; get_dyn_size()]; | ~ @@ -96,7 +96,7 @@ error[E0308]: mismatched types LL | let k = vec!['c', 10]; | ^^ expected `char`, found `u8` | -help: replace comma with semicolon to create a vector +help: replace the comma with a semicolon to create a vector | LL | let k = vec!['c'; 10]; | ~