selene_lib/lints/
mixed_table.rs1use super::*;
2use crate::ast_util::range;
3use std::convert::Infallible;
4
5use full_moon::{
6 ast::{self, Ast},
7 visitors::Visitor,
8};
9
10pub struct MixedTableLint;
11
12impl Lint for MixedTableLint {
13 type Config = ();
14 type Error = Infallible;
15
16 const SEVERITY: Severity = Severity::Warning;
17 const LINT_TYPE: LintType = LintType::Correctness;
18
19 fn new(_: Self::Config) -> Result<Self, Self::Error> {
20 Ok(MixedTableLint)
21 }
22
23 fn pass(&self, ast: &Ast, _: &Context, _: &AstContext) -> Vec<Diagnostic> {
24 let mut visitor = MixedTableVisitor::default();
25
26 visitor.visit_ast(ast);
27
28 let mut diagnostics = Vec::new();
29
30 for mixed_table in visitor.mixed_tables {
31 diagnostics.push(Diagnostic::new_complete(
32 "mixed_table",
33 "mixed tables should be avoided, as they can cause confusing and hard to debug issues such as during iteration or encoding".to_owned(),
34 Label::new(mixed_table.range),
35 vec!["help: change this table to either an array or dictionary".to_owned()],
36 Vec::new(),
37 ));
38 }
39
40 diagnostics
41 }
42}
43
44#[derive(Default)]
45struct MixedTableVisitor {
46 mixed_tables: Vec<MixedTable>,
47}
48
49struct MixedTable {
50 range: (usize, usize),
51}
52
53impl Visitor for MixedTableVisitor {
54 fn visit_table_constructor(&mut self, node: &ast::TableConstructor) {
55 let mut last_key_field_starting_range = 0;
56 let mut last_no_key_field_starting_range = 0;
57
58 for field in node.fields() {
59 if let ast::Field::NoKey(_) = field {
60 if last_key_field_starting_range > 0 {
61 self.mixed_tables.push(MixedTable {
62 range: (last_key_field_starting_range, range(field).1),
63 });
64 return;
65 }
66 last_no_key_field_starting_range = range(field).0;
67 } else {
68 if last_no_key_field_starting_range > 0 {
69 self.mixed_tables.push(MixedTable {
70 range: (last_no_key_field_starting_range, range(field).1),
71 });
72 return;
73 }
74 last_key_field_starting_range = range(field).0;
75 }
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::{super::test_util::test_lint, *};
83
84 #[test]
85 fn test_mixed_table() {
86 test_lint(
87 MixedTableLint::new(()).unwrap(),
88 "mixed_table",
89 "mixed_table",
90 );
91 }
92}