1 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
2 use clippy_utils
::source
::{position_before_rarrow, snippet_opt}
;
3 use if_chain
::if_chain
;
4 use rustc_ast
::{ast, visit::FnKind, ClosureBinder}
;
5 use rustc_errors
::Applicability
;
6 use rustc_lint
::{EarlyContext, EarlyLintPass}
;
7 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
8 use rustc_span
::source_map
::Span
;
9 use rustc_span
::BytePos
;
11 declare_clippy_lint
! {
13 /// Checks for unit (`()`) expressions that can be removed.
15 /// ### Why is this bad?
16 /// Such expressions add no value, but can make the code
17 /// less readable. Depending on formatting they can make a `break` or `return`
18 /// statement look like a function call.
22 /// fn return_unit() -> () {
28 /// fn return_unit() {}
30 #[clippy::version = "1.31.0"]
33 "needless unit expression"
36 declare_lint_pass
!(UnusedUnit
=> [UNUSED_UNIT
]);
38 impl EarlyLintPass
for UnusedUnit
{
39 fn check_fn(&mut self, cx
: &EarlyContext
<'_
>, kind
: FnKind
<'_
>, span
: Span
, _
: ast
::NodeId
) {
41 if let ast
::FnRetTy
::Ty(ref ty
) = kind
.decl().output
;
42 if let ast
::TyKind
::Tup(ref vals
) = ty
.kind
;
43 if vals
.is_empty() && !ty
.span
.from_expansion() && 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
);
55 fn check_block(&mut self, cx
: &EarlyContext
<'_
>, block
: &ast
::Block
) {
57 if let Some(stmt
) = block
.stmts
.last();
58 if let ast
::StmtKind
::Expr(ref expr
) = stmt
.kind
;
59 if is_unit_expr(expr
);
60 let ctxt
= block
.span
.ctxt();
61 if stmt
.span
.ctxt() == ctxt
&& expr
.span
.ctxt() == ctxt
;
68 "unneeded unit expression",
69 "remove the final `()`",
71 Applicability
::MachineApplicable
,
77 fn check_expr(&mut self, cx
: &EarlyContext
<'_
>, e
: &ast
::Expr
) {
79 ast
::ExprKind
::Ret(Some(ref expr
)) | ast
::ExprKind
::Break(_
, Some(ref expr
)) => {
80 if is_unit_expr(expr
) && !expr
.span
.from_expansion() {
88 Applicability
::MachineApplicable
,
96 fn check_poly_trait_ref(&mut self, cx
: &EarlyContext
<'_
>, poly
: &ast
::PolyTraitRef
) {
97 let segments
= &poly
.trait_ref
.path
.segments
;
100 if segments
.len() == 1;
101 if ["Fn", "FnMut", "FnOnce"].contains(&segments
[0].ident
.name
.as_str());
102 if let Some(args
) = &segments
[0].args
;
103 if let ast
::GenericArgs
::Parenthesized(generic_args
) = &**args
;
104 if let ast
::FnRetTy
::Ty(ty
) = &generic_args
.output
;
105 if ty
.kind
.is_unit();
107 lint_unneeded_unit_return(cx
, ty
, generic_args
.span
);
115 fn get_def(span
: Span
) -> Option
<Span
> {
116 if span
.from_expansion() {
117 Some(span
.ctxt().outer_expn_data().def_site
)
123 // is this expr a `()` unit?
124 fn is_unit_expr(expr
: &ast
::Expr
) -> bool
{
125 if let ast
::ExprKind
::Tup(ref vals
) = expr
.kind
{
132 fn lint_unneeded_unit_return(cx
: &EarlyContext
<'_
>, ty
: &ast
::Ty
, span
: Span
) {
133 let (ret_span
, appl
) =
134 snippet_opt(cx
, span
.with_hi(ty
.span
.hi())).map_or((ty
.span
, Applicability
::MaybeIncorrect
), |fn_source
| {
135 position_before_rarrow(&fn_source
).map_or((ty
.span
, Applicability
::MaybeIncorrect
), |rpos
| {
137 #[expect(clippy::cast_possible_truncation)]
138 ty
.span
.with_lo(BytePos(span
.lo().0 + rpos
as u32)),
139 Applicability
::MachineApplicable
,
147 "unneeded unit return type",
148 "remove the `-> ()`",