From ef2b84ddf119c950272c5f1eb321f3f9e90bedd4 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 8 Sep 2019 09:48:45 +0300 Subject: 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 --- 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 ++++++++++++++++-- 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 crates/ra_hir/src/debug.rs (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index 7b7974f5b..f7f124904 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 000000000..5a835741d --- /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 7c2a68992..24ee84f86 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 972f0ece5..8dcea5071 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), }) } } -- cgit v1.2.3 From e5a8093dd497518c177d3c22404d80da44905336 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 9 Sep 2019 12:39:59 +0300 Subject: document module --- crates/ra_hir/src/debug.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) (limited to 'crates/ra_hir') diff --git a/crates/ra_hir/src/debug.rs b/crates/ra_hir/src/debug.rs index 5a835741d..87f3180c3 100644 --- a/crates/ra_hir/src/debug.rs +++ b/crates/ra_hir/src/debug.rs @@ -1,4 +1,24 @@ -use std::{cell::Cell, fmt}; +//! printf debugging infrastructure for rust-analyzer. +//! +//! When you print a hir type, like a module, using `eprintln!("{:?}", module)`, +//! you usually get back a numeric ID, which doesn't tell you much: +//! `Module(92)`. +//! +//! This module adds convenience `debug` methods to various types, which resolve +//! the id to a human-readable location info: +//! +//! ```not_rust +//! eprintln!("{:?}", module.debug(db)); +//! => +//! Module { name: collections, path: "liballoc/collections/mod.rs" } +//! ``` +//! +//! Note that to get this info, we might need to execute queries! So +//! +//! * don't use the `debug` methods for logging +//! * when debugging, be aware that interference is possible. + +use std::fmt; use ra_db::{CrateId, FileId}; @@ -50,15 +70,14 @@ impl HirDebugDatabase for DB { } } -fn debug_fn(f: impl FnOnce(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Debug { - struct DebugFn(Cell>); +fn debug_fn(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Debug { + struct DebugFn(F); - impl) -> fmt::Result> fmt::Debug for DebugFn { + impl) -> fmt::Result> fmt::Debug for DebugFn { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let f = self.0.take().unwrap(); - f(fmt) + (&self.0)(fmt) } } - DebugFn(Cell::new(Some(f))) + DebugFn(f) } -- cgit v1.2.3