sqruff_lib/rules/aliasing/
al03.rs

1use ahash::AHashMap;
2use sqruff_lib_core::dialects::syntax::{SyntaxKind, SyntaxSet};
3use sqruff_lib_core::parser::segments::base::ErasedSegment;
4use sqruff_lib_core::utils::functional::segments::Segments;
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(Debug, Clone)]
13pub struct RuleAL03;
14
15impl Rule for RuleAL03 {
16    fn load_from_config(&self, _config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
17        Ok(RuleAL03.erased())
18    }
19
20    fn name(&self) -> &'static str {
21        "aliasing.expression"
22    }
23
24    fn description(&self) -> &'static str {
25        "Column expression without alias. Use explicit `AS` clause."
26    }
27
28    fn long_description(&self) -> &'static str {
29        r#"
30**Anti-pattern**
31
32In this example, there is no alias for both sums.
33
34```sql
35SELECT
36    sum(a),
37    sum(b)
38FROM foo
39```
40
41**Best practice**
42
43Add aliases.
44
45```sql
46SELECT
47    sum(a) AS a_sum,
48    sum(b) AS b_sum
49FROM foo
50```
51"#
52    }
53
54    fn groups(&self) -> &'static [RuleGroups] {
55        &[RuleGroups::All, RuleGroups::Core, RuleGroups::Aliasing]
56    }
57
58    fn eval(&self, context: &RuleContext) -> Vec<LintResult> {
59        let functional_context = FunctionalContext::new(context);
60        let segment = functional_context.segment();
61        let children = segment.children(None);
62
63        if children.any(Some(|it| it.get_type() == SyntaxKind::AliasExpression)) {
64            return Vec::new();
65        }
66
67        // Ignore if it's a function with EMITS clause as EMITS is equivalent to AS
68        if !children
69            .select(
70                Some(|sp: &ErasedSegment| sp.is_type(SyntaxKind::Function)),
71                None,
72                None,
73                None,
74            )
75            .children(None)
76            .select(
77                Some(|sp: &ErasedSegment| sp.is_type(SyntaxKind::EmitsSegment)),
78                None,
79                None,
80                None,
81            )
82            .is_empty()
83        {
84            return Vec::new();
85        }
86
87        if !children
88            .children(None)
89            .select(
90                Some(|it: &ErasedSegment| it.is_type(SyntaxKind::CastExpression)),
91                None,
92                None,
93                None,
94            )
95            .is_empty()
96            && !children
97                .children(None)
98                .select(
99                    Some(|it: &ErasedSegment| it.is_type(SyntaxKind::CastExpression)),
100                    None,
101                    None,
102                    None,
103                )
104                .children(None)
105                .any(Some(|it| it.is_type(SyntaxKind::Function)))
106        {
107            return Vec::new();
108        }
109
110        let parent_stack = functional_context.parent_stack();
111
112        if parent_stack
113            .find_last(Some(|it| it.is_type(SyntaxKind::CommonTableExpression)))
114            .children(None)
115            .any(Some(|it| it.is_type(SyntaxKind::CTEColumnList)))
116        {
117            return Vec::new();
118        }
119
120        let select_clause_children = children.select(
121            Some(|it: &ErasedSegment| !it.is_type(SyntaxKind::Star)),
122            None,
123            None,
124            None,
125        );
126        let is_complex_clause = recursively_check_is_complex(select_clause_children);
127
128        if !is_complex_clause {
129            return Vec::new();
130        }
131
132        if context
133            .config
134            .get("allow_scalar", "rules")
135            .as_bool()
136            .unwrap()
137        {
138            let immediate_parent = parent_stack.find_last(None);
139            let elements =
140                immediate_parent.children(Some(|it| it.is_type(SyntaxKind::SelectClauseElement)));
141
142            if elements.len() > 1 {
143                return vec![LintResult::new(
144                    context.segment.clone().into(),
145                    Vec::new(),
146                    None,
147                    None,
148                )];
149            }
150
151            return Vec::new();
152        }
153
154        vec![LintResult::new(
155            context.segment.clone().into(),
156            Vec::new(),
157            None,
158            None,
159        )]
160    }
161
162    fn crawl_behaviour(&self) -> Crawler {
163        SegmentSeekerCrawler::new(const { SyntaxSet::new(&[SyntaxKind::SelectClauseElement]) })
164            .into()
165    }
166}
167
168fn recursively_check_is_complex(select_clause_or_exp_children: Segments) -> bool {
169    let selector: Option<fn(&ErasedSegment) -> bool> = Some(|it: &ErasedSegment| {
170        !matches!(
171            it.get_type(),
172            SyntaxKind::Whitespace
173                | SyntaxKind::Newline
174                | SyntaxKind::ColumnReference
175                | SyntaxKind::WildcardExpression
176                | SyntaxKind::Bracketed
177        )
178    });
179
180    let filtered = select_clause_or_exp_children.select(selector, None, None, None);
181    let remaining_count = filtered.len();
182
183    if remaining_count == 0 {
184        return false;
185    }
186
187    let first_el = filtered.find_first::<fn(&ErasedSegment) -> _>(None);
188
189    if remaining_count > 1 || !first_el.all(Some(|it| it.is_type(SyntaxKind::Expression))) {
190        return true;
191    }
192
193    recursively_check_is_complex(first_el.children(None))
194}