1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: get_parent_expr;
3
3
use clippy_utils:: source:: snippet_with_context;
4
- use clippy_utils:: ty:: is_type_lang_item;
4
+ use clippy_utils:: ty:: { is_type_lang_item, peel_mid_ty_refs } ;
5
5
use if_chain:: if_chain;
6
+ use rustc_ast:: util:: parser:: PREC_PREFIX ;
6
7
use rustc_errors:: Applicability ;
7
8
use rustc_hir:: { BorrowKind , Expr , ExprKind , LangItem , Mutability } ;
8
- use rustc_lint:: { LateContext , LateLintPass } ;
9
+ use rustc_lint:: { LateContext , LateLintPass , Lint } ;
10
+ use rustc_middle:: ty:: adjustment:: { Adjust , AutoBorrow , AutoBorrowMutability } ;
11
+ use rustc_middle:: ty:: subst:: GenericArg ;
9
12
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
10
13
11
14
declare_clippy_lint ! {
@@ -39,7 +42,34 @@ declare_clippy_lint! {
39
42
"redundant slicing of the whole range of a type"
40
43
}
41
44
42
- declare_lint_pass ! ( RedundantSlicing => [ REDUNDANT_SLICING ] ) ;
45
+ declare_clippy_lint ! {
46
+ /// ### What it does
47
+ /// Checks for slicing expressions which are equivalent to dereferencing the
48
+ /// value.
49
+ ///
50
+ /// ### Why is this bad?
51
+ /// Some people may prefer to dereference rather than slice.
52
+ ///
53
+ /// ### Example
54
+ /// ```rust
55
+ /// let vec = vec![1, 2, 3];
56
+ /// let slice = &vec[..];
57
+ /// ```
58
+ /// Use instead:
59
+ /// ```rust
60
+ /// let vec = vec![1, 2, 3];
61
+ /// let slice = &*vec;
62
+ /// ```
63
+ #[ clippy:: version = "1.60.0" ]
64
+ pub DEREF_BY_SLICING ,
65
+ restriction,
66
+ "slicing instead of dereferencing"
67
+ }
68
+
69
+ declare_lint_pass ! ( RedundantSlicing => [ REDUNDANT_SLICING , DEREF_BY_SLICING ] ) ;
70
+
71
+ static REDUNDANT_SLICING_LINT : ( & Lint , & str ) = ( REDUNDANT_SLICING , "redundant slicing of the whole range" ) ;
72
+ static DEREF_BY_SLICING_LINT : ( & Lint , & str ) = ( DEREF_BY_SLICING , "slicing when dereferencing would work" ) ;
43
73
44
74
impl < ' tcx > LateLintPass < ' tcx > for RedundantSlicing {
45
75
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
@@ -53,34 +83,84 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
53
83
if addressee. span. ctxt( ) == ctxt;
54
84
if let ExprKind :: Index ( indexed, range) = addressee. kind;
55
85
if is_type_lang_item( cx, cx. typeck_results( ) . expr_ty_adjusted( range) , LangItem :: RangeFull ) ;
56
- if cx. typeck_results( ) . expr_ty( expr) == cx. typeck_results( ) . expr_ty( indexed) ;
57
86
then {
87
+ let ( expr_ty, expr_ref_count) = peel_mid_ty_refs( cx. typeck_results( ) . expr_ty( expr) ) ;
88
+ let ( indexed_ty, indexed_ref_count) = peel_mid_ty_refs( cx. typeck_results( ) . expr_ty( indexed) ) ;
89
+ let parent_expr = get_parent_expr( cx, expr) ;
90
+ let needs_parens_for_prefix = parent_expr. map_or( false , |parent| {
91
+ parent. precedence( ) . order( ) > PREC_PREFIX
92
+ } ) ;
58
93
let mut app = Applicability :: MachineApplicable ;
59
- let snip = snippet_with_context( cx, indexed. span, ctxt, ".." , & mut app) . 0 ;
60
94
61
- let ( reborrow_str, help_str) = if mutability == Mutability :: Mut {
62
- // The slice was used to reborrow the mutable reference.
63
- ( "&mut *" , "reborrow the original value instead" )
64
- } else if matches!(
65
- get_parent_expr( cx, expr) ,
66
- Some ( Expr {
67
- kind: ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mut , _) ,
68
- ..
69
- } )
70
- ) {
71
- // The slice was used to make a temporary reference.
72
- ( "&*" , "reborrow the original value instead" )
95
+ let ( ( lint, msg) , help, sugg) = if expr_ty == indexed_ty {
96
+ if expr_ref_count > indexed_ref_count {
97
+ // Indexing takes self by reference and can't return a reference to that
98
+ // reference as it's a local variable. The only way this could happen is if
99
+ // `self` contains a reference to the `Self` type. If this occurs then the
100
+ // lint no longer applies as it's essentially a field access, which is not
101
+ // redundant.
102
+ return ;
103
+ }
104
+ let deref_count = indexed_ref_count - expr_ref_count;
105
+
106
+ let ( lint, reborrow_str, help_str) = if mutability == Mutability :: Mut {
107
+ // The slice was used to reborrow the mutable reference.
108
+ ( DEREF_BY_SLICING_LINT , "&mut *" , "reborrow the original value instead" )
109
+ } else if matches!(
110
+ parent_expr,
111
+ Some ( Expr {
112
+ kind: ExprKind :: AddrOf ( BorrowKind :: Ref , Mutability :: Mut , _) ,
113
+ ..
114
+ } )
115
+ ) || cx. typeck_results( ) . expr_adjustments( expr) . first( ) . map_or( false , |a| {
116
+ matches!( a. kind, Adjust :: Borrow ( AutoBorrow :: Ref ( _, AutoBorrowMutability :: Mut { .. } ) ) )
117
+ } ) {
118
+ // The slice was used to make a temporary reference.
119
+ ( DEREF_BY_SLICING_LINT , "&*" , "reborrow the original value instead" )
120
+ } else if deref_count != 0 {
121
+ ( DEREF_BY_SLICING_LINT , "" , "dereference the original value instead" )
122
+ } else {
123
+ ( REDUNDANT_SLICING_LINT , "" , "use the original value instead" )
124
+ } ;
125
+
126
+ let snip = snippet_with_context( cx, indexed. span, ctxt, ".." , & mut app) . 0 ;
127
+ let sugg = if ( deref_count != 0 || !reborrow_str. is_empty( ) ) && needs_parens_for_prefix {
128
+ format!( "({}{}{})" , reborrow_str, "*" . repeat( deref_count) , snip)
129
+ } else {
130
+ format!( "{}{}{}" , reborrow_str, "*" . repeat( deref_count) , snip)
131
+ } ;
132
+
133
+ ( lint, help_str, sugg)
134
+ } else if let Some ( target_id) = cx. tcx. lang_items( ) . deref_target( ) {
135
+ if let Ok ( deref_ty) = cx. tcx. try_normalize_erasing_regions(
136
+ cx. param_env,
137
+ cx. tcx. mk_projection( target_id, cx. tcx. mk_substs( [ GenericArg :: from( indexed_ty) ] . into_iter( ) ) ) ,
138
+ ) {
139
+ if deref_ty == expr_ty {
140
+ let snip = snippet_with_context( cx, indexed. span, ctxt, ".." , & mut app) . 0 ;
141
+ let sugg = if needs_parens_for_prefix {
142
+ format!( "(&{}{}*{})" , mutability. prefix_str( ) , "*" . repeat( indexed_ref_count) , snip)
143
+ } else {
144
+ format!( "&{}{}*{}" , mutability. prefix_str( ) , "*" . repeat( indexed_ref_count) , snip)
145
+ } ;
146
+ ( DEREF_BY_SLICING_LINT , "dereference the original value instead" , sugg)
147
+ } else {
148
+ return ;
149
+ }
150
+ } else {
151
+ return ;
152
+ }
73
153
} else {
74
- ( "" , "use the original value instead" )
154
+ return ;
75
155
} ;
76
156
77
157
span_lint_and_sugg(
78
158
cx,
79
- REDUNDANT_SLICING ,
159
+ lint ,
80
160
expr. span,
81
- "redundant slicing of the whole range" ,
82
- help_str ,
83
- format! ( "{}{}" , reborrow_str , snip ) ,
161
+ msg ,
162
+ help ,
163
+ sugg ,
84
164
app,
85
165
) ;
86
166
}
0 commit comments