@@ -3,10 +3,9 @@ use clippy_utils::diagnostics::span_lint_and_then;
3
3
use clippy_utils:: msrvs:: Msrv ;
4
4
use clippy_utils:: { is_in_const_context, is_in_test} ;
5
5
use rustc_data_structures:: fx:: FxHashMap ;
6
- use rustc_hir:: def:: DefKind ;
7
6
use rustc_hir:: { self as hir, AmbigArg , Expr , ExprKind , HirId , QPath , RustcVersion , StabilityLevel , StableSince } ;
8
7
use rustc_lint:: { LateContext , LateLintPass } ;
9
- use rustc_middle:: ty:: TyCtxt ;
8
+ use rustc_middle:: ty:: { self , TyCtxt } ;
10
9
use rustc_session:: impl_lint_pass;
11
10
use rustc_span:: def_id:: { CrateNum , DefId } ;
12
11
use rustc_span:: { ExpnKind , Span , sym} ;
@@ -83,6 +82,10 @@ pub struct IncompatibleMsrv {
83
82
availability_cache : FxHashMap < ( DefId , bool ) , Availability > ,
84
83
check_in_tests : bool ,
85
84
core_crate : Option < CrateNum > ,
85
+
86
+ // The most recently called path. Used to skip checking the path after it's
87
+ // been checked when visiting the call expression.
88
+ called_path : Option < HirId > ,
86
89
}
87
90
88
91
impl_lint_pass ! ( IncompatibleMsrv => [ INCOMPATIBLE_MSRV ] ) ;
@@ -98,6 +101,7 @@ impl IncompatibleMsrv {
98
101
. iter ( )
99
102
. find ( |krate| tcx. crate_name ( * * krate) == sym:: core)
100
103
. copied ( ) ,
104
+ called_path : None ,
101
105
}
102
106
}
103
107
@@ -140,7 +144,14 @@ impl IncompatibleMsrv {
140
144
}
141
145
142
146
/// Emit lint if `def_id`, associated with `node` and `span`, is below the current MSRV.
143
- fn emit_lint_if_under_msrv ( & mut self , cx : & LateContext < ' _ > , def_id : DefId , node : HirId , span : Span ) {
147
+ fn emit_lint_if_under_msrv (
148
+ & mut self ,
149
+ cx : & LateContext < ' _ > ,
150
+ needs_const : bool ,
151
+ def_id : DefId ,
152
+ node : HirId ,
153
+ span : Span ,
154
+ ) {
144
155
if def_id. is_local ( ) {
145
156
// We don't check local items since their MSRV is supposed to always be valid.
146
157
return ;
@@ -158,10 +169,6 @@ impl IncompatibleMsrv {
158
169
return ;
159
170
}
160
171
161
- let needs_const = cx. enclosing_body . is_some ( )
162
- && is_in_const_context ( cx)
163
- && matches ! ( cx. tcx. def_kind( def_id) , DefKind :: AssocFn | DefKind :: Fn ) ;
164
-
165
172
if ( self . check_in_tests || !is_in_test ( cx. tcx , node) )
166
173
&& let Some ( current) = self . msrv . current ( cx)
167
174
&& let Availability :: Since ( version) = self . get_def_id_availability ( cx. tcx , def_id, needs_const)
@@ -190,16 +197,35 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
190
197
match expr. kind {
191
198
ExprKind :: MethodCall ( _, _, _, span) => {
192
199
if let Some ( method_did) = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) {
193
- self . emit_lint_if_under_msrv ( cx, method_did, expr. hir_id , span) ;
200
+ self . emit_lint_if_under_msrv ( cx, is_in_const_context ( cx ) , method_did, expr. hir_id , span) ;
194
201
}
195
202
} ,
203
+ ExprKind :: Call ( callee, _)
204
+ if let ExprKind :: Path ( qpath @ ( QPath :: Resolved ( ..) | QPath :: TypeRelative ( ..) ) ) = callee. kind =>
205
+ {
206
+ self . called_path = Some ( callee. hir_id ) ;
207
+ let needs_const = is_in_const_context ( cx) ;
208
+ let def_id = if let Some ( def_id) = cx. qpath_res ( & qpath, callee. hir_id ) . opt_def_id ( ) {
209
+ def_id
210
+ } else if needs_const && let ty:: FnDef ( def_id, _) = * cx. typeck_results ( ) . expr_ty ( callee) . kind ( ) {
211
+ // Edge case where a function is first assigned then called.
212
+ // We previously would have warned for the non-const MSRV, when
213
+ // checking the path, but now that it's called the const MSRV
214
+ // must also be met.
215
+ def_id
216
+ } else {
217
+ return ;
218
+ } ;
219
+ self . emit_lint_if_under_msrv ( cx, needs_const, def_id, expr. hir_id , callee. span ) ;
220
+ } ,
196
221
// Desugaring into function calls by the compiler will use `QPath::LangItem` variants. Those should
197
222
// not be linted as they will not be generated in older compilers if the function is not available,
198
223
// and the compiler is allowed to call unstable functions.
199
- ExprKind :: Path ( qpath @ ( QPath :: Resolved ( ..) | QPath :: TypeRelative ( ..) ) ) => {
200
- if let Some ( path_def_id) = cx. qpath_res ( & qpath, expr. hir_id ) . opt_def_id ( ) {
201
- self . emit_lint_if_under_msrv ( cx, path_def_id, expr. hir_id , expr. span ) ;
202
- }
224
+ ExprKind :: Path ( qpath @ ( QPath :: Resolved ( ..) | QPath :: TypeRelative ( ..) ) )
225
+ if let Some ( path_def_id) = cx. qpath_res ( & qpath, expr. hir_id ) . opt_def_id ( )
226
+ && self . called_path != Some ( expr. hir_id ) =>
227
+ {
228
+ self . emit_lint_if_under_msrv ( cx, false , path_def_id, expr. hir_id , expr. span ) ;
203
229
} ,
204
230
_ => { } ,
205
231
}
@@ -211,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
211
237
// `CStr` and `CString` have been moved around but have been available since Rust 1.0.0
212
238
&& !matches ! ( cx. tcx. get_diagnostic_name( ty_def_id) , Some ( sym:: cstr_type | sym:: cstring_type) )
213
239
{
214
- self . emit_lint_if_under_msrv ( cx, ty_def_id, hir_ty. hir_id , hir_ty. span ) ;
240
+ self . emit_lint_if_under_msrv ( cx, false , ty_def_id, hir_ty. hir_id , hir_ty. span ) ;
215
241
}
216
242
}
217
243
}
0 commit comments