sqruff_lib/rules/convention/
cv07.rs1use ahash::AHashMap;
2use sqruff_lib_core::dialects::syntax::SyntaxKind;
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, RootOnlyCrawler};
10
11#[derive(Default, Clone, Debug)]
12pub struct RuleCV07;
13
14impl Rule for RuleCV07 {
15 fn load_from_config(&self, _config: &AHashMap<String, Value>) -> Result<ErasedRule, String> {
16 Ok(RuleCV07.erased())
17 }
18
19 fn name(&self) -> &'static str {
20 "convention.statement_brackets"
21 }
22
23 fn description(&self) -> &'static str {
24 "Top-level statements should not be wrapped in brackets."
25 }
26
27 fn long_description(&self) -> &'static str {
28 r#"
29**Anti-pattern**
30
31A top-level statement is wrapped in brackets.
32
33```sql
34 (SELECT
35 foo
36 FROM bar)
37
38 -- This also applies to statements containing a sub-query.
39
40 (SELECT
41 foo
42 FROM (SELECT * FROM bar))
43```
44
45**Best practice**
46
47Don’t wrap top-level statements in brackets.
48
49```sql
50 SELECT
51 foo
52 FROM bar
53
54 -- Likewise for statements containing a sub-query.
55
56 SELECT
57 foo
58 FROM (SELECT * FROM bar)
59```
60"#
61 }
62
63 fn groups(&self) -> &'static [RuleGroups] {
64 &[RuleGroups::All, RuleGroups::Convention]
65 }
66
67 fn eval(&self, context: &RuleContext) -> Vec<LintResult> {
68 let mut results = vec![];
69
70 for (parent, bracketed_segment) in Self::iter_bracketed_statements(context.segment.clone())
71 {
72 let bracket_set = [SyntaxKind::StartBracket, SyntaxKind::EndBracket];
73
74 let mut filtered_children: Vec<ErasedSegment> = bracketed_segment
75 .segments()
76 .iter()
77 .filter(|&segment| !bracket_set.contains(&segment.get_type()) && !segment.is_meta())
78 .cloned()
79 .collect();
80
81 let to_lift_predicate = |segment: &ErasedSegment| {
86 segment.is_type(SyntaxKind::Whitespace)
87 || segment.is_type(SyntaxKind::InlineComment)
88 };
89 let leading = filtered_children
90 .clone()
91 .into_iter()
92 .take_while(to_lift_predicate)
93 .collect::<Vec<_>>();
94 let trailing = filtered_children
95 .clone()
96 .into_iter()
97 .rev()
98 .take_while(to_lift_predicate)
99 .collect::<Vec<_>>();
100
101 let lift_nodes = leading
102 .iter()
103 .chain(trailing.iter())
104 .cloned()
105 .collect::<Vec<_>>();
106 let mut fixes = vec![];
107 if !lift_nodes.is_empty() {
108 fixes.push(LintFix::create_before(parent.clone(), leading.clone()));
109 fixes.push(LintFix::create_after(parent, trailing.clone(), None));
110 fixes.extend(lift_nodes.into_iter().map(LintFix::delete));
111 filtered_children = filtered_children
112 [leading.len()..filtered_children.len() - trailing.len()]
113 .into();
114 }
115
116 fixes.push(LintFix::replace(
117 bracketed_segment.clone(),
118 filtered_children,
119 None,
120 ));
121
122 results.push(LintResult::new(Some(bracketed_segment), fixes, None, None))
123 }
124 results
125 }
126
127 fn is_fix_compatible(&self) -> bool {
128 true
129 }
130
131 fn crawl_behaviour(&self) -> Crawler {
132 RootOnlyCrawler.into()
133 }
134}
135
136impl RuleCV07 {
137 fn iter_statements(file_segment: ErasedSegment) -> Vec<ErasedSegment> {
138 file_segment
139 .segments()
140 .iter()
141 .filter_map(|seg| {
142 if seg.is_type(SyntaxKind::Batch) {
143 Some(
144 seg.segments()
145 .iter()
146 .filter(|seg| seg.is_type(SyntaxKind::Statement))
147 .cloned()
148 .collect::<Vec<_>>(),
149 )
150 } else if seg.is_type(SyntaxKind::Statement) {
151 Some(vec![seg.clone()])
152 } else {
153 None
154 }
155 })
156 .flatten()
157 .collect()
158 }
159
160 fn iter_bracketed_statements(
161 file_segment: ErasedSegment,
162 ) -> Vec<(ErasedSegment, ErasedSegment)> {
163 Self::iter_statements(file_segment)
164 .into_iter()
165 .flat_map(|stmt| {
166 stmt.segments()
167 .iter()
168 .filter_map(|seg| {
169 if seg.is_type(SyntaxKind::Bracketed) {
170 Some((stmt.clone(), seg.clone()))
171 } else {
172 None
173 }
174 })
175 .collect::<Vec<_>>()
176 })
177 .collect()
178 }
179}