solidhunter_lib/rules/best_practises/
function_max_lines.rs1use osmium_libs_solidity_ast_extractor::{FunctionBody, Spanned};
2
3use crate::linter::SolidFile;
4use crate::rules::types::*;
5use crate::types::*;
6
7pub const RULE_ID: &str = "function-max-lines";
9
10const DEFAULT_SEVERITY: Severity = Severity::WARNING;
12const DEFAULT_MAX_LINES: usize = 50;
13
14pub struct FunctionMaxLines {
15 number_max_lines: usize,
16 data: RuleEntry,
17}
18
19impl RuleType for FunctionMaxLines {
20 fn diagnose(&self, _file: &SolidFile, _files: &[SolidFile]) -> Vec<LintDiag> {
21 let mut res = Vec::new();
22
23 for contract in
24 osmium_libs_solidity_ast_extractor::retriever::retrieve_contract_nodes(&_file.data)
25 {
26 for function in
27 osmium_libs_solidity_ast_extractor::retriever::retrieve_functions_nodes(&contract)
28 {
29 let report = check_function_lines(&function, self.number_max_lines);
30 if let Some(report) = report {
31 let start = report.start.line;
32 let end = report.end.line;
33
34 res.push(LintDiag {
35 id: RULE_ID.to_string(),
36 range: report,
37 severity: self.data.severity,
38 code: None,
39 source: None,
40 message: format!(
41 "Function body contains {} lines but allowed no more than {} lines",
42 end - start,
43 self.number_max_lines
44 ),
45 uri: _file.path.clone(),
46 });
47 }
48 }
49 }
50 res
51 }
52}
53
54fn check_function_lines(
56 function: &osmium_libs_solidity_ast_extractor::ItemFunction,
57 nb_max_line: usize,
58) -> Option<Range> {
59 if let FunctionBody::Block(block) = &function.body {
60 let line_diff = block.span().end().line - block.span().start().line;
61 let start_span = function.name.span().start();
62 let end_span = block.span().end();
63 if line_diff > nb_max_line {
64 return Some(Range {
65 start: Position {
66 line: start_span.line,
67 character: start_span.column,
68 },
69 end: Position {
70 line: end_span.line,
71 character: end_span.column,
72 },
73 });
74 }
75 }
76 None
77}
78
79impl FunctionMaxLines {
80 pub fn create(data: RuleEntry) -> Box<dyn RuleType> {
81 let mut max_number_lines = DEFAULT_MAX_LINES;
82
83 if let Some(data) = &data.data {
84 let parsed: Result<usize, serde_json::Error> = serde_json::from_value(data.clone());
85 match parsed {
86 Ok(val) => max_number_lines = val,
87 Err(_) => {
88 eprintln!("{} rule : bad config data", RULE_ID);
89 }
90 }
91 } else {
92 eprintln!("{} rule : bad config data", RULE_ID);
93 }
94 let rule = FunctionMaxLines {
95 number_max_lines: max_number_lines,
96 data,
97 };
98 Box::new(rule)
99 }
100
101 pub fn create_default() -> RuleEntry {
102 RuleEntry {
103 id: RULE_ID.to_string(),
104 severity: DEFAULT_SEVERITY,
105 data: Some(DEFAULT_MAX_LINES.into()),
106 }
107 }
108}