1
Fork 0

Suggest Semicolon in Incorrect Repeat Expressions

This commit is contained in:
Veera 2024-07-29 13:24:26 -04:00
parent 872aa75867
commit 98cc3457af
9 changed files with 144 additions and 25 deletions

View file

@ -7,7 +7,7 @@ use rustc_ast::token::CommentKind;
use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::util::parser::{AssocOp, ExprPrecedence};
use rustc_ast::{ use rustc_ast::{
self as ast, AttrId, AttrStyle, DelimArgs, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, 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::{ pub use rustc_ast::{
BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, ByRef, CaptureBy, 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` /// 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 /// `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 /// silent, only signaling the ownership system. By doing this, suggestions that check the

View file

@ -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_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_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 = hir_typeck_return_stmt_outside_of_fn_body =
{$statement_kind} statement outside of function body {$statement_kind} statement outside of function body
.encl_body_label = the {$statement_kind} is part of this body... .encl_body_label = the {$statement_kind} is part of this body...

View file

@ -30,7 +30,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if expr_ty == expected { if expr_ty == expected {
return; return;
} }
self.annotate_alternative_method_deref(err, expr, error); self.annotate_alternative_method_deref(err, expr, error);
self.explain_self_literal(err, expr, expected, expr_ty); 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_missing_unwrap_expect(err, expr, expected, expr_ty)
|| self.suggest_remove_last_method_call(err, expr, expected) || self.suggest_remove_last_method_call(err, expr, expected)
|| self.suggest_associated_const(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_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
|| self.suggest_option_to_bool(err, expr, expr_ty, expected) || self.suggest_option_to_bool(err, expr, expr_ty, expected)
|| self.suggest_compatible_variants(err, expr, expected, expr_ty) || self.suggest_compatible_variants(err, expr, expected, expr_ty)

View file

@ -846,3 +846,16 @@ pub(crate) struct PassFnItemToVariadicFunction {
pub sugg_span: Span, pub sugg_span: Span,
pub replace: String, 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,
}

View file

@ -1319,14 +1319,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let span = expr.span.shrink_to_hi(); let span = expr.span.shrink_to_hi();
let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) { let subdiag = if self.type_is_copy_modulo_regions(self.param_env, ty) {
errors::OptionResultRefMismatch::Copied { span, def_path } errors::OptionResultRefMismatch::Copied { span, def_path }
} else if let Some(clone_did) = self.tcx.lang_items().clone_trait() } else if self.type_is_clone_modulo_regions(self.param_env, ty) {
&& rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions(
self,
self.param_env,
ty,
clone_did,
)
{
errors::OptionResultRefMismatch::Cloned { span, def_path } errors::OptionResultRefMismatch::Cloned { span, def_path }
} else { } else {
return false; 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 /// 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) /// sole field is of the found type, suggest such variants. (Issue #42764)
pub(crate) fn suggest_compatible_variants( pub(crate) fn suggest_compatible_variants(

View file

@ -27,7 +27,7 @@ use crate::infer::canonical::Canonical;
use crate::ty::InferTy::*; use crate::ty::InferTy::*;
use crate::ty::{ use crate::ty::{
self, AdtDef, BoundRegionKind, Discr, GenericArg, GenericArgs, GenericArgsRef, List, ParamEnv, 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 // 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] #[inline]
pub fn is_never(self) -> bool { pub fn is_never(self) -> bool {
matches!(self.kind(), Never) matches!(self.kind(), Never)

View file

@ -47,6 +47,12 @@ impl<'tcx> InferCtxt<'tcx> {
traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, copy_def_id) 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 { 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); 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) traits::type_known_to_meet_bound_modulo_regions(self, param_env, ty, lang_item)

View file

@ -14,12 +14,12 @@ fn get_dyn_size() -> usize {
fn main() { fn main() {
let a = ["a", 10]; let a = ["a", 10];
//~^ ERROR mismatched types //~^ 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; const size_b: usize = 20;
let b = [Type, size_b]; let b = [Type, size_b];
//~^ ERROR mismatched types //~^ 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 size_c: usize = 13;
let c = [Type, size_c]; let c = [Type, size_c];
@ -35,7 +35,7 @@ fn main() {
let f = ["f", get_size()]; let f = ["f", get_size()];
//~^ ERROR mismatched types //~^ 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()]; let m = ["m", get_dyn_size()];
//~^ ERROR mismatched types //~^ ERROR mismatched types
@ -43,20 +43,20 @@ fn main() {
// is_vec, is_clone, is_usize_like // is_vec, is_clone, is_usize_like
let g = vec![String::new(), 10]; let g = vec![String::new(), 10];
//~^ ERROR mismatched types //~^ 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 dyn_size = 10;
let h = vec![Type, dyn_size]; let h = vec![Type, dyn_size];
//~^ ERROR mismatched types //~^ 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()]; let i = vec![Type, get_dyn_size()];
//~^ ERROR mismatched types //~^ 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]; let k = vec!['c', 10];
//~^ ERROR mismatched types //~^ 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]; let j = vec![Type, 10_u8];
//~^ ERROR mismatched types //~^ ERROR mismatched types

View file

@ -4,7 +4,7 @@ error[E0308]: mismatched types
LL | let a = ["a", 10]; LL | let a = ["a", 10];
| ^^ expected `&str`, found integer | ^^ 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]; LL | let a = ["a"; 10];
| ~ | ~
@ -15,7 +15,7 @@ error[E0308]: mismatched types
LL | let b = [Type, size_b]; LL | let b = [Type, size_b];
| ^^^^^^ expected `Type`, found `usize` | ^^^^^^ 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]; LL | let b = [Type; size_b];
| ~ | ~
@ -46,7 +46,7 @@ error[E0308]: mismatched types
LL | let f = ["f", get_size()]; LL | let f = ["f", get_size()];
| ^^^^^^^^^^ expected `&str`, found `usize` | ^^^^^^^^^^ 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()]; LL | let f = ["f"; get_size()];
| ~ | ~
@ -63,7 +63,7 @@ error[E0308]: mismatched types
LL | let g = vec![String::new(), 10]; LL | let g = vec![String::new(), 10];
| ^^ expected `String`, found integer | ^^ 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]; LL | let g = vec![String::new(); 10];
| ~ | ~
@ -74,7 +74,7 @@ error[E0308]: mismatched types
LL | let h = vec![Type, dyn_size]; LL | let h = vec![Type, dyn_size];
| ^^^^^^^^ expected `Type`, found integer | ^^^^^^^^ 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]; LL | let h = vec![Type; dyn_size];
| ~ | ~
@ -85,7 +85,7 @@ error[E0308]: mismatched types
LL | let i = vec![Type, get_dyn_size()]; LL | let i = vec![Type, get_dyn_size()];
| ^^^^^^^^^^^^^^ expected `Type`, found `usize` | ^^^^^^^^^^^^^^ 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()]; LL | let i = vec![Type; get_dyn_size()];
| ~ | ~
@ -96,7 +96,7 @@ error[E0308]: mismatched types
LL | let k = vec!['c', 10]; LL | let k = vec!['c', 10];
| ^^ expected `char`, found `u8` | ^^ 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]; LL | let k = vec!['c'; 10];
| ~ | ~