rigsql_rules/convention/
cv06.rs1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::LintViolation;
5
6#[derive(Debug, Default)]
8pub struct RuleCV06;
9
10impl Rule for RuleCV06 {
11 fn code(&self) -> &'static str {
12 "CV06"
13 }
14 fn name(&self) -> &'static str {
15 "convention.terminator"
16 }
17 fn description(&self) -> &'static str {
18 "Statements must end with a semicolon."
19 }
20 fn explanation(&self) -> &'static str {
21 "All SQL statements should be terminated with a semicolon. While some databases \
22 accept statements without terminators, including them is good practice for \
23 portability and clarity."
24 }
25 fn groups(&self) -> &[RuleGroup] {
26 &[RuleGroup::Convention]
27 }
28 fn is_fixable(&self) -> bool {
29 true
30 }
31
32 fn crawl_type(&self) -> CrawlType {
33 CrawlType::Segment(vec![SegmentType::Statement])
34 }
35
36 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
37 let children = ctx.segment.children();
38 if children.is_empty() {
39 return vec![];
40 }
41
42 let has_semicolon = children
44 .iter()
45 .rev()
46 .find(|s| !s.segment_type().is_trivia())
47 .is_some_and(|s| s.segment_type() == SegmentType::Semicolon);
48
49 if !has_semicolon {
50 let span = ctx.segment.span();
51 return vec![LintViolation::new(
52 self.code(),
53 "Statement is not terminated with a semicolon.",
54 rigsql_core::Span::new(span.end, span.end),
55 )];
56 }
57
58 vec![]
59 }
60}