diff --git a/Cargo.lock b/Cargo.lock index 693f55b9..4afda75d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3867,6 +3867,7 @@ dependencies = [ "dirs", "indexmap", "num_cpus", + "rayon", "rustyline", "serde", "serde_json", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 713f027b..061aab9b 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -42,6 +42,7 @@ bincode = "1.3.3" dirs = "6.0" num_cpus = "1.15" ureq = { version = "2.9" } +rayon = "1.12.0" [build-dependencies] winres = "0.1.12" diff --git a/cli/src/commands/lint.rs b/cli/src/commands/lint.rs index e2c1e182..c2590cc2 100644 --- a/cli/src/commands/lint.rs +++ b/cli/src/commands/lint.rs @@ -4,6 +4,7 @@ use crate::commands::migrate::migrate_source; use crate::exit_code::ExitCode; +use rayon::prelude::*; use std::path::{Path, PathBuf}; pub fn execute(path_str: Option<&str>, fix: bool) -> ExitCode { @@ -43,46 +44,52 @@ pub fn execute(path_str: Option<&str>, fix: bool) -> ExitCode { } let linter = techscript_linter::Linter::new(); - let mut violation_count = 0; - let mut fixed_count = 0; - for file in files_to_lint { - if let Ok(content) = std::fs::read_to_string(&file) { - let mut reporter = techscript_errors::DiagnosticReporter::new(); - if let Ok(tokens) = techscript_lexer::lex(&content, &mut reporter) { - if let Ok(program) = techscript_parser::parse(&tokens, &mut reporter) { - if let Ok(checked) = techscript_semantic::analyze(program, &mut reporter) { - let violations = linter.lint(&checked); - violation_count += violations.len(); + let (violation_count, fixed_count) = files_to_lint + .into_par_iter() + .map(|file| { + let mut local_violations = 0; + let mut local_fixed = 0; - let source_mgr = techscript_common::SourceManager::new(); - let renderer = - crate::diagnostics::DiagnosticRenderer::auto_detect(&source_mgr); - for diag in violations { - let rich = crate::diagnostics::RichDiagnostic::from_legacy( - &diag, - techscript_common::FileId(0), - ); - renderer.emit(&rich); + if let Ok(content) = std::fs::read_to_string(&file) { + let mut reporter = techscript_errors::DiagnosticReporter::new(); + if let Ok(tokens) = techscript_lexer::lex(&content, &mut reporter) { + if let Ok(program) = techscript_parser::parse(&tokens, &mut reporter) { + if let Ok(checked) = techscript_semantic::analyze(program, &mut reporter) { + let violations = linter.lint(&checked); + local_violations += violations.len(); + + let source_mgr = techscript_common::SourceManager::new(); + let renderer = + crate::diagnostics::DiagnosticRenderer::auto_detect(&source_mgr); + for diag in violations { + let rich = crate::diagnostics::RichDiagnostic::from_legacy( + &diag, + techscript_common::FileId(0), + ); + renderer.emit(&rich); + } } } } - } - // Fix deprecation warnings - if fix { - let migrated = migrate_source(&content); - if migrated != content { - if let Err(e) = std::fs::write(&file, migrated) { - eprintln!("Error writing fixed file {:?}: {}", file, e); - } else { - println!("Fixed deprecated keywords in: {:?}", file); - fixed_count += 1; + // Fix deprecation warnings + if fix { + let migrated = migrate_source(&content); + if migrated != content { + if let Err(e) = std::fs::write(&file, migrated) { + eprintln!("Error writing fixed file {:?}: {}", file, e); + } else { + println!("Fixed deprecated keywords in: {:?}", file); + local_fixed += 1; + } } } } - } - } + + (local_violations, local_fixed) + }) + .reduce(|| (0, 0), |a, b| (a.0 + b.0, a.1 + b.1)); if fix && fixed_count > 0 { println!("Fixed deprecation warnings in {} file(s).", fixed_count); diff --git a/tools/linter/src/lib.rs b/tools/linter/src/lib.rs index e8d2e81e..af893156 100644 --- a/tools/linter/src/lib.rs +++ b/tools/linter/src/lib.rs @@ -8,7 +8,7 @@ use techscript_errors::{Diagnostic, DiagnosticLevel, ErrorCode}; use techscript_semantic::CheckedProgram; /// Trait definition for static analysis lint rules. -pub trait LintRule { +pub trait LintRule: Send + Sync { fn name(&self) -> &'static str; fn check(&self, program: &CheckedProgram) -> Vec; }