From ef2b84ddf119c950272c5f1eb321f3f9e90bedd4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 8 Sep 2019 09:48:45 +0300 Subject: [PATCH] introduce hir debugging infra This is to make debugging rust-analyzer easier. The idea is that `dbg!(krate.debug(db))` will print the actual, fuzzy crate name, instead of precise ID. Debug printing infra is a separate thing, to make sure that the actual hir doesn't have access to global information. Do not use `.debug` for `log::` logging: debugging executes queries, and might introduce unneded dependencies to the crate graph --- Cargo.lock | 1 + crates/ra_db/src/input.rs | 16 ++++++-- crates/ra_hir/src/db.rs | 3 +- crates/ra_hir/src/debug.rs | 64 ++++++++++++++++++++++++++++++ crates/ra_hir/src/lib.rs | 1 + crates/ra_hir/src/mock.rs | 25 +++++++++++- crates/ra_ide_api/src/change.rs | 15 ++++++- crates/ra_ide_api/src/db.rs | 31 ++++++++++++++- crates/ra_lsp_server/Cargo.toml | 1 + crates/ra_lsp_server/src/world.rs | 7 +++- crates/ra_project_model/src/lib.rs | 20 ++++++---- 11 files changed, 166 insertions(+), 18 deletions(-) create mode 100644 crates/ra_hir/src/debug.rs diff --git a/Cargo.lock b/Cargo.lock index f93f11a829..ceb26961e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1060,6 +1060,7 @@ dependencies = [ "lsp-server 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types 0.61.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ra_db 0.1.0", "ra_ide_api 0.1.0", "ra_prof 0.1.0", "ra_project_model 0.1.0", diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index d1ee3c0368..a1ace61b65 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs @@ -82,6 +82,12 @@ pub struct CyclicDependencies; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CrateId(pub u32); +impl CrateId { + pub fn shift(self, amount: u32) -> CrateId { + CrateId(self.0 + amount) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Edition { Edition2018, @@ -178,15 +184,19 @@ impl CrateGraph { /// Extends this crate graph by adding a complete disjoint second crate /// graph. - pub fn extend(&mut self, other: CrateGraph) { + /// + /// The ids of the crates in the `other` graph are shifted by the return + /// amount. + pub fn extend(&mut self, other: CrateGraph) -> u32 { let start = self.arena.len() as u32; self.arena.extend(other.arena.into_iter().map(|(id, mut data)| { - let new_id = CrateId(id.0 + start); + let new_id = id.shift(start); for dep in &mut data.dependencies { - dep.crate_id = CrateId(dep.crate_id.0 + start); + dep.crate_id = dep.crate_id.shift(start); } (new_id, data) })); + start } fn dfs_find(&self, target: CrateId, from: CrateId, visited: &mut FxHashSet) -> bool { diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 7b7974f5bb..f7f124904e 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -5,6 +5,7 @@ use ra_syntax::{ast, Parse, SmolStr, SyntaxNode}; use crate::{ adt::{EnumData, StructData}, + debug::HirDebugDatabase, generics::{GenericDef, GenericParams}, ids, impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks}, @@ -83,7 +84,7 @@ pub trait AstDatabase: InternDatabase { // This database uses `AstDatabase` internally, #[salsa::query_group(DefDatabaseStorage)] #[salsa::requires(AstDatabase)] -pub trait DefDatabase: InternDatabase { +pub trait DefDatabase: InternDatabase + HirDebugDatabase { #[salsa::invoke(crate::adt::StructData::struct_data_query)] fn struct_data(&self, s: Struct) -> Arc; diff --git a/crates/ra_hir/src/debug.rs b/crates/ra_hir/src/debug.rs new file mode 100644 index 0000000000..5a835741d1 --- /dev/null +++ b/crates/ra_hir/src/debug.rs @@ -0,0 +1,64 @@ +use std::{cell::Cell, fmt}; + +use ra_db::{CrateId, FileId}; + +use crate::{db::HirDatabase, Crate, Module, Name}; + +impl Crate { + pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ { + debug_fn(move |fmt| db.debug_crate(self, fmt)) + } +} + +impl Module { + pub fn debug(self, db: &impl HirDebugDatabase) -> impl fmt::Debug + '_ { + debug_fn(move |fmt| db.debug_module(self, fmt)) + } +} + +pub trait HirDebugHelper: HirDatabase { + fn crate_name(&self, _krate: CrateId) -> Option { + None + } + fn file_path(&self, _file_id: FileId) -> Option { + None + } +} + +pub trait HirDebugDatabase { + fn debug_crate(&self, krate: Crate, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; + fn debug_module(&self, module: Module, fmt: &mut fmt::Formatter<'_>) -> fmt::Result; +} + +impl HirDebugDatabase for DB { + fn debug_crate(&self, krate: Crate, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_tuple("Crate"); + match self.crate_name(krate.crate_id) { + Some(name) => builder.field(&name), + None => builder.field(&krate.crate_id), + } + .finish() + } + + fn debug_module(&self, module: Module, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let file_id = module.definition_source(self).file_id.original_file(self); + let path = self.file_path(file_id); + fmt.debug_struct("Module") + .field("name", &module.name(self).unwrap_or_else(Name::missing)) + .field("path", &path.unwrap_or_else(|| "N/A".to_string())) + .finish() + } +} + +fn debug_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Debug { + struct DebugFn(Cell>); + + impl) -> fmt::Result> fmt::Debug for DebugFn { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let f = self.0.take().unwrap(); + f(fmt) + } + } + + DebugFn(Cell::new(Some(f))) +} diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 7c2a68992c..24ee84f86a 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -20,6 +20,7 @@ macro_rules! impl_froms { } mod either; +pub mod debug; pub mod db; #[macro_use] diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs index 972f0ece59..8dcea50713 100644 --- a/crates/ra_hir/src/mock.rs +++ b/crates/ra_hir/src/mock.rs @@ -2,13 +2,14 @@ use std::{panic, sync::Arc}; use parking_lot::Mutex; use ra_db::{ - salsa, CrateGraph, Edition, FileId, FilePosition, SourceDatabase, SourceRoot, SourceRootId, + salsa, CrateGraph, CrateId, Edition, FileId, FilePosition, SourceDatabase, SourceRoot, + SourceRootId, }; use relative_path::RelativePathBuf; use rustc_hash::FxHashMap; use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; -use crate::{db, diagnostics::DiagnosticSink}; +use crate::{db, debug::HirDebugHelper, diagnostics::DiagnosticSink}; pub const WORKSPACE: SourceRootId = SourceRootId(0); @@ -24,10 +25,22 @@ pub struct MockDatabase { events: Mutex>>>, runtime: salsa::Runtime, files: FxHashMap, + crate_names: Arc>, + file_paths: Arc>, } impl panic::RefUnwindSafe for MockDatabase {} +impl HirDebugHelper for MockDatabase { + fn crate_name(&self, krate: CrateId) -> Option { + self.crate_names.get(&krate).cloned() + } + + fn file_path(&self, file_id: FileId) -> Option { + self.file_paths.get(&file_id).cloned() + } +} + impl MockDatabase { pub fn with_files(fixture: &str) -> MockDatabase { let (db, position) = MockDatabase::from_fixture(fixture); @@ -62,6 +75,7 @@ impl MockDatabase { for (crate_name, (crate_root, edition, _)) in graph.0.iter() { let crate_root = self.file_id_of(&crate_root); let crate_id = crate_graph.add_crate_root(crate_root, *edition); + Arc::make_mut(&mut self.crate_names).insert(crate_id, crate_name.clone()); ids.insert(crate_name, crate_id); } for (crate_name, (_, _, deps)) in graph.0.iter() { @@ -151,8 +165,11 @@ impl MockDatabase { let is_crate_root = rel_path == "lib.rs" || rel_path == "/main.rs"; let file_id = FileId(self.files.len() as u32); + let prev = self.files.insert(path.to_string(), file_id); assert!(prev.is_none(), "duplicate files in the text fixture"); + Arc::make_mut(&mut self.file_paths).insert(file_id, path.to_string()); + let text = Arc::new(text.to_string()); self.set_file_text(file_id, text); self.set_file_relative_path(file_id, rel_path.clone()); @@ -200,6 +217,8 @@ impl Default for MockDatabase { events: Default::default(), runtime: salsa::Runtime::default(), files: FxHashMap::default(), + crate_names: Default::default(), + file_paths: Default::default(), }; db.set_crate_graph(Default::default()); db @@ -213,6 +232,8 @@ impl salsa::ParallelDatabase for MockDatabase { runtime: self.runtime.snapshot(self), // only the root database can be used to get file_id by path. files: FxHashMap::default(), + file_paths: Arc::clone(&self.file_paths), + crate_names: Arc::clone(&self.crate_names), }) } } diff --git a/crates/ra_ide_api/src/change.rs b/crates/ra_ide_api/src/change.rs index 89631935af..0d52f5ffb7 100644 --- a/crates/ra_ide_api/src/change.rs +++ b/crates/ra_ide_api/src/change.rs @@ -2,7 +2,7 @@ use std::{fmt, sync::Arc, time}; use ra_db::{ salsa::{Database, Durability, SweepStrategy}, - CrateGraph, FileId, SourceDatabase, SourceRoot, SourceRootId, + CrateGraph, CrateId, FileId, SourceDatabase, SourceRoot, SourceRootId, }; use ra_prof::{memory_usage, profile, Bytes}; use ra_syntax::SourceFile; @@ -11,7 +11,7 @@ use relative_path::RelativePathBuf; use rustc_hash::FxHashMap; use crate::{ - db::RootDatabase, + db::{DebugData, RootDatabase}, status::syntax_tree_stats, symbol_index::{SymbolIndex, SymbolsDatabase}, }; @@ -23,6 +23,7 @@ pub struct AnalysisChange { files_changed: Vec<(FileId, Arc)>, libraries_added: Vec, crate_graph: Option, + debug_data: DebugData, } impl fmt::Debug for AnalysisChange { @@ -83,6 +84,14 @@ impl AnalysisChange { pub fn set_crate_graph(&mut self, graph: CrateGraph) { self.crate_graph = Some(graph); } + + pub fn set_debug_crate_name(&mut self, crate_id: CrateId, name: String) { + self.debug_data.crate_names.insert(crate_id, name); + } + + pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) { + self.debug_data.root_paths.insert(source_root_id, path); + } } #[derive(Debug)] @@ -200,6 +209,8 @@ impl RootDatabase { if let Some(crate_graph) = change.crate_graph { self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) } + + Arc::make_mut(&mut self.debug_data).merge(change.debug_data) } fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { diff --git a/crates/ra_ide_api/src/db.rs b/crates/ra_ide_api/src/db.rs index f2e6b8f126..4c5159d612 100644 --- a/crates/ra_ide_api/src/db.rs +++ b/crates/ra_ide_api/src/db.rs @@ -2,8 +2,9 @@ use std::{sync::Arc, time}; use ra_db::{ salsa::{self, Database, Durability}, - Canceled, CheckCanceled, FileId, SourceDatabase, + Canceled, CheckCanceled, CrateId, FileId, SourceDatabase, SourceRootId, }; +use rustc_hash::FxHashMap; use crate::{ symbol_index::{self, SymbolsDatabase}, @@ -23,10 +24,23 @@ use crate::{ pub(crate) struct RootDatabase { runtime: salsa::Runtime, pub(crate) feature_flags: Arc, + pub(crate) debug_data: Arc, pub(crate) last_gc: time::Instant, pub(crate) last_gc_check: time::Instant, } +impl hir::debug::HirDebugHelper for RootDatabase { + fn crate_name(&self, krate: CrateId) -> Option { + self.debug_data.crate_names.get(&krate).cloned() + } + fn file_path(&self, file_id: FileId) -> Option { + let source_root_id = self.file_source_root(file_id); + let source_root_path = self.debug_data.root_paths.get(&source_root_id)?; + let file_path = self.file_relative_path(file_id); + Some(format!("{}/{}", source_root_path, file_path.display())) + } +} + impl salsa::Database for RootDatabase { fn salsa_runtime(&self) -> &salsa::Runtime { &self.runtime @@ -58,6 +72,7 @@ impl RootDatabase { last_gc: time::Instant::now(), last_gc_check: time::Instant::now(), feature_flags: Arc::new(feature_flags), + debug_data: Default::default(), }; db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); db.set_local_roots_with_durability(Default::default(), Durability::HIGH); @@ -77,6 +92,7 @@ impl salsa::ParallelDatabase for RootDatabase { last_gc: self.last_gc, last_gc_check: self.last_gc_check, feature_flags: Arc::clone(&self.feature_flags), + debug_data: Arc::clone(&self.debug_data), }) } } @@ -90,3 +106,16 @@ fn line_index(db: &impl ra_db::SourceDatabase, file_id: FileId) -> Arc, + pub(crate) crate_names: FxHashMap, +} + +impl DebugData { + pub(crate) fn merge(&mut self, other: DebugData) { + self.root_paths.extend(other.root_paths.into_iter()); + self.crate_names.extend(other.crate_names.into_iter()); + } +} diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml index 46a0f958c0..677d81835e 100644 --- a/crates/ra_lsp_server/Cargo.toml +++ b/crates/ra_lsp_server/Cargo.toml @@ -18,6 +18,7 @@ parking_lot = "0.9.0" jod-thread = "0.1.0" ra_vfs = "0.4.0" ra_syntax = { path = "../ra_syntax" } +ra_db = { path = "../ra_db" } ra_text_edit = { path = "../ra_text_edit" } ra_ide_api = { path = "../ra_ide_api" } lsp-server = "0.2.0" diff --git a/crates/ra_lsp_server/src/world.rs b/crates/ra_lsp_server/src/world.rs index 086ecd587f..232409c3be 100644 --- a/crates/ra_lsp_server/src/world.rs +++ b/crates/ra_lsp_server/src/world.rs @@ -92,6 +92,7 @@ impl WorldState { let vfs_root_path = vfs.root2path(r); let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); change.add_root(SourceRootId(r.0), is_local); + change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string()); } // Create crate graph from all the workspaces @@ -101,7 +102,11 @@ impl WorldState { vfs_file.map(|f| FileId(f.0)) }; for ws in workspaces.iter() { - crate_graph.extend(ws.to_crate_graph(&mut load)); + let (graph, crate_names) = ws.to_crate_graph(&mut load); + let shift = crate_graph.extend(graph); + for (crate_id, name) in crate_names { + change.set_debug_crate_name(crate_id.shift(shift), name) + } } change.set_crate_graph(crate_graph); diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 9b2f534e71..4fa32dc340 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs @@ -9,7 +9,7 @@ use std::{ path::{Path, PathBuf}, }; -use ra_db::{CrateGraph, Edition, FileId}; +use ra_db::{CrateGraph, CrateId, Edition, FileId}; use rustc_hash::FxHashMap; use serde_json::from_reader; @@ -113,8 +113,12 @@ impl ProjectWorkspace { } } - pub fn to_crate_graph(&self, load: &mut dyn FnMut(&Path) -> Option) -> CrateGraph { + pub fn to_crate_graph( + &self, + load: &mut dyn FnMut(&Path) -> Option, + ) -> (CrateGraph, FxHashMap) { let mut crate_graph = CrateGraph::default(); + let mut names = FxHashMap::default(); match self { ProjectWorkspace::Json { project } => { let mut crates = FxHashMap::default(); @@ -151,10 +155,9 @@ impl ProjectWorkspace { let mut sysroot_crates = FxHashMap::default(); for krate in sysroot.crates() { if let Some(file_id) = load(krate.root(&sysroot)) { - sysroot_crates.insert( - krate, - crate_graph.add_crate_root(file_id, Edition::Edition2018), - ); + let crate_id = crate_graph.add_crate_root(file_id, Edition::Edition2018); + sysroot_crates.insert(krate, crate_id); + names.insert(crate_id, krate.name(&sysroot).to_string()); } } for from in sysroot.crates() { @@ -182,6 +185,7 @@ impl ProjectWorkspace { if let Some(file_id) = load(root) { let edition = pkg.edition(&cargo); let crate_id = crate_graph.add_crate_root(file_id, edition); + names.insert(crate_id, pkg.name(&cargo).to_string()); if tgt.kind(&cargo) == TargetKind::Lib { lib_tgt = Some(crate_id); pkg_to_lib_crate.insert(pkg, crate_id); @@ -212,7 +216,7 @@ impl ProjectWorkspace { } } - // Now add a dep ednge from all targets of upstream to the lib + // Now add a dep edge from all targets of upstream to the lib // target of downstream. for pkg in cargo.packages() { for dep in pkg.dependencies(&cargo) { @@ -233,7 +237,7 @@ impl ProjectWorkspace { } } } - crate_graph + (crate_graph, names) } pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> {