From 40076b577fc0bbc06be2b6c061e1ca41c0f038be Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 13 Apr 2025 12:13:28 +0200 Subject: [PATCH] internal: Render sigantures with view hir command --- crates/hir-def/src/expr_store.rs | 2 +- crates/hir-def/src/expr_store/pretty.rs | 107 +++++++++++++++++++--- crates/hir/src/semantics.rs | 31 ++++++- crates/hir/src/semantics/source_to_def.rs | 11 +-- crates/hir/src/source_analyzer.rs | 12 +-- crates/ide/src/view_hir.rs | 27 ++---- 6 files changed, 146 insertions(+), 44 deletions(-) diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index eb66e592d6..9349ee7740 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs @@ -4,7 +4,7 @@ pub mod body; mod expander; pub mod lower; pub mod path; -pub(crate) mod pretty; +pub mod pretty; pub mod scope; #[cfg(test)] mod tests; diff --git a/crates/hir-def/src/expr_store/pretty.rs b/crates/hir-def/src/expr_store/pretty.rs index 01476deb62..5e3580e590 100644 --- a/crates/hir-def/src/expr_store/pretty.rs +++ b/crates/hir-def/src/expr_store/pretty.rs @@ -10,8 +10,9 @@ use hir_expand::{Lookup, mod_path::PathKind}; use itertools::Itertools; use span::Edition; +use crate::signatures::StructFlags; use crate::{ - DefWithBodyId, ItemTreeLoc, TypeParamId, + AdtId, DefWithBodyId, GenericDefId, ItemTreeLoc, TypeParamId, VariantId, expr_store::path::{GenericArg, GenericArgs}, hir::{ Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement, @@ -21,6 +22,7 @@ use crate::{ signatures::{FnFlags, FunctionSignature, StructSignature}, type_ref::{ConstRef, LifetimeRef, Mutability, TraitBoundModifier, TypeBound, UseArgRef}, }; +use crate::{item_tree::FieldsShape, signatures::FieldData}; use super::*; @@ -40,13 +42,13 @@ macro_rules! wln { } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub(crate) enum LineFormat { +pub enum LineFormat { Oneline, Newline, Indentation, } -pub(crate) fn print_body_hir( +pub fn print_body_hir( db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId, @@ -112,7 +114,93 @@ pub(crate) fn print_body_hir( p.buf } -pub(crate) fn print_path( +pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: Edition) -> String { + let header = match owner { + VariantId::StructId(it) => { + it.lookup(db).id.resolved(db, |it| format!("struct {}", it.name.display(db, edition))) + } + VariantId::EnumVariantId(enum_variant_id) => { + let loc = enum_variant_id.lookup(db); + let enum_loc = loc.parent.lookup(db); + format!( + "enum {}::{}", + enum_loc.id.item_tree(db)[enum_loc.id.value].name.display(db, edition), + loc.id.item_tree(db)[loc.id.value].name.display(db, edition), + ) + } + VariantId::UnionId(union_id) => union_id + .lookup(db) + .id + .resolved(db, |it| format!("union {}", it.name.display(db, edition))), + }; + + let fields = db.variant_fields(owner); + + let mut p = Printer { + db, + store: &fields.store, + buf: header, + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; + match fields.shape { + FieldsShape::Record => wln!(p, " {{"), + FieldsShape::Tuple => wln!(p, "("), + FieldsShape::Unit => (), + } + + for (_, data) in fields.fields().iter() { + let FieldData { name, type_ref, visibility, is_unsafe } = data; + match visibility { + crate::item_tree::RawVisibility::Module(interned, _visibility_explicitness) => { + w!(p, "{}", interned.display(db, p.edition)) + } + crate::item_tree::RawVisibility::Public => w!(p, "pub "), + } + if *is_unsafe { + w!(p, "unsafe "); + } + w!(p, "{}: ", name.display(db, p.edition)); + p.print_type_ref(*type_ref); + } + + match fields.shape { + FieldsShape::Record => wln!(p, "}}"), + FieldsShape::Tuple => wln!(p, ");"), + FieldsShape::Unit => wln!(p, ";"), + } + p.buf +} + +pub fn print_signature(db: &dyn DefDatabase, owner: GenericDefId, edition: Edition) -> String { + match owner { + GenericDefId::AdtId(id) => match id { + AdtId::StructId(id) => { + let signature = db.struct_signature(id); + print_struct(db, &signature, edition) + } + AdtId::UnionId(id) => { + format!("unimplemented {id:?}") + } + AdtId::EnumId(id) => { + format!("unimplemented {id:?}") + } + }, + GenericDefId::ConstId(id) => format!("unimplemented {id:?}"), + GenericDefId::FunctionId(id) => { + let signature = db.function_signature(id); + print_function(db, &signature, edition) + } + GenericDefId::ImplId(id) => format!("unimplemented {id:?}"), + GenericDefId::StaticId(id) => format!("unimplemented {id:?}"), + GenericDefId::TraitAliasId(id) => format!("unimplemented {id:?}"), + GenericDefId::TraitId(id) => format!("unimplemented {id:?}"), + GenericDefId::TypeAliasId(id) => format!("unimplemented {id:?}"), + } +} + +pub fn print_path( db: &dyn DefDatabase, store: &ExpressionStore, path: &Path, @@ -130,14 +218,11 @@ pub(crate) fn print_path( p.buf } -pub(crate) fn print_struct( +pub fn print_struct( db: &dyn DefDatabase, StructSignature { name, generic_params, store, flags, shape, repr }: &StructSignature, edition: Edition, ) -> String { - use crate::item_tree::FieldsShape; - use crate::signatures::StructFlags; - let mut p = Printer { db, store, @@ -180,7 +265,7 @@ pub(crate) fn print_struct( p.buf } -pub(crate) fn print_function( +pub fn print_function( db: &dyn DefDatabase, FunctionSignature { name, @@ -342,7 +427,7 @@ fn print_generic_params(db: &dyn DefDatabase, generic_params: &GenericParams, p: } } -pub(crate) fn print_expr_hir( +pub fn print_expr_hir( db: &dyn DefDatabase, store: &ExpressionStore, _owner: DefWithBodyId, @@ -361,7 +446,7 @@ pub(crate) fn print_expr_hir( p.buf } -pub(crate) fn print_pat_hir( +pub fn print_pat_hir( db: &dyn DefDatabase, store: &ExpressionStore, _owner: DefWithBodyId, diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 39130d1659..c0f99e09e3 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -20,7 +20,7 @@ use hir_def::{ type_ref::Mutability, }; use hir_expand::{ - ExpandResult, FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt, + ExpandResult, FileRange, HirFileIdExt, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt, attrs::collect_attrs, builtin::{BuiltinFnLikeExpander, EagerExpander}, db::ExpandDatabase, @@ -739,6 +739,35 @@ impl<'db> SemanticsImpl<'db> { } } + pub fn debug_hir_at(&self, token: SyntaxToken) -> Option { + self.analyze_no_infer(&token.parent()?).and_then(|it| { + Some(match it.body_or_sig.as_ref()? { + crate::source_analyzer::BodyOrSig::Body { def, body, .. } => { + hir_def::expr_store::pretty::print_body_hir( + self.db, + body, + *def, + it.file_id.edition(self.db), + ) + } + &crate::source_analyzer::BodyOrSig::VariantFields { def, .. } => { + hir_def::expr_store::pretty::print_variant_body_hir( + self.db, + def, + it.file_id.edition(self.db), + ) + } + &crate::source_analyzer::BodyOrSig::Sig { def, .. } => { + hir_def::expr_store::pretty::print_signature( + self.db, + def, + it.file_id.edition(self.db), + ) + } + }) + }) + } + /// Maps a node down by mapping its first and last token down. pub fn descend_node_into_attributes(&self, node: N) -> SmallVec<[N; 1]> { // This might not be the correct way to do this, but it works for now diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 06100b30f8..2f06a1dc68 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -220,7 +220,7 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn module_to_def(&mut self, src: InFile<&ast::Module>) -> Option { let _p = tracing::info_span!("module_to_def").entered(); let parent_declaration = self - .ancestors_with_macros(src.syntax_ref(), |_, ancestor, _| { + .parent_ancestors_with_macros(src.syntax_ref(), |_, ancestor, _| { ancestor.map(Either::::cast).transpose() }) .map(|it| it.transpose()); @@ -519,7 +519,7 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn find_container(&mut self, src: InFile<&SyntaxNode>) -> Option { let _p = tracing::info_span!("find_container").entered(); - let def = self.ancestors_with_macros(src, |this, container, child| { + let def = self.parent_ancestors_with_macros(src, |this, container, child| { this.container_to_def(container, child) }); if let Some(def) = def { @@ -532,7 +532,7 @@ impl SourceToDefCtx<'_, '_> { } fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option { - self.ancestors_with_macros(src, |this, InFile { file_id, value }, _| { + self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| { let item = ast::Item::cast(value)?; match &item { ast::Item::Fn(it) => this.fn_to_def(InFile::new(file_id, it)).map(Into::into), @@ -555,7 +555,7 @@ impl SourceToDefCtx<'_, '_> { // FIXME: Remove this when we do inference in signatures fn find_pat_or_label_container(&mut self, src: InFile<&SyntaxNode>) -> Option { - self.ancestors_with_macros(src, |this, InFile { file_id, value }, _| { + self.parent_ancestors_with_macros(src, |this, InFile { file_id, value }, _| { let item = match ast::Item::cast(value.clone()) { Some(it) => it, None => { @@ -577,8 +577,7 @@ impl SourceToDefCtx<'_, '_> { } /// Skips the attributed item that caused the macro invocation we are climbing up - /// - fn ancestors_with_macros( + fn parent_ancestors_with_macros( &mut self, node: InFile<&SyntaxNode>, mut cb: impl FnMut( diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 6a39f9cab8..b86fe1fa39 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -60,11 +60,11 @@ use triomphe::Arc; pub(crate) struct SourceAnalyzer { pub(crate) file_id: HirFileId, pub(crate) resolver: Resolver, - body_or_sig: Option, + pub(crate) body_or_sig: Option, } #[derive(Debug)] -enum BodyOrSig { +pub(crate) enum BodyOrSig { Body { def: DefWithBodyId, body: Arc, @@ -73,12 +73,12 @@ enum BodyOrSig { }, // To be folded into body once it is considered one VariantFields { - _def: VariantId, + def: VariantId, store: Arc, source_map: Arc, }, Sig { - _def: GenericDefId, + def: GenericDefId, store: Arc, source_map: Arc, // infer: Option>, @@ -143,7 +143,7 @@ impl SourceAnalyzer { let resolver = def.resolver(db); SourceAnalyzer { resolver, - body_or_sig: Some(BodyOrSig::Sig { _def: def, store, source_map }), + body_or_sig: Some(BodyOrSig::Sig { def, store, source_map }), file_id, } } @@ -159,7 +159,7 @@ impl SourceAnalyzer { SourceAnalyzer { resolver, body_or_sig: Some(BodyOrSig::VariantFields { - _def: def, + def, store: fields.store.clone(), source_map, }), diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs index 954917d4c0..ec5e993f5a 100644 --- a/crates/ide/src/view_hir.rs +++ b/crates/ide/src/view_hir.rs @@ -1,6 +1,6 @@ -use hir::{DefWithBody, Semantics}; +use hir::Semantics; use ide_db::{FilePosition, RootDatabase}; -use syntax::{AstNode, algo::ancestors_at_offset, ast}; +use syntax::AstNode; // Feature: View Hir // @@ -10,21 +10,10 @@ use syntax::{AstNode, algo::ancestors_at_offset, ast}; // // ![View Hir](https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif) pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String { - body_hir(db, position).unwrap_or_else(|| "Not inside a function body".to_owned()) -} - -fn body_hir(db: &RootDatabase, position: FilePosition) -> Option { - let sema = Semantics::new(db); - let source_file = sema.parse_guess_edition(position.file_id); - - let item = ancestors_at_offset(source_file.syntax(), position.offset) - .filter(|it| !ast::MacroCall::can_cast(it.kind())) - .find_map(ast::Item::cast)?; - let def: DefWithBody = match item { - ast::Item::Fn(it) => sema.to_def(&it)?.into(), - ast::Item::Const(it) => sema.to_def(&it)?.into(), - ast::Item::Static(it) => sema.to_def(&it)?.into(), - _ => return None, - }; - Some(def.debug_hir(db)) + (|| { + let sema = Semantics::new(db); + let source_file = sema.parse_guess_edition(position.file_id); + sema.debug_hir_at(source_file.syntax().token_at_offset(position.offset).next()?) + })() + .unwrap_or_else(|| "Not inside a lowerable item".to_owned()) }