Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
603 changes: 267 additions & 336 deletions crates/flowscope-core/src/linter/mod.rs

Large diffs are not rendered by default.

497 changes: 431 additions & 66 deletions crates/flowscope-core/src/linter/rule.rs

Large diffs are not rendered by default.

29 changes: 8 additions & 21 deletions crates/flowscope-core/src/linter/rules/al_001.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! SQLFluff parity: configurable table aliasing style (`explicit`/`implicit`).

use crate::linter::config::LintConfig;
use crate::linter::rule::{LintContext, LintRule};
use crate::linter::rule::{BuiltinLintRule, RuleContext};
use crate::types::{issue_codes, Dialect, Issue, IssueAutofixApplicability, IssuePatchEdit, Span};
use sqlparser::ast::{
CreateView, Expr, FromTable, Ident, Merge, Query, SetExpr, Statement, TableFactor,
Expand Down Expand Up @@ -65,7 +65,7 @@ impl Default for AliasingTableStyle {
}
}

impl LintRule for AliasingTableStyle {
impl BuiltinLintRule for AliasingTableStyle {
fn code(&self) -> &'static str {
issue_codes::LINT_AL_001
}
Expand All @@ -78,7 +78,7 @@ impl LintRule for AliasingTableStyle {
"Implicit/explicit aliasing of table."
}

fn check(&self, statement: &Statement, ctx: &LintContext) -> Vec<Issue> {
fn check_with_context(&self, statement: &Statement, ctx: &RuleContext) -> Vec<Issue> {
let mut issues = Vec::new();
let tokens =
tokenized_for_context(ctx).or_else(|| tokenized(ctx.statement_sql(), ctx.dialect()));
Expand Down Expand Up @@ -147,7 +147,7 @@ fn autofix_edits_for_occurrence(

fn alias_occurrence_in_statement(
alias: &Ident,
ctx: &LintContext,
ctx: &RuleContext,
tokens: Option<&[LocatedToken]>,
) -> Option<AliasOccurrence> {
let tokens = tokens?;
Expand Down Expand Up @@ -530,7 +530,7 @@ fn tokenized(sql: &str, dialect: Dialect) -> Option<Vec<LocatedToken>> {
Some(out)
}

fn tokenized_for_context(ctx: &LintContext) -> Option<Vec<LocatedToken>> {
fn tokenized_for_context(ctx: &RuleContext) -> Option<Vec<LocatedToken>> {
let statement_start = ctx.statement_range.start;
ctx.with_document_tokens(|tokens| {
if tokens.is_empty() {
Expand Down Expand Up @@ -622,14 +622,7 @@ mod tests {
.iter()
.enumerate()
.flat_map(|(index, stmt)| {
rule.check(
stmt,
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: index,
},
)
rule.check_with_context(stmt, &RuleContext::new(sql, 0..sql.len(), index))
})
.collect()
}
Expand Down Expand Up @@ -683,14 +676,8 @@ mod tests {
.iter()
.enumerate()
.flat_map(|(index, stmt)| {
AliasingTableStyle::default().check(
stmt,
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: index,
},
)
AliasingTableStyle::default()
.check_with_context(stmt, &RuleContext::new(sql, 0..sql.len(), index))
})
.collect::<Vec<_>>();
assert_eq!(issues.len(), 2);
Expand Down
29 changes: 8 additions & 21 deletions crates/flowscope-core/src/linter/rules/al_002.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! SQLFluff parity: configurable column aliasing style (`explicit`/`implicit`).

use crate::linter::config::LintConfig;
use crate::linter::rule::{LintContext, LintRule};
use crate::linter::rule::{BuiltinLintRule, RuleContext};
use crate::types::{issue_codes, Dialect, Issue, IssueAutofixApplicability, IssuePatchEdit, Span};
use sqlparser::ast::{Ident, SelectItem, Spanned, Statement};
use sqlparser::tokenizer::{Token, TokenWithSpan, Tokenizer, Whitespace};
Expand Down Expand Up @@ -64,7 +64,7 @@ impl Default for AliasingColumnStyle {
}
}

impl LintRule for AliasingColumnStyle {
impl BuiltinLintRule for AliasingColumnStyle {
fn code(&self) -> &'static str {
issue_codes::LINT_AL_002
}
Expand All @@ -77,7 +77,7 @@ impl LintRule for AliasingColumnStyle {
"Implicit/explicit aliasing of columns."
}

fn check(&self, statement: &Statement, ctx: &LintContext) -> Vec<Issue> {
fn check_with_context(&self, statement: &Statement, ctx: &RuleContext) -> Vec<Issue> {
let mut issues = Vec::new();
let tokens =
tokenized_for_context(ctx).or_else(|| tokenized(ctx.statement_sql(), ctx.dialect()));
Expand Down Expand Up @@ -152,7 +152,7 @@ fn autofix_edits_for_occurrence(
fn alias_occurrence_in_statement(
alias: &Ident,
item: &SelectItem,
ctx: &LintContext,
ctx: &RuleContext,
tokens: Option<&[LocatedToken]>,
) -> Option<AliasOccurrence> {
let tokens = tokens?;
Expand Down Expand Up @@ -261,7 +261,7 @@ fn tokenized(sql: &str, dialect: Dialect) -> Option<Vec<LocatedToken>> {
Some(out)
}

fn tokenized_for_context(ctx: &LintContext) -> Option<Vec<LocatedToken>> {
fn tokenized_for_context(ctx: &RuleContext) -> Option<Vec<LocatedToken>> {
let statement_start = ctx.statement_range.start;
ctx.with_document_tokens(|tokens| {
if tokens.is_empty() {
Expand Down Expand Up @@ -353,14 +353,7 @@ mod tests {
.iter()
.enumerate()
.flat_map(|(index, stmt)| {
rule.check(
stmt,
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: index,
},
)
rule.check_with_context(stmt, &RuleContext::new(sql, 0..sql.len(), index))
})
.collect()
}
Expand Down Expand Up @@ -453,14 +446,8 @@ mod tests {
fn allows_tsql_assignment_style_alias() {
let sql = "select alias1 = col1";
let statements = parse_sql_with_dialect(sql, Dialect::Mssql).expect("parse");
let issues = AliasingColumnStyle::default().check(
&statements[0],
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
},
);
let issues = AliasingColumnStyle::default()
.check_with_context(&statements[0], &RuleContext::new(sql, 0..sql.len(), 0));
assert!(issues.is_empty());
}
}
20 changes: 8 additions & 12 deletions crates/flowscope-core/src/linter/rules/al_003.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! an explicit alias for clarity and portability.

use crate::linter::config::LintConfig;
use crate::linter::rule::{LintContext, LintRule};
use crate::linter::rule::{BuiltinLintRule, RuleContext};
use crate::types::{issue_codes, Issue};
use sqlparser::ast::*;

Expand All @@ -29,7 +29,7 @@ impl Default for ImplicitAlias {
}
}

impl LintRule for ImplicitAlias {
impl BuiltinLintRule for ImplicitAlias {
fn code(&self) -> &'static str {
issue_codes::LINT_AL_003
}
Expand All @@ -42,7 +42,7 @@ impl LintRule for ImplicitAlias {
"Column expression without alias. Use explicit `AS` clause."
}

fn check(&self, stmt: &Statement, ctx: &LintContext) -> Vec<Issue> {
fn check_with_context(&self, stmt: &Statement, ctx: &RuleContext) -> Vec<Issue> {
let mut issues = Vec::new();
check_statement(stmt, ctx, self.allow_scalar, &mut issues);
issues
Expand All @@ -51,7 +51,7 @@ impl LintRule for ImplicitAlias {

fn check_statement(
stmt: &Statement,
ctx: &LintContext,
ctx: &RuleContext,
allow_scalar: bool,
issues: &mut Vec<Issue>,
) {
Expand All @@ -76,7 +76,7 @@ fn check_statement(

fn check_query(
query: &Query,
ctx: &LintContext,
ctx: &RuleContext,
allow_scalar: bool,
issues: &mut Vec<Issue>,
has_cte_column_list: bool,
Expand All @@ -95,7 +95,7 @@ fn check_query(

fn check_set_expr(
body: &SetExpr,
ctx: &LintContext,
ctx: &RuleContext,
allow_scalar: bool,
issues: &mut Vec<Issue>,
has_cte_column_list: bool,
Expand Down Expand Up @@ -212,14 +212,10 @@ mod tests {

fn check_sql_with_rule(sql: &str, rule: ImplicitAlias) -> Vec<Issue> {
let stmts = parse_sql(sql).unwrap();
let ctx = LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
};
let ctx = RuleContext::new(sql, 0..sql.len(), 0);
let mut issues = Vec::new();
for stmt in &stmts {
issues.extend(rule.check(stmt, &ctx));
issues.extend(rule.check_with_context(stmt, &ctx));
}
issues
}
Expand Down
75 changes: 16 additions & 59 deletions crates/flowscope-core/src/linter/rules/al_004.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Table aliases should be unique within a query scope.

use crate::linter::config::LintConfig;
use crate::linter::rule::{LintContext, LintRule};
use crate::linter::rule::{BuiltinLintRule, RuleContext};
use crate::types::{issue_codes, Issue};
use sqlparser::ast::{
CreateView, Expr, FunctionArg, FunctionArgExpr, FunctionArguments, Query, Select, SetExpr,
Expand Down Expand Up @@ -65,7 +65,7 @@ impl Default for AliasingUniqueTable {
}
}

impl LintRule for AliasingUniqueTable {
impl BuiltinLintRule for AliasingUniqueTable {
fn code(&self) -> &'static str {
issue_codes::LINT_AL_004
}
Expand All @@ -78,7 +78,7 @@ impl LintRule for AliasingUniqueTable {
"Table aliases should be unique within each clause."
}

fn check(&self, statement: &Statement, ctx: &LintContext) -> Vec<Issue> {
fn check_with_context(&self, statement: &Statement, ctx: &RuleContext) -> Vec<Issue> {
if first_duplicate_table_alias_in_statement(statement, self.alias_case_check).is_none() {
return Vec::new();
}
Expand Down Expand Up @@ -664,14 +664,7 @@ mod tests {
.iter()
.enumerate()
.flat_map(|(index, statement)| {
rule.check(
statement,
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: index,
},
)
rule.check_with_context(statement, &RuleContext::new(sql, 0..sql.len(), index))
})
.collect()
}
Expand Down Expand Up @@ -770,14 +763,8 @@ mod tests {
serde_json::json!({"alias_case_check": "case_sensitive"}),
)]),
});
let issues = rule.check(
&statements[0],
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
},
);
let issues =
rule.check_with_context(&statements[0], &RuleContext::new(sql, 0..sql.len(), 0));
assert!(issues.is_empty());
}

Expand All @@ -793,14 +780,8 @@ mod tests {
serde_json::json!({"alias_case_check": "case_sensitive"}),
)]),
});
let issues = rule.check(
&statements[0],
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
},
);
let issues =
rule.check_with_context(&statements[0], &RuleContext::new(sql, 0..sql.len(), 0));
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].code, issue_codes::LINT_AL_004);
}
Expand All @@ -817,14 +798,8 @@ mod tests {
serde_json::json!({"alias_case_check": "quoted_cs_naked_upper"}),
)]),
});
let issues = rule.check(
&statements[0],
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
},
);
let issues =
rule.check_with_context(&statements[0], &RuleContext::new(sql, 0..sql.len(), 0));
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].code, issue_codes::LINT_AL_004);
}
Expand All @@ -841,14 +816,8 @@ mod tests {
serde_json::json!({"alias_case_check": "quoted_cs_naked_upper"}),
)]),
});
let issues = rule.check(
&statements[0],
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
},
);
let issues =
rule.check_with_context(&statements[0], &RuleContext::new(sql, 0..sql.len(), 0));
assert!(issues.is_empty());
}

Expand All @@ -864,14 +833,8 @@ mod tests {
serde_json::json!({"alias_case_check": "quoted_cs_naked_lower"}),
)]),
});
let issues = rule.check(
&statements[0],
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
},
);
let issues =
rule.check_with_context(&statements[0], &RuleContext::new(sql, 0..sql.len(), 0));
assert_eq!(issues.len(), 1);
assert_eq!(issues[0].code, issue_codes::LINT_AL_004);
}
Expand All @@ -888,14 +851,8 @@ mod tests {
serde_json::json!({"alias_case_check": "quoted_cs_naked_lower"}),
)]),
});
let issues = rule.check(
&statements[0],
&LintContext {
sql,
statement_range: 0..sql.len(),
statement_index: 0,
},
);
let issues =
rule.check_with_context(&statements[0], &RuleContext::new(sql, 0..sql.len(), 0));
assert!(issues.is_empty());
}
}
Loading
Loading