Skip to main content

rigsql_rules/convention/
cv06.rs

1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::LintViolation;
5
6/// CV06: Statements must end with a semicolon.
7#[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        // Check if the last non-trivia child is a semicolon
43        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}