Skip to content

Commit

Permalink
feat:(lint, walker), add lint check in resolver.
Browse files Browse the repository at this point in the history
1. Add warning level diagnotics, including 'UnusedImportWarning', 'ReimportWarning' and 'ImportPostionWarning'

2. Add 'lint_check_module' method to Resolver and is called in the 'check()' method.

3. Add KCL Lint stuct, For details see issue kcl-lang#109

fix kcl-lang#102, kcl-lang#109
  • Loading branch information
He1pa committed Jul 15, 2022
1 parent 7959955 commit 2b4eb8e
Show file tree
Hide file tree
Showing 18 changed files with 1,035 additions and 11 deletions.
1 change: 1 addition & 0 deletions kclvm/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

264 changes: 264 additions & 0 deletions kclvm/ast/src/walker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -990,3 +990,267 @@ pub fn walk_comment<'ctx, V: Walker<'ctx>>(walker: &mut V, comment: &'ctx ast::C
pub fn walk_module<'ctx, V: Walker<'ctx>>(walker: &mut V, module: &'ctx ast::Module) {
walk_list!(walker, walk_stmt, module.body)
}
/// Each method of the `MutSelfWalker` trait returns void type and does not need to modify the AST.
/// We can use it to traverse the AST and do some check at the same time, For example, in the process
/// of lint checking, we can use it to check each AST node and generate diagnostcs.
pub trait MutSelfWalker<'ctx> {
fn walk_expr_stmt(&mut self, expr_stmt: &'ctx ast::ExprStmt) {
for expr in &expr_stmt.exprs {
self.walk_expr(&expr.node)
}
}

fn walk_type_alias_stmt(&mut self, type_alias_stmt: &'ctx ast::TypeAliasStmt) {
self.walk_identifier(&type_alias_stmt.type_name.node);
}
fn walk_unification_stmt(&mut self, unification_stmt: &'ctx ast::UnificationStmt) {
self.walk_identifier(&unification_stmt.target.node);
self.walk_schema_expr(&unification_stmt.value.node);
}
fn walk_assign_stmt(&mut self, assign_stmt: &'ctx ast::AssignStmt) {
for target in &assign_stmt.targets {
self.walk_identifier(&target.node)
}
self.walk_expr(&assign_stmt.value.node);
}
fn walk_aug_assign_stmt(&mut self, aug_assign_stmt: &'ctx ast::AugAssignStmt) {
self.walk_identifier(&aug_assign_stmt.target.node);
self.walk_expr(&aug_assign_stmt.value.node);
}
fn walk_assert_stmt(&mut self, assert_stmt: &'ctx ast::AssertStmt) {
self.walk_expr(&assert_stmt.test.node);
walk_if!(self, walk_expr, assert_stmt.if_cond);
walk_if!(self, walk_expr, assert_stmt.msg);
}
fn walk_if_stmt(&mut self, if_stmt: &'ctx ast::IfStmt) {
self.walk_expr(&if_stmt.cond.node);
walk_list!(self, walk_stmt, if_stmt.body);
walk_list!(self, walk_stmt, if_stmt.orelse);
}
fn walk_import_stmt(&mut self, _import_stmt: &'ctx ast::ImportStmt) {
// Nothing to do
}
fn walk_schema_attr(&mut self, schema_attr: &'ctx ast::SchemaAttr) {
walk_list!(self, walk_call_expr, schema_attr.decorators);
walk_if!(self, walk_expr, schema_attr.value);
}
fn walk_schema_stmt(&mut self, schema_stmt: &'ctx ast::SchemaStmt) {
walk_if!(self, walk_identifier, schema_stmt.parent_name);
walk_if!(self, walk_identifier, schema_stmt.for_host_name);
walk_if!(self, walk_arguments, schema_stmt.args);
if let Some(schema_index_signature) = &schema_stmt.index_signature {
let value = &schema_index_signature.node.value;
walk_if!(self, walk_expr, value);
}
walk_list!(self, walk_identifier, schema_stmt.mixins);
walk_list!(self, walk_call_expr, schema_stmt.decorators);
walk_list!(self, walk_check_expr, schema_stmt.checks);
walk_list!(self, walk_stmt, schema_stmt.body);
}
fn walk_rule_stmt(&mut self, rule_stmt: &'ctx ast::RuleStmt) {
walk_list!(self, walk_identifier, rule_stmt.parent_rules);
walk_list!(self, walk_call_expr, rule_stmt.decorators);
walk_list!(self, walk_check_expr, rule_stmt.checks);
walk_if!(self, walk_arguments, rule_stmt.args);
walk_if!(self, walk_identifier, rule_stmt.for_host_name);
}
fn walk_quant_expr(&mut self, quant_expr: &'ctx ast::QuantExpr) {
self.walk_expr(&quant_expr.target.node);
walk_list!(self, walk_identifier, quant_expr.variables);
self.walk_expr(&quant_expr.test.node);
walk_if!(self, walk_expr, quant_expr.if_cond);
}
fn walk_if_expr(&mut self, if_expr: &'ctx ast::IfExpr) {
self.walk_expr(&if_expr.cond.node);
self.walk_expr(&if_expr.body.node);
self.walk_expr(&if_expr.orelse.node);
}
fn walk_unary_expr(&mut self, unary_expr: &'ctx ast::UnaryExpr) {
self.walk_expr(&unary_expr.operand.node);
}
fn walk_binary_expr(&mut self, binary_expr: &'ctx ast::BinaryExpr) {
self.walk_expr(&binary_expr.left.node);
self.walk_expr(&binary_expr.right.node);
}
fn walk_selector_expr(&mut self, selector_expr: &'ctx ast::SelectorExpr) {
self.walk_expr(&selector_expr.value.node);
self.walk_identifier(&selector_expr.attr.node);
}
fn walk_call_expr(&mut self, call_expr: &'ctx ast::CallExpr) {
self.walk_expr(&call_expr.func.node);
walk_list!(self, walk_expr, call_expr.args);
walk_list!(self, walk_keyword, call_expr.keywords);
}
fn walk_subscript(&mut self, subscript: &'ctx ast::Subscript) {
self.walk_expr(&subscript.value.node);
walk_if!(self, walk_expr, subscript.index);
walk_if!(self, walk_expr, subscript.lower);
walk_if!(self, walk_expr, subscript.upper);
walk_if!(self, walk_expr, subscript.step);
}
fn walk_paren_expr(&mut self, paren_expr: &'ctx ast::ParenExpr) {
self.walk_expr(&paren_expr.expr.node);
}
fn walk_list_expr(&mut self, list_expr: &'ctx ast::ListExpr) {
walk_list!(self, walk_expr, list_expr.elts);
}
fn walk_list_comp(&mut self, list_comp: &'ctx ast::ListComp) {
self.walk_expr(&list_comp.elt.node);
walk_list!(self, walk_comp_clause, list_comp.generators);
}
fn walk_list_if_item_expr(&mut self, list_if_item_expr: &'ctx ast::ListIfItemExpr) {
self.walk_expr(&list_if_item_expr.if_cond.node);
walk_list!(self, walk_expr, list_if_item_expr.exprs);
walk_if!(self, walk_expr, list_if_item_expr.orelse);
}
fn walk_starred_expr(&mut self, starred_expr: &'ctx ast::StarredExpr) {
self.walk_expr(&starred_expr.value.node);
}
fn walk_dict_comp(&mut self, dict_comp: &'ctx ast::DictComp) {
if let Some(key) = &dict_comp.entry.key {
self.walk_expr(&key.node);
}
self.walk_expr(&dict_comp.entry.value.node);
walk_list!(self, walk_comp_clause, dict_comp.generators);
}
fn walk_config_if_entry_expr(
&mut self,
config_if_entry_expr: &'ctx ast::ConfigIfEntryExpr,
) {
self.walk_expr(&config_if_entry_expr.if_cond.node);
for config_entry in &config_if_entry_expr.items {
walk_if!(self, walk_expr, config_entry.node.key);
self.walk_expr(&config_entry.node.value.node);
}
walk_if!(self, walk_expr, config_if_entry_expr.orelse);
}
fn walk_comp_clause(&mut self, comp_clause: &'ctx ast::CompClause) {
walk_list!(self, walk_identifier, comp_clause.targets);
self.walk_expr(&comp_clause.iter.node);
walk_list!(self, walk_expr, comp_clause.ifs);
}
fn walk_schema_expr(&mut self, schema_expr: &'ctx ast::SchemaExpr) {
self.walk_identifier(&schema_expr.name.node);
walk_list!(self, walk_expr, schema_expr.args);
walk_list!(self, walk_keyword, schema_expr.kwargs);
self.walk_expr(&schema_expr.config.node);
}
fn walk_config_expr(&mut self, config_expr: &'ctx ast::ConfigExpr) {
for config_entry in &config_expr.items {
walk_if!(self, walk_expr, config_entry.node.key);
self.walk_expr(&config_entry.node.value.node);
}
}
fn walk_check_expr(&mut self, check_expr: &'ctx ast::CheckExpr) {
self.walk_expr(&check_expr.test.node);
walk_if!(self, walk_expr, check_expr.if_cond);
walk_if!(self, walk_expr, check_expr.msg);
}
fn walk_lambda_expr(&mut self, lambda_expr: &'ctx ast::LambdaExpr) {
walk_if!(self, walk_arguments, lambda_expr.args);
walk_list!(self, walk_stmt, lambda_expr.body);
}
fn walk_keyword(&mut self, keyword: &'ctx ast::Keyword) {
self.walk_identifier(&keyword.arg.node);
if let Some(v) = &keyword.value {
self.walk_expr(&v.node)
}
}
fn walk_arguments(&mut self, arguments: &'ctx ast::Arguments) {
walk_list!(self, walk_identifier, arguments.args);
for default in &arguments.defaults {
if let Some(d) = default {
self.walk_expr(&d.node)
}
}
}
fn walk_compare(&mut self, compare: &'ctx ast::Compare) {
self.walk_expr(&compare.left.node);
walk_list!(self, walk_expr, compare.comparators);
}
fn walk_identifier(&mut self, identifier: &'ctx ast::Identifier) {
// Nothing to do.
let _ = identifier;
}
fn walk_number_lit(&mut self, number_lit: &'ctx ast::NumberLit) {
let _ = number_lit;
}
fn walk_string_lit(&mut self, string_lit: &'ctx ast::StringLit) {
// Nothing to do.
let _ = string_lit;
}
fn walk_name_constant_lit(&mut self, name_constant_lit: &'ctx ast::NameConstantLit) {
// Nothing to do.
let _ = name_constant_lit;
}
fn walk_joined_string(&mut self, joined_string: &'ctx ast::JoinedString) {
walk_list!(self, walk_expr, joined_string.values);
}
fn walk_formatted_value(&mut self, formatted_value: &'ctx ast::FormattedValue) {
self.walk_expr(&formatted_value.value.node);
}
fn walk_comment(&mut self, comment: &'ctx ast::Comment) {
// Nothing to do.
let _ = comment;
}
fn walk_module(&mut self, module: &'ctx ast::Module) {
walk_list!(self, walk_stmt, module.body)
}
fn walk_stmt(&mut self, stmt: &'ctx ast::Stmt) {
match stmt {
ast::Stmt::TypeAlias(type_alias) => self.walk_type_alias_stmt(type_alias),
ast::Stmt::Expr(expr_stmt) => self.walk_expr_stmt(expr_stmt),
ast::Stmt::Unification(unification_stmt) => {
self.walk_unification_stmt(unification_stmt)
}
ast::Stmt::Assign(assign_stmt) => self.walk_assign_stmt(assign_stmt),
ast::Stmt::AugAssign(aug_assign_stmt) => self.walk_aug_assign_stmt(aug_assign_stmt),
ast::Stmt::Assert(assert_stmt) => self.walk_assert_stmt(assert_stmt),
ast::Stmt::If(if_stmt) => self.walk_if_stmt(if_stmt),
ast::Stmt::Import(import_stmt) => self.walk_import_stmt(import_stmt),
ast::Stmt::SchemaAttr(schema_attr) => self.walk_schema_attr(schema_attr),
ast::Stmt::Schema(schema_stmt) => self.walk_schema_stmt(schema_stmt),
ast::Stmt::Rule(rule_stmt) => self.walk_rule_stmt(rule_stmt),
}
}
fn walk_expr(&mut self, expr: &'ctx ast::Expr) {
match expr {
ast::Expr::Identifier(identifier) => self.walk_identifier(identifier),
ast::Expr::Unary(unary_expr) => self.walk_unary_expr(unary_expr),
ast::Expr::Binary(binary_expr) => self.walk_binary_expr(binary_expr),
ast::Expr::If(if_expr) => self.walk_if_expr(if_expr),
ast::Expr::Selector(selector_expr) => self.walk_selector_expr(selector_expr),
ast::Expr::Call(call_expr) => self.walk_call_expr(call_expr),
ast::Expr::Paren(paren_expr) => self.walk_paren_expr(paren_expr),
ast::Expr::Quant(quant_expr) => self.walk_quant_expr(quant_expr),
ast::Expr::List(list_expr) => self.walk_list_expr(list_expr),
ast::Expr::ListIfItem(list_if_item_expr) => {
self.walk_list_if_item_expr(list_if_item_expr)
}
ast::Expr::ListComp(list_comp) => self.walk_list_comp(list_comp),
ast::Expr::Starred(starred_expr) => self.walk_starred_expr(starred_expr),
ast::Expr::DictComp(dict_comp) => self.walk_dict_comp(dict_comp),
ast::Expr::ConfigIfEntry(config_if_entry_expr) => {
self.walk_config_if_entry_expr(config_if_entry_expr)
}
ast::Expr::CompClause(comp_clause) => self.walk_comp_clause(comp_clause),
ast::Expr::Schema(schema_expr) => self.walk_schema_expr(schema_expr),
ast::Expr::Config(config_expr) => self.walk_config_expr(config_expr),
ast::Expr::Check(check) => self.walk_check_expr(check),
ast::Expr::Lambda(lambda) => self.walk_lambda_expr(lambda),
ast::Expr::Subscript(subscript) => self.walk_subscript(subscript),
ast::Expr::Keyword(keyword) => self.walk_keyword(keyword),
ast::Expr::Arguments(arguments) => self.walk_arguments(arguments),
ast::Expr::Compare(compare) => self.walk_compare(compare),
ast::Expr::NumberLit(number_lit) => self.walk_number_lit(number_lit),
ast::Expr::StringLit(string_lit) => self.walk_string_lit(string_lit),
ast::Expr::NameConstantLit(name_constant_lit) => {
self.walk_name_constant_lit(name_constant_lit)
}
ast::Expr::JoinedString(joined_string) => self.walk_joined_string(joined_string),
ast::Expr::FormattedValue(formatted_value) => {
self.walk_formatted_value(formatted_value)
}
}
}
}
4 changes: 2 additions & 2 deletions kclvm/error/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use kclvm_span::Loc;
use rustc_span::Pos;
use termcolor::{Color, ColorSpec};

use crate::ErrorKind;
use crate::{ErrorKind, WarningKind};

/// Diagnostic structure.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Expand Down Expand Up @@ -134,7 +134,7 @@ pub struct Message {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum DiagnosticId {
Error(ErrorKind),
Warning(String),
Warning(WarningKind),
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
Expand Down
26 changes: 26 additions & 0 deletions kclvm/error/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,29 @@ impl ErrorKind {
return format!("{:?}", self);
}
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Warning {
pub code: &'static str,
pub kind: ErrorKind,
pub message: Option<&'static str>,
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum WarningKind {
UnusedImportWarning,
ReimportWarning,
ImportPositionWarning,
}

impl std::fmt::Display for WarningKind {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}

impl WarningKind {
pub fn name(&self) -> String {
return format!("{:?}", self);
}
}
24 changes: 24 additions & 0 deletions kclvm/error/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,30 @@ impl Handler {
self
}

/// Add an warning into the handler
/// ```
/// use kclvm_error::*;
/// let mut handler = Handler::default();
/// handler.add_warning(WarningKind::UnusedImportWarning, &[
/// Message {
/// pos: Position::dummy_pos(),
/// style: Style::LineAndColumn,
/// message: "Module 'a' imported but unused.".to_string(),
/// note: None,
/// }],
/// );
/// ```
pub fn add_warning(&mut self, warning: WarningKind, msgs: &[Message]) -> &mut Self {
let diag = Diagnostic {
level: Level::Warning,
messages: msgs.to_owned(),
code: Some(DiagnosticId::Warning(warning)),
};
self.add_diagnostic(diag);

self
}

/// Store a diagnostics
#[inline]
fn add_diagnostic(&mut self, diagnostic: Diagnostic) -> &mut Self {
Expand Down
3 changes: 3 additions & 0 deletions kclvm/sema/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ once_cell = "1.5.2"
fancy-regex = "0.7.1"
unicode_names2 = "0.4"
petgraph = "0.6.0"
regex = "1.5"
kclvm-ast = {path = "../ast", version = "0.1.0"}
kclvm-runtime = {path = "../runtime", version = "0.1.0"}
kclvm-error = {path = "../error", version = "0.1.0"}
Expand All @@ -28,3 +29,5 @@ criterion = "0.3"
name = "my_benchmark"
harness = false

[lib]
bench = false
12 changes: 10 additions & 2 deletions kclvm/sema/benches/my_benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use criterion::{criterion_group, criterion_main, Criterion};
use kclvm_parser::load_program;
use kclvm_sema::pre_process::pre_process_program;
use kclvm_sema::resolver::*;
use kclvm_sema::ty::*;

pub fn criterion_benchmark(c: &mut Criterion) {
Expand All @@ -15,5 +18,10 @@ pub fn criterion_benchmark(c: &mut Criterion) {
});
}

criterion_group!(benches, criterion_benchmark);
pub fn criterion_benchmark_resolver(c: &mut Criterion) {
let mut program = load_program(&["./src/resolver/test_data/import.k"], None).unwrap();
c.bench_function("resolver", |b| b.iter(|| resolve_program(&mut program)));
}

criterion_group!(benches, criterion_benchmark, criterion_benchmark_resolver);
criterion_main!(benches);
Loading

0 comments on commit 2b4eb8e

Please sign in to comment.