From f62c73a97286a49c228cd13c32ed37340afd2c1d Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Mon, 13 Apr 2020 05:44:35 -0700 Subject: add diagnostics subcommand to rust-analyzer CLI --- crates/rust-analyzer/src/bin/args.rs | 34 +++++++++++++ crates/rust-analyzer/src/bin/main.rs | 4 ++ crates/rust-analyzer/src/cli.rs | 8 ++-- crates/rust-analyzer/src/cli/diagnostics.rs | 74 +++++++++++++++++++++++++++++ 4 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 crates/rust-analyzer/src/cli/diagnostics.rs (limited to 'crates') diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs index 3cf394bb4..25367df45 100644 --- a/crates/rust-analyzer/src/bin/args.rs +++ b/crates/rust-analyzer/src/bin/args.rs @@ -35,6 +35,10 @@ pub(crate) enum Command { what: BenchWhat, load_output_dirs: bool, }, + Diagnostics { + path: PathBuf, + load_output_dirs: bool, + }, RunServer, Version, } @@ -209,6 +213,36 @@ ARGS: let load_output_dirs = matches.contains("--load-output-dirs"); Command::Bench { path, what, load_output_dirs } } + "diagnostics" => { + if matches.contains(["-h", "--help"]) { + eprintln!( + "\ +ra-cli-diagnostics + +USAGE: + rust-analyzer diagnostics [FLAGS] [PATH] + +FLAGS: + -h, --help Prints help information + --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis + +ARGS: + " + ); + return Ok(Err(HelpPrinted)); + } + + let load_output_dirs = matches.contains("--load-output-dirs"); + let path = { + let mut trailing = matches.free()?; + if trailing.len() != 1 { + bail!("Invalid flags"); + } + trailing.pop().unwrap().into() + }; + + Command::Diagnostics { path, load_output_dirs } + } _ => { eprintln!( "\ diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 608f4f67b..4edd617ee 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -39,6 +39,10 @@ fn main() -> Result<()> { cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)? } + args::Command::Diagnostics { path, load_output_dirs } => { + cli::diagnostics(path.as_ref(), load_output_dirs)? + } + args::Command::RunServer => run_server()?, args::Command::Version => println!("rust-analyzer {}", env!("REV")), } diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index c9738d101..a865a7c7e 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs @@ -3,6 +3,7 @@ mod load_cargo; mod analysis_stats; mod analysis_bench; +mod diagnostics; mod progress_report; use std::io::Read; @@ -12,6 +13,10 @@ use ra_ide::{file_structure, Analysis}; use ra_prof::profile; use ra_syntax::{AstNode, SourceFile}; +pub use analysis_bench::{analysis_bench, BenchWhat, Position}; +pub use analysis_stats::analysis_stats; +pub use diagnostics::diagnostics; + #[derive(Clone, Copy)] pub enum Verbosity { Spammy, @@ -60,9 +65,6 @@ pub fn highlight(rainbow: bool) -> Result<()> { Ok(()) } -pub use analysis_bench::{analysis_bench, BenchWhat, Position}; -pub use analysis_stats::analysis_stats; - fn file() -> Result { let text = read_stdin()?; Ok(SourceFile::parse(&text).tree()) diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs new file mode 100644 index 000000000..f5aab89a2 --- /dev/null +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -0,0 +1,74 @@ +//! Analyze all files in project for diagnostics. Exits with a non-zero status +//! code if any errors are found. + +use anyhow::anyhow; +use ra_db::{SourceDatabaseExt, SourceRootId}; +use ra_ide::{Analysis, Severity}; +use std::{collections::HashSet, path::Path}; + +use crate::cli::{load_cargo::load_cargo, Result}; +use hir::{db::HirDatabase, Crate, Module}; + +pub fn diagnostics(path: &Path, load_output_dirs: bool) -> Result<()> { + let (host, roots) = load_cargo(path, load_output_dirs)?; + let db = host.raw_database(); + let analysis = host.analysis(); + let members = roots + .into_iter() + .filter_map( + |(source_root_id, project_root)| { + if project_root.is_member() { + Some(source_root_id) + } else { + None + } + }, + ) + .collect::>(); + + let mut found_error = false; + let mut visited_modules = HashSet::new(); + for krate in Crate::all(db) { + let module = krate.root_module(db).expect("crate without root module"); + check_module(module, db, &mut visited_modules, &members, &analysis, &mut found_error); + } + + println!(); + println!("diagnostic scan complete"); + + if found_error { + println!(); + Err(anyhow!("diagnostic error detected")) + } else { + Ok(()) + } +} + +fn check_module( + module: Module, + db: &(impl HirDatabase + SourceDatabaseExt), + visited_modules: &mut HashSet, + members: &HashSet, + analysis: &Analysis, + found_error: &mut bool, +) { + let file_id = module.definition_source(db).file_id.original_file(db); + if !visited_modules.contains(&module) { + if members.contains(&db.file_source_root(file_id)) { + println!("processing: {}", db.file_relative_path(file_id)); + for diagnostic in analysis.diagnostics(file_id).unwrap() { + if matches!(diagnostic.severity, Severity::Error) { + *found_error = true; + } + + println!("{:?}", diagnostic); + } + } + + visited_modules.insert(module); + + for child_module in module.children(db) { + check_module(child_module, db, visited_modules, members, analysis, found_error); + } + } +} -- cgit v1.2.3 From f9701efbe706e8569a41847d76c4b914f479df32 Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Tue, 14 Apr 2020 04:35:34 -0700 Subject: cli diagnostics walk files --- crates/rust-analyzer/src/bin/args.rs | 7 ++- crates/rust-analyzer/src/bin/main.rs | 4 +- crates/rust-analyzer/src/cli/diagnostics.rs | 90 ++++++++++++++--------------- 3 files changed, 51 insertions(+), 50 deletions(-) (limited to 'crates') diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs index 25367df45..f5981588a 100644 --- a/crates/rust-analyzer/src/bin/args.rs +++ b/crates/rust-analyzer/src/bin/args.rs @@ -38,6 +38,9 @@ pub(crate) enum Command { Diagnostics { path: PathBuf, load_output_dirs: bool, + /// Include files which are not modules. In rust-analyzer + /// this would include the parser test files. + all: bool, }, RunServer, Version, @@ -225,6 +228,7 @@ USAGE: FLAGS: -h, --help Prints help information --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis + --all Include all files rather than only modules ARGS: " @@ -233,6 +237,7 @@ ARGS: } let load_output_dirs = matches.contains("--load-output-dirs"); + let all = matches.contains("--all"); let path = { let mut trailing = matches.free()?; if trailing.len() != 1 { @@ -241,7 +246,7 @@ ARGS: trailing.pop().unwrap().into() }; - Command::Diagnostics { path, load_output_dirs } + Command::Diagnostics { path, load_output_dirs, all } } _ => { eprintln!( diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 4edd617ee..7cfc44f01 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -39,8 +39,8 @@ fn main() -> Result<()> { cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)? } - args::Command::Diagnostics { path, load_output_dirs } => { - cli::diagnostics(path.as_ref(), load_output_dirs)? + args::Command::Diagnostics { path, load_output_dirs, all } => { + cli::diagnostics(path.as_ref(), load_output_dirs, all)? } args::Command::RunServer => run_server()?, diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index f5aab89a2..2d92c1f78 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -1,36 +1,61 @@ -//! Analyze all files in project for diagnostics. Exits with a non-zero status +//! Analyze all modules in a project for diagnostics. Exits with a non-zero status //! code if any errors are found. use anyhow::anyhow; -use ra_db::{SourceDatabaseExt, SourceRootId}; -use ra_ide::{Analysis, Severity}; +use ra_db::{SourceDatabase, SourceDatabaseExt}; +use ra_ide::Severity; use std::{collections::HashSet, path::Path}; use crate::cli::{load_cargo::load_cargo, Result}; -use hir::{db::HirDatabase, Crate, Module}; +use hir::Semantics; -pub fn diagnostics(path: &Path, load_output_dirs: bool) -> Result<()> { +pub fn diagnostics(path: &Path, load_output_dirs: bool, all: bool) -> Result<()> { let (host, roots) = load_cargo(path, load_output_dirs)?; let db = host.raw_database(); let analysis = host.analysis(); + let semantics = Semantics::new(db); let members = roots .into_iter() - .filter_map( - |(source_root_id, project_root)| { - if project_root.is_member() { - Some(source_root_id) - } else { - None - } - }, - ) + .filter_map(|(source_root_id, project_root)| { + // filter out dependencies + if project_root.is_member() { + Some(source_root_id) + } else { + None + } + }) .collect::>(); let mut found_error = false; - let mut visited_modules = HashSet::new(); - for krate in Crate::all(db) { - let module = krate.root_module(db).expect("crate without root module"); - check_module(module, db, &mut visited_modules, &members, &analysis, &mut found_error); + let mut visited_files = HashSet::new(); + let crate_graph = db.crate_graph(); + for crate_id in crate_graph.iter() { + let krate = &crate_graph[crate_id]; + if let Some(crate_name) = &krate.display_name { + println!("processing crate: {}", crate_name); + } else { + println!("processing crate: unknown"); + } + for file_id in db.source_root(db.file_source_root(krate.root_file_id)).walk() { + // Filter out files which are not actually modules (unless `--all` flag is + // passed). In the rust-analyzer repository this filters out the parser test files. + if semantics.to_module_def(file_id).is_some() || all { + if !visited_files.contains(&file_id) { + if members.contains(&db.file_source_root(file_id)) { + println!("processing module: {}", db.file_relative_path(file_id)); + for diagnostic in analysis.diagnostics(file_id).unwrap() { + if matches!(diagnostic.severity, Severity::Error) { + found_error = true; + } + + println!("{:?}", diagnostic); + } + } + + visited_files.insert(file_id); + } + } + } } println!(); @@ -43,32 +68,3 @@ pub fn diagnostics(path: &Path, load_output_dirs: bool) -> Result<()> { Ok(()) } } - -fn check_module( - module: Module, - db: &(impl HirDatabase + SourceDatabaseExt), - visited_modules: &mut HashSet, - members: &HashSet, - analysis: &Analysis, - found_error: &mut bool, -) { - let file_id = module.definition_source(db).file_id.original_file(db); - if !visited_modules.contains(&module) { - if members.contains(&db.file_source_root(file_id)) { - println!("processing: {}", db.file_relative_path(file_id)); - for diagnostic in analysis.diagnostics(file_id).unwrap() { - if matches!(diagnostic.severity, Severity::Error) { - *found_error = true; - } - - println!("{:?}", diagnostic); - } - } - - visited_modules.insert(module); - - for child_module in module.children(db) { - check_module(child_module, db, visited_modules, members, analysis, found_error); - } - } -} -- cgit v1.2.3 From 6be972770e75db5ec9f9ae2504786a9d7e957e4a Mon Sep 17 00:00:00 2001 From: Josh Mcguigan Date: Tue, 14 Apr 2020 05:32:32 -0700 Subject: diagnostics cli, iterate over members --- crates/ra_hir/src/code_model.rs | 6 ++++- crates/rust-analyzer/src/cli/diagnostics.rs | 40 ++++++++++++++++------------- 2 files changed, 27 insertions(+), 19 deletions(-) (limited to 'crates') diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs index 9baebf643..3801fce23 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs @@ -25,7 +25,7 @@ use hir_ty::{ autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, }; -use ra_db::{CrateId, Edition, FileId}; +use ra_db::{CrateId, CrateName, Edition, FileId}; use ra_prof::profile; use ra_syntax::{ ast::{self, AttrsOwner, NameOwner}, @@ -91,6 +91,10 @@ impl Crate { db.crate_graph()[self.id].edition } + pub fn display_name(self, db: &dyn HirDatabase) -> Option { + db.crate_graph()[self.id].display_name.as_ref().cloned() + } + pub fn all(db: &dyn HirDatabase) -> Vec { db.crate_graph().iter().map(|id| Crate { id }).collect() } diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 2d92c1f78..92664b415 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -2,7 +2,7 @@ //! code if any errors are found. use anyhow::anyhow; -use ra_db::{SourceDatabase, SourceDatabaseExt}; +use ra_db::SourceDatabaseExt; use ra_ide::Severity; use std::{collections::HashSet, path::Path}; @@ -28,28 +28,32 @@ pub fn diagnostics(path: &Path, load_output_dirs: bool, all: bool) -> Result<()> let mut found_error = false; let mut visited_files = HashSet::new(); - let crate_graph = db.crate_graph(); - for crate_id in crate_graph.iter() { - let krate = &crate_graph[crate_id]; - if let Some(crate_name) = &krate.display_name { - println!("processing crate: {}", crate_name); - } else { - println!("processing crate: unknown"); - } - for file_id in db.source_root(db.file_source_root(krate.root_file_id)).walk() { + for source_root_id in members { + for file_id in db.source_root(source_root_id).walk() { // Filter out files which are not actually modules (unless `--all` flag is // passed). In the rust-analyzer repository this filters out the parser test files. if semantics.to_module_def(file_id).is_some() || all { if !visited_files.contains(&file_id) { - if members.contains(&db.file_source_root(file_id)) { - println!("processing module: {}", db.file_relative_path(file_id)); - for diagnostic in analysis.diagnostics(file_id).unwrap() { - if matches!(diagnostic.severity, Severity::Error) { - found_error = true; - } - - println!("{:?}", diagnostic); + let crate_name = if let Some(module) = semantics.to_module_def(file_id) { + if let Some(name) = module.krate().display_name(db) { + format!("{}", name) + } else { + String::from("unknown") } + } else { + String::from("unknown") + }; + println!( + "processing crate: {}, module: {}", + crate_name, + db.file_relative_path(file_id) + ); + for diagnostic in analysis.diagnostics(file_id).unwrap() { + if matches!(diagnostic.severity, Severity::Error) { + found_error = true; + } + + println!("{:?}", diagnostic); } visited_files.insert(file_id); -- cgit v1.2.3