rigsql_rules/layout/
lt15.rs1use rigsql_core::{Segment, SegmentType};
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::LintViolation;
5
6#[derive(Debug, Default)]
10pub struct RuleLT15;
11
12impl Rule for RuleLT15 {
13 fn code(&self) -> &'static str {
14 "LT15"
15 }
16 fn name(&self) -> &'static str {
17 "layout.distinct"
18 }
19 fn description(&self) -> &'static str {
20 "DISTINCT used with parentheses."
21 }
22 fn explanation(&self) -> &'static str {
23 "DISTINCT is not a function and should not be used with parentheses. \
24 'SELECT DISTINCT(col)' is misleading because the parentheses don't do anything. \
25 Write 'SELECT DISTINCT col' instead."
26 }
27 fn groups(&self) -> &[RuleGroup] {
28 &[RuleGroup::Layout]
29 }
30 fn is_fixable(&self) -> bool {
31 true
32 }
33
34 fn crawl_type(&self) -> CrawlType {
35 CrawlType::Segment(vec![SegmentType::SelectClause])
36 }
37
38 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
39 let children = ctx.segment.children();
40
41 for (i, child) in children.iter().enumerate() {
43 if let Segment::Token(t) = child {
44 if t.segment_type == SegmentType::Keyword
45 && t.token.text.eq_ignore_ascii_case("DISTINCT")
46 {
47 for next in &children[i + 1..] {
49 if next.segment_type().is_trivia() {
50 continue;
51 }
52 if next.segment_type() == SegmentType::LParen
53 || next.segment_type() == SegmentType::ParenExpression
54 {
55 return vec![LintViolation::new(
56 self.code(),
57 "DISTINCT should not be followed by parentheses.",
58 t.token.span,
59 )];
60 }
61 break;
62 }
63 }
64 }
65 }
66
67 vec![]
68 }
69}