aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/code_model.rs6
-rw-r--r--crates/rust-analyzer/src/bin/args.rs39
-rw-r--r--crates/rust-analyzer/src/bin/main.rs4
-rw-r--r--crates/rust-analyzer/src/cli.rs8
-rw-r--r--crates/rust-analyzer/src/cli/diagnostics.rs74
5 files changed, 127 insertions, 4 deletions
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::{
25 autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy, 25 autoderef, display::HirFormatter, expr::ExprValidator, method_resolution, ApplicationTy,
26 Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor, 26 Canonical, InEnvironment, Substs, TraitEnvironment, Ty, TyDefId, TypeCtor,
27}; 27};
28use ra_db::{CrateId, Edition, FileId}; 28use ra_db::{CrateId, CrateName, Edition, FileId};
29use ra_prof::profile; 29use ra_prof::profile;
30use ra_syntax::{ 30use ra_syntax::{
31 ast::{self, AttrsOwner, NameOwner}, 31 ast::{self, AttrsOwner, NameOwner},
@@ -91,6 +91,10 @@ impl Crate {
91 db.crate_graph()[self.id].edition 91 db.crate_graph()[self.id].edition
92 } 92 }
93 93
94 pub fn display_name(self, db: &dyn HirDatabase) -> Option<CrateName> {
95 db.crate_graph()[self.id].display_name.as_ref().cloned()
96 }
97
94 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { 98 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
95 db.crate_graph().iter().map(|id| Crate { id }).collect() 99 db.crate_graph().iter().map(|id| Crate { id }).collect()
96 } 100 }
diff --git a/crates/rust-analyzer/src/bin/args.rs b/crates/rust-analyzer/src/bin/args.rs
index 3cf394bb4..f5981588a 100644
--- a/crates/rust-analyzer/src/bin/args.rs
+++ b/crates/rust-analyzer/src/bin/args.rs
@@ -35,6 +35,13 @@ pub(crate) enum Command {
35 what: BenchWhat, 35 what: BenchWhat,
36 load_output_dirs: bool, 36 load_output_dirs: bool,
37 }, 37 },
38 Diagnostics {
39 path: PathBuf,
40 load_output_dirs: bool,
41 /// Include files which are not modules. In rust-analyzer
42 /// this would include the parser test files.
43 all: bool,
44 },
38 RunServer, 45 RunServer,
39 Version, 46 Version,
40} 47}
@@ -209,6 +216,38 @@ ARGS:
209 let load_output_dirs = matches.contains("--load-output-dirs"); 216 let load_output_dirs = matches.contains("--load-output-dirs");
210 Command::Bench { path, what, load_output_dirs } 217 Command::Bench { path, what, load_output_dirs }
211 } 218 }
219 "diagnostics" => {
220 if matches.contains(["-h", "--help"]) {
221 eprintln!(
222 "\
223ra-cli-diagnostics
224
225USAGE:
226 rust-analyzer diagnostics [FLAGS] [PATH]
227
228FLAGS:
229 -h, --help Prints help information
230 --load-output-dirs Load OUT_DIR values by running `cargo check` before analysis
231 --all Include all files rather than only modules
232
233ARGS:
234 <PATH>"
235 );
236 return Ok(Err(HelpPrinted));
237 }
238
239 let load_output_dirs = matches.contains("--load-output-dirs");
240 let all = matches.contains("--all");
241 let path = {
242 let mut trailing = matches.free()?;
243 if trailing.len() != 1 {
244 bail!("Invalid flags");
245 }
246 trailing.pop().unwrap().into()
247 };
248
249 Command::Diagnostics { path, load_output_dirs, all }
250 }
212 _ => { 251 _ => {
213 eprintln!( 252 eprintln!(
214 "\ 253 "\
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 608f4f67b..7cfc44f01 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -39,6 +39,10 @@ fn main() -> Result<()> {
39 cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)? 39 cli::analysis_bench(args.verbosity, path.as_ref(), what, load_output_dirs)?
40 } 40 }
41 41
42 args::Command::Diagnostics { path, load_output_dirs, all } => {
43 cli::diagnostics(path.as_ref(), load_output_dirs, all)?
44 }
45
42 args::Command::RunServer => run_server()?, 46 args::Command::RunServer => run_server()?,
43 args::Command::Version => println!("rust-analyzer {}", env!("REV")), 47 args::Command::Version => println!("rust-analyzer {}", env!("REV")),
44 } 48 }
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 @@
3mod load_cargo; 3mod load_cargo;
4mod analysis_stats; 4mod analysis_stats;
5mod analysis_bench; 5mod analysis_bench;
6mod diagnostics;
6mod progress_report; 7mod progress_report;
7 8
8use std::io::Read; 9use std::io::Read;
@@ -12,6 +13,10 @@ use ra_ide::{file_structure, Analysis};
12use ra_prof::profile; 13use ra_prof::profile;
13use ra_syntax::{AstNode, SourceFile}; 14use ra_syntax::{AstNode, SourceFile};
14 15
16pub use analysis_bench::{analysis_bench, BenchWhat, Position};
17pub use analysis_stats::analysis_stats;
18pub use diagnostics::diagnostics;
19
15#[derive(Clone, Copy)] 20#[derive(Clone, Copy)]
16pub enum Verbosity { 21pub enum Verbosity {
17 Spammy, 22 Spammy,
@@ -60,9 +65,6 @@ pub fn highlight(rainbow: bool) -> Result<()> {
60 Ok(()) 65 Ok(())
61} 66}
62 67
63pub use analysis_bench::{analysis_bench, BenchWhat, Position};
64pub use analysis_stats::analysis_stats;
65
66fn file() -> Result<SourceFile> { 68fn file() -> Result<SourceFile> {
67 let text = read_stdin()?; 69 let text = read_stdin()?;
68 Ok(SourceFile::parse(&text).tree()) 70 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..92664b415
--- /dev/null
+++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -0,0 +1,74 @@
1//! Analyze all modules in a project for diagnostics. Exits with a non-zero status
2//! code if any errors are found.
3
4use anyhow::anyhow;
5use ra_db::SourceDatabaseExt;
6use ra_ide::Severity;
7use std::{collections::HashSet, path::Path};
8
9use crate::cli::{load_cargo::load_cargo, Result};
10use hir::Semantics;
11
12pub fn diagnostics(path: &Path, load_output_dirs: bool, all: bool) -> Result<()> {
13 let (host, roots) = load_cargo(path, load_output_dirs)?;
14 let db = host.raw_database();
15 let analysis = host.analysis();
16 let semantics = Semantics::new(db);
17 let members = roots
18 .into_iter()
19 .filter_map(|(source_root_id, project_root)| {
20 // filter out dependencies
21 if project_root.is_member() {
22 Some(source_root_id)
23 } else {
24 None
25 }
26 })
27 .collect::<HashSet<_>>();
28
29 let mut found_error = false;
30 let mut visited_files = HashSet::new();
31 for source_root_id in members {
32 for file_id in db.source_root(source_root_id).walk() {
33 // Filter out files which are not actually modules (unless `--all` flag is
34 // passed). In the rust-analyzer repository this filters out the parser test files.
35 if semantics.to_module_def(file_id).is_some() || all {
36 if !visited_files.contains(&file_id) {
37 let crate_name = if let Some(module) = semantics.to_module_def(file_id) {
38 if let Some(name) = module.krate().display_name(db) {
39 format!("{}", name)
40 } else {
41 String::from("unknown")
42 }
43 } else {
44 String::from("unknown")
45 };
46 println!(
47 "processing crate: {}, module: {}",
48 crate_name,
49 db.file_relative_path(file_id)
50 );
51 for diagnostic in analysis.diagnostics(file_id).unwrap() {
52 if matches!(diagnostic.severity, Severity::Error) {
53 found_error = true;
54 }
55
56 println!("{:?}", diagnostic);
57 }
58
59 visited_files.insert(file_id);
60 }
61 }
62 }
63 }
64
65 println!();
66 println!("diagnostic scan complete");
67
68 if found_error {
69 println!();
70 Err(anyhow!("diagnostic error detected"))
71 } else {
72 Ok(())
73 }
74}