12
12
//! be considered a bug.
13
13
14
14
use crate :: paths:: { PathNS , lookup_path} ;
15
+ use rustc_ast:: { LitFloatType , LitIntType , LitKind } ;
15
16
use rustc_hir:: def:: { DefKind , Res } ;
16
17
use rustc_hir:: def_id:: DefId ;
17
18
use rustc_hir:: intravisit:: { InferKind , Visitor , VisitorExt , walk_qpath, walk_ty} ;
18
- use rustc_hir:: { self as hir, AmbigArg , Expr , ExprKind , GenericArgs , HirId , Node , PathSegment , QPath , TyKind } ;
19
+ use rustc_hir:: { self as hir, AmbigArg , Expr , ExprKind , GenericArgs , HirId , Node , Param , PathSegment , QPath , TyKind } ;
19
20
use rustc_lint:: LateContext ;
20
21
use rustc_middle:: ty:: { self , AdtDef , GenericArgKind , Ty } ;
21
22
use rustc_span:: Span ;
@@ -24,30 +25,32 @@ mod certainty;
24
25
use certainty:: { Certainty , Meet , join, meet} ;
25
26
26
27
pub fn expr_type_is_certain ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> bool {
27
- expr_type_certainty ( cx, expr) . is_certain ( )
28
+ expr_type_certainty ( cx, expr, false ) . is_certain ( )
28
29
}
29
30
30
- fn expr_type_certainty ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) -> Certainty {
31
+ /// Determine the type certainty of `expr`. `in_arg` indicates that the expression happens within
32
+ /// the evaluation of a function or method call argument.
33
+ fn expr_type_certainty ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , in_arg : bool ) -> Certainty {
31
34
let certainty = match & expr. kind {
32
35
ExprKind :: Unary ( _, expr)
33
36
| ExprKind :: Field ( expr, _)
34
37
| ExprKind :: Index ( expr, _, _)
35
- | ExprKind :: AddrOf ( _, _, expr) => expr_type_certainty ( cx, expr) ,
38
+ | ExprKind :: AddrOf ( _, _, expr) => expr_type_certainty ( cx, expr, in_arg ) ,
36
39
37
- ExprKind :: Array ( exprs) => join ( exprs. iter ( ) . map ( |expr| expr_type_certainty ( cx, expr) ) ) ,
40
+ ExprKind :: Array ( exprs) => join ( exprs. iter ( ) . map ( |expr| expr_type_certainty ( cx, expr, in_arg ) ) ) ,
38
41
39
42
ExprKind :: Call ( callee, args) => {
40
- let lhs = expr_type_certainty ( cx, callee) ;
43
+ let lhs = expr_type_certainty ( cx, callee, false ) ;
41
44
let rhs = if type_is_inferable_from_arguments ( cx, expr) {
42
- meet ( args. iter ( ) . map ( |arg| expr_type_certainty ( cx, arg) ) )
45
+ meet ( args. iter ( ) . map ( |arg| expr_type_certainty ( cx, arg, true ) ) )
43
46
} else {
44
47
Certainty :: Uncertain
45
48
} ;
46
49
lhs. join_clearing_def_ids ( rhs)
47
50
} ,
48
51
49
52
ExprKind :: MethodCall ( method, receiver, args, _) => {
50
- let mut receiver_type_certainty = expr_type_certainty ( cx, receiver) ;
53
+ let mut receiver_type_certainty = expr_type_certainty ( cx, receiver, false ) ;
51
54
// Even if `receiver_type_certainty` is `Certain(Some(..))`, the `Self` type in the method
52
55
// identified by `type_dependent_def_id(..)` can differ. This can happen as a result of a `deref`,
53
56
// for example. So update the `DefId` in `receiver_type_certainty` (if any).
@@ -59,24 +62,48 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty {
59
62
let lhs = path_segment_certainty ( cx, receiver_type_certainty, method, false ) ;
60
63
let rhs = if type_is_inferable_from_arguments ( cx, expr) {
61
64
meet (
62
- std:: iter:: once ( receiver_type_certainty) . chain ( args. iter ( ) . map ( |arg| expr_type_certainty ( cx, arg) ) ) ,
65
+ std:: iter:: once ( receiver_type_certainty)
66
+ . chain ( args. iter ( ) . map ( |arg| expr_type_certainty ( cx, arg, true ) ) ) ,
63
67
)
64
68
} else {
65
69
Certainty :: Uncertain
66
70
} ;
67
71
lhs. join ( rhs)
68
72
} ,
69
73
70
- ExprKind :: Tup ( exprs) => meet ( exprs. iter ( ) . map ( |expr| expr_type_certainty ( cx, expr) ) ) ,
74
+ ExprKind :: Tup ( exprs) => meet ( exprs. iter ( ) . map ( |expr| expr_type_certainty ( cx, expr, in_arg ) ) ) ,
71
75
72
- ExprKind :: Binary ( _, lhs, rhs) => expr_type_certainty ( cx, lhs) . meet ( expr_type_certainty ( cx, rhs) ) ,
76
+ ExprKind :: Binary ( _, lhs, rhs) => {
77
+ // If one of the side of the expression is uncertain, the certainty will come from the other side,
78
+ // with no information on the type.
79
+ match (
80
+ expr_type_certainty ( cx, lhs, in_arg) ,
81
+ expr_type_certainty ( cx, rhs, in_arg) ,
82
+ ) {
83
+ ( Certainty :: Uncertain , Certainty :: Certain ( _) ) | ( Certainty :: Certain ( _) , Certainty :: Uncertain ) => {
84
+ Certainty :: Certain ( None )
85
+ } ,
86
+ ( l, r) => l. meet ( r) ,
87
+ }
88
+ } ,
73
89
74
- ExprKind :: Lit ( _) => Certainty :: Certain ( None ) ,
90
+ ExprKind :: Lit ( lit) => {
91
+ if !in_arg
92
+ && matches ! (
93
+ lit. node,
94
+ LitKind :: Int ( _, LitIntType :: Unsuffixed ) | LitKind :: Float ( _, LitFloatType :: Unsuffixed )
95
+ )
96
+ {
97
+ Certainty :: Uncertain
98
+ } else {
99
+ Certainty :: Certain ( None )
100
+ }
101
+ } ,
75
102
76
103
ExprKind :: Cast ( _, ty) => type_certainty ( cx, ty) ,
77
104
78
105
ExprKind :: If ( _, if_expr, Some ( else_expr) ) => {
79
- expr_type_certainty ( cx, if_expr) . join ( expr_type_certainty ( cx, else_expr) )
106
+ expr_type_certainty ( cx, if_expr, in_arg ) . join ( expr_type_certainty ( cx, else_expr, in_arg ) )
80
107
} ,
81
108
82
109
ExprKind :: Path ( qpath) => qpath_certainty ( cx, qpath, false ) ,
@@ -188,6 +215,20 @@ fn qpath_certainty(cx: &LateContext<'_>, qpath: &QPath<'_>, resolves_to_type: bo
188
215
certainty
189
216
}
190
217
218
+ /// Tries to tell whether `param` resolves to something certain, e.g., a non-wildcard type if
219
+ /// present. The certainty `DefId` is cleared before returning.
220
+ fn param_certainty ( cx : & LateContext < ' _ > , param : & Param < ' _ > ) -> Certainty {
221
+ let owner_did = cx. tcx . hir_enclosing_body_owner ( param. hir_id ) ;
222
+ let Some ( fn_decl) = cx. tcx . hir_fn_decl_by_hir_id ( cx. tcx . local_def_id_to_hir_id ( owner_did) ) else {
223
+ return Certainty :: Uncertain ;
224
+ } ;
225
+ let inputs = fn_decl. inputs ;
226
+ let body_params = cx. tcx . hir_body_owned_by ( owner_did) . params ;
227
+ std:: iter:: zip ( body_params, inputs)
228
+ . find ( |( p, _) | p. hir_id == param. hir_id )
229
+ . map_or ( Certainty :: Uncertain , |( _, ty) | type_certainty ( cx, ty) . clear_def_id ( ) )
230
+ }
231
+
191
232
fn path_segment_certainty (
192
233
cx : & LateContext < ' _ > ,
193
234
parent_certainty : Certainty ,
@@ -240,15 +281,16 @@ fn path_segment_certainty(
240
281
241
282
// `get_parent` because `hir_id` refers to a `Pat`, and we're interested in the node containing the `Pat`.
242
283
Res :: Local ( hir_id) => match cx. tcx . parent_hir_node ( hir_id) {
243
- // An argument's type is always certain.
244
- Node :: Param ( ..) => Certainty :: Certain ( None ) ,
284
+ // A parameter's type is not always certain, as it may come from an untyped closure definition,
285
+ // or from a wildcard in a typed closure definition.
286
+ Node :: Param ( param) => param_certainty ( cx, param) ,
245
287
// A local's type is certain if its type annotation is certain or it has an initializer whose
246
288
// type is certain.
247
289
Node :: LetStmt ( local) => {
248
290
let lhs = local. ty . map_or ( Certainty :: Uncertain , |ty| type_certainty ( cx, ty) ) ;
249
291
let rhs = local
250
292
. init
251
- . map_or ( Certainty :: Uncertain , |init| expr_type_certainty ( cx, init) ) ;
293
+ . map_or ( Certainty :: Uncertain , |init| expr_type_certainty ( cx, init, false ) ) ;
252
294
let certainty = lhs. join ( rhs) ;
253
295
if resolves_to_type {
254
296
certainty
0 commit comments