sqruff_lib/rules/aliasing/
al09.rs1use ahash::AHashMap;
2use sqruff_lib_core::dialects::syntax::{SyntaxKind, SyntaxSet};
3use sqruff_lib_core::lint_fix::LintFix;
4use sqruff_lib_core::parser::segments::base::ErasedSegment;
5
6use crate::core::config::Value;
7use crate::core::rules::base::{Erased, ErasedRule, LintResult, Rule, RuleGroups};
8use crate::core::rules::context::RuleContext;
9use crate::core::rules::crawlers::{Crawler, SegmentSeekerCrawler};
10use crate::utils::functional::context::FunctionalContext;
11
12#[derive(Default, Clone, Debug)]
13pub struct RuleAL09;
14
15impl Rule for RuleAL09 {
16 fn load_from_config(&self, _config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
17 Ok(RuleAL09.erased())
18 }
19
20 fn name(&self) -> &'static str {
21 "aliasing.self_alias.column"
22 }
23
24 fn description(&self) -> &'static str {
25 "Find self-aliased columns and fix them"
26 }
27
28 fn long_description(&self) -> &'static str {
29 r#"
30**Anti-pattern**
31
32Aliasing the column to itself.
33
34```sql
35SELECT
36 col AS col
37FROM table;
38```
39
40**Best practice**
41
42Not to use alias to rename the column to its original name. Self-aliasing leads to redundant code without changing any functionality.
43
44```sql
45SELECT
46 col
47FROM table;
48```
49"#
50 }
51
52 fn groups(&self) -> &'static [RuleGroups] {
53 &[RuleGroups::All, RuleGroups::Core, RuleGroups::Aliasing]
54 }
55
56 fn eval(&self, context: &RuleContext) -> Vec<LintResult> {
57 let mut violations = Vec::new();
58
59 let children = FunctionalContext::new(context).segment().children(None);
60
61 for clause_element in children.select(
62 Some(|sp: &ErasedSegment| sp.is_type(SyntaxKind::SelectClauseElement)),
63 None,
64 None,
65 None,
66 ) {
67 let clause_element_raw_segment = clause_element.get_raw_segments();
68
69 let column =
70 clause_element.child(const { &SyntaxSet::new(&[SyntaxKind::ColumnReference]) });
71 let alias_expression =
72 clause_element.child(const { &SyntaxSet::new(&[SyntaxKind::AliasExpression]) });
73
74 if let Some(column) = column {
75 if let Some(alias_expression) = alias_expression {
76 if column
77 .child(
78 const {
79 &SyntaxSet::new(&[
80 SyntaxKind::Identifier,
81 SyntaxKind::NakedIdentifier,
82 ])
83 },
84 )
85 .is_some()
86 || column
87 .child(const { &SyntaxSet::new(&[SyntaxKind::QuotedIdentifier]) })
88 .is_some()
89 {
90 let Some(whitespace) = clause_element
91 .child(const { &SyntaxSet::new(&[SyntaxKind::Whitespace]) })
92 else {
93 return Vec::new();
94 };
95
96 let column_identifier = if let Some(quoted_identifier) =
97 column.child(const { &SyntaxSet::new(&[SyntaxKind::QuotedIdentifier]) })
98 {
99 quoted_identifier.clone()
100 } else {
101 column
102 .children(
103 const {
104 &SyntaxSet::new(&[
105 SyntaxKind::Identifier,
106 SyntaxKind::NakedIdentifier,
107 ])
108 },
109 )
110 .last()
111 .expect("No naked_identifier found")
112 .clone()
113 };
114
115 let alias_identifier = alias_expression
116 .child(const { &SyntaxSet::new(&[SyntaxKind::NakedIdentifier]) })
117 .or_else(|| {
118 alias_expression.child(
119 const { &SyntaxSet::new(&[SyntaxKind::QuotedIdentifier]) },
120 )
121 })
122 .expect("identifier is none");
123
124 if column_identifier
125 .raw()
126 .eq_ignore_ascii_case(alias_identifier.raw())
127 {
128 let fixes = vec![
129 LintFix::delete(whitespace),
130 LintFix::delete(alias_expression),
131 ];
132
133 violations.push(LintResult::new(
134 Some(clause_element_raw_segment[0].clone()),
135 fixes,
136 Some("Column should not be self-aliased.".into()),
137 None,
138 ));
139 }
140 }
141 }
142 }
143 }
144
145 violations
146 }
147
148 fn crawl_behaviour(&self) -> Crawler {
149 SegmentSeekerCrawler::new(const { SyntaxSet::new(&[SyntaxKind::SelectClause]) }).into()
150 }
151}