rigsql_rules/ambiguous/
am08.rs1use rigsql_core::SegmentType;
2
3use crate::rule::{CrawlType, Rule, RuleContext, RuleGroup};
4use crate::violation::LintViolation;
5
6#[derive(Debug, Default)]
11pub struct RuleAM08;
12
13impl Rule for RuleAM08 {
14 fn code(&self) -> &'static str {
15 "AM08"
16 }
17 fn name(&self) -> &'static str {
18 "ambiguous.join_condition"
19 }
20 fn description(&self) -> &'static str {
21 "Implicit cross join in FROM clause."
22 }
23 fn explanation(&self) -> &'static str {
24 "Using commas to separate tables in a FROM clause creates an implicit cross join \
25 (Cartesian product). Use explicit CROSS JOIN syntax instead, or use appropriate \
26 JOIN ... ON clauses if you intend a different join type."
27 }
28 fn groups(&self) -> &[RuleGroup] {
29 &[RuleGroup::Ambiguous]
30 }
31 fn is_fixable(&self) -> bool {
32 false
33 }
34
35 fn crawl_type(&self) -> CrawlType {
36 CrawlType::Segment(vec![SegmentType::FromClause])
37 }
38
39 fn eval(&self, ctx: &RuleContext) -> Vec<LintViolation> {
40 let children = ctx.segment.children();
41
42 children
43 .iter()
44 .filter(|c| c.segment_type() == SegmentType::Comma)
45 .map(|comma| {
46 LintViolation::with_msg_key(
47 self.code(),
48 "Implicit cross join via comma in FROM clause. Use explicit JOIN.",
49 comma.span(),
50 "rules.AM08.msg",
51 vec![],
52 )
53 })
54 .collect()
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61 use crate::test_utils::lint_sql;
62
63 #[test]
64 fn test_am08_flags_comma_join() {
65 let violations = lint_sql("SELECT a FROM t, u", RuleAM08);
66 assert_eq!(violations.len(), 1);
67 }
68
69 #[test]
70 fn test_am08_accepts_explicit_join() {
71 let violations = lint_sql("SELECT a FROM t INNER JOIN u ON t.id = u.id", RuleAM08);
72 assert_eq!(violations.len(), 0);
73 }
74
75 #[test]
76 fn test_am08_flags_multiple_commas() {
77 let violations = lint_sql("SELECT a FROM t, u, v", RuleAM08);
78 assert_eq!(violations.len(), 2);
79 }
80}