@@ -8,89 +8,95 @@ use rustc_errors::Applicability;
8
8
use rustc_hir:: { Expr , ExprKind , LangItem , Path , QPath } ;
9
9
use rustc_lint:: LateContext ;
10
10
use rustc_middle:: mir:: Const ;
11
- use rustc_middle:: ty:: { Adt , Ty } ;
11
+ use rustc_middle:: ty:: { Adt , Ty , TypeckResults } ;
12
12
use rustc_span:: Span ;
13
13
use rustc_span:: symbol:: sym;
14
14
15
15
use super :: TRUNCATE_WITH_DRAIN ;
16
16
17
17
// Add `String` here when it is added to diagnostic items
18
- const ACCEPTABLE_TYPES : [ rustc_span:: Symbol ; 2 ] = [ sym:: Vec , sym:: VecDeque ] ;
18
+ const ACCEPTABLE_TYPES_WITH_ARG : [ rustc_span:: Symbol ; 2 ] = [ sym:: Vec , sym:: VecDeque ] ;
19
19
20
- pub fn is_range_open_ended ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , container_path : Option < & Path < ' _ > > ) -> bool {
21
- let ty = cx. typeck_results ( ) . expr_ty ( expr) ;
22
- if let Some ( higher:: Range { start, end, limits } ) = higher:: Range :: hir ( expr) {
23
- let start_is_none_or_min = start. is_none_or ( |start| {
20
+ pub fn is_range_open_ended < ' a > (
21
+ cx : & LateContext < ' a > ,
22
+ range : higher:: Range < ' _ > ,
23
+ ty : Ty < ' a > ,
24
+ container_path : Option < & Path < ' _ > > ,
25
+ ) -> bool {
26
+ let higher:: Range { start, end, limits } = range;
27
+ let start_is_none_or_min = start. map_or ( true , |start| {
28
+ if let Adt ( _, subst) = ty. kind ( )
29
+ && let bnd_ty = subst. type_at ( 0 )
30
+ && let Some ( min_val) = bnd_ty. numeric_min_val ( cx. tcx )
31
+ && let Some ( min_const) = mir_to_const ( cx. tcx , Const :: from_ty_const ( min_val, bnd_ty, cx. tcx ) )
32
+ && let Some ( start_const) = ConstEvalCtxt :: new ( cx) . eval ( start)
33
+ {
34
+ start_const == min_const
35
+ } else {
36
+ false
37
+ }
38
+ } ) ;
39
+ let end_is_none_or_max = end. map_or ( true , |end| match limits {
40
+ RangeLimits :: Closed => {
24
41
if let Adt ( _, subst) = ty. kind ( )
25
42
&& let bnd_ty = subst. type_at ( 0 )
26
- && let Some ( min_val ) = bnd_ty. numeric_min_val ( cx. tcx )
27
- && let Some ( min_const ) = mir_to_const ( cx. tcx , Const :: from_ty_const ( min_val , bnd_ty, cx. tcx ) )
28
- && let Some ( start_const ) = ConstEvalCtxt :: new ( cx) . eval ( start )
43
+ && let Some ( max_val ) = bnd_ty. numeric_max_val ( cx. tcx )
44
+ && let Some ( max_const ) = mir_to_const ( cx. tcx , Const :: from_ty_const ( max_val , bnd_ty, cx. tcx ) )
45
+ && let Some ( end_const ) = ConstEvalCtxt :: new ( cx) . eval ( end )
29
46
{
30
- start_const == min_const
47
+ end_const == max_const
31
48
} else {
32
49
false
33
50
}
34
- } ) ;
35
- let end_is_none_or_max = end. is_none_or ( |end| match limits {
36
- RangeLimits :: Closed => {
37
- if let Adt ( _, subst) = ty. kind ( )
38
- && let bnd_ty = subst. type_at ( 0 )
39
- && let Some ( max_val) = bnd_ty. numeric_max_val ( cx. tcx )
40
- && let Some ( max_const) = mir_to_const ( cx. tcx , Const :: from_ty_const ( max_val, bnd_ty, cx. tcx ) )
41
- && let Some ( end_const) = ConstEvalCtxt :: new ( cx) . eval ( end)
42
- {
43
- end_const == max_const
44
- } else {
45
- false
46
- }
47
- } ,
48
- RangeLimits :: HalfOpen => {
49
- if let Some ( container_path) = container_path
50
- && let ExprKind :: MethodCall ( name, self_arg, [ ] , _) = end. kind
51
- && name. ident . name == sym:: len
52
- && let ExprKind :: Path ( QPath :: Resolved ( None , path) ) = self_arg. kind
53
- {
54
- container_path. res == path. res
55
- } else {
56
- false
57
- }
58
- } ,
59
- } ) ;
60
- return !start_is_none_or_min && end_is_none_or_max;
61
- }
62
- false
63
- }
64
-
65
- fn is_handled_collection_type ( cx : & LateContext < ' _ > , expr_ty : Ty < ' _ > ) -> bool {
66
- ACCEPTABLE_TYPES . iter ( ) . any ( |& ty| is_type_diagnostic_item ( cx, expr_ty, ty) )
67
- // String type is a lang item but not a diagnostic item for now so we need a separate check
68
- || is_type_lang_item ( cx, expr_ty, LangItem :: String )
51
+ } ,
52
+ RangeLimits :: HalfOpen => {
53
+ if let Some ( container_path) = container_path
54
+ && let ExprKind :: MethodCall ( name, self_arg, [ ] , _) = end. kind
55
+ && name. ident . name == sym:: len
56
+ && let ExprKind :: Path ( QPath :: Resolved ( None , path) ) = self_arg. kind
57
+ {
58
+ container_path. res == path. res
59
+ } else {
60
+ false
61
+ }
62
+ } ,
63
+ } ) ;
64
+ return !start_is_none_or_min && end_is_none_or_max;
69
65
}
70
66
71
67
pub ( super ) fn check ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , recv : & Expr < ' _ > , span : Span , arg : Option < & Expr < ' _ > > ) {
72
68
if let Some ( arg) = arg {
73
- let expr_ty = cx. typeck_results ( ) . expr_ty ( expr ) ;
74
- if is_handled_collection_type ( cx, expr_ty )
69
+ let typeck_results = cx. typeck_results ( ) ;
70
+ if match_acceptable_type ( cx, recv , typeck_results , & ACCEPTABLE_TYPES_WITH_ARG )
75
71
&& let ExprKind :: Path ( QPath :: Resolved ( None , container_path) ) = recv. kind
76
- && let Some ( range) = higher:: Range :: hir ( expr )
77
- && is_range_open_ended ( cx , arg , Some ( container_path ) )
78
- {
79
- if let Some ( adt) = expr_ty. ty_adt_def ( )
72
+ && let Some ( range) = higher:: Range :: hir ( arg )
73
+ && let higher :: Range { start : Some ( start ) , .. } = range
74
+ && is_range_open_ended ( cx , range , typeck_results . expr_ty ( arg ) , Some ( container_path ) )
75
+ && let Some ( adt) = typeck_results . expr_ty ( recv ) . ty_adt_def ( )
80
76
// Use `opt_item_name` while `String` is not a diagnostic item
81
77
&& let Some ( ty_name) = cx. tcx . opt_item_name ( adt. did ( ) )
82
- {
83
- span_lint_and_sugg (
84
- cx,
85
- TRUNCATE_WITH_DRAIN ,
86
- span. with_hi ( expr. span . hi ( ) ) ,
87
- format ! ( "`drain` used to truncate a `{ty_name}`" ) ,
88
- "use" ,
89
- // Can safely unwrap here because we don't lint on 0.. ranges
90
- format ! ( "truncate({})" , snippet( cx, range. start. unwrap( ) . span, "0" ) ) ,
91
- Applicability :: MachineApplicable ,
92
- ) ;
93
- }
78
+ {
79
+ span_lint_and_sugg (
80
+ cx,
81
+ TRUNCATE_WITH_DRAIN ,
82
+ span. with_hi ( expr. span . hi ( ) ) ,
83
+ format ! ( "`drain` used to truncate a `{ty_name}`" ) ,
84
+ "try" ,
85
+ format ! ( "truncate({})" , snippet( cx, start. span, "0" ) ) ,
86
+ Applicability :: MachineApplicable ,
87
+ ) ;
94
88
}
95
89
}
96
90
}
91
+
92
+ fn match_acceptable_type (
93
+ cx : & LateContext < ' _ > ,
94
+ expr : & Expr < ' _ > ,
95
+ typeck_results : & TypeckResults < ' _ > ,
96
+ types : & [ rustc_span:: Symbol ] ,
97
+ ) -> bool {
98
+ let expr_ty = typeck_results. expr_ty ( expr) . peel_refs ( ) ;
99
+ types. iter ( ) . any ( |& ty| is_type_diagnostic_item ( cx, expr_ty, ty) )
100
+ // String type is a lang item but not a diagnostic item for now so we need a separate check
101
+ || is_type_lang_item ( cx, expr_ty, LangItem :: String )
102
+ }
0 commit comments