about summary refs log tree commit diff
diff options
context:
space:
mode:
authorVeera <sveera.2001@gmail.com>2024-07-29 13:24:26 -0400
committerVeera <sveera.2001@gmail.com>2024-12-21 02:30:50 +0000
commit98cc3457af5655f289dd7a9de0ff8433697ea105 (patch)
tree235638a331650709f9090b4cb4721429422679bc
parent872aa75867ea24e23dd4698e43c109fba260c561 (diff)
downloadrust-98cc3457af5655f289dd7a9de0ff8433697ea105.tar.gz
rust-98cc3457af5655f289dd7a9de0ff8433697ea105.zip
Suggest Semicolon in Incorrect Repeat Expressions
-rw-r--r--compiler/rustc_hir/src/hir.rs14
-rw-r--r--compiler/rustc_hir_typeck/messages.ftl2
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/errors.rs13
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs90
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs14
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs6
-rw-r--r--tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs14
-rw-r--r--tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr14
9 files changed, 144 insertions, 25 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 398b694ae6b..dd8fb1f7eb6 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 a93da52b270..0f424a39840 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 e51323fc5c8..56f7a2c1150 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 ff09583cc65..4eed2bc1238 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 964ef5b2106..c17218b611e 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 045c483d6a5..c45238b1eb0 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 ee708564a80..f373706b296 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 ec0daa4e1ce..c76e7a1d716 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 d1bd9b590ad..95eddbde9e6 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];
    |                     ~