Skip to content

Commit 4c43ae6

Browse files
Suggest Semicolon in Incorrect Repeat Expressions
1 parent 0546ee4 commit 4c43ae6

File tree

9 files changed

+144
-25
lines changed

9 files changed

+144
-25
lines changed

compiler/rustc_hir/src/hir.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::fmt;
33
use rustc_ast::util::parser::ExprPrecedence;
44
use rustc_ast::{
55
self as ast, Attribute, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label,
6-
LitKind, TraitObjectSyntax, UintTy,
6+
LitIntType, LitKind, TraitObjectSyntax, UintTy,
77
};
88
pub use rustc_ast::{
99
BinOp, BinOpKind, BindingMode, BorrowKind, ByRef, CaptureBy, ImplPolarity, IsAuto, Movability,
@@ -1828,6 +1828,18 @@ impl Expr<'_> {
18281828
}
18291829
}
18301830

1831+
/// Check if expression is an integer literal that can be used
1832+
/// where `usize` is expected.
1833+
pub fn is_size_lit(&self) -> bool {
1834+
matches!(
1835+
self.kind,
1836+
ExprKind::Lit(Lit {
1837+
node: LitKind::Int(_, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::Usize)),
1838+
..
1839+
})
1840+
)
1841+
}
1842+
18311843
/// If `Self.kind` is `ExprKind::DropTemps(expr)`, drill down until we get a non-`DropTemps`
18321844
/// `Expr`. This is used in suggestions to ignore this `ExprKind` as it is semantically
18331845
/// silent, only signaling the ownership system. By doing this, suggestions that check the

compiler/rustc_hir_typeck/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this ret
155155
hir_typeck_remove_semi_for_coerce_semi = the `match` is a statement because of this semicolon, consider removing it
156156
hir_typeck_remove_semi_for_coerce_suggestion = remove this semicolon
157157
158+
hir_typeck_replace_comma_with_semicolon = replace the comma with a semicolon to create {$descr}
159+
158160
hir_typeck_return_stmt_outside_of_fn_body =
159161
{$statement_kind} statement outside of function body
160162
.encl_body_label = the {$statement_kind} is part of this body...

compiler/rustc_hir_typeck/src/demand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3131
if expr_ty == expected {
3232
return;
3333
}
34-
3534
self.annotate_alternative_method_deref(err, expr, error);
3635
self.explain_self_literal(err, expr, expected, expr_ty);
3736

@@ -40,6 +39,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
4039
|| self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty)
4140
|| self.suggest_remove_last_method_call(err, expr, expected)
4241
|| self.suggest_associated_const(err, expr, expected)
42+
|| self.suggest_semicolon_in_repeat_expr(err, expr, expr_ty)
4343
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
4444
|| self.suggest_option_to_bool(err, expr, expr_ty, expected)
4545
|| self.suggest_compatible_variants(err, expr, expected, expr_ty)

compiler/rustc_hir_typeck/src/errors.rs

+13
Original file line numberDiff line numberDiff line change
@@ -724,3 +724,16 @@ pub(crate) struct PassToVariadicFunction<'a, 'tcx> {
724724
#[note(hir_typeck_teach_help)]
725725
pub(crate) teach: bool,
726726
}
727+
728+
#[derive(Subdiagnostic)]
729+
#[suggestion(
730+
hir_typeck_replace_comma_with_semicolon,
731+
applicability = "machine-applicable",
732+
style = "verbose",
733+
code = "; "
734+
)]
735+
pub(crate) struct ReplaceCommaWithSemicolon {
736+
#[primary_span]
737+
pub comma_span: Span,
738+
pub descr: &'static str,
739+
}

compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs

+82-8
Original file line numberDiff line numberDiff line change
@@ -1321,14 +1321,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13211321
let span = expr.span.shrink_to_hi();
13221322
let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
13231323
errors::OptionResultRefMismatch::Copied { span, def_path }
1324-
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
1325-
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
1326-
self,
1327-
self.param_env,
1328-
ty,
1329-
clone_did,
1330-
)
1331-
{
1324+
} else if self.type_is_clone_modulo_regions(self.param_env, ty) {
13321325
errors::OptionResultRefMismatch::Cloned { span, def_path }
13331326
} else {
13341327
return false;
@@ -2183,6 +2176,87 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21832176
}
21842177
}
21852178

2179+
/// Suggest replacing comma with semicolon in incorrect repeat expressions
2180+
/// like `["_", 10]` or `vec![String::new(), 10]`.
2181+
pub(crate) fn suggest_semicolon_in_repeat_expr(
2182+
&self,
2183+
err: &mut Diag<'_>,
2184+
expr: &hir::Expr<'_>,
2185+
expr_ty: Ty<'tcx>,
2186+
) -> bool {
2187+
// Check if `expr` is contained in array of two elements
2188+
if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2189+
&& let hir::ExprKind::Array(elements) = array_expr.kind
2190+
&& let [first, second] = &elements[..]
2191+
&& second.hir_id == expr.hir_id
2192+
{
2193+
// Span between the two elements of the array
2194+
let comma_span = first.span.between(second.span);
2195+
2196+
// Check if `expr` is a constant value of type `usize`.
2197+
// This can only detect const variable declarations and
2198+
// calls to const functions.
2199+
2200+
// Checking this here instead of rustc_hir::hir because
2201+
// this check needs access to `self.tcx` but rustc_hir
2202+
// has no access to `TyCtxt`.
2203+
let expr_is_const_usize = expr_ty.is_usize()
2204+
&& match expr.kind {
2205+
ExprKind::Path(QPath::Resolved(
2206+
None,
2207+
Path { res: Res::Def(DefKind::Const, _), .. },
2208+
)) => true,
2209+
ExprKind::Call(
2210+
Expr {
2211+
kind:
2212+
ExprKind::Path(QPath::Resolved(
2213+
None,
2214+
Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2215+
)),
2216+
..
2217+
},
2218+
_,
2219+
) => self.tcx.is_const_fn(*fn_def_id),
2220+
_ => false,
2221+
};
2222+
2223+
// Type of the first element is guaranteed to be checked
2224+
// when execution reaches here because `mismatched types`
2225+
// error occurs only when type of second element of array
2226+
// is not the same as type of first element.
2227+
let first_ty = self.typeck_results.borrow().expr_ty(first);
2228+
2229+
// `array_expr` is from a macro `vec!["a", 10]` if
2230+
// 1. array expression's span is imported from a macro
2231+
// 2. first element of array implements `Clone` trait
2232+
// 3. second element is an integer literal or is an expression of `usize` like type
2233+
if self.tcx.sess.source_map().is_imported(array_expr.span)
2234+
&& self.type_is_clone_modulo_regions(self.param_env, first_ty)
2235+
&& (expr.is_size_lit() || expr_ty.is_usize_like())
2236+
{
2237+
err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2238+
comma_span,
2239+
descr: "a vector",
2240+
});
2241+
return true;
2242+
}
2243+
2244+
// `array_expr` is from an array `["a", 10]` if
2245+
// 1. first element of array implements `Copy` trait
2246+
// 2. second element is an integer literal or is a const value of type `usize`
2247+
if self.type_is_copy_modulo_regions(self.param_env, first_ty)
2248+
&& (expr.is_size_lit() || expr_is_const_usize)
2249+
{
2250+
err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2251+
comma_span,
2252+
descr: "an array",
2253+
});
2254+
return true;
2255+
}
2256+
}
2257+
false
2258+
}
2259+
21862260
/// If the expected type is an enum (Issue #55250) with any variants whose
21872261
/// sole field is of the found type, suggest such variants. (Issue #42764)
21882262
pub(crate) fn suggest_compatible_variants(

compiler/rustc_middle/src/ty/sty.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::infer::canonical::Canonical;
2929
use crate::ty::InferTy::*;
3030
use crate::ty::{
3131
self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv,
32-
Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor,
32+
Region, Ty, TyCtxt, TypeFlags, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
3333
};
3434

3535
// Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
@@ -1005,6 +1005,18 @@ impl<'tcx> Ty<'tcx> {
10051005
}
10061006
}
10071007

1008+
/// Check if type is an `usize`.
1009+
#[inline]
1010+
pub fn is_usize(self) -> bool {
1011+
matches!(self.kind(), Uint(UintTy::Usize))
1012+
}
1013+
1014+
/// Check if type is an `usize` or an integral type variable.
1015+
#[inline]
1016+
pub fn is_usize_like(self) -> bool {
1017+
matches!(self.kind(), Uint(UintTy::Usize) | Infer(IntVar(_)))
1018+
}
1019+
10081020
#[inline]
10091021
pub fn is_never(self) -> bool {
10101022
matches!(self.kind(), Never)

compiler/rustc_trait_selection/src/infer.rs

+6
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@ impl<'tcx> InferCtxt<'tcx> {
4343
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id)
4444
}
4545

46+
fn type_is_clone_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
47+
let ty = self.resolve_vars_if_possible(ty);
48+
let clone_def_id = self.tcx.require_lang_item(LangItem::Clone, None);
49+
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id)
50+
}
51+
4652
fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
4753
let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
4854
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item)

tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.rs

+7-7
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ fn get_dyn_size() -> usize {
1414
fn main() {
1515
let a = ["a", 10];
1616
//~^ ERROR mismatched types
17-
//~| HELP replace comma with semicolon to create an array
17+
//~| HELP replace the comma with a semicolon to create an array
1818

1919
const size_b: usize = 20;
2020
let b = [Type, size_b];
2121
//~^ ERROR mismatched types
22-
//~| HELP replace comma with semicolon to create an array
22+
//~| HELP replace the comma with a semicolon to create an array
2323

2424
let size_c: usize = 13;
2525
let c = [Type, size_c];
@@ -35,28 +35,28 @@ fn main() {
3535

3636
let f = ["f", get_size()];
3737
//~^ ERROR mismatched types
38-
//~| HELP replace comma with semicolon to create an array
38+
//~| HELP replace the comma with a semicolon to create an array
3939

4040
let m = ["m", get_dyn_size()];
4141
//~^ ERROR mismatched types
4242

4343
// is_vec, is_clone, is_usize_like
4444
let g = vec![String::new(), 10];
4545
//~^ ERROR mismatched types
46-
//~| HELP replace comma with semicolon to create a vector
46+
//~| HELP replace the comma with a semicolon to create a vector
4747

4848
let dyn_size = 10;
4949
let h = vec![Type, dyn_size];
5050
//~^ ERROR mismatched types
51-
//~| HELP replace comma with semicolon to create a vector
51+
//~| HELP replace the comma with a semicolon to create a vector
5252

5353
let i = vec![Type, get_dyn_size()];
5454
//~^ ERROR mismatched types
55-
//~| HELP replace comma with semicolon to create a vector
55+
//~| HELP replace the comma with a semicolon to create a vector
5656

5757
let k = vec!['c', 10];
5858
//~^ ERROR mismatched types
59-
//~| HELP replace comma with semicolon to create a vector
59+
//~| HELP replace the comma with a semicolon to create a vector
6060

6161
let j = vec![Type, 10_u8];
6262
//~^ ERROR mismatched types

tests/ui/repeat-expr/typo-in-repeat-expr-issue-80173.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
44
LL | let a = ["a", 10];
55
| ^^ expected `&str`, found integer
66
|
7-
help: replace comma with semicolon to create an array
7+
help: replace the comma with a semicolon to create an array
88
|
99
LL | let a = ["a"; 10];
1010
| ~
@@ -15,7 +15,7 @@ error[E0308]: mismatched types
1515
LL | let b = [Type, size_b];
1616
| ^^^^^^ expected `Type`, found `usize`
1717
|
18-
help: replace comma with semicolon to create an array
18+
help: replace the comma with a semicolon to create an array
1919
|
2020
LL | let b = [Type; size_b];
2121
| ~
@@ -46,7 +46,7 @@ error[E0308]: mismatched types
4646
LL | let f = ["f", get_size()];
4747
| ^^^^^^^^^^ expected `&str`, found `usize`
4848
|
49-
help: replace comma with semicolon to create an array
49+
help: replace the comma with a semicolon to create an array
5050
|
5151
LL | let f = ["f"; get_size()];
5252
| ~
@@ -63,7 +63,7 @@ error[E0308]: mismatched types
6363
LL | let g = vec![String::new(), 10];
6464
| ^^ expected `String`, found integer
6565
|
66-
help: replace comma with semicolon to create a vector
66+
help: replace the comma with a semicolon to create a vector
6767
|
6868
LL | let g = vec![String::new(); 10];
6969
| ~
@@ -74,7 +74,7 @@ error[E0308]: mismatched types
7474
LL | let h = vec![Type, dyn_size];
7575
| ^^^^^^^^ expected `Type`, found integer
7676
|
77-
help: replace comma with semicolon to create a vector
77+
help: replace the comma with a semicolon to create a vector
7878
|
7979
LL | let h = vec![Type; dyn_size];
8080
| ~
@@ -85,7 +85,7 @@ error[E0308]: mismatched types
8585
LL | let i = vec![Type, get_dyn_size()];
8686
| ^^^^^^^^^^^^^^ expected `Type`, found `usize`
8787
|
88-
help: replace comma with semicolon to create a vector
88+
help: replace the comma with a semicolon to create a vector
8989
|
9090
LL | let i = vec![Type; get_dyn_size()];
9191
| ~
@@ -96,7 +96,7 @@ error[E0308]: mismatched types
9696
LL | let k = vec!['c', 10];
9797
| ^^ expected `char`, found `u8`
9898
|
99-
help: replace comma with semicolon to create a vector
99+
help: replace the comma with a semicolon to create a vector
100100
|
101101
LL | let k = vec!['c'; 10];
102102
| ~

0 commit comments

Comments
 (0)