New lint: unnecessary_semicolon (#14032)

This lint detects and removes the unnecessary semicolon after a `match`
or `if` statement returning `()`. It seems to be quite a common
"mistake", given the number of hits (88) we had in the Clippy sources
themselves.

The lint doesn't bother about loops, as `rustfmt` already removes the
extra semicolon. It doesn't handle blocks either, as an extra block
level, followed or not by a semicolon, is likely intentional.

I propose to put the lint in `pedantic`, as putting it in `style` seems
quite hazardous given the number of hits.

Note: there exists a `redundant-semicolon` lint in the compiler, but it
is an early lint and cannot check that the expression evaluates to `()`,
so it ignores the cases we're handling here.

----

changelog: [`unnecessary_semicolon`]: new lint
This commit is contained in:
llogiq 2025-01-20 17:39:37 +00:00 committed by GitHub
commit 8f1b4bb87a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
83 changed files with 236 additions and 88 deletions

View file

@ -475,7 +475,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
_ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
};
};
}
},
ExprKind::Path(QPath::Resolved(_, path))
if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?)

View file

@ -815,7 +815,7 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &'
e = ep;
},
_ => break e,
};
}
};
result.reverse();
(result, root)
@ -2045,7 +2045,7 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t
{
return Some(expr);
}
};
}
None
}

View file

@ -251,7 +251,7 @@ impl<'a> PanicExpn<'a> {
// This has no argument
if name == "panic_cold_explicit" {
return Some(Self::Empty);
};
}
let [arg, rest @ ..] = args else {
return None;

View file

@ -125,7 +125,7 @@ impl<'a> NumericLiteral<'a> {
integer = &digits[..exp_start];
} else {
fraction = Some(&digits[integer.len() + 1..exp_start]);
};
}
exponent = Some((&digits[exp_start..=i], &digits[i + 1..]));
break;
},

View file

@ -118,7 +118,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) {
return true;
}
};
}
},
_ => (),
}

View file

@ -55,7 +55,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
&& let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id))
{
receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id);
};
}
let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false);
let rhs = if type_is_inferable_from_arguments(cx, expr) {
meet(