solidhunter_lib/rules/best_practises/
payable_fallback.rs

1use osmium_libs_solidity_ast_extractor::retriever::{
2    retrieve_contract_nodes, retrieve_functions_nodes,
3};
4use osmium_libs_solidity_ast_extractor::{ItemFunction, Mutability, Spanned};
5
6use crate::linter::SolidFile;
7use crate::rules::types::*;
8use crate::types::*;
9
10// global
11pub const RULE_ID: &str = "payable-fallback";
12
13// specific
14const DEFAULT_SEVERITY: Severity = Severity::WARNING;
15const DEFAULT_MESSAGE: &str = "When fallback is not payable you will not be able to receive ether";
16
17pub struct PayableFallback {
18    data: RuleEntry,
19}
20
21impl RuleType for PayableFallback {
22    fn diagnose(&self, file: &SolidFile, _files: &[SolidFile]) -> Vec<LintDiag> {
23        let mut res = Vec::new();
24        let reports = check_fallback_payable(file);
25
26        for report in reports.into_iter().flatten() {
27            res.push(LintDiag {
28                id: RULE_ID.to_string(),
29                severity: self.data.severity,
30                range: report,
31                code: None,
32                source: None,
33                message: DEFAULT_MESSAGE.to_string(),
34                uri: file.path.clone(),
35            });
36        }
37        res
38    }
39}
40
41fn check_fallback_payable(file: &SolidFile) -> Vec<Option<Range>> {
42    let mut res: Vec<Option<Range>> = Vec::new();
43
44    let contracts = retrieve_contract_nodes(&file.data);
45    for contract in contracts {
46        let functions = retrieve_functions_nodes(&contract);
47
48        for function in functions {
49            if function.kind.is_fallback()
50                || (function.kind.is_function() && function.name.is_none())
51            {
52                res = check_attribute(res, function);
53            }
54        }
55    }
56    res
57}
58
59fn check_attribute(mut res: Vec<Option<Range>>, function: ItemFunction) -> Vec<Option<Range>> {
60    let mut is_payable = false;
61    for attributes in function.attributes.iter() {
62        if attributes.mutability().is_some()
63            && Mutability::is_payable(attributes.mutability().unwrap())
64        {
65            is_payable = true;
66        }
67    }
68    if !is_payable {
69        res.push(create_report(function));
70    }
71    res
72}
73
74fn create_report(function: ItemFunction) -> Option<Range> {
75    Some(Range {
76        start: Position {
77            line: function.attributes.span().start().line,
78            character: function.attributes.span().start().column + 1,
79        },
80        end: Position {
81            line: function.attributes.span().end().line,
82            character: function.attributes.span().end().column,
83        },
84    })
85}
86
87impl PayableFallback {
88    pub fn create(data: RuleEntry) -> Box<dyn RuleType> {
89        let rule = PayableFallback { data };
90        Box::new(rule)
91    }
92
93    pub fn create_default() -> RuleEntry {
94        RuleEntry {
95            id: RULE_ID.to_string(),
96            severity: DEFAULT_SEVERITY,
97            data: None,
98        }
99    }
100}