rigsql_rules/aliasing/
al02.rs1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::utils::{has_as_keyword, is_false_alias};
5use crate::violation::{LintViolation, SourceEdit};
6
7#[derive(Debug, Default)]
12pub struct RuleAL02;
13
14impl Rule for RuleAL02 {
15 fn code(&self) -> &'static str {
16 "AL02"
17 }
18 fn name(&self) -> &'static str {
19 "aliasing.column"
20 }
21 fn description(&self) -> &'static str {
22 "Implicit column aliasing is not allowed."
23 }
24 fn explanation(&self) -> &'static str {
25 "Column aliases should use the explicit AS keyword. \
26 'SELECT col alias' is harder to read than 'SELECT col AS alias'. \
27 This is especially important for complex expressions."
28 }
29 fn groups(&self) -> &[RuleGroup] {
30 &[RuleGroup::Aliasing]
31 }
32 fn is_fixable(&self) -> bool {
33 true
34 }
35
36 fn crawl_type(&self) -> CrawlType {
37 CrawlType::Segment(vec![SegmentType::AliasExpression])
38 }
39
40 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
41 let is_in_select = ctx
43 .parent
44 .is_some_and(|p| p.segment_type() == SegmentType::SelectClause);
45
46 if !is_in_select {
47 return vec![];
48 }
49
50 if is_false_alias(ctx.segment.children()) {
52 return vec![];
53 }
54
55 if !has_as_keyword(ctx.segment.children()) {
56 let children = ctx.segment.children();
57 let fix = children
58 .iter()
59 .rev()
60 .find(|c| !c.segment_type().is_trivia())
61 .map(|alias| SourceEdit::insert(alias.span().start, "AS "));
62
63 return vec![LintViolation::with_fix(
64 self.code(),
65 "Implicit column aliasing not allowed. Use explicit AS keyword.",
66 ctx.segment.span(),
67 fix.into_iter().collect(),
68 )];
69 }
70
71 vec![]
72 }
73}