sqrust_rules/layout/
no_multiple_statements_on_line.rs1use sqrust_core::{Diagnostic, FileContext, Rule};
2
3pub struct NoMultipleStatementsOnLine;
4
5impl Rule for NoMultipleStatementsOnLine {
6 fn name(&self) -> &'static str {
7 "Layout/NoMultipleStatementsOnLine"
8 }
9
10 fn check(&self, ctx: &FileContext) -> Vec<Diagnostic> {
11 find_violations(&ctx.source, self.name())
12 }
13}
14
15fn find_violations(source: &str, rule_name: &'static str) -> Vec<Diagnostic> {
16 let bytes = source.as_bytes();
17 let len = bytes.len();
18 let mut diags = Vec::new();
19
20 let mut i = 0;
21 let mut in_string = false;
22 let mut in_line_comment = false;
23 let mut block_depth: usize = 0;
24
25 while i < len {
26 if bytes[i] == b'\n' {
28 in_line_comment = false;
29 i += 1;
30 continue;
31 }
32
33 if in_line_comment {
35 i += 1;
36 continue;
37 }
38
39 if !in_string && block_depth == 0 && bytes[i] == b'\'' {
41 in_string = true;
42 i += 1;
43 continue;
44 }
45 if in_string {
46 if bytes[i] == b'\'' {
47 if i + 1 < len && bytes[i + 1] == b'\'' {
49 i += 2;
50 continue;
51 }
52 in_string = false;
53 }
54 i += 1;
55 continue;
56 }
57
58 if block_depth == 0 && i + 1 < len && bytes[i] == b'/' && bytes[i + 1] == b'*' {
60 block_depth += 1;
61 i += 2;
62 continue;
63 }
64 if block_depth > 0 {
66 if i + 1 < len && bytes[i] == b'*' && bytes[i + 1] == b'/' {
67 block_depth -= 1;
68 i += 2;
69 } else {
70 i += 1;
71 }
72 continue;
73 }
74
75 if i + 1 < len && bytes[i] == b'-' && bytes[i + 1] == b'-' {
77 in_line_comment = true;
78 i += 2;
79 continue;
80 }
81
82 if bytes[i] == b';' {
84 let mut j = i + 1;
85
86 while j < len && (bytes[j] == b' ' || bytes[j] == b'\t') {
88 j += 1;
89 }
90
91 if j >= len || bytes[j] == b'\n' || bytes[j] == b'\r' {
93 i += 1;
94 continue;
95 }
96
97 if j + 1 < len && bytes[j] == b'-' && bytes[j + 1] == b'-' {
99 i += 1;
100 continue;
101 }
102
103 let (line, col) = byte_offset_to_line_col(source, j);
105 diags.push(Diagnostic {
106 rule: rule_name,
107 message: "Multiple statements on the same line; each statement should be on its own line".to_string(),
108 line,
109 col,
110 });
111 }
112
113 i += 1;
114 }
115
116 diags
117}
118
119fn byte_offset_to_line_col(source: &str, offset: usize) -> (usize, usize) {
121 let mut line = 1usize;
122 let mut line_start = 0usize;
123 for (i, ch) in source.char_indices() {
124 if i == offset {
125 break;
126 }
127 if ch == '\n' {
128 line += 1;
129 line_start = i + 1;
130 }
131 }
132 let col = offset - line_start + 1;
133 (line, col)
134}