Skip to content

Commit

Permalink
refacto(core): switch to semantic query and remove custom db
Browse files Browse the repository at this point in the history
  • Loading branch information
0xLucqs committed Aug 22, 2024
1 parent 855b77b commit b12283a
Show file tree
Hide file tree
Showing 15 changed files with 304 additions and 276 deletions.
203 changes: 129 additions & 74 deletions Cargo.lock

Large diffs are not rendered by default.

25 changes: 13 additions & 12 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ license = "Apache-2.0"
license-file = "LICENSE"

[workspace.dependencies]
cairo-lang-compiler = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-parser = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-utils = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-semantic = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-filesystem = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-diagnostics = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-test-plugin = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-lowering = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-syntax = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-defs = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-test-utils = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-language-server = { git = "https://github.com/0xLucqs/cairo", branch = "lucas/multiline_diag" }
cairo-lang-compiler = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-parser = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-utils = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-semantic = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-filesystem = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-diagnostics = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-test-plugin = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-lowering = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-syntax = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-defs = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-test-utils = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
cairo-lang-language-server = { git = "https://github.com/starkware-libs/cairo", branch = "main" }
salsa = "0.16.1"
indoc = "2"
test-case = "3.0"
pretty_assertions = "1.4.0"
ctor = "0.2.8"
paste = "1.0.15"
itertools = "0.13.0"
regex = "1.10.6"
1 change: 1 addition & 0 deletions crates/cairo-lint-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ cairo-lang-syntax.workspace = true
cairo-lang-defs.workspace = true
cairo-lang-language-server.workspace = true
salsa.workspace = true
regex.workspace = true

[dev-dependencies]
indoc.workspace = true
Expand Down
103 changes: 0 additions & 103 deletions crates/cairo-lint-core/src/db.rs

This file was deleted.

6 changes: 3 additions & 3 deletions crates/cairo-lint-core/src/fix.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cairo_lang_compiler::db::RootDatabase;
use cairo_lang_defs::plugin::PluginDiagnostic;
use cairo_lang_filesystem::span::TextSpan;
use cairo_lang_semantic::diagnostic::SemanticDiagnosticKind;
Expand All @@ -7,7 +8,6 @@ use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{SyntaxNode, TypedSyntaxNode};
use cairo_lang_utils::Upcast;

use crate::db::AnalysisDatabase;
use crate::lints::single_match::is_expr_unit;
use crate::plugin::{diagnostic_kind_from_message, CairoLintKind};

Expand All @@ -17,7 +17,7 @@ pub struct Fix {
pub suggestion: String,
}

pub fn fix_semantic_diagnostic(db: &AnalysisDatabase, diag: &SemanticDiagnostic) -> String {
pub fn fix_semantic_diagnostic(db: &RootDatabase, diag: &SemanticDiagnostic) -> String {
match diag.kind {
SemanticDiagnosticKind::UnusedVariable => {
format!("_{}", diag.stable_location.syntax_node(db.upcast()).get_text(db.upcast()))
Expand Down Expand Up @@ -62,7 +62,7 @@ impl Fixer {
first_expr.expression(db).as_syntax_node().get_text_without_trivia(db),
)
}
pub fn fix_plugin_diagnostic(&self, db: &AnalysisDatabase, diag: &PluginDiagnostic) -> String {
pub fn fix_plugin_diagnostic(&self, db: &RootDatabase, diag: &PluginDiagnostic) -> String {
match diagnostic_kind_from_message(&diag.message) {
CairoLintKind::DestructMatch => self.fix_destruct_match(db, diag.stable_ptr.lookup(db.upcast())),
_ => "".to_owned(),
Expand Down
1 change: 0 additions & 1 deletion crates/cairo-lint-core/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![feature(let_chains)]
pub mod db;
pub mod fix;
pub mod lints;
pub mod plugin;
49 changes: 49 additions & 0 deletions crates/cairo-lint-core/src/lints/loops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use cairo_lang_defs::plugin::PluginDiagnostic;
use cairo_lang_diagnostics::Severity;
use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_semantic::{Arenas, Expr, ExprLoop, Statement};

pub const LOOP_MATCH_POP_FRONT: &str =
"you seem to be trying to use `loop` for iterating over a span. Consider using `for in`";

const SPAN_MATCH_POP_FRONT: &str = "\"SpanImpl::pop_front\"";

pub fn check_loop_match_pop_front(
db: &dyn SemanticGroup,
loop_expr: &ExprLoop,
diagnostics: &mut Vec<PluginDiagnostic>,
arenas: &Arenas,
) {
let Expr::Block(expr_block) = &arenas.exprs[loop_expr.body] else {
return;
};
if let Some(tail) = &expr_block.tail
&& let Expr::Match(expr_match) = &arenas.exprs[*tail]
&& let Expr::FunctionCall(func_call) = &arenas.exprs[expr_match.matched_expr]
{
if func_call.function.name(db) == SPAN_MATCH_POP_FRONT {
diagnostics.push(PluginDiagnostic {
stable_ptr: loop_expr.stable_ptr.into(),
message: LOOP_MATCH_POP_FRONT.to_owned(),
severity: Severity::Warning,
});
return;
}
}
for statement in &expr_block.statements {
if let Statement::Expr(stmt_expr) = &arenas.statements[*statement]
&& let Expr::Match(expr_match) = &arenas.exprs[stmt_expr.expr]
{
let Expr::FunctionCall(func_call) = &arenas.exprs[expr_match.matched_expr] else {
continue;
};
if func_call.function.name(db) == SPAN_MATCH_POP_FRONT {
diagnostics.push(PluginDiagnostic {
stable_ptr: loop_expr.stable_ptr.into(),
message: LOOP_MATCH_POP_FRONT.to_owned(),
severity: Severity::Warning,
})
}
}
}
}
1 change: 1 addition & 0 deletions crates/cairo-lint-core/src/lints/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod loops;
pub mod single_match;
66 changes: 29 additions & 37 deletions crates/cairo-lint-core/src/lints/single_match.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use cairo_lang_defs::ids::{FileIndex, ModuleFileId, ModuleId};
use cairo_lang_defs::plugin::PluginDiagnostic;
use cairo_lang_diagnostics::{DiagnosticsBuilder, Severity};
use cairo_lang_diagnostics::Severity;
use cairo_lang_semantic::corelib::unit_ty;
use cairo_lang_semantic::db::SemanticGroup;
use cairo_lang_semantic::diagnostic::NotFoundItemType;
use cairo_lang_semantic::expr::inference::InferenceId;
use cairo_lang_semantic::resolve::{AsSegments, ResolvedGenericItem, Resolver};
use cairo_lang_syntax::node::ast::{Expr, ExprBlock, ExprListParenthesized, ExprMatch, Pattern, Statement};
use cairo_lang_semantic::{Arenas, ExprMatch, Pattern};
use cairo_lang_syntax::node::ast::{Expr as AstExpr, ExprBlock, ExprListParenthesized, Statement};
use cairo_lang_syntax::node::db::SyntaxGroup;
use cairo_lang_syntax::node::{TypedStablePtr, TypedSyntaxNode};
use cairo_lang_utils::try_extract_matches;

pub const DESTRUCT_MATCH: &str =
"you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
Expand All @@ -25,7 +22,7 @@ fn is_block_expr_unit_without_comment(block_expr: &ExprBlock, db: &dyn SyntaxGro
}
if statements.len() == 1
&& let Statement::Expr(statement_expr) = &statements[0]
&& let Expr::Tuple(tuple_expr) = statement_expr.expr(db)
&& let AstExpr::Tuple(tuple_expr) = statement_expr.expr(db)
{
let tuple_node = tuple_expr.as_syntax_node();
if tuple_node.span(db).start != tuple_node.span_start_without_trivia(db) {
Expand All @@ -37,73 +34,68 @@ fn is_block_expr_unit_without_comment(block_expr: &ExprBlock, db: &dyn SyntaxGro
}
}

pub fn is_expr_unit(expr: Expr, db: &dyn SyntaxGroup) -> bool {
pub fn is_expr_unit(expr: AstExpr, db: &dyn SyntaxGroup) -> bool {
match expr {
Expr::Block(block_expr) => is_block_expr_unit_without_comment(&block_expr, db),
Expr::Tuple(tuple_expr) => is_expr_list_parenthesised_unit(&tuple_expr, db),
AstExpr::Block(block_expr) => is_block_expr_unit_without_comment(&block_expr, db),
AstExpr::Tuple(tuple_expr) => is_expr_list_parenthesised_unit(&tuple_expr, db),
_ => false,
}
}
pub fn check_single_match(
db: &dyn SemanticGroup,
match_expr: &ExprMatch,
diagnostics: &mut Vec<PluginDiagnostic>,
module_id: &ModuleId,
arenas: &Arenas,
) {
let syntax_db = db.upcast();
let arms = match_expr.arms(syntax_db).elements(syntax_db);
let arms = &match_expr.arms;
let mut is_single_armed = false;
let mut is_complete = false;
let mut is_destructuring = false;
if arms.len() == 2 {

if arms.len() == 2 && match_expr.ty == unit_ty(db) {
let first_arm = &arms[0];
let second_arm = &arms[1];
let mut enum_len = None;
let mut is_first_arm_unit = false;
if let Some(pattern) = first_arm.patterns(syntax_db).elements(syntax_db).first() {
match pattern {
Pattern::Underscore(_) => return,
Pattern::Enum(pat) => {
let mut diagnostics = DiagnosticsBuilder::default();
let path = pat.path(syntax_db).to_segments(syntax_db);
let item = Resolver::new(db, ModuleFileId(*module_id, FileIndex(0)), InferenceId::NoContext)
.resolve_generic_path(&mut diagnostics, path, NotFoundItemType::Identifier)
.unwrap();
let generic_variant = try_extract_matches!(item, ResolvedGenericItem::Variant).unwrap();
enum_len = Some(db.enum_variants(generic_variant.enum_id).unwrap().len());
if let Some(pattern) = first_arm.patterns.first() {
match &arenas.patterns[*pattern] {
Pattern::Otherwise(_) => return,
Pattern::EnumVariant(enum_pat) => {
enum_len = Some(db.enum_variants(enum_pat.variant.concrete_enum_id.enum_id(db)).unwrap().len());
is_destructuring = true;
}
Pattern::Struct(_) => {
is_destructuring = true;
}
_ => (),
}
is_first_arm_unit = is_expr_unit(first_arm.expression(syntax_db), syntax_db)
};
};
if let Some(pattern) = second_arm.patterns(syntax_db).elements(syntax_db).first() {
match pattern {
Pattern::Underscore(_) => {
if let Some(pattern) = second_arm.patterns.first() {
match &arenas.patterns[*pattern] {
Pattern::Otherwise(_) => {
is_complete = true;
}
Pattern::Enum(_) => {
Pattern::EnumVariant(_) => {
if enum_len == Some(2) {
is_complete = true;
}
}
_ => (),
}
};

is_single_armed =
is_expr_unit(second_arm.expression(syntax_db), syntax_db) && is_complete || is_first_arm_unit;
is_expr_unit(arenas.exprs[second_arm.expression].stable_ptr().lookup(db.upcast()), db.upcast())
&& is_complete;
};
};

match (is_single_armed, is_destructuring) {
(true, false) => diagnostics.push(PluginDiagnostic {
stable_ptr: match_expr.stable_ptr().untyped(),
stable_ptr: match_expr.stable_ptr.into(),
message: MATCH_FOR_EQUALITY.to_string(),
severity: Severity::Warning,
}),
(true, true) => diagnostics.push(PluginDiagnostic {
stable_ptr: match_expr.stable_ptr().untyped(),
stable_ptr: match_expr.stable_ptr.into(),
message: DESTRUCT_MATCH.to_string(),
severity: Severity::Warning,
}),
Expand Down
Loading

0 comments on commit b12283a

Please sign in to comment.