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
This commit is contained in:
Aleksey Kladov 2019-09-08 09:48:45 +03:00
parent 734a43e95a
commit ef2b84ddf1
11 changed files with 166 additions and 18 deletions

1
Cargo.lock generated
View file

@ -1060,6 +1060,7 @@ dependencies = [
"lsp-server 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "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_ide_api 0.1.0",
"ra_prof 0.1.0", "ra_prof 0.1.0",
"ra_project_model 0.1.0", "ra_project_model 0.1.0",

View file

@ -82,6 +82,12 @@ pub struct CyclicDependencies;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct CrateId(pub u32); 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Edition { pub enum Edition {
Edition2018, Edition2018,
@ -178,15 +184,19 @@ impl CrateGraph {
/// Extends this crate graph by adding a complete disjoint second crate /// Extends this crate graph by adding a complete disjoint second crate
/// graph. /// 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; let start = self.arena.len() as u32;
self.arena.extend(other.arena.into_iter().map(|(id, mut data)| { 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 { 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) (new_id, data)
})); }));
start
} }
fn dfs_find(&self, target: CrateId, from: CrateId, visited: &mut FxHashSet<CrateId>) -> bool { fn dfs_find(&self, target: CrateId, from: CrateId, visited: &mut FxHashSet<CrateId>) -> bool {

View file

@ -5,6 +5,7 @@ use ra_syntax::{ast, Parse, SmolStr, SyntaxNode};
use crate::{ use crate::{
adt::{EnumData, StructData}, adt::{EnumData, StructData},
debug::HirDebugDatabase,
generics::{GenericDef, GenericParams}, generics::{GenericDef, GenericParams},
ids, ids,
impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks}, impl_block::{ImplBlock, ImplSourceMap, ModuleImplBlocks},
@ -83,7 +84,7 @@ pub trait AstDatabase: InternDatabase {
// This database uses `AstDatabase` internally, // This database uses `AstDatabase` internally,
#[salsa::query_group(DefDatabaseStorage)] #[salsa::query_group(DefDatabaseStorage)]
#[salsa::requires(AstDatabase)] #[salsa::requires(AstDatabase)]
pub trait DefDatabase: InternDatabase { pub trait DefDatabase: InternDatabase + HirDebugDatabase {
#[salsa::invoke(crate::adt::StructData::struct_data_query)] #[salsa::invoke(crate::adt::StructData::struct_data_query)]
fn struct_data(&self, s: Struct) -> Arc<StructData>; fn struct_data(&self, s: Struct) -> Arc<StructData>;

View file

@ -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<String> {
None
}
fn file_path(&self, _file_id: FileId) -> Option<String> {
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<DB: HirDebugHelper> 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<F>(Cell<Option<F>>);
impl<F: FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Debug for DebugFn<F> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let f = self.0.take().unwrap();
f(fmt)
}
}
DebugFn(Cell::new(Some(f)))
}

View file

@ -20,6 +20,7 @@ macro_rules! impl_froms {
} }
mod either; mod either;
pub mod debug;
pub mod db; pub mod db;
#[macro_use] #[macro_use]

View file

@ -2,13 +2,14 @@ use std::{panic, sync::Arc};
use parking_lot::Mutex; use parking_lot::Mutex;
use ra_db::{ 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 relative_path::RelativePathBuf;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 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); pub const WORKSPACE: SourceRootId = SourceRootId(0);
@ -24,10 +25,22 @@ pub struct MockDatabase {
events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>, events: Mutex<Option<Vec<salsa::Event<MockDatabase>>>>,
runtime: salsa::Runtime<MockDatabase>, runtime: salsa::Runtime<MockDatabase>,
files: FxHashMap<String, FileId>, files: FxHashMap<String, FileId>,
crate_names: Arc<FxHashMap<CrateId, String>>,
file_paths: Arc<FxHashMap<FileId, String>>,
} }
impl panic::RefUnwindSafe for MockDatabase {} impl panic::RefUnwindSafe for MockDatabase {}
impl HirDebugHelper for MockDatabase {
fn crate_name(&self, krate: CrateId) -> Option<String> {
self.crate_names.get(&krate).cloned()
}
fn file_path(&self, file_id: FileId) -> Option<String> {
self.file_paths.get(&file_id).cloned()
}
}
impl MockDatabase { impl MockDatabase {
pub fn with_files(fixture: &str) -> MockDatabase { pub fn with_files(fixture: &str) -> MockDatabase {
let (db, position) = MockDatabase::from_fixture(fixture); let (db, position) = MockDatabase::from_fixture(fixture);
@ -62,6 +75,7 @@ impl MockDatabase {
for (crate_name, (crate_root, edition, _)) in graph.0.iter() { for (crate_name, (crate_root, edition, _)) in graph.0.iter() {
let crate_root = self.file_id_of(&crate_root); let crate_root = self.file_id_of(&crate_root);
let crate_id = crate_graph.add_crate_root(crate_root, *edition); 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); ids.insert(crate_name, crate_id);
} }
for (crate_name, (_, _, deps)) in graph.0.iter() { 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 is_crate_root = rel_path == "lib.rs" || rel_path == "/main.rs";
let file_id = FileId(self.files.len() as u32); let file_id = FileId(self.files.len() as u32);
let prev = self.files.insert(path.to_string(), file_id); let prev = self.files.insert(path.to_string(), file_id);
assert!(prev.is_none(), "duplicate files in the text fixture"); 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()); let text = Arc::new(text.to_string());
self.set_file_text(file_id, text); self.set_file_text(file_id, text);
self.set_file_relative_path(file_id, rel_path.clone()); self.set_file_relative_path(file_id, rel_path.clone());
@ -200,6 +217,8 @@ impl Default for MockDatabase {
events: Default::default(), events: Default::default(),
runtime: salsa::Runtime::default(), runtime: salsa::Runtime::default(),
files: FxHashMap::default(), files: FxHashMap::default(),
crate_names: Default::default(),
file_paths: Default::default(),
}; };
db.set_crate_graph(Default::default()); db.set_crate_graph(Default::default());
db db
@ -213,6 +232,8 @@ impl salsa::ParallelDatabase for MockDatabase {
runtime: self.runtime.snapshot(self), runtime: self.runtime.snapshot(self),
// only the root database can be used to get file_id by path. // only the root database can be used to get file_id by path.
files: FxHashMap::default(), files: FxHashMap::default(),
file_paths: Arc::clone(&self.file_paths),
crate_names: Arc::clone(&self.crate_names),
}) })
} }
} }

View file

@ -2,7 +2,7 @@ use std::{fmt, sync::Arc, time};
use ra_db::{ use ra_db::{
salsa::{Database, Durability, SweepStrategy}, salsa::{Database, Durability, SweepStrategy},
CrateGraph, FileId, SourceDatabase, SourceRoot, SourceRootId, CrateGraph, CrateId, FileId, SourceDatabase, SourceRoot, SourceRootId,
}; };
use ra_prof::{memory_usage, profile, Bytes}; use ra_prof::{memory_usage, profile, Bytes};
use ra_syntax::SourceFile; use ra_syntax::SourceFile;
@ -11,7 +11,7 @@ use relative_path::RelativePathBuf;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use crate::{ use crate::{
db::RootDatabase, db::{DebugData, RootDatabase},
status::syntax_tree_stats, status::syntax_tree_stats,
symbol_index::{SymbolIndex, SymbolsDatabase}, symbol_index::{SymbolIndex, SymbolsDatabase},
}; };
@ -23,6 +23,7 @@ pub struct AnalysisChange {
files_changed: Vec<(FileId, Arc<String>)>, files_changed: Vec<(FileId, Arc<String>)>,
libraries_added: Vec<LibraryData>, libraries_added: Vec<LibraryData>,
crate_graph: Option<CrateGraph>, crate_graph: Option<CrateGraph>,
debug_data: DebugData,
} }
impl fmt::Debug for AnalysisChange { impl fmt::Debug for AnalysisChange {
@ -83,6 +84,14 @@ impl AnalysisChange {
pub fn set_crate_graph(&mut self, graph: CrateGraph) { pub fn set_crate_graph(&mut self, graph: CrateGraph) {
self.crate_graph = Some(graph); 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)] #[derive(Debug)]
@ -200,6 +209,8 @@ impl RootDatabase {
if let Some(crate_graph) = change.crate_graph { if let Some(crate_graph) = change.crate_graph {
self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) 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) { fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {

View file

@ -2,8 +2,9 @@ use std::{sync::Arc, time};
use ra_db::{ use ra_db::{
salsa::{self, Database, Durability}, salsa::{self, Database, Durability},
Canceled, CheckCanceled, FileId, SourceDatabase, Canceled, CheckCanceled, CrateId, FileId, SourceDatabase, SourceRootId,
}; };
use rustc_hash::FxHashMap;
use crate::{ use crate::{
symbol_index::{self, SymbolsDatabase}, symbol_index::{self, SymbolsDatabase},
@ -23,10 +24,23 @@ use crate::{
pub(crate) struct RootDatabase { pub(crate) struct RootDatabase {
runtime: salsa::Runtime<RootDatabase>, runtime: salsa::Runtime<RootDatabase>,
pub(crate) feature_flags: Arc<FeatureFlags>, pub(crate) feature_flags: Arc<FeatureFlags>,
pub(crate) debug_data: Arc<DebugData>,
pub(crate) last_gc: time::Instant, pub(crate) last_gc: time::Instant,
pub(crate) last_gc_check: time::Instant, pub(crate) last_gc_check: time::Instant,
} }
impl hir::debug::HirDebugHelper for RootDatabase {
fn crate_name(&self, krate: CrateId) -> Option<String> {
self.debug_data.crate_names.get(&krate).cloned()
}
fn file_path(&self, file_id: FileId) -> Option<String> {
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 { impl salsa::Database for RootDatabase {
fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> { fn salsa_runtime(&self) -> &salsa::Runtime<RootDatabase> {
&self.runtime &self.runtime
@ -58,6 +72,7 @@ impl RootDatabase {
last_gc: time::Instant::now(), last_gc: time::Instant::now(),
last_gc_check: time::Instant::now(), last_gc_check: time::Instant::now(),
feature_flags: Arc::new(feature_flags), feature_flags: Arc::new(feature_flags),
debug_data: Default::default(),
}; };
db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
db.set_local_roots_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: self.last_gc,
last_gc_check: self.last_gc_check, last_gc_check: self.last_gc_check,
feature_flags: Arc::clone(&self.feature_flags), 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<LineIndex
let text = db.file_text(file_id); let text = db.file_text(file_id);
Arc::new(LineIndex::new(&*text)) Arc::new(LineIndex::new(&*text))
} }
#[derive(Debug, Default, Clone)]
pub(crate) struct DebugData {
pub(crate) root_paths: FxHashMap<SourceRootId, String>,
pub(crate) crate_names: FxHashMap<CrateId, String>,
}
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());
}
}

View file

@ -18,6 +18,7 @@ parking_lot = "0.9.0"
jod-thread = "0.1.0" jod-thread = "0.1.0"
ra_vfs = "0.4.0" ra_vfs = "0.4.0"
ra_syntax = { path = "../ra_syntax" } ra_syntax = { path = "../ra_syntax" }
ra_db = { path = "../ra_db" }
ra_text_edit = { path = "../ra_text_edit" } ra_text_edit = { path = "../ra_text_edit" }
ra_ide_api = { path = "../ra_ide_api" } ra_ide_api = { path = "../ra_ide_api" }
lsp-server = "0.2.0" lsp-server = "0.2.0"

View file

@ -92,6 +92,7 @@ impl WorldState {
let vfs_root_path = vfs.root2path(r); let vfs_root_path = vfs.root2path(r);
let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));
change.add_root(SourceRootId(r.0), is_local); 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 // Create crate graph from all the workspaces
@ -101,7 +102,11 @@ impl WorldState {
vfs_file.map(|f| FileId(f.0)) vfs_file.map(|f| FileId(f.0))
}; };
for ws in workspaces.iter() { 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); change.set_crate_graph(crate_graph);

View file

@ -9,7 +9,7 @@ use std::{
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
use ra_db::{CrateGraph, Edition, FileId}; use ra_db::{CrateGraph, CrateId, Edition, FileId};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde_json::from_reader; use serde_json::from_reader;
@ -113,8 +113,12 @@ impl ProjectWorkspace {
} }
} }
pub fn to_crate_graph(&self, load: &mut dyn FnMut(&Path) -> Option<FileId>) -> CrateGraph { pub fn to_crate_graph(
&self,
load: &mut dyn FnMut(&Path) -> Option<FileId>,
) -> (CrateGraph, FxHashMap<CrateId, String>) {
let mut crate_graph = CrateGraph::default(); let mut crate_graph = CrateGraph::default();
let mut names = FxHashMap::default();
match self { match self {
ProjectWorkspace::Json { project } => { ProjectWorkspace::Json { project } => {
let mut crates = FxHashMap::default(); let mut crates = FxHashMap::default();
@ -151,10 +155,9 @@ impl ProjectWorkspace {
let mut sysroot_crates = FxHashMap::default(); let mut sysroot_crates = FxHashMap::default();
for krate in sysroot.crates() { for krate in sysroot.crates() {
if let Some(file_id) = load(krate.root(&sysroot)) { if let Some(file_id) = load(krate.root(&sysroot)) {
sysroot_crates.insert( let crate_id = crate_graph.add_crate_root(file_id, Edition::Edition2018);
krate, sysroot_crates.insert(krate, crate_id);
crate_graph.add_crate_root(file_id, Edition::Edition2018), names.insert(crate_id, krate.name(&sysroot).to_string());
);
} }
} }
for from in sysroot.crates() { for from in sysroot.crates() {
@ -182,6 +185,7 @@ impl ProjectWorkspace {
if let Some(file_id) = load(root) { if let Some(file_id) = load(root) {
let edition = pkg.edition(&cargo); let edition = pkg.edition(&cargo);
let crate_id = crate_graph.add_crate_root(file_id, edition); 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 { if tgt.kind(&cargo) == TargetKind::Lib {
lib_tgt = Some(crate_id); lib_tgt = Some(crate_id);
pkg_to_lib_crate.insert(pkg, 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. // target of downstream.
for pkg in cargo.packages() { for pkg in cargo.packages() {
for dep in pkg.dependencies(&cargo) { 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> { pub fn workspace_root_for(&self, path: &Path) -> Option<&Path> {