1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::source
::{position_before_rarrow, snippet_opt}
;
3 use rustc_ast
::visit
::FnKind
;
4 use rustc_ast
::{ast, ClosureBinder}
;
5 use rustc_errors
::Applicability
;
6 use rustc_lint
::{EarlyContext, EarlyLintPass}
;
7 use rustc_session
::declare_lint_pass
;
8 use rustc_span
::{BytePos, Span}
;
10 declare_clippy_lint
! {
12 /// Checks for unit (`()`) expressions that can be removed.
14 /// ### Why is this bad?
15 /// Such expressions add no value, but can make the code
16 /// less readable. Depending on formatting they can make a `break` or `return`
17 /// statement look like a function call.
21 /// fn return_unit() -> () {
27 /// fn return_unit() {}
29 #[clippy::version = "1.31.0"]
32 "needless unit expression"
35 declare_lint_pass
!(UnusedUnit
=> [UNUSED_UNIT
]);
37 impl EarlyLintPass
for UnusedUnit
{
38 fn check_fn(&mut self, cx
: &EarlyContext
<'_
>, kind
: FnKind
<'_
>, span
: Span
, _
: ast
::NodeId
) {
39 if let ast
::FnRetTy
::Ty(ref ty
) = kind
.decl().output
40 && let ast
::TyKind
::Tup(ref vals
) = ty
.kind
42 && !ty
.span
.from_expansion()
43 && get_def(span
) == get_def(ty
.span
)
45 // implicit types in closure signatures are forbidden when `for<...>` is present
46 if let FnKind
::Closure(&ClosureBinder
::For { .. }
, ..) = kind
{
50 lint_unneeded_unit_return(cx
, ty
, span
);
54 fn check_block(&mut self, cx
: &EarlyContext
<'_
>, block
: &ast
::Block
) {
55 if let Some(stmt
) = block
.stmts
.last()
56 && let ast
::StmtKind
::Expr(ref expr
) = stmt
.kind
58 && let ctxt
= block
.span
.ctxt()
59 && stmt
.span
.ctxt() == ctxt
60 && expr
.span
.ctxt() == ctxt
61 && expr
.attrs
.is_empty()
68 "unneeded unit expression",
69 "remove the final `()`",
71 Applicability
::MachineApplicable
,
76 fn check_expr(&mut self, cx
: &EarlyContext
<'_
>, e
: &ast
::Expr
) {
78 ast
::ExprKind
::Ret(Some(ref expr
)) | ast
::ExprKind
::Break(_
, Some(ref expr
)) => {
79 if is_unit_expr(expr
) && !expr
.span
.from_expansion() {
87 Applicability
::MachineApplicable
,
95 fn check_poly_trait_ref(&mut self, cx
: &EarlyContext
<'_
>, poly
: &ast
::PolyTraitRef
) {
96 let segments
= &poly
.trait_ref
.path
.segments
;
98 if segments
.len() == 1
99 && ["Fn", "FnMut", "FnOnce"].contains(&segments
[0].ident
.name
.as_str())
100 && let Some(args
) = &segments
[0].args
101 && let ast
::GenericArgs
::Parenthesized(generic_args
) = &**args
102 && let ast
::FnRetTy
::Ty(ty
) = &generic_args
.output
105 lint_unneeded_unit_return(cx
, ty
, generic_args
.span
);
112 fn get_def(span
: Span
) -> Option
<Span
> {
113 if span
.from_expansion() {
114 Some(span
.ctxt().outer_expn_data().def_site
)
120 // is this expr a `()` unit?
121 fn is_unit_expr(expr
: &ast
::Expr
) -> bool
{
122 if let ast
::ExprKind
::Tup(ref vals
) = expr
.kind
{
129 fn lint_unneeded_unit_return(cx
: &EarlyContext
<'_
>, ty
: &ast
::Ty
, span
: Span
) {
130 let (ret_span
, appl
) =
131 snippet_opt(cx
, span
.with_hi(ty
.span
.hi())).map_or((ty
.span
, Applicability
::MaybeIncorrect
), |fn_source
| {
132 position_before_rarrow(&fn_source
).map_or((ty
.span
, Applicability
::MaybeIncorrect
), |rpos
| {
134 #[expect(clippy::cast_possible_truncation)]
135 ty
.span
.with_lo(BytePos(span
.lo().0 + rpos
as u32)),
136 Applicability
::MachineApplicable
,
144 "unneeded unit return type",
145 "remove the `-> ()`",