rigsql_rules/aliasing/
al01.rs1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::utils::{has_as_keyword, insert_as_keyword_fix, is_false_alias};
5use crate::violation::LintViolation;
6
7#[derive(Debug, Default)]
11pub struct RuleAL01;
12
13impl Rule for RuleAL01 {
14 fn code(&self) -> &'static str {
15 "AL01"
16 }
17 fn name(&self) -> &'static str {
18 "aliasing.table"
19 }
20 fn description(&self) -> &'static str {
21 "Implicit aliasing of table/column is not allowed."
22 }
23 fn explanation(&self) -> &'static str {
24 "Aliases should use the explicit AS keyword for clarity. \
25 'SELECT a alias_name' is harder to read than 'SELECT a AS alias_name'. \
26 Explicit aliasing makes the intent clear and prevents ambiguity."
27 }
28 fn groups(&self) -> &[RuleGroup] {
29 &[RuleGroup::Aliasing]
30 }
31 fn is_fixable(&self) -> bool {
32 true
33 }
34
35 fn crawl_type(&self) -> CrawlType {
36 CrawlType::Segment(vec![SegmentType::AliasExpression])
37 }
38
39 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
40 let children = ctx.segment.children();
41 if is_false_alias(children) || has_as_keyword(children) {
42 return vec![];
43 }
44
45 vec![LintViolation::with_fix_and_msg_key(
46 self.code(),
47 "Implicit aliasing not allowed. Use explicit AS keyword.",
48 ctx.segment.span(),
49 insert_as_keyword_fix(children),
50 "rules.AL01.msg",
51 vec![],
52 )]
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59 use crate::test_utils::lint_sql;
60
61 #[test]
62 fn test_al01_flags_implicit_alias() {
63 let violations = lint_sql("SELECT a b FROM t", RuleAL01);
64 assert_eq!(violations.len(), 1);
65 }
66
67 #[test]
68 fn test_al01_accepts_explicit_alias() {
69 let violations = lint_sql("SELECT a AS b FROM t", RuleAL01);
70 assert_eq!(violations.len(), 0);
71 }
72
73 #[test]
74 fn test_al01_fix_inserts_as() {
75 let violations = lint_sql("SELECT a b FROM t", RuleAL01);
76 assert_eq!(violations.len(), 1);
77 assert!(!violations[0].fixes.is_empty());
78 assert!(violations[0]
79 .fixes
80 .iter()
81 .any(|f| f.new_text.contains("AS")));
82 }
83}