svlint/opt/rustwide/workdir/src/syntaxrules/
style_operator_integer.rs

1use crate::config::ConfigOption;
2use crate::linter::{SyntaxRule, SyntaxRuleResult};
3use regex::Regex;
4use sv_parser::{NodeEvent, RefNode, SyntaxTree};
5
6#[derive(Default)]
7pub struct StyleOperatorInteger {
8    re_split: Option<Regex>,
9    re_op: Option<Regex>,
10    re_succ: Option<Regex>,
11}
12
13impl SyntaxRule for StyleOperatorInteger {
14    fn check(
15        &mut self,
16        syntax_tree: &SyntaxTree,
17        event: &NodeEvent,
18        _option: &ConfigOption,
19    ) -> SyntaxRuleResult {
20        /*
21        re_split extracts operator from anything following it.
22        re_op is used to selectively apply this rule to specific operators.
23        re_succ matches what is allowed after the operator.
24            - newline
25            - exactly 1space, then comment
26            - exactly 1space, then nothing
27        */
28        if self.re_split.is_none() {
29            self.re_split = Some(Regex::new(r"(?P<op>\S+)(?P<succ>(?s:.)*)").unwrap());
30        }
31        if self.re_op.is_none() {
32            let operators =
33                [ "&" // {{{
34                , "\\|"
35                , "\\^"
36                , "\\^~"
37                , "~\\^"
38                , ">>"
39                , "<<"
40                , ">>>"
41                , "<<<"
42                ].join("|"); // }}}
43
44            self.re_op = Some(Regex::new(format!("^({})$", operators).as_str()).unwrap());
45        }
46        if self.re_succ.is_none() {
47            self.re_succ = Some(Regex::new(r"^([\n\v\f\r]| /| $)").unwrap());
48        }
49
50        let node = match event {
51            NodeEvent::Enter(x) => x,
52            NodeEvent::Leave(_) => {
53                return SyntaxRuleResult::Pass;
54            }
55        };
56
57        let s: Option<&str> = match node {
58            RefNode::BinaryOperator(x) => {
59                Some(syntax_tree.get_str(*x).unwrap())
60            }
61            RefNode::BinaryModulePathOperator(x) => {
62                Some(syntax_tree.get_str(*x).unwrap())
63            }
64            _ => None,
65        };
66
67        if let Some(t) = s {
68            let re_split = self.re_split.as_ref().unwrap();
69            let re_op = self.re_op.as_ref().unwrap();
70            let caps = re_split.captures(&t).unwrap();
71
72            if re_op.is_match(&caps[1]) {
73                let re_succ = self.re_succ.as_ref().unwrap();
74
75                if re_succ.is_match(&caps[2]) {
76                    SyntaxRuleResult::Pass
77                } else {
78                    SyntaxRuleResult::Fail
79                }
80            } else {
81                SyntaxRuleResult::Pass
82            }
83        } else {
84            SyntaxRuleResult::Pass
85        }
86    }
87
88    fn name(&self) -> String {
89        String::from("style_operator_integer")
90    }
91
92    fn hint(&self, _option: &ConfigOption) -> String {
93        String::from("Follow operator with a newline or exactly 1 space.")
94    }
95
96    fn reason(&self) -> String {
97        String::from("Consistent use of whitespace enhances readability by reducing visual noise.")
98    }
99}