Skip to content

Commit d45665c

Browse files
Suggest Semicolon in Incorrect Repeat Expressions
1 parent c6a694f commit d45665c

File tree

7 files changed

+123
-9
lines changed

7 files changed

+123
-9
lines changed

compiler/rustc_hir/src/hir.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1807,6 +1807,18 @@ impl Expr<'_> {
18071807
}
18081808
}
18091809

1810+
/// Check if expression is an integer literal that can be used
1811+
/// where `usize` is expected.
1812+
pub fn is_size_lit(&self) -> bool {
1813+
matches!(
1814+
self.kind,
1815+
ExprKind::Lit(Lit {
1816+
node: LitKind::Int(_, LitIntType::Unsuffixed | LitIntType::Unsigned(UintTy::Usize)),
1817+
..
1818+
})
1819+
)
1820+
}
1821+
18101822
/// If `Self.kind` is `ExprKind::DropTemps(expr)`, drill down until we get a non-`DropTemps`
18111823
/// `Expr`. This is used in suggestions to ignore this `ExprKind` as it is semantically
18121824
/// 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 comma with 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
@@ -30,7 +30,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3030
if expr_ty == expected {
3131
return;
3232
}
33-
3433
self.annotate_alternative_method_deref(err, expr, error);
3534
self.explain_self_literal(err, expr, expected, expr_ty);
3635

@@ -39,6 +38,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3938
|| self.suggest_missing_unwrap_expect(err, expr, expected, expr_ty)
4039
|| self.suggest_remove_last_method_call(err, expr, expected)
4140
|| self.suggest_associated_const(err, expr, expected)
41+
|| self.suggest_semicolon_in_repeat_expr(err, expr, expr_ty)
4242
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
4343
|| self.suggest_option_to_bool(err, expr, expr_ty, expected)
4444
|| 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<'tcx, 'a> {
724724
#[note(hir_typeck_teach_help)]
725725
pub(crate) teach: Option<()>,
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 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

+74-8
Original file line numberDiff line numberDiff line change
@@ -1299,14 +1299,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
12991299
let span = expr.span.shrink_to_hi();
13001300
let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
13011301
errors::OptionResultRefMismatch::Copied { span, def_path }
1302-
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait()
1303-
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
1304-
self,
1305-
self.param_env,
1306-
ty,
1307-
clone_did,
1308-
)
1309-
{
1302+
} else if self.type_is_clone_modulo_regions(self.param_env, ty) {
13101303
errors::OptionResultRefMismatch::Cloned { span, def_path }
13111304
} else {
13121305
return false;
@@ -2159,6 +2152,79 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21592152
}
21602153
}
21612154

2155+
/// Suggest replacing comma with semicolon in incorrect repeat expressions
2156+
/// like `["_", 10]` or `vec![String::new(), 10]`.
2157+
pub(crate) fn suggest_semicolon_in_repeat_expr(
2158+
&self,
2159+
err: &mut Diag<'_>,
2160+
expr: &hir::Expr<'_>,
2161+
expr_ty: Ty<'tcx>,
2162+
) -> bool {
2163+
// Check if `expr` is contained in array of two elements
2164+
if let hir::Node::Expr(array_expr) = self.tcx.parent_hir_node(expr.hir_id)
2165+
&& let hir::ExprKind::Array(elements) = array_expr.kind
2166+
&& let [first, second] = &elements[..]
2167+
&& second.hir_id == expr.hir_id
2168+
{
2169+
// Span between the two elements of the array
2170+
let comma_span = first.span.between(second.span);
2171+
2172+
// Check if `expr` is a constant value of type `usize`.
2173+
// This can only detect const variable declarations and
2174+
// calls to const functions.
2175+
2176+
// Checking this here instead of rustc_hir::hir because
2177+
// this check needs access to `self.tcx` but rustc_hir
2178+
// has no access to `TyCtxt`.
2179+
let expr_is_const_usize = expr_ty.is_usize()
2180+
&& match expr.kind {
2181+
ExprKind::Path(QPath::Resolved(
2182+
None,
2183+
Path { res: Res::Def(DefKind::Const, _), .. },
2184+
)) => true,
2185+
ExprKind::Call(
2186+
Expr {
2187+
kind:
2188+
ExprKind::Path(QPath::Resolved(
2189+
None,
2190+
Path { res: Res::Def(DefKind::Fn, fn_def_id), .. },
2191+
)),
2192+
..
2193+
},
2194+
_,
2195+
) => self.tcx.is_const_fn(*fn_def_id),
2196+
_ => false,
2197+
};
2198+
2199+
// `array_expr` is from a macro `vec!["a", 10]` if
2200+
// 1. array expression's span is imported from a macro
2201+
// 2. first element of array implements `Clone` trait
2202+
// 3. second element is an integer literal or is an expression of `usize` like type
2203+
if self.tcx.sess.source_map().is_imported(array_expr.span)
2204+
&& self.type_is_clone_modulo_regions(self.param_env, self.check_expr(first))
2205+
&& (second.is_size_lit() || expr_ty.is_usize_like())
2206+
{
2207+
err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2208+
comma_span,
2209+
descr: "a vector",
2210+
});
2211+
return true;
2212+
} else if self.type_is_copy_modulo_regions(self.param_env, self.check_expr(first))
2213+
&& (second.is_size_lit() || expr_is_const_usize)
2214+
{
2215+
// `array_expr` is from an array `["a", 10]` if
2216+
// 1. first element of array implements `Copy` trait
2217+
// 2. second element is an integer literal or is a const value of type `usize`
2218+
err.subdiagnostic(errors::ReplaceCommaWithSemicolon {
2219+
comma_span,
2220+
descr: "an array",
2221+
});
2222+
return true;
2223+
}
2224+
}
2225+
false
2226+
}
2227+
21622228
/// If the expected type is an enum (Issue #55250) with any variants whose
21632229
/// sole field is of the found type, suggest such variants. (Issue #42764)
21642230
pub(crate) fn suggest_compatible_variants(

compiler/rustc_middle/src/ty/sty.rs

+12
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,18 @@ impl<'tcx> Ty<'tcx> {
988988
}
989989
}
990990

991+
/// Check if type is an `usize`.
992+
#[inline]
993+
pub fn is_usize(self) -> bool {
994+
matches!(self.kind(), Uint(UintTy::Usize))
995+
}
996+
997+
/// Check if type is an `usize` or an integral type variable.
998+
#[inline]
999+
pub fn is_usize_like(self) -> bool {
1000+
matches!(self.kind(), Uint(UintTy::Usize) | Infer(IntVar(_)))
1001+
}
1002+
9911003
#[inline]
9921004
pub fn is_never(self) -> bool {
9931005
matches!(self.kind(), Never)

compiler/rustc_trait_selection/src/infer.rs

+9
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ impl<'tcx> InferCtxt<'tcx> {
2626
})
2727
}
2828

29+
/// Check if `ty` implements the `Copy` trait.
2930
fn type_is_copy_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
3031
let ty = self.resolve_vars_if_possible(ty);
3132

@@ -42,6 +43,14 @@ impl<'tcx> InferCtxt<'tcx> {
4243
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id)
4344
}
4445

46+
/// Check if `ty` implements the `Clone` trait.
47+
fn type_is_clone_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
48+
let ty = self.resolve_vars_if_possible(ty);
49+
let clone_def_id = self.tcx.require_lang_item(LangItem::Clone, None);
50+
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, clone_def_id)
51+
}
52+
53+
/// Check if `ty` implements the `Sized` trait.
4554
fn type_is_sized_modulo_regions(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
4655
let lang_item = self.tcx.require_lang_item(LangItem::Sized, None);
4756
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item)

0 commit comments

Comments
 (0)