solidhunter_lib/rules/best_practises/
function_max_lines.rs

1use osmium_libs_solidity_ast_extractor::{FunctionBody, Spanned};
2
3use crate::linter::SolidFile;
4use crate::rules::types::*;
5use crate::types::*;
6
7// global
8pub const RULE_ID: &str = "function-max-lines";
9
10// specific
11const 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
54// returns a struct containing the line number of the start and end of the function if it is too long
55fn 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}