11use clippy_utils:: diagnostics:: span_lint_and_sugg;
22use clippy_utils:: get_parent_expr;
33use 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 } ;
55use if_chain:: if_chain;
6+ use rustc_ast:: util:: parser:: PREC_PREFIX ;
67use rustc_errors:: Applicability ;
78use 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 ;
912use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1013
1114declare_clippy_lint ! {
@@ -39,7 +42,34 @@ declare_clippy_lint! {
3942 "redundant slicing of the whole range of a type"
4043}
4144
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" ) ;
4373
4474impl < ' tcx > LateLintPass < ' tcx > for RedundantSlicing {
4575 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > ) {
@@ -53,34 +83,84 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
5383 if addressee. span. ctxt( ) == ctxt;
5484 if let ExprKind :: Index ( indexed, range) = addressee. kind;
5585 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) ;
5786 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+ } ) ;
5893 let mut app = Applicability :: MachineApplicable ;
59- let snip = snippet_with_context( cx, indexed. span, ctxt, ".." , & mut app) . 0 ;
6094
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+ }
73153 } else {
74- ( "" , "use the original value instead" )
154+ return ;
75155 } ;
76156
77157 span_lint_and_sugg(
78158 cx,
79- REDUNDANT_SLICING ,
159+ lint ,
80160 expr. span,
81- "redundant slicing of the whole range" ,
82- help_str ,
83- format! ( "{}{}" , reborrow_str , snip ) ,
161+ msg ,
162+ help ,
163+ sugg ,
84164 app,
85165 ) ;
86166 }
0 commit comments