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