ra_ap_rust_analyzer/cli/
diagnostics.rs1use project_model::{CargoConfig, RustLibSource};
5use rustc_hash::FxHashSet;
6
7use hir::{db::HirDatabase, sym, Crate, HirFileIdExt, Module};
8use ide::{AnalysisHost, AssistResolveStrategy, Diagnostic, DiagnosticsConfig, Severity};
9use ide_db::{base_db::SourceRootDatabase, LineIndexDatabase};
10use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice};
11
12use crate::cli::flags;
13
14impl flags::Diagnostics {
15 pub fn run(self) -> anyhow::Result<()> {
16 const STACK_SIZE: usize = 1024 * 1024 * 8;
17
18 let handle = stdx::thread::Builder::new(stdx::thread::ThreadIntent::LatencySensitive)
19 .name("BIG_STACK_THREAD".into())
20 .stack_size(STACK_SIZE)
21 .spawn(|| self.run_())
22 .unwrap();
23
24 handle.join()
25 }
26 fn run_(self) -> anyhow::Result<()> {
27 let cargo_config = CargoConfig {
28 sysroot: Some(RustLibSource::Discover),
29 all_targets: true,
30 ..Default::default()
31 };
32 let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv {
33 let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p));
34 ProcMacroServerChoice::Explicit(path)
35 } else {
36 ProcMacroServerChoice::Sysroot
37 };
38 let load_cargo_config = LoadCargoConfig {
39 load_out_dirs_from_check: !self.disable_build_scripts,
40 with_proc_macro_server,
41 prefill_caches: false,
42 };
43 let (db, _vfs, _proc_macro) =
44 load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
45 let host = AnalysisHost::with_database(db);
46 let db = host.raw_database();
47 let analysis = host.analysis();
48
49 let mut found_error = false;
50 let mut visited_files = FxHashSet::default();
51
52 let work = all_modules(db).into_iter().filter(|module| {
53 let file_id = module.definition_source_file_id(db).original_file(db);
54 let source_root = db.file_source_root(file_id.into());
55 let source_root = db.source_root(source_root);
56 !source_root.is_library
57 });
58
59 for module in work {
60 let file_id = module.definition_source_file_id(db).original_file(db);
61 if !visited_files.contains(&file_id) {
62 let crate_name =
63 module.krate().display_name(db).as_deref().unwrap_or(&sym::unknown).to_owned();
64 println!(
65 "processing crate: {crate_name}, module: {}",
66 _vfs.file_path(file_id.into())
67 );
68 for diagnostic in analysis
69 .full_diagnostics(
70 &DiagnosticsConfig::test_sample(),
71 AssistResolveStrategy::None,
72 file_id.into(),
73 )
74 .unwrap()
75 {
76 if matches!(diagnostic.severity, Severity::Error) {
77 found_error = true;
78 }
79
80 let Diagnostic { code, message, range, severity, .. } = diagnostic;
81 let line_index = db.line_index(range.file_id);
82 let start = line_index.line_col(range.range.start());
83 let end = line_index.line_col(range.range.end());
84 println!("{severity:?} {code:?} from {start:?} to {end:?}: {message}");
85 }
86
87 visited_files.insert(file_id);
88 }
89 }
90
91 println!();
92 println!("diagnostic scan complete");
93
94 if found_error {
95 println!();
96 anyhow::bail!("diagnostic error detected")
97 }
98
99 Ok(())
100 }
101}
102
103fn all_modules(db: &dyn HirDatabase) -> Vec<Module> {
104 let mut worklist: Vec<_> =
105 Crate::all(db).into_iter().map(|krate| krate.root_module()).collect();
106 let mut modules = Vec::new();
107
108 while let Some(module) = worklist.pop() {
109 modules.push(module);
110 worklist.extend(module.children(db));
111 }
112
113 modules
114}