Skip to content

Commit b70d082

Browse files
authored
feat: add report logger (#12)
* build(deps): add new dependency to the colored crate Signed-off-by: Luca Georges Francois <luca@quartz.technology> * feat(log): replace metadata by a reporter able to log Signed-off-by: Luca Georges Francois <luca@quartz.technology> --------- Signed-off-by: Luca Georges Francois <luca@quartz.technology>
1 parent 9669999 commit b70d082

File tree

11 files changed

+196
-79
lines changed

11 files changed

+196
-79
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ clap = { version = "4", features = ["derive"] }
1717
syn-solidity = "0.3.0"
1818
syn = { version = "2.0.26", features = ["full"] }
1919
proc-macro2 = { version = "1.0.65", features = ["span-locations"]}
20+
colored = "2.0.4"

src/commands/scan.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clap::Parser;
33
use std::fs;
44
use syn_solidity::{File, Item};
55

6-
use crate::scanners::{memory::Metadata, Registry};
6+
use crate::scanners::{result::Reporter, Registry};
77

88
#[derive(Debug, Parser)]
99
pub struct Command {
@@ -19,13 +19,16 @@ impl Command {
1919
let ast_root: File =
2020
syn::parse_str(&code).with_context(|| "Failed to parse code into AST".to_string())?;
2121
let ast_items: &[Item] = &ast_root.items;
22-
let metadata = Metadata::new(file_path);
2322
let scanners_registry = Registry::default();
23+
let mut reporter = Reporter::default();
2424

2525
scanners_registry
2626
.get_scanners()
2727
.iter()
28-
.for_each(|s| s.execute(ast_items, &metadata));
28+
.for_each(|s| s.execute(ast_items, &mut reporter));
29+
30+
reporter.log(file_path);
31+
2932
Ok(())
3033
}
3134
}

src/scanners/implementations/missing_comments.rs

Lines changed: 73 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,32 @@ use syn_solidity::{
22
Item, ItemContract, ItemEnum, ItemError, ItemEvent, ItemFunction, ItemStruct, ItemUdt,
33
};
44

5-
use crate::scanners::{memory::Metadata, Scanner};
5+
use crate::scanners::{
6+
result::{Reporter, Severity},
7+
Scanner,
8+
};
69

710
/// A scanner responsible for looking at various Solidity items and reporting if documentation (comments) is missing.
811
#[derive(Default)]
912
pub struct MissingComments {}
1013

1114
impl MissingComments {
1215
/// Scan every item in the provided contract object and looks for missing documentation.
13-
fn scan_in_contract(&self, contract: &ItemContract, metadata: &Metadata) {
16+
fn scan_in_contract(&self, contract: &ItemContract, reporter: &mut Reporter) {
1417
for item in &contract.body {
1518
match item {
1619
Item::Enum(enumeration) => {
17-
self.check_missing_comments_for_enum(enumeration, metadata)
20+
self.check_missing_comments_for_enum(enumeration, reporter)
1821
}
19-
Item::Error(error) => self.check_missing_comments_for_error(error, metadata),
20-
Item::Event(event) => self.check_missing_comments_for_event(event, metadata),
22+
Item::Error(error) => self.check_missing_comments_for_error(error, reporter),
23+
Item::Event(event) => self.check_missing_comments_for_event(event, reporter),
2124
Item::Function(function) => {
22-
self.check_missing_comments_for_function(function, metadata)
25+
self.check_missing_comments_for_function(function, reporter)
2326
}
2427
Item::Struct(structure) => {
25-
self.check_missing_comments_for_structure(structure, metadata)
28+
self.check_missing_comments_for_structure(structure, reporter)
2629
}
27-
Item::Udt(udt) => self.check_missing_comments_for_udt(udt, metadata),
30+
Item::Udt(udt) => self.check_missing_comments_for_udt(udt, reporter),
2831
/* Contracts can not be declared inside other contracts */
2932
/* Import directives don't have attributes. */
3033
/* Pragma directives don't have attributes. */
@@ -44,14 +47,20 @@ impl MissingComments {
4447
/// */
4548
/// contract SimpleContract {}
4649
/// ```
47-
fn check_missing_comments_for_contract(&self, contract: &ItemContract, metadata: &Metadata) {
50+
fn check_missing_comments_for_contract(
51+
&self,
52+
contract: &ItemContract,
53+
reporter: &mut Reporter,
54+
) {
4855
if contract.attrs.is_empty() {
4956
let line = contract.span().start().line;
5057
let column = contract.span().start().column;
5158

52-
println!(
53-
"{:}:{:}:{:} - Missing comment on contract definition",
54-
metadata.file_path, line, column
59+
reporter.report(
60+
line,
61+
column,
62+
Severity::Warning,
63+
"Missing comment on contract definition",
5564
)
5665
}
5766
}
@@ -68,14 +77,16 @@ impl MissingComments {
6877
/// Agate,
6978
/// }
7079
/// ```
71-
fn check_missing_comments_for_enum(&self, enumeration: &ItemEnum, metadata: &Metadata) {
80+
fn check_missing_comments_for_enum(&self, enumeration: &ItemEnum, reporter: &mut Reporter) {
7281
if enumeration.attrs.is_empty() {
7382
let line = enumeration.span().start().line;
7483
let column = enumeration.span().start().column;
7584

76-
println!(
77-
"{:}:{:}:{:} - Missing comment on enum definition",
78-
metadata.file_path, line, column
85+
reporter.report(
86+
line,
87+
column,
88+
Severity::Warning,
89+
"Missing comment on enum definition",
7990
)
8091
}
8192
}
@@ -87,14 +98,16 @@ impl MissingComments {
8798
/// /// @dev Emitted when a new Quartz has been mined!
8899
/// event QuartzMined(QuartzType indexed variety);
89100
/// ```
90-
fn check_missing_comments_for_event(&self, event: &ItemEvent, metadata: &Metadata) {
101+
fn check_missing_comments_for_event(&self, event: &ItemEvent, reporter: &mut Reporter) {
91102
if event.attrs.is_empty() {
92103
let line = event.span().start().line;
93104
let column = event.span().start().column;
94105

95-
println!(
96-
"{:}:{:}:{:} - Missing comment on event definition",
97-
metadata.file_path, line, column
106+
reporter.report(
107+
line,
108+
column,
109+
Severity::Warning,
110+
"Missing comment on event definition",
98111
)
99112
}
100113
}
@@ -106,14 +119,16 @@ impl MissingComments {
106119
/// /// @dev Your favorite stone was broken :(
107120
/// error BrokenQuartz();
108121
/// ```
109-
fn check_missing_comments_for_error(&self, error: &ItemError, metadata: &Metadata) {
122+
fn check_missing_comments_for_error(&self, error: &ItemError, reporter: &mut Reporter) {
110123
if error.attrs.is_empty() {
111124
let line = error.span().start().line;
112125
let column = error.span().start().column;
113126

114-
println!(
115-
"{:}:{:}:{:} - Missing comment on error definition",
116-
metadata.file_path, line, column
127+
reporter.report(
128+
line,
129+
column,
130+
Severity::Warning,
131+
"Missing comment on error definition",
117132
)
118133
}
119134
}
@@ -127,14 +142,20 @@ impl MissingComments {
127142
/// return true;
128143
/// }
129144
/// ```
130-
fn check_missing_comments_for_function(&self, function: &ItemFunction, metadata: &Metadata) {
145+
fn check_missing_comments_for_function(
146+
&self,
147+
function: &ItemFunction,
148+
reporter: &mut Reporter,
149+
) {
131150
if function.attrs.is_empty() {
132151
let line = function.span().start().line;
133152
let column = function.span().start().column;
134153

135-
println!(
136-
"{:}:{:}:{:} - Missing comment for function definition",
137-
metadata.file_path, line, column
154+
reporter.report(
155+
line,
156+
column,
157+
Severity::Warning,
158+
"Missing comment for function definition",
138159
)
139160
}
140161
}
@@ -148,14 +169,20 @@ impl MissingComments {
148169
/// QuartzType variety;
149170
/// }
150171
/// ```
151-
fn check_missing_comments_for_structure(&self, structure: &ItemStruct, metadata: &Metadata) {
172+
fn check_missing_comments_for_structure(
173+
&self,
174+
structure: &ItemStruct,
175+
reporter: &mut Reporter,
176+
) {
152177
if structure.attrs.is_empty() {
153178
let line = structure.span().start().line;
154179
let column = structure.span().start().column;
155180

156-
println!(
157-
"{:}:{:}:{:} - Missing comment for structure definition",
158-
metadata.file_path, line, column
181+
reporter.report(
182+
line,
183+
column,
184+
Severity::Warning,
185+
"Missing comment for structure definition",
159186
)
160187
}
161188
}
@@ -167,39 +194,41 @@ impl MissingComments {
167194
/// /// @dev Got this one from OpenZeppelin, I ran out of Quartz references.
168195
/// type ShortString is bytes32;
169196
/// ```
170-
fn check_missing_comments_for_udt(&self, udt: &ItemUdt, metadata: &Metadata) {
197+
fn check_missing_comments_for_udt(&self, udt: &ItemUdt, reporter: &mut Reporter) {
171198
if udt.attrs.is_empty() {
172199
let line = udt.span().start().line;
173200
let column = udt.span().start().column;
174201

175-
println!(
176-
"{:}:{:}:{:} - Missing comment for user-defined type definition",
177-
metadata.file_path, line, column
202+
reporter.report(
203+
line,
204+
column,
205+
Severity::Warning,
206+
"Missing comment for user-defined type definition",
178207
)
179208
}
180209
}
181210
}
182211

183212
impl Scanner for MissingComments {
184213
/// Scans every root item (recursively if there is a contract) and reports missing documentation.
185-
fn execute(&self, ast: &[Item], metadata: &Metadata) {
214+
fn execute(&self, ast: &[Item], reporter: &mut Reporter) {
186215
for item in ast {
187216
match item {
188217
Item::Contract(contract) => {
189-
self.check_missing_comments_for_contract(contract, metadata);
190-
self.scan_in_contract(contract, metadata)
218+
self.check_missing_comments_for_contract(contract, reporter);
219+
self.scan_in_contract(contract, reporter)
191220
}
192221
Item::Enum(enumeration) => {
193-
self.check_missing_comments_for_enum(enumeration, metadata)
222+
self.check_missing_comments_for_enum(enumeration, reporter)
194223
}
195-
Item::Error(error) => self.check_missing_comments_for_error(error, metadata),
224+
Item::Error(error) => self.check_missing_comments_for_error(error, reporter),
196225
Item::Function(function) => {
197-
self.check_missing_comments_for_function(function, metadata)
226+
self.check_missing_comments_for_function(function, reporter)
198227
}
199228
Item::Struct(structure) => {
200-
self.check_missing_comments_for_structure(structure, metadata)
229+
self.check_missing_comments_for_structure(structure, reporter)
201230
}
202-
Item::Udt(udt) => self.check_missing_comments_for_udt(udt, metadata),
231+
Item::Udt(udt) => self.check_missing_comments_for_udt(udt, reporter),
203232
/* Events can not be declared at the root level. */
204233
/* Import directives don't have attributes. */
205234
/* Pragma directives don't have attributes. */

src/scanners/implementations/mutable_functions.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
use crate::scanners::{memory::Metadata, Scanner};
1+
use crate::scanners::{
2+
result::{Reporter, Severity},
3+
Scanner,
4+
};
25
use syn_solidity::{FunctionAttribute, Item, ItemContract, ItemFunction, Mutability};
36

47
#[derive(Default)]
58
pub struct MutableFunctions {}
69

710
impl MutableFunctions {
811
/// Scans all functions from the contract while discarding the other items.
9-
fn scan_contract(&self, contract: &ItemContract, metadata: &Metadata) {
12+
fn scan_contract(&self, contract: &ItemContract, reporter: &mut Reporter) {
1013
for item in &contract.body {
1114
if let Item::Function(function) = item {
12-
self.scan_function(function, metadata)
15+
self.scan_function(function, reporter)
1316
}
1417
}
1518
}
1619

1720
/// Reports if a function is able to mutate the contract state.
18-
fn scan_function(&self, function: &ItemFunction, metadata: &Metadata) {
21+
fn scan_function(&self, function: &ItemFunction, reporter: &mut Reporter) {
1922
// TODO: There is probably a cleaner way to check this.
2023
if function.kind.as_str() == "modifier" {
2124
return;
@@ -36,20 +39,22 @@ impl MutableFunctions {
3639
let line = function.span().start().line;
3740
let column = function.span().start().column;
3841

39-
println!(
40-
"{:}:{:}:{:} - Function can mutate contract state",
41-
metadata.file_path, line, column
42+
reporter.report(
43+
line,
44+
column,
45+
Severity::Info,
46+
"Function can mutate contract state",
4247
)
4348
}
4449
}
4550

4651
impl Scanner for MutableFunctions {
4752
/// Scans every contract and reports functions able to mutate the storage state.
48-
fn execute(&self, ast: &[Item], metadata: &Metadata) {
53+
fn execute(&self, ast: &[Item], reporter: &mut Reporter) {
4954
for item in ast {
5055
// Mutable functions are only located inside contracts.
5156
if let Item::Contract(contract) = item {
52-
self.scan_contract(contract, metadata)
57+
self.scan_contract(contract, reporter)
5358
}
5459
}
5560
}

src/scanners/implementations/mutable_variables.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
1-
use crate::scanners::{memory::Metadata, Scanner};
1+
use crate::scanners::{
2+
result::{Reporter, Severity},
3+
Scanner,
4+
};
25
use syn_solidity::{Item, ItemContract, VariableAttribute, VariableDefinition};
36

47
#[derive(Default)]
58
pub struct MutableVariables {}
69

710
impl MutableVariables {
811
/// Scans all variables from the contract while discarding the other items.
9-
fn scan_contract(&self, contract: &ItemContract, metadata: &Metadata) {
12+
fn scan_contract(&self, contract: &ItemContract, reporter: &mut Reporter) {
1013
for item in &contract.body {
1114
if let Item::Variable(variable) = item {
12-
self.scan_variable(variable, metadata)
15+
self.scan_variable(variable, reporter)
1316
}
1417
}
1518
}
1619

1720
/// Reports if a variable is likely to mutate.
18-
fn scan_variable(&self, variable: &VariableDefinition, metadata: &Metadata) {
21+
fn scan_variable(&self, variable: &VariableDefinition, reporter: &mut Reporter) {
1922
let immutable_variable_attributes = [
2023
&VariableAttribute::Constant(Default::default()),
2124
&VariableAttribute::Immutable(Default::default()),
@@ -30,19 +33,21 @@ impl MutableVariables {
3033
let line = variable.span().start().line;
3134
let column = variable.span().start().column;
3235

33-
println!(
34-
"{:}:{:}:{:} - Variable state can be changed",
35-
metadata.file_path, line, column
36+
reporter.report(
37+
line,
38+
column,
39+
Severity::Info,
40+
"Variable state can be changed",
3641
)
3742
}
3843
}
3944

4045
impl Scanner for MutableVariables {
4146
/// Scans every contract and reports variables able to mutate.
42-
fn execute(&self, ast: &[Item], metadata: &Metadata) {
47+
fn execute(&self, ast: &[Item], reporter: &mut Reporter) {
4348
for item in ast {
4449
if let Item::Contract(contract) = item {
45-
self.scan_contract(contract, metadata)
50+
self.scan_contract(contract, reporter)
4651
}
4752
}
4853
}
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use syn_solidity::Item;
22

3-
use crate::scanners::{memory::Metadata, Scanner};
3+
use crate::scanners::{result::Reporter, Scanner};
44

55
#[derive(Default)]
66
pub struct MutationGrapher {}
77

88
impl MutationGrapher {}
99

1010
impl Scanner for MutationGrapher {
11-
fn execute(&self, _ast: &[Item], _metadata: &Metadata) {
11+
fn execute(&self, _ast: &[Item], _reporter: &mut Reporter) {
12+
/*
1213
println!("todo!")
14+
*/
1315
}
1416
}

0 commit comments

Comments
 (0)