Skip to content

Commit

Permalink
refacto(core): change file structure for lints
Browse files Browse the repository at this point in the history
  • Loading branch information
0xLucqs committed Aug 9, 2024
1 parent ae55fe2 commit a209b17
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 84 deletions.
2 changes: 2 additions & 0 deletions crates/cairo-lint-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(let_chains)]
pub mod db;
pub mod fix;
pub mod lints;
pub mod plugin;
1 change: 1 addition & 0 deletions crates/cairo-lint-core/src/lints/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod single_match;
70 changes: 70 additions & 0 deletions crates/cairo-lint-core/src/lints/single_match.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use cairo_lang_defs::plugin::PluginDiagnostic;
use cairo_lang_diagnostics::Severity;
use cairo_lang_syntax::node::ast::{Expr, ExprBlock, ExprListParenthesized, ExprMatch, Pattern, Statement};
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};

pub const DESTRUCT_MATCH: &str =
"you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
pub const MATCH_FOR_EQUALITY: &str = "you seem to be trying to use `match` for an equality check. Consider using `if`";

fn tuple_expr_in_block_expr(
db: &dyn SyntaxGroup,
block_expr: &ExprBlock,
is_single_armed: &mut bool,
) -> Option<ExprListParenthesized> {
let statements = block_expr.statements(db).elements(db);
if statements.is_empty() {
*is_single_armed = true;
}
if statements.len() == 1
&& let Statement::Expr(statement_expr) = &statements[0]
&& let Expr::Tuple(tuple_expr) = statement_expr.expr(db)
{
Some(tuple_expr)
} else {
None
}
}
pub fn check_destruct_match(db: &dyn SyntaxGroup, match_expr: &ExprMatch, diagnostics: &mut Vec<PluginDiagnostic>) {
let arms = match_expr.arms(db).elements(db);
let mut is_single_armed = false;
let mut is_destructuring = false;
if arms.len() == 2 {
for arm in arms {
let patterns = arm.patterns(db).elements(db);
match &patterns[0] {
Pattern::Underscore(_) => {
let tuple_expr = match arm.expression(db) {
Expr::Block(block_expr) => tuple_expr_in_block_expr(db, &block_expr, &mut is_single_armed),
Expr::Tuple(tuple_expr) => Some(tuple_expr),
_ => None,
};
is_single_armed =
tuple_expr.is_some_and(|list| list.expressions(db).elements(db).is_empty()) || is_single_armed;
}

Pattern::Enum(pat) => {
is_destructuring = !pat.pattern(db).as_syntax_node().get_text(db).is_empty();
}
Pattern::Struct(pat) => {
is_destructuring = !pat.as_syntax_node().get_text(db).is_empty();
}
_ => (),
};
}
};
match (is_single_armed, is_destructuring) {
(true, false) => diagnostics.push(PluginDiagnostic {
stable_ptr: match_expr.stable_ptr().untyped(),
message: MATCH_FOR_EQUALITY.to_string(),
severity: Severity::Warning,
}),
(true, true) => diagnostics.push(PluginDiagnostic {
stable_ptr: match_expr.stable_ptr().untyped(),
message: DESTRUCT_MATCH.to_string(),
severity: Severity::Warning,
}),
(_, _) => (),
}
}
90 changes: 7 additions & 83 deletions crates/cairo-lint-core/src/plugin.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use std::ops::Deref;

use cairo_lang_defs::ids::{ModuleId, ModuleItemId};
use cairo_lang_defs::plugin::PluginDiagnostic;
use cairo_lang_diagnostics::Severity;
use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_semantic::plugin::{AnalyzerPlugin, PluginSuite};
use cairo_lang_syntax::node::ast::{Expr, ExprMatch, Pattern, Statement};
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::ast::ExprMatch;
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
use cairo_lang_syntax::node::TypedSyntaxNode;

use crate::lints::single_match;

pub fn cairo_lint_plugin_suite() -> PluginSuite {
let mut suite = PluginSuite::default();
Expand All @@ -27,86 +25,12 @@ pub enum CairoLintKind {

pub fn diagnostic_kind_from_message(message: &str) -> CairoLintKind {
match message {
CairoLint::DESTRUCT_MATCH => CairoLintKind::DestructMatch,
CairoLint::MATCH_FOR_EQUALITY => CairoLintKind::MatchForEquality,
single_match::DESTRUCT_MATCH => CairoLintKind::DestructMatch,
single_match::MATCH_FOR_EQUALITY => CairoLintKind::MatchForEquality,
_ => CairoLintKind::Unknown,
}
}

impl CairoLint {
const DESTRUCT_MATCH: &'static str =
"you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
const MATCH_FOR_EQUALITY: &'static str =
"you seem to be trying to use `match` for an equality check. Consider using `if`";

pub fn check_destruct_match(
&self,
db: &dyn SyntaxGroup,
match_expr: &ExprMatch,
diagnostics: &mut Vec<PluginDiagnostic>,
) {
let arms = match_expr.arms(db).deref().elements(db);
let mut is_single_armed = false;
let mut is_destructuring = false;
if arms.len() == 2 {
for arm in arms {
let patterns = arm.patterns(db).elements(db);
match patterns[0].clone() {
Pattern::Underscore(_) => {
let tuple_expr = match arm.expression(db) {
Expr::Block(block_expr) => {
let statements = block_expr.statements(db).elements(db);
if statements.is_empty() {
is_single_armed = true;
}
if statements.len() == 1 {
match &statements[0] {
Statement::Expr(statement_expr) => {
if let Expr::Tuple(tuple_expr) = statement_expr.expr(db) {
Some(tuple_expr)
} else {
None
}
}
_ => None,
}
} else {
None
}
}
Expr::Tuple(tuple_expr) => Some(tuple_expr),
_ => None,
};
is_single_armed = tuple_expr.is_some_and(|list| list.expressions(db).elements(db).is_empty())
|| is_single_armed;
}

Pattern::Enum(pat) => {
is_destructuring = !pat.pattern(db).as_syntax_node().get_text(db).is_empty();
}
Pattern::Struct(pat) => {
is_destructuring = !pat.as_syntax_node().get_text(db).is_empty();
}
_ => (),
};
}
};
match (is_single_armed, is_destructuring) {
(true, false) => diagnostics.push(PluginDiagnostic {
stable_ptr: match_expr.stable_ptr().untyped(),
message: Self::MATCH_FOR_EQUALITY.to_string(),
severity: Severity::Warning,
}),
(true, true) => diagnostics.push(PluginDiagnostic {
stable_ptr: match_expr.stable_ptr().untyped(),
message: Self::DESTRUCT_MATCH.to_string(),
severity: Severity::Warning,
}),
(_, _) => (),
}
}
}

impl AnalyzerPlugin for CairoLint {
fn diagnostics(&self, db: &dyn SemanticGroup, module_id: ModuleId) -> Vec<PluginDiagnostic> {
let mut diags = Vec::new();
Expand All @@ -121,7 +45,7 @@ impl AnalyzerPlugin for CairoLint {
let descendants = func.as_syntax_node().descendants(db.upcast());
for descendant in descendants.into_iter() {
match descendant.kind(db.upcast()) {
SyntaxKind::ExprMatch => self.check_destruct_match(
SyntaxKind::ExprMatch => single_match::check_destruct_match(
db.upcast(),
&ExprMatch::from_syntax_node(db.upcast(), descendant),
&mut diags,
Expand Down
2 changes: 1 addition & 1 deletion rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[toolchain]
channel = "1.80.0"
channel = "nightly"
components = ["rustfmt", "clippy"]
profile = "minimal"

0 comments on commit a209b17

Please sign in to comment.