Skip to content

Commit 1e33bd5

Browse files
committed
clean up code and add rustdocs
1 parent bc4a212 commit 1e33bd5

File tree

1 file changed

+58
-73
lines changed
  • lints/duplicate-mutable-accounts/src

1 file changed

+58
-73
lines changed

lints/duplicate-mutable-accounts/src/lib.rs

Lines changed: 58 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,28 @@ extern crate rustc_ast;
55
extern crate rustc_hir;
66
extern crate rustc_span;
77

8+
use std::collections::{HashMap, VecDeque};
9+
use std::default::Default;
10+
811
use rustc_ast::{
912
token::{Delimiter, Token, TokenKind},
1013
tokenstream::{CursorRef, DelimSpan, TokenStream, TokenTree, TreeAndSpacing},
1114
AttrKind, Attribute, MacArgs,
1215
};
13-
use rustc_hir::def::Res;
14-
use rustc_hir::*;
16+
use rustc_hir::{def::Res, FieldDef, GenericArg, QPath, TyKind, VariantData};
1517
use rustc_lint::{LateContext, LateLintPass};
1618
use rustc_span::{
1719
def_id::DefId,
1820
symbol::{Ident, Symbol},
1921
Span, DUMMY_SP,
2022
};
21-
use std::collections::{HashMap, VecDeque};
22-
use std::default::Default;
23-
24-
2523

2624
use clippy_utils::{diagnostics::span_lint_and_help, ty::match_type};
2725
use if_chain::if_chain;
2826
use solana_lints::paths;
2927

28+
const ANCHOR_ACCOUNT_GENERIC_ARG_COUNT: usize = 2;
29+
3030
dylint_linting::impl_late_lint! {
3131
/// **What it does:**
3232
///
@@ -56,42 +56,14 @@ struct DuplicateMutableAccounts {
5656
}
5757

5858
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-
8059
fn check_struct_def(&mut self, cx: &LateContext<'tcx>, variant_data: &'tcx VariantData<'tcx>) {
8160
if let VariantData::Struct(fields, _) = variant_data {
8261
fields.iter().for_each(|field| {
8362
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);
9567
then {
9668
if let Some(v) = self.accounts.get_mut(&account_id) {
9769
v.push((field.ident.name, field.span));
@@ -105,37 +77,29 @@ impl<'tcx> LateLintPass<'tcx> for DuplicateMutableAccounts {
10577
}
10678

10779
fn check_attribute(&mut self, _: &LateContext<'tcx>, attribute: &'tcx Attribute) {
108-
// println!("{:#?}", self.accounts);
10980
if_chain! {
11081
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();
11483
if name.as_str() == "account";
11584
if let MacArgs::Delimited(_, _, token_stream) = &attr_item.args;
11685
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);
12089
}
121-
12290
}
12391
}
12492
}
12593

12694
fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
12795
// println!("{:#?}", self);
128-
for (_k, v) in self.accounts.iter() {
96+
for (_, v) in self.accounts.iter() {
12997
if v.len() > 1 {
130-
// generate static set of possible constraints
13198
let gen_constraints = generate_possible_expected_constraints(v);
13299

133100
for ((one, symmetric), symbols) in gen_constraints {
134-
// println!("{:#?}\n {:#?}", one, symmetric);
135101
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
139103
let mut spans: Vec<Span> = Vec::new();
140104
for (sym, span) in v {
141105
if &symbols.0 == sym || &symbols.1 == sym {
@@ -144,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for DuplicateMutableAccounts {
144108
}
145109

146110
// TODO: for some reason, will only print out 2 messages, not 3
147-
// println!("{:?}", spans);
148111
span_lint_and_help(
149112
cx,
150113
DUPLICATE_MUTABLE_ACCOUNTS,
@@ -160,11 +123,27 @@ impl<'tcx> LateLintPass<'tcx> for DuplicateMutableAccounts {
160123
}
161124
}
162125

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> {
164146
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
168147
if let TyKind::Path(qpath) = &ty.kind;
169148
if let QPath::Resolved(_, path) = qpath;
170149
if let Res::Def(_, def_id) = path.res;
@@ -176,8 +155,7 @@ fn get_anchor_account_type(segment: &PathSegment<'_>) -> Option<DefId> {
176155
}
177156
}
178157

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`.
181159
fn split(stream: CursorRef, delimiter: TokenKind) -> Vec<TokenStream> {
182160
let mut split_streams: Vec<TokenStream> = Vec::new();
183161
let mut temp: Vec<TreeAndSpacing> = Vec::new();
@@ -195,12 +173,17 @@ fn split(stream: CursorRef, delimiter: TokenKind) -> Vec<TokenStream> {
195173
split_streams
196174
}
197175

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)`.
199183
fn generate_possible_expected_constraints(
200-
values: &Vec<(Symbol, Span)>,
184+
identical_types: &Vec<(Symbol, Span)>,
201185
) -> 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());
204187
let mut gen_set = Vec::new();
205188

206189
for _ in 0..deq.len() - 1 {
@@ -209,32 +192,31 @@ fn generate_possible_expected_constraints(
209192
for (other, _) in &deq {
210193
let stream = create_key_check_constraint_tokenstream(&first, other);
211194
let symmetric_stream = create_key_check_constraint_tokenstream(other, &first);
212-
// println!("{:#?}", stream);
213-
214195
gen_set.push(((stream, symmetric_stream), (first, other.clone())));
215196
}
216197
}
217198
gen_set
218199
}
219200

220-
// TODO: figure out more efficient way to do this
201+
/// Returns a `TokenStream` of form: constraint = `a`.key() != `b`.key().
221202
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.
222205
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")),
225207
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())),
227209
TreeAndSpacing::from(TokenTree::Token(Token::new(TokenKind::Dot, DUMMY_SP))),
228-
TreeAndSpacing::from(create_token("key")),
210+
TreeAndSpacing::from(create_token_from_ident("key")),
229211
TreeAndSpacing::from(TokenTree::Delimited(
230212
DelimSpan::dummy(),
231213
Delimiter::Parenthesis,
232214
TokenStream::new(vec![]),
233215
)),
234216
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())),
236218
TreeAndSpacing::from(TokenTree::Token(Token::new(TokenKind::Dot, DUMMY_SP))),
237-
TreeAndSpacing::from(create_token("key")),
219+
TreeAndSpacing::from(create_token_from_ident("key")),
238220
TreeAndSpacing::from(TokenTree::Delimited(
239221
DelimSpan::dummy(),
240222
Delimiter::Parenthesis,
@@ -245,7 +227,8 @@ fn create_key_check_constraint_tokenstream(a: &Symbol, b: &Symbol) -> TokenStrea
245227
TokenStream::new(constraint)
246228
}
247229

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 {
249232
let ident = Ident::from_str(s);
250233
TokenTree::Token(Token::from_ast_ident(ident))
251234
}
@@ -254,6 +237,8 @@ fn create_token(s: &str) -> TokenTree {
254237
pub struct Streams(Vec<TokenStream>);
255238

256239
impl Streams {
240+
/// Returns true if `self` contains `other`, by comparing if there is an
241+
/// identical `TokenStream` in `self` regardless of span.
257242
fn contains(&self, other: TokenStream) -> bool {
258243
self.0.iter().any(|stream| stream.eq_unspanned(&other))
259244
}

0 commit comments

Comments
 (0)