lisette-semantics 0.2.14

Little language inspired by Rust that compiles to Go
Documentation
pub(crate) mod attributes;
pub(crate) mod casing;
mod checks;
pub(crate) mod visitor;

use std::cell::RefCell;

use crate::context::AnalysisContext;
use crate::passes::PARALLEL_THRESHOLD;
use crate::store::Store;
use diagnostics::LisetteDiagnostic;
use diagnostics::LocalSink;
use rayon::prelude::*;
use rustc_hash::FxHashMap as HashMap;
use syntax::ast::{Expression, Pattern};
use syntax::program::{File, Module};

pub struct LintContext<'a> {
    pub ast: &'a [Expression],
    pub source: &'a str,
    pub is_d_lis: bool,
    pub files: &'a HashMap<u32, File>,
    pub module_id: &'a str,
    pub store: &'a Store,
}

pub(crate) fn run(analysis: &AnalysisContext, sink: &LocalSink) {
    let store = analysis.store;

    let mut modules: Vec<&Module> = store
        .modules
        .values()
        .filter(|m| !m.is_internal())
        .collect();
    modules.sort_unstable_by(|a, b| a.id.cmp(&b.id));

    if modules.len() < PARALLEL_THRESHOLD {
        for module in &modules {
            run_module(module, store, sink);
        }
        return;
    }

    let worker_sinks: Vec<LocalSink> = modules
        .par_iter()
        .map(|module| {
            let local_sink = LocalSink::new();
            run_module(module, store, &local_sink);
            local_sink
        })
        .collect();
    sink.extend(LocalSink::merge(worker_sinks));
}

fn run_module(module: &Module, store: &Store, sink: &LocalSink) {
    for file in module.files.values() {
        let ctx = LintContext {
            ast: &file.items,
            source: &file.source,
            is_d_lis: file.is_d_lis(),
            files: &module.files,
            module_id: &module.id,
            store,
        };
        let mut diagnostics = AstLintGroup.check(&ctx);
        diagnostics.sort_by(LisetteDiagnostic::sort_key);
        sink.extend(diagnostics);
    }
}

use attributes::{check_attributes, check_struct_attributes};
use checks::{
    check_bool_literal_comparison, check_double_negation, check_duplicate_logical_operand,
    check_empty_match_arm, check_excess_parens_on_condition, check_expression_naming,
    check_identical_if_branches, check_invisible_in_string_expression,
    check_invisible_in_string_pattern, check_match_literal_collection, check_pattern_naming,
    check_replaceable_with_zero_fill, check_rest_only_slice_pattern, check_self_assignment,
    check_self_comparison, check_single_arm_match, check_uninterpolated_fstring,
    check_unnecessary_raw_string_expression, check_unnecessary_raw_string_pattern,
    check_unsigned_comparison, check_verbose_failure_propagation,
};
use visitor::visit_ast;

type ExpressionCheck = fn(&Expression, &mut Vec<LisetteDiagnostic>);
type PatternCheck = fn(&Pattern, &mut Vec<LisetteDiagnostic>);

const EXPRESSION_CHECKS: &[ExpressionCheck] = &[
    check_double_negation,
    check_self_comparison,
    check_unsigned_comparison,
    check_self_assignment,
    check_bool_literal_comparison,
    check_identical_if_branches,
    check_empty_match_arm,
    check_excess_parens_on_condition,
    check_match_literal_collection,
    check_single_arm_match,
    check_uninterpolated_fstring,
    check_unnecessary_raw_string_expression,
    check_invisible_in_string_expression,
    check_verbose_failure_propagation,
    check_struct_attributes,
    check_attributes,
];

const PATTERN_CHECKS: &[PatternCheck] = &[
    check_rest_only_slice_pattern,
    check_unnecessary_raw_string_pattern,
    check_invisible_in_string_pattern,
];

pub struct AstLintGroup;

impl AstLintGroup {
    pub fn check(&self, ctx: &LintContext) -> Vec<LisetteDiagnostic> {
        let diagnostics = RefCell::new(Vec::new());
        let is_d_lis = ctx.is_d_lis;
        let files = ctx.files;
        let store = ctx.store;
        let module_id = ctx.module_id;
        let source = ctx.source;

        visit_ast(
            ctx.ast,
            &mut |expression| {
                let mut sink = diagnostics.borrow_mut();
                for check in EXPRESSION_CHECKS {
                    check(expression, &mut sink);
                }
                check_duplicate_logical_operand(expression, files, &mut sink);
                check_expression_naming(expression, is_d_lis, &mut sink);
                check_replaceable_with_zero_fill(expression, store, module_id, source, &mut sink);
            },
            &mut |pattern| {
                let mut sink = diagnostics.borrow_mut();
                for check in PATTERN_CHECKS {
                    check(pattern, &mut sink);
                }
                check_pattern_naming(pattern, is_d_lis, &mut sink);
            },
        );

        diagnostics.into_inner()
    }
}