1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
use super::super::{CertRule, RuleViolation};
use crate::manifest::{RuleCategory, Severity};
use tree_sitter::Node;
pub struct Exp19C;
impl CertRule for Exp19C {
fn rule_id(&self) -> &'static str {
"EXP19-C"
}
fn description(&self) -> &'static str {
"Use braces for the body of an if, for, or while statement"
}
fn severity(&self) -> Severity {
Severity::Low
}
fn category(&self) -> RuleCategory {
RuleCategory::Recommendation
}
fn cert_id(&self) -> &'static str {
"EXP19-C"
}
fn check(&self, node: &Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
// Recursively check for control flow statements without braces
violations.extend(self.check_node(*node, source));
violations
}
}
impl Exp19C {
/// Recursively check nodes for control flow statements without compound statement bodies
fn check_node(&self, node: Node, source: &str) -> Vec<RuleViolation> {
let mut violations = Vec::new();
// Check if this is a control flow statement that needs braces
match node.kind() {
"if_statement" => {
if let Some(violation) = self.check_if_statement(node, source) {
violations.push(violation);
}
}
"for_statement" => {
if let Some(violation) = self.check_for_statement(node, source) {
violations.push(violation);
}
}
"while_statement" => {
if let Some(violation) = self.check_while_statement(node, source) {
violations.push(violation);
}
}
"do_statement" => {
if let Some(violation) = self.check_do_statement(node, source) {
violations.push(violation);
}
}
_ => {}
}
// Recursively check all children
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
violations.extend(self.check_node(child, source));
}
violations
}
/// Check if an if_statement has braces around its body
fn check_if_statement(&self, if_node: Node, _source: &str) -> Option<RuleViolation> {
// Get the consequence (then-branch) of the if statement
if let Some(consequence) = if_node.child_by_field_name("consequence") {
// If the consequence is not a compound_statement (braced block), it's a violation
if consequence.kind() != "compound_statement" {
return Some(self.create_violation(
if_node,
"if",
"if statement body should be enclosed in braces {}",
));
}
}
// Check the else-branch if it exists
if let Some(alternative) = if_node.child_by_field_name("alternative") {
// Skip checking if the alternative is another if_statement (else if)
// We'll check that if_statement separately in recursion
if alternative.kind() == "if_statement" {
return None; // else-if chain, will be checked recursively
}
// The alternative can be wrapped in an else_clause node in tree-sitter-c
// We need to check the actual body inside it
let body_to_check = if alternative.kind() == "else_clause" {
// Find the actual statement inside the else_clause
let mut body = None;
let mut cursor = alternative.walk();
for child in alternative.children(&mut cursor) {
// Skip the "else" keyword, look for the actual statement
if child.kind() != "else" {
body = Some(child);
break;
}
}
body
} else {
Some(alternative)
};
if let Some(body) = body_to_check {
// Skip if it's an if_statement (else if case inside else_clause)
if body.kind() == "if_statement" {
return None;
}
// Check if the body is a compound_statement (braced block)
if body.kind() != "compound_statement" {
return Some(self.create_violation(
if_node,
"else",
"else statement body should be enclosed in braces {}",
));
}
}
}
None
}
/// Check if a for_statement has braces around its body
fn check_for_statement(&self, for_node: Node, _source: &str) -> Option<RuleViolation> {
if let Some(body) = for_node.child_by_field_name("body") {
if body.kind() != "compound_statement" {
return Some(self.create_violation(
for_node,
"for",
"for statement body should be enclosed in braces {}",
));
}
}
None
}
/// Check if a while_statement has braces around its body
fn check_while_statement(&self, while_node: Node, _source: &str) -> Option<RuleViolation> {
if let Some(body) = while_node.child_by_field_name("body") {
if body.kind() != "compound_statement" {
return Some(self.create_violation(
while_node,
"while",
"while statement body should be enclosed in braces {}",
));
}
}
None
}
/// Check if a do_statement has braces around its body
fn check_do_statement(&self, do_node: Node, _source: &str) -> Option<RuleViolation> {
if let Some(body) = do_node.child_by_field_name("body") {
if body.kind() != "compound_statement" {
return Some(self.create_violation(
do_node,
"do-while",
"do-while statement body should be enclosed in braces {}",
));
}
}
None
}
/// Create a rule violation for a control flow statement without braces
fn create_violation(
&self,
statement_node: Node,
statement_type: &str,
message: &str,
) -> RuleViolation {
let suggestion = format!(
"Add braces around the {} statement body: {} {{ /* body */ }}",
statement_type, statement_type
);
RuleViolation {
rule_id: self.rule_id().to_string(),
severity: self.severity(),
message: message.to_string(),
file_path: String::new(),
line: statement_node.start_position().row + 1,
column: statement_node.start_position().column + 1,
suggestion: Some(suggestion),
..Default::default()
}
}
}