2
2
#![ warn( unused_extern_crates) ]
3
3
4
4
extern crate rustc_ast;
5
- extern crate rustc_ast_pretty;
6
- extern crate rustc_data_structures;
7
- extern crate rustc_errors;
8
5
extern crate rustc_hir;
9
- extern crate rustc_hir_pretty;
10
- extern crate rustc_index;
11
- extern crate rustc_infer;
12
- extern crate rustc_lexer;
13
- extern crate rustc_middle;
14
- extern crate rustc_mir_dataflow;
15
- extern crate rustc_parse;
16
- extern crate rustc_parse_format;
17
6
extern crate rustc_span;
18
- extern crate rustc_target;
19
- extern crate rustc_trait_selection;
20
- extern crate rustc_typeck;
21
7
22
- use rustc_lint:: LateLintPass ;
8
+ use rustc_ast:: {
9
+ token:: TokenKind ,
10
+ tokenstream:: { TokenStream , TokenTree } ,
11
+ AttrKind , Attribute , MacArgs ,
12
+ } ;
13
+ use rustc_hir:: def:: Res ;
14
+ use rustc_hir:: * ;
15
+ use rustc_lint:: { LateContext , LateLintPass } ;
16
+ use rustc_span:: { def_id:: DefId , symbol:: Symbol , Span } ;
17
+ use std:: collections:: HashMap ;
18
+ use std:: default:: Default ;
23
19
24
- dylint_linting:: declare_late_lint! {
20
+ use clippy_utils:: { diagnostics:: span_lint_and_help, ty:: match_type} ;
21
+ use if_chain:: if_chain;
22
+ use solana_lints:: paths;
23
+
24
+ dylint_linting:: impl_late_lint! {
25
25
/// **What it does:**
26
26
///
27
27
/// **Why is this bad?**
@@ -39,18 +39,134 @@ dylint_linting::declare_late_lint! {
39
39
/// ```
40
40
pub DUPLICATE_MUTABLE_ACCOUNTS ,
41
41
Warn ,
42
- "description goes here"
42
+ "description goes here" ,
43
+ DuplicateMutableAccounts :: default ( )
44
+ }
45
+
46
+ #[ derive( Default , Debug ) ]
47
+ struct DuplicateMutableAccounts {
48
+ accounts : HashMap < DefId , Vec < ( Symbol , Span ) > > ,
49
+ streams : Vec < Stream > ,
43
50
}
44
51
45
52
impl < ' tcx > LateLintPass < ' tcx > for DuplicateMutableAccounts {
46
- // A list of things you might check can be found here:
47
- // https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html
53
+ fn check_struct_def ( & mut self , cx : & LateContext < ' tcx > , variant_data : & ' tcx VariantData < ' tcx > ) {
54
+ if let VariantData :: Struct ( fields, _) = variant_data {
55
+ fields. iter ( ) . for_each ( |field| {
56
+ if_chain ! {
57
+ // grab the def_id of the field type
58
+ let ty = field. ty;
59
+ if let TyKind :: Path ( qpath) = & ty. kind;
60
+ if let QPath :: Resolved ( _, path) = qpath;
61
+ if let Res :: Def ( _, def_id) = path. res;
62
+ // match the type of the field
63
+ let ty = cx. tcx. type_of( def_id) ;
64
+ // check it is an anchor account type
65
+ if match_type( cx, ty, & paths:: ANCHOR_ACCOUNT ) ;
66
+ // check the type of T, the second generic arg
67
+ let account_id = get_anchor_account_type( & path. segments[ 0 ] ) . unwrap( ) ;
68
+ then {
69
+ if let Some ( v) = self . accounts. get_mut( & account_id) {
70
+ v. push( ( field. ident. name, field. span) ) ;
71
+ } else {
72
+ self . accounts. insert( account_id, vec![ ( field. ident. name, field. span) ] ) ;
73
+ }
74
+ }
75
+ }
76
+ } ) ;
77
+ }
78
+ }
79
+
80
+ fn check_attribute ( & mut self , _: & LateContext < ' tcx > , attribute : & ' tcx Attribute ) {
81
+ // println!("{:#?}", self.accounts);
82
+ if_chain ! {
83
+ if let AttrKind :: Normal ( attr_item, _) = & attribute. kind;
84
+ let name = attr_item. path. segments[ 0 ] . ident. name;
85
+ // for some reason #[account] doesn't match when no args, maybe take away
86
+ // the code to check name, and just check it has constraint args?
87
+ if name. as_str( ) == "account" ;
88
+ if let MacArgs :: Delimited ( _, _, token_stream) = & attr_item. args;
89
+ then {
90
+ self . streams. push( Stream ( token_stream. clone( ) ) ) ;
91
+ // println!("{:#?}", attribute);
92
+ }
93
+ }
94
+ }
95
+
96
+ fn check_crate_post ( & mut self , cx : & LateContext < ' tcx > ) {
97
+ // println!("{:#?}", self);
98
+ for ( _k, v) in self . accounts . iter ( ) {
99
+ // if multiple same accounts, check that there is a tokenstream such that
100
+ // all necessary expressions are in it: x.key(), y.key(), !=
101
+ // where x and y are the field name from self.accounts
102
+ if v. len ( ) > 1 {
103
+ if !has_satisfying_stream ( & self . streams , v) {
104
+ span_lint_and_help (
105
+ cx,
106
+ DUPLICATE_MUTABLE_ACCOUNTS ,
107
+ v[ 0 ] . 1 ,
108
+ "identical account types" ,
109
+ Some ( v[ 1 ] . 1 ) ,
110
+ & format ! ( "add an anchor key check constraint: #[account(constraint = {}.key() != {}.key())]" , v[ 0 ] . 0 , v[ 1 ] . 0 )
111
+ ) ;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ }
117
+
118
+ fn get_anchor_account_type ( segment : & PathSegment < ' _ > ) -> Option < DefId > {
119
+ if_chain ! {
120
+ if let Some ( generic_args) = segment. args;
121
+ if let GenericArg :: Type ( ty) = & generic_args. args[ 1 ] ; // the account type is the second generic arg
122
+ if let TyKind :: Path ( qpath) = & ty. kind;
123
+ if let QPath :: Resolved ( _, path) = qpath;
124
+ if let Res :: Def ( _, def_id) = path. res;
125
+ then {
126
+ Some ( def_id)
127
+ } else {
128
+ None
129
+ }
130
+ }
131
+ }
132
+
133
+ fn has_satisfying_stream ( streams : & Vec < Stream > , field_names : & Vec < ( Symbol , Span ) > ) -> bool {
134
+ for stream in streams {
135
+ if stream. contains ( TokenKind :: Ne )
136
+ && field_names
137
+ . iter ( )
138
+ // TODO: if true, will not match. figure out what the bool signifies
139
+ . all ( |( sym, _) | stream. contains ( TokenKind :: Ident ( * sym, false ) ) )
140
+ {
141
+ return true ;
142
+ }
143
+ }
144
+ return false ;
145
+ }
146
+
147
+ #[ derive( Debug ) ]
148
+ pub struct Stream ( TokenStream ) ;
149
+
150
+ impl Stream {
151
+ fn contains ( & self , x : TokenKind ) -> bool {
152
+ for token_tree in self . 0 . trees ( ) {
153
+ if let TokenTree :: Token ( t) = token_tree {
154
+ if t. kind == x {
155
+ // println!("The type {:?} matches {:?}", t.kind, x);
156
+ return true ;
157
+ }
158
+ }
159
+ }
160
+ return false ;
161
+ }
162
+ }
163
+
164
+ #[ test]
165
+ fn insecure ( ) {
166
+ dylint_testing:: ui_test_example ( env ! ( "CARGO_PKG_NAME" ) , "insecure" ) ;
48
167
}
49
168
50
169
#[ test]
51
- fn ui ( ) {
52
- dylint_testing:: ui_test (
53
- env ! ( "CARGO_PKG_NAME" ) ,
54
- & std:: path:: Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) ) . join ( "ui" ) ,
55
- ) ;
170
+ fn secure ( ) {
171
+ dylint_testing:: ui_test_example ( env ! ( "CARGO_PKG_NAME" ) , "secure" ) ;
56
172
}
0 commit comments