@@ -5,28 +5,28 @@ extern crate rustc_ast;
5
5
extern crate rustc_hir;
6
6
extern crate rustc_span;
7
7
8
+ use std:: collections:: { HashMap , VecDeque } ;
9
+ use std:: default:: Default ;
10
+
8
11
use rustc_ast:: {
9
12
token:: { Delimiter , Token , TokenKind } ,
10
13
tokenstream:: { CursorRef , DelimSpan , TokenStream , TokenTree , TreeAndSpacing } ,
11
14
AttrKind , Attribute , MacArgs ,
12
15
} ;
13
- use rustc_hir:: def:: Res ;
14
- use rustc_hir:: * ;
16
+ use rustc_hir:: { def:: Res , FieldDef , GenericArg , QPath , TyKind , VariantData } ;
15
17
use rustc_lint:: { LateContext , LateLintPass } ;
16
18
use rustc_span:: {
17
19
def_id:: DefId ,
18
20
symbol:: { Ident , Symbol } ,
19
21
Span , DUMMY_SP ,
20
22
} ;
21
- use std:: collections:: { HashMap , VecDeque } ;
22
- use std:: default:: Default ;
23
-
24
-
25
23
26
24
use clippy_utils:: { diagnostics:: span_lint_and_help, ty:: match_type} ;
27
25
use if_chain:: if_chain;
28
26
use solana_lints:: paths;
29
27
28
+ const ANCHOR_ACCOUNT_GENERIC_ARG_COUNT : usize = 2 ;
29
+
30
30
dylint_linting:: impl_late_lint! {
31
31
/// **What it does:**
32
32
///
@@ -56,42 +56,14 @@ struct DuplicateMutableAccounts {
56
56
}
57
57
58
58
impl < ' tcx > LateLintPass < ' tcx > for DuplicateMutableAccounts {
59
- // fn check_mod(
60
- // &mut self,
61
- // cx: &LateContext<'tcx>,
62
- // _: &'tcx Mod<'tcx>,
63
- // span: Span,
64
- // _: HirId
65
- // ) {
66
- // println!("new");
67
- // for _ in 0..3 {
68
- // println!("linting");
69
- // span_lint_and_help(
70
- // cx,
71
- // DUPLICATE_MUTABLE_ACCOUNTS,
72
- // span,
73
- // "dummy",
74
- // None,
75
- // ""
76
- // );
77
- // }
78
- // }
79
-
80
59
fn check_struct_def ( & mut self , cx : & LateContext < ' tcx > , variant_data : & ' tcx VariantData < ' tcx > ) {
81
60
if let VariantData :: Struct ( fields, _) = variant_data {
82
61
fields. iter ( ) . for_each ( |field| {
83
62
if_chain ! {
84
- // grab the def_id of the field type
85
- let ty = field. ty;
86
- if let TyKind :: Path ( qpath) = & ty. kind;
87
- if let QPath :: Resolved ( _, path) = qpath;
88
- if let Res :: Def ( _, def_id) = path. res;
89
- // match the type of the field
90
- let ty = cx. tcx. type_of( def_id) ;
91
- // check it is an anchor account type
92
- if match_type( cx, ty, & paths:: ANCHOR_ACCOUNT ) ;
93
- // check the type of T, the second generic arg
94
- let account_id = get_anchor_account_type( & path. segments[ 0 ] ) . unwrap( ) ;
63
+ if let Some ( def_id) = get_def_id( field. ty) ;
64
+ let middle_ty = cx. tcx. type_of( def_id) ;
65
+ if match_type( cx, middle_ty, & paths:: ANCHOR_ACCOUNT ) ;
66
+ if let Some ( account_id) = get_anchor_account_type_def_id( field) ;
95
67
then {
96
68
if let Some ( v) = self . accounts. get_mut( & account_id) {
97
69
v. push( ( field. ident. name, field. span) ) ;
@@ -105,37 +77,29 @@ impl<'tcx> LateLintPass<'tcx> for DuplicateMutableAccounts {
105
77
}
106
78
107
79
fn check_attribute ( & mut self , _: & LateContext < ' tcx > , attribute : & ' tcx Attribute ) {
108
- // println!("{:#?}", self.accounts);
109
80
if_chain ! {
110
81
if let AttrKind :: Normal ( attr_item, _) = & attribute. kind;
111
- let name = attr_item. path. segments[ 0 ] . ident. name; // TODO: can use name_or_empty
112
- // for some reason #[account] doesn't match when no args, maybe take away
113
- // the code to check name, and just check it has constraint args?
82
+ let name = attribute. name_or_empty( ) ;
114
83
if name. as_str( ) == "account" ;
115
84
if let MacArgs :: Delimited ( _, _, token_stream) = & attr_item. args;
116
85
then {
117
- for split in split ( token_stream . trees ( ) , TokenKind :: Comma ) {
118
- // println!("{:#?}", split);
119
- self . streams. 0 . push( split ) ;
86
+ // Parse each constraint as a separate TokenStream
87
+ for delimited_stream in split ( token_stream . trees ( ) , TokenKind :: Comma ) {
88
+ self . streams. 0 . push( delimited_stream ) ;
120
89
}
121
-
122
90
}
123
91
}
124
92
}
125
93
126
94
fn check_crate_post ( & mut self , cx : & LateContext < ' tcx > ) {
127
95
// println!("{:#?}", self);
128
- for ( _k , v) in self . accounts . iter ( ) {
96
+ for ( _ , v) in self . accounts . iter ( ) {
129
97
if v. len ( ) > 1 {
130
- // generate static set of possible constraints
131
98
let gen_constraints = generate_possible_expected_constraints ( v) ;
132
99
133
100
for ( ( one, symmetric) , symbols) in gen_constraints {
134
- // println!("{:#?}\n {:#?}", one, symmetric);
135
101
if !( self . streams . contains ( one) || self . streams . contains ( symmetric) ) {
136
- println ! ( "lint for {} {}" , symbols. 0 , symbols. 1 ) ;
137
-
138
- // stupid way to get spans for offending types
102
+ // get spans for offending types
139
103
let mut spans: Vec < Span > = Vec :: new ( ) ;
140
104
for ( sym, span) in v {
141
105
if & symbols. 0 == sym || & symbols. 1 == sym {
@@ -144,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for DuplicateMutableAccounts {
144
108
}
145
109
146
110
// TODO: for some reason, will only print out 2 messages, not 3
147
- // println!("{:?}", spans);
148
111
span_lint_and_help (
149
112
cx,
150
113
DUPLICATE_MUTABLE_ACCOUNTS ,
@@ -160,11 +123,27 @@ impl<'tcx> LateLintPass<'tcx> for DuplicateMutableAccounts {
160
123
}
161
124
}
162
125
163
- fn get_anchor_account_type ( segment : & PathSegment < ' _ > ) -> Option < DefId > {
126
+ /// Returns the `DefId` of the anchor account type, ie, `T` in `Account<'info, T>`.
127
+ /// Returns `None` if the type of `field` is not an anchor account.
128
+ fn get_anchor_account_type_def_id ( field : & FieldDef ) -> Option < DefId > {
129
+ if_chain ! {
130
+ if let TyKind :: Path ( qpath) = & field. ty. kind;
131
+ if let QPath :: Resolved ( _, path) = qpath;
132
+ if path. segments. len( ) > 0 ;
133
+ if let Some ( generic_args) = path. segments[ 0 ] . args;
134
+ if generic_args. args. len( ) == ANCHOR_ACCOUNT_GENERIC_ARG_COUNT ;
135
+ if let GenericArg :: Type ( hir_ty) = & generic_args. args[ 1 ] ;
136
+ then {
137
+ get_def_id( hir_ty)
138
+ } else {
139
+ None
140
+ }
141
+ }
142
+ }
143
+
144
+ /// Returns the `DefId` of `ty`, an hir type. Returns `None` if cannot resolve type.
145
+ fn get_def_id ( ty : & rustc_hir:: Ty ) -> Option < DefId > {
164
146
if_chain ! {
165
- // TODO: the following logic to get def_id is a repeated pattern
166
- if let Some ( generic_args) = segment. args;
167
- if let GenericArg :: Type ( ty) = & generic_args. args[ 1 ] ; // the account type is the second generic arg
168
147
if let TyKind :: Path ( qpath) = & ty. kind;
169
148
if let QPath :: Resolved ( _, path) = qpath;
170
149
if let Res :: Def ( _, def_id) = path. res;
@@ -176,8 +155,7 @@ fn get_anchor_account_type(segment: &PathSegment<'_>) -> Option<DefId> {
176
155
}
177
156
}
178
157
179
- // collect elements into a TokenStream until encounter delim then stop collecting. create a new vec.
180
- // continue until reaching end of stream
158
+ /// Splits `stream` into a vector of substreams, separated by `delimiter`.
181
159
fn split ( stream : CursorRef , delimiter : TokenKind ) -> Vec < TokenStream > {
182
160
let mut split_streams: Vec < TokenStream > = Vec :: new ( ) ;
183
161
let mut temp: Vec < TreeAndSpacing > = Vec :: new ( ) ;
@@ -195,12 +173,17 @@ fn split(stream: CursorRef, delimiter: TokenKind) -> Vec<TokenStream> {
195
173
split_streams
196
174
}
197
175
198
- /// Generates a static set of a possible expected key check constraints necessary for `values`.
176
+ /// Generates a static set of possible expected key check constraints necessary for `identical_types`.
177
+ /// `identical_types` is a set of identical types that must have a key check constraint. A vector of tuples
178
+ /// is returned, where each element represents a particular constraint.
179
+ ///
180
+ /// Specifically, the first field of the tuple is a tuple of `TokenStream`s, which represent the constraint and its
181
+ /// symmetric value, e.g., `a!=b` and `b!=a`. The second field of the tuple is a tuple of symbols, which
182
+ /// represent the identifiers being compared. Following the previous example, this would be `(a, b)`.
199
183
fn generate_possible_expected_constraints (
200
- values : & Vec < ( Symbol , Span ) > ,
184
+ identical_types : & Vec < ( Symbol , Span ) > ,
201
185
) -> Vec < ( ( TokenStream , TokenStream ) , ( Symbol , Symbol ) ) > {
202
- // TODO: may start with a VecDeque in the first place?
203
- let mut deq = VecDeque :: from ( values. clone ( ) ) ;
186
+ let mut deq = VecDeque :: from ( identical_types. clone ( ) ) ;
204
187
let mut gen_set = Vec :: new ( ) ;
205
188
206
189
for _ in 0 ..deq. len ( ) - 1 {
@@ -209,32 +192,31 @@ fn generate_possible_expected_constraints(
209
192
for ( other, _) in & deq {
210
193
let stream = create_key_check_constraint_tokenstream ( & first, other) ;
211
194
let symmetric_stream = create_key_check_constraint_tokenstream ( other, & first) ;
212
- // println!("{:#?}", stream);
213
-
214
195
gen_set. push ( ( ( stream, symmetric_stream) , ( first, other. clone ( ) ) ) ) ;
215
196
}
216
197
}
217
198
gen_set
218
199
}
219
200
220
- // TODO: figure out more efficient way to do this
201
+ /// Returns a `TokenStream` of form: constraint = `a`.key() != `b`.key().
221
202
fn create_key_check_constraint_tokenstream ( a : & Symbol , b : & Symbol ) -> TokenStream {
203
+ // TODO: may be more efficient way to do this, since the stream is effectively fixed
204
+ // and determined. Only two tokens are variable.
222
205
let constraint = vec ! [
223
- // TODO: test string matching by changing some string
224
- TreeAndSpacing :: from( create_token( "constraint" ) ) ,
206
+ TreeAndSpacing :: from( create_token_from_ident( "constraint" ) ) ,
225
207
TreeAndSpacing :: from( TokenTree :: Token ( Token :: new( TokenKind :: Eq , DUMMY_SP ) ) ) ,
226
- TreeAndSpacing :: from( create_token ( a. as_str( ) ) ) ,
208
+ TreeAndSpacing :: from( create_token_from_ident ( a. as_str( ) ) ) ,
227
209
TreeAndSpacing :: from( TokenTree :: Token ( Token :: new( TokenKind :: Dot , DUMMY_SP ) ) ) ,
228
- TreeAndSpacing :: from( create_token ( "key" ) ) ,
210
+ TreeAndSpacing :: from( create_token_from_ident ( "key" ) ) ,
229
211
TreeAndSpacing :: from( TokenTree :: Delimited (
230
212
DelimSpan :: dummy( ) ,
231
213
Delimiter :: Parenthesis ,
232
214
TokenStream :: new( vec![ ] ) ,
233
215
) ) ,
234
216
TreeAndSpacing :: from( TokenTree :: Token ( Token :: new( TokenKind :: Ne , DUMMY_SP ) ) ) ,
235
- TreeAndSpacing :: from( create_token ( b. as_str( ) ) ) ,
217
+ TreeAndSpacing :: from( create_token_from_ident ( b. as_str( ) ) ) ,
236
218
TreeAndSpacing :: from( TokenTree :: Token ( Token :: new( TokenKind :: Dot , DUMMY_SP ) ) ) ,
237
- TreeAndSpacing :: from( create_token ( "key" ) ) ,
219
+ TreeAndSpacing :: from( create_token_from_ident ( "key" ) ) ,
238
220
TreeAndSpacing :: from( TokenTree :: Delimited (
239
221
DelimSpan :: dummy( ) ,
240
222
Delimiter :: Parenthesis ,
@@ -245,7 +227,8 @@ fn create_key_check_constraint_tokenstream(a: &Symbol, b: &Symbol) -> TokenStrea
245
227
TokenStream :: new ( constraint)
246
228
}
247
229
248
- fn create_token ( s : & str ) -> TokenTree {
230
+ /// Returns a `TokenTree::Token` which has `TokenKind::Ident`, with the string set to `s`.
231
+ fn create_token_from_ident ( s : & str ) -> TokenTree {
249
232
let ident = Ident :: from_str ( s) ;
250
233
TokenTree :: Token ( Token :: from_ast_ident ( ident) )
251
234
}
@@ -254,6 +237,8 @@ fn create_token(s: &str) -> TokenTree {
254
237
pub struct Streams ( Vec < TokenStream > ) ;
255
238
256
239
impl Streams {
240
+ /// Returns true if `self` contains `other`, by comparing if there is an
241
+ /// identical `TokenStream` in `self` regardless of span.
257
242
fn contains ( & self , other : TokenStream ) -> bool {
258
243
self . 0 . iter ( ) . any ( |stream| stream. eq_unspanned ( & other) )
259
244
}
0 commit comments