1
- use rustc:: hir:: Expr ;
2
- use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintContext , LintPass } ;
1
+ use rustc:: hir;
2
+ use rustc:: hir:: { Expr , Local } ;
3
+ use rustc:: lint:: { LateContext , LateLintPass , LintArray , LintPass } ;
3
4
use rustc:: ty;
4
- use utils:: { match_type, span_lint_and_sugg, walk_ptrs_ty} ;
5
- use utils:: paths;
5
+ use crate :: utils:: { match_qpath , match_type, match_type_parameter , snippet , span_lint_and_sugg, walk_ptrs_ty, get_type_parameter } ;
6
+ use crate :: utils:: paths;
6
7
7
8
/// **What it does:**
8
9
/// Checks for usage of `Rc<String>` or `Rc<Vec<T>>`.
9
10
///
10
11
/// **Why is this bad?**
11
- /// Using a `Rc<str>` or `Rc<[T]>` is more efficient and easy to construct with
12
+ /// Using `Rc<str>` or `Rc<[T]>` is more efficient and easy to construct with
12
13
/// `into()`.
13
14
///
14
15
/// **Known problems:**
@@ -23,15 +24,15 @@ use utils::paths;
23
24
/// let bad_ref: Rc<Vec<usize>> = Rc::new(vec!(1, 2, 3));
24
25
///
25
26
/// // Good
26
- /// let good_ref: Rc<[usize]> = Rc::new( vec!(1, 2, 3).into() );
27
+ /// let good_ref: Rc<[usize]> = vec!(1, 2, 3).into();
27
28
/// ```
28
29
declare_clippy_lint ! {
29
30
pub USE_SHARED_FROM_SLICE ,
30
31
nursery,
31
- "use `into()` to construct `Rc` from slice "
32
+ "constructing reference-counted type from `Vec` or `String` "
32
33
}
33
34
34
- #[ derive( Copy , Clone , Debug ) ]
35
+ #[ derive( Copy , Clone ) ]
35
36
pub struct Pass ;
36
37
37
38
impl LintPass for Pass {
@@ -40,31 +41,99 @@ impl LintPass for Pass {
40
41
}
41
42
}
42
43
43
- impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Pass {
44
- fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
45
- let expr_ty = walk_ptrs_ty ( cx. tables . expr_ty ( expr) ) ;
44
+ /// If the given `expr` is constructing an `Rc` or `Arc` containing a `Vec` or
45
+ /// `String`, output a suggestion to fix accordingly.
46
+ fn check_rc_construction < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
47
+ let expr_ty = walk_ptrs_ty ( cx. tables . expr_ty ( expr) ) ;
48
+
49
+ // Check for expressions with the type `Rc<Vec<T>>`.
50
+ if_chain ! {
51
+ // Check if this expression is constructing an `Rc` or `Arc`.
52
+ let is_rc = match_type( cx, expr_ty, & paths:: RC ) ;
53
+ let is_arc = match_type( cx, expr_ty, & paths:: ARC ) ;
54
+ if is_rc || is_arc;
55
+
56
+ // Check if the `Rc` or `Arc` is constructed with `Vec` or `String`.
57
+ if let ty:: TyAdt ( _, subst) = expr_ty. sty;
58
+ let arg_type = subst. type_at( 0 ) ;
59
+ let arg_is_vec = match_type( cx, arg_type, & paths:: VEC ) ;
60
+ let arg_is_string = match_type( cx, arg_type, & paths:: STRING ) ;
61
+ if arg_is_vec || arg_is_string;
62
+
63
+ // Get the argument, to use for writing out the lint message.
64
+ if let hir:: ExprCall ( _, ref args) = expr. node;
65
+ if let Some ( arg) = args. get( 0 ) ;
46
66
47
- // Check for expressions with the type `Rc<Vec<T>>`.
48
- if_chain ! {
49
- if let ty:: TyAdt ( _, subst) = expr_ty. sty;
50
- if match_type( cx, expr_ty, & paths:: RC ) ;
51
- if match_type( cx, subst. type_at( 1 ) , & paths:: VEC ) ;
52
- then {
53
- cx. sess( ) . note_without_error( & format!( "{:?}" , subst) ) ;
54
- span_lint_and_sugg(
55
- cx,
56
- USE_SHARED_FROM_SLICE ,
57
- expr. span,
58
- "constructing reference-counted type from vec" ,
59
- "consider using `into()`" ,
60
- format!( "{}" , "TODO" ) ,
61
- ) ;
67
+ then {
68
+ if arg_is_vec {
69
+ let msg = "avoid constructing reference-counted type from Vec; convert from slice instead" ;
70
+ let help = "use" ;
71
+ let argument = snippet( cx, arg. span. source_callsite( ) , ".." ) ;
72
+ let sugg = format!( "{}.into()" , argument) ;
73
+ span_lint_and_sugg( cx, USE_SHARED_FROM_SLICE , expr. span, & msg, help, sugg) ;
74
+ } else if arg_is_string {
75
+ let msg = "avoid constructing reference-counted type from String; convert from slice instead" ;
76
+ let help = "use" ;
77
+ let argument = snippet( cx, arg. span. source_callsite( ) , ".." ) ;
78
+ let sugg = format!( "{}.as_str().into()" , argument) ;
79
+ span_lint_and_sugg( cx, USE_SHARED_FROM_SLICE , expr. span, & msg, help, sugg) ;
62
80
}
63
81
}
82
+ }
83
+ }
84
+
85
+ /// Check a type declaration to lint, such as in
86
+ ///
87
+ /// let x: Rc<String> = Rc::new(some_string)
88
+ ///
89
+ /// If `ty`, the declared type, is an `Rc` or `Arc` containing a `Vec` or
90
+ /// `String` then output a suggestion to change it.
91
+ fn check_rc_type < ' a , ' tcx > ( cx : & LateContext < ' a , ' tcx > , ty : & hir:: Ty ) {
92
+ match ty. node {
93
+ hir:: TyPath ( ref qpath) => {
94
+ let matches_rc = match_qpath ( qpath, & paths:: RC ) ;
95
+ let matches_arc = match_qpath ( qpath, & paths:: ARC ) ;
96
+ if matches_rc || matches_arc {
97
+ let has_vec = match_type_parameter ( cx, qpath, & paths:: VEC ) ;
98
+ let has_string = match_type_parameter ( cx, qpath, & paths:: STRING ) ;
99
+ // Keep the type for making suggestions later.
100
+ let constructor = if matches_arc { "Arc" } else { "Rc" } ;
101
+ if_chain ! {
102
+ if has_vec;
103
+ // In the case we have something like `Rc<Vec<usize>>`, get the inner parameter
104
+ // type out from the parameter type of the `Rc`; so in this example, get the
105
+ // type `usize`. Use this to suggest using the type `Rc<[usize]>` instead.
106
+ let mut vec_ty = get_type_parameter( qpath) . expect( "" ) ;
107
+ if let hir:: TyPath ( ref vec_qpath) = vec_ty. node;
108
+ if let Some ( param_ty) = get_type_parameter( & vec_qpath) ;
109
+ then {
110
+ let msg = "use slice instead of `Vec` in reference-counted type" ;
111
+ let help = "use" ;
112
+ let sugg = format!( "{}<[{}]>" , constructor, snippet( cx, param_ty. span. source_callsite( ) , ".." ) ) ;
113
+ span_lint_and_sugg( cx, USE_SHARED_FROM_SLICE , ty. span, msg, help, sugg) ;
114
+ }
115
+ }
116
+ if has_string {
117
+ //ty.node = TyPath(hir::Resolved(None, P()))
118
+ let msg = "use slice instead of `String` in reference-counted type" ;
119
+ let help = "use" ;
120
+ let sugg = format ! ( "{}<str>" , constructor) ;
121
+ span_lint_and_sugg ( cx, USE_SHARED_FROM_SLICE , ty. span , msg, help, sugg) ;
122
+ }
123
+ }
124
+ } ,
125
+ _ => { } ,
126
+ }
127
+ }
64
128
65
- // TODO
66
- // Check for expressions with the type `Rc<String>`.
67
- // Check for expressions with the type `Arc<String>`.
68
- // Check for expressions with the type `Arc<Vec<T>>`.
129
+ impl < ' a , ' tcx > LateLintPass < ' a , ' tcx > for Pass {
130
+ fn check_expr ( & mut self , cx : & LateContext < ' a , ' tcx > , expr : & ' tcx Expr ) {
131
+ check_rc_construction ( cx, expr) ;
132
+ }
133
+
134
+ fn check_local ( & mut self , cx : & LateContext < ' a , ' tcx > , local : & Local ) {
135
+ if let Some ( ref ty) = local. ty {
136
+ check_rc_type ( cx, ty) ;
137
+ }
69
138
}
70
139
}
0 commit comments