sqruff_lib/rules/aliasing/
al09.rs

1use 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}