1
+ use clippy_config:: Conf ;
1
2
use clippy_utils:: diagnostics:: span_lint_and_sugg;
3
+ use clippy_utils:: msrvs:: { self , Msrv } ;
2
4
use clippy_utils:: source:: { SpanRangeExt , position_before_rarrow} ;
3
- use rustc_ast :: visit :: FnKind ;
4
- use rustc_ast:: { ClosureBinder , ast } ;
5
+ use clippy_utils :: { is_never_expr , is_unit_expr } ;
6
+ use rustc_ast:: { Block , StmtKind } ;
5
7
use rustc_errors:: Applicability ;
6
- use rustc_lint:: { EarlyContext , EarlyLintPass } ;
7
- use rustc_session:: declare_lint_pass;
8
- use rustc_span:: { BytePos , Span } ;
8
+ use rustc_hir:: def_id:: LocalDefId ;
9
+ use rustc_hir:: intravisit:: FnKind ;
10
+ use rustc_hir:: {
11
+ AssocItemConstraintKind , Body , Expr , ExprKind , FnDecl , FnRetTy , GenericArgsParentheses , Node , PolyTraitRef , Term ,
12
+ Ty , TyKind ,
13
+ } ;
14
+ use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
15
+ use rustc_session:: impl_lint_pass;
16
+ use rustc_span:: { BytePos , Span , sym} ;
9
17
10
18
declare_clippy_lint ! {
11
19
/// ### What it does
@@ -32,29 +40,101 @@ declare_clippy_lint! {
32
40
"needless unit expression"
33
41
}
34
42
35
- declare_lint_pass ! ( UnusedUnit => [ UNUSED_UNIT ] ) ;
43
+ pub struct UnusedUnit {
44
+ msrv : Msrv ,
45
+ }
36
46
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
41
- && vals. is_empty ( )
42
- && !ty. span . from_expansion ( )
43
- && get_def ( span) == get_def ( ty. span )
47
+ impl_lint_pass ! ( UnusedUnit => [ UNUSED_UNIT ] ) ;
48
+
49
+ impl UnusedUnit {
50
+ pub fn new ( conf : & ' static Conf ) -> Self {
51
+ Self { msrv : conf. msrv }
52
+ }
53
+ }
54
+
55
+ impl < ' tcx > LateLintPass < ' tcx > for UnusedUnit {
56
+ fn check_fn (
57
+ & mut self ,
58
+ cx : & LateContext < ' tcx > ,
59
+ kind : FnKind < ' tcx > ,
60
+ decl : & ' tcx FnDecl < ' tcx > ,
61
+ body : & ' tcx Body < ' tcx > ,
62
+ span : Span ,
63
+ def_id : LocalDefId ,
64
+ ) {
65
+ if let FnRetTy :: Return ( hir_ty) = decl. output
66
+ && is_unit_ty ( hir_ty)
67
+ && !hir_ty. span . from_expansion ( )
68
+ && get_def ( span) == get_def ( hir_ty. span )
44
69
{
45
70
// implicit types in closure signatures are forbidden when `for<...>` is present
46
- if let FnKind :: Closure ( & ClosureBinder :: For { .. } , ..) = kind {
71
+ if let FnKind :: Closure = kind
72
+ && let Node :: Expr ( expr) = cx. tcx . hir_node_by_def_id ( def_id)
73
+ && let ExprKind :: Closure ( closure) = expr. kind
74
+ && !closure. bound_generic_params . is_empty ( )
75
+ {
47
76
return ;
48
77
}
49
78
50
- lint_unneeded_unit_return ( cx, ty, span) ;
79
+ // unit never type fallback is no longer supported since Rust 2024. For more information,
80
+ // see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/never-type-fallback.html>
81
+ if self . msrv . meets ( cx, msrvs:: UNIT_NEVER_TYPE_FALLBACK )
82
+ && let ExprKind :: Block ( block, _) = body. value . kind
83
+ && let Some ( expr) = block. expr
84
+ && is_never_expr ( cx, expr) . is_some ( )
85
+ {
86
+ return ;
87
+ }
88
+
89
+ lint_unneeded_unit_return ( cx, hir_ty. span , span) ;
51
90
}
52
91
}
53
92
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
93
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
94
+ if let ExprKind :: Ret ( Some ( expr) ) | ExprKind :: Break ( _, Some ( expr) ) = expr. kind
57
95
&& is_unit_expr ( expr)
96
+ && !expr. span . from_expansion ( )
97
+ {
98
+ span_lint_and_sugg (
99
+ cx,
100
+ UNUSED_UNIT ,
101
+ expr. span ,
102
+ "unneeded `()`" ,
103
+ "remove the `()`" ,
104
+ String :: new ( ) ,
105
+ Applicability :: MachineApplicable ,
106
+ ) ;
107
+ }
108
+ }
109
+
110
+ fn check_poly_trait_ref ( & mut self , cx : & LateContext < ' tcx > , poly : & ' tcx PolyTraitRef < ' tcx > ) {
111
+ let segments = & poly. trait_ref . path . segments ;
112
+
113
+ if segments. len ( ) == 1
114
+ && [ "Fn" , "FnMut" , "FnOnce" ] . contains ( & segments[ 0 ] . ident . name . as_str ( ) )
115
+ && let Some ( args) = segments[ 0 ] . args
116
+ && args. parenthesized == GenericArgsParentheses :: ParenSugar
117
+ && let constraints = & args. constraints
118
+ && constraints. len ( ) == 1
119
+ && constraints[ 0 ] . ident . name == sym:: Output
120
+ && let AssocItemConstraintKind :: Equality { term : Term :: Ty ( hir_ty) } = constraints[ 0 ] . kind
121
+ && args. span_ext . hi ( ) != poly. span . hi ( )
122
+ && !hir_ty. span . from_expansion ( )
123
+ && is_unit_ty ( hir_ty)
124
+ {
125
+ lint_unneeded_unit_return ( cx, hir_ty. span , poly. span ) ;
126
+ }
127
+ }
128
+ }
129
+
130
+ impl EarlyLintPass for UnusedUnit {
131
+ /// Check for unit expressions in blocks. This is left in the early pass because some macros
132
+ /// expand its inputs as-is, making it invisible to the late pass. See #4076.
133
+ fn check_block ( & mut self , cx : & EarlyContext < ' _ > , block : & Block ) {
134
+ if let Some ( stmt) = block. stmts . last ( )
135
+ && let StmtKind :: Expr ( expr) = & stmt. kind
136
+ && let rustc_ast:: ExprKind :: Tup ( inner) = & expr. kind
137
+ && inner. is_empty ( )
58
138
&& let ctxt = block. span . ctxt ( )
59
139
&& stmt. span . ctxt ( ) == ctxt
60
140
&& expr. span . ctxt ( ) == ctxt
@@ -72,39 +152,10 @@ impl EarlyLintPass for UnusedUnit {
72
152
) ;
73
153
}
74
154
}
155
+ }
75
156
76
- fn check_expr ( & mut self , cx : & EarlyContext < ' _ > , e : & ast:: Expr ) {
77
- match e. kind {
78
- ast:: ExprKind :: Ret ( Some ( ref expr) ) | ast:: ExprKind :: Break ( _, Some ( ref expr) ) => {
79
- if is_unit_expr ( expr) && !expr. span . from_expansion ( ) {
80
- span_lint_and_sugg (
81
- cx,
82
- UNUSED_UNIT ,
83
- expr. span ,
84
- "unneeded `()`" ,
85
- "remove the `()`" ,
86
- String :: new ( ) ,
87
- Applicability :: MachineApplicable ,
88
- ) ;
89
- }
90
- } ,
91
- _ => ( ) ,
92
- }
93
- }
94
-
95
- fn check_poly_trait_ref ( & mut self , cx : & EarlyContext < ' _ > , poly : & ast:: PolyTraitRef ) {
96
- let segments = & poly. trait_ref . path . segments ;
97
-
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
103
- && ty. kind . is_unit ( )
104
- {
105
- lint_unneeded_unit_return ( cx, ty, generic_args. span ) ;
106
- }
107
- }
157
+ fn is_unit_ty ( ty : & Ty < ' _ > ) -> bool {
158
+ matches ! ( ty. kind, TyKind :: Tup ( [ ] ) )
108
159
}
109
160
110
161
// get the def site
@@ -117,24 +168,15 @@ fn get_def(span: Span) -> Option<Span> {
117
168
}
118
169
}
119
170
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 {
123
- vals. is_empty ( )
124
- } else {
125
- false
126
- }
127
- }
128
-
129
- fn lint_unneeded_unit_return ( cx : & EarlyContext < ' _ > , ty : & ast:: Ty , span : Span ) {
171
+ fn lint_unneeded_unit_return ( cx : & LateContext < ' _ > , ty_span : Span , span : Span ) {
130
172
let ( ret_span, appl) =
131
- span. with_hi ( ty . span . hi ( ) )
173
+ span. with_hi ( ty_span . hi ( ) )
132
174
. get_source_text ( cx)
133
- . map_or ( ( ty . span , Applicability :: MaybeIncorrect ) , |src| {
134
- position_before_rarrow ( & src) . map_or ( ( ty . span , Applicability :: MaybeIncorrect ) , |rpos| {
175
+ . map_or ( ( ty_span , Applicability :: MaybeIncorrect ) , |src| {
176
+ position_before_rarrow ( & src) . map_or ( ( ty_span , Applicability :: MaybeIncorrect ) , |rpos| {
135
177
(
136
178
#[ expect( clippy:: cast_possible_truncation) ]
137
- ty . span . with_lo ( BytePos ( span. lo ( ) . 0 + rpos as u32 ) ) ,
179
+ ty_span . with_lo ( BytePos ( span. lo ( ) . 0 + rpos as u32 ) ) ,
138
180
Applicability :: MachineApplicable ,
139
181
)
140
182
} )
0 commit comments