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::ErasedSegment;
5
6use crate::core::config::Value;
7use crate::core::rules::context::RuleContext;
8use crate::core::rules::crawlers::{Crawler, SegmentSeekerCrawler};
9use crate::core::rules::{Erased, ErasedRule, LintResult, Rule, RuleGroups};
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_all();
60
61        for clause_element in
62            children.filter(|sp: &ErasedSegment| sp.is_type(SyntaxKind::SelectClauseElement))
63        {
64            let clause_element_raw_segment = clause_element.get_raw_segments();
65
66            let column =
67                clause_element.child(const { &SyntaxSet::new(&[SyntaxKind::ColumnReference]) });
68            let alias_expression =
69                clause_element.child(const { &SyntaxSet::new(&[SyntaxKind::AliasExpression]) });
70
71            if let Some(column) = column
72                && let Some(alias_expression) = alias_expression
73                    && (column
74                        .child(
75                            const {
76                                &SyntaxSet::new(&[
77                                    SyntaxKind::Identifier,
78                                    SyntaxKind::NakedIdentifier,
79                                ])
80                            },
81                        )
82                        .is_some()
83                        || column
84                            .child(const { &SyntaxSet::new(&[SyntaxKind::QuotedIdentifier]) })
85                            .is_some())
86                    {
87                        let Some(whitespace) = clause_element
88                            .child(const { &SyntaxSet::new(&[SyntaxKind::Whitespace]) })
89                        else {
90                            return Vec::new();
91                        };
92
93                        let column_identifier = if let Some(quoted_identifier) =
94                            column.child(const { &SyntaxSet::new(&[SyntaxKind::QuotedIdentifier]) })
95                        {
96                            quoted_identifier.clone()
97                        } else {
98                            column
99                                .children(
100                                    const {
101                                        &SyntaxSet::new(&[
102                                            SyntaxKind::Identifier,
103                                            SyntaxKind::NakedIdentifier,
104                                        ])
105                                    },
106                                )
107                                .last()
108                                .expect("No naked_identifier found")
109                                .clone()
110                        };
111
112                        let alias_identifier = alias_expression
113                            .child(const { &SyntaxSet::new(&[SyntaxKind::NakedIdentifier]) })
114                            .or_else(|| {
115                                alias_expression.child(
116                                    const { &SyntaxSet::new(&[SyntaxKind::QuotedIdentifier]) },
117                                )
118                            })
119                            .expect("identifier is none");
120
121                        if column_identifier
122                            .raw()
123                            .eq_ignore_ascii_case(alias_identifier.raw())
124                        {
125                            let fixes = vec![
126                                LintFix::delete(whitespace),
127                                LintFix::delete(alias_expression),
128                            ];
129
130                            violations.push(LintResult::new(
131                                Some(clause_element_raw_segment[0].clone()),
132                                fixes,
133                                Some("Column should not be self-aliased.".into()),
134                                None,
135                            ));
136                        }
137                    }
138        }
139
140        violations
141    }
142
143    fn crawl_behaviour(&self) -> Crawler {
144        SegmentSeekerCrawler::new(const { SyntaxSet::new(&[SyntaxKind::SelectClause]) }).into()
145    }
146}