Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/body.rs')
-rw-r--r--crates/hir-def/src/expr_store/body.rs175
1 files changed, 175 insertions, 0 deletions
diff --git a/crates/hir-def/src/expr_store/body.rs b/crates/hir-def/src/expr_store/body.rs
new file mode 100644
index 0000000000..a55fec4f8b
--- /dev/null
+++ b/crates/hir-def/src/expr_store/body.rs
@@ -0,0 +1,175 @@
+//! Defines `Body`: a lowered representation of functions, statics and
+//! consts.
+use std::ops;
+
+use hir_expand::{InFile, Lookup};
+use la_arena::{Idx, RawIdx};
+use span::Edition;
+use syntax::ast;
+use triomphe::Arc;
+
+use crate::{
+ db::DefDatabase,
+ expander::Expander,
+ expr_store::{lower, pretty, ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr},
+ hir::{BindingId, ExprId, PatId},
+ item_tree::AttrOwner,
+ src::HasSource,
+ DefWithBodyId, HasModule,
+};
+
+/// The body of an item (function, const etc.).
+#[derive(Debug, Eq, PartialEq)]
+pub struct Body {
+ pub store: ExpressionStore,
+ /// The patterns for the function's parameters. While the parameter types are
+ /// part of the function signature, the patterns are not (they don't change
+ /// the external type of the function).
+ ///
+ /// If this `Body` is for the body of a constant, this will just be
+ /// empty.
+ pub params: Box<[PatId]>,
+ pub self_param: Option<BindingId>,
+ /// The `ExprId` of the actual body expression.
+ pub body_expr: ExprId,
+}
+
+impl ops::Deref for Body {
+ type Target = ExpressionStore;
+
+ fn deref(&self) -> &Self::Target {
+ &self.store
+ }
+}
+
+/// An item body together with the mapping from syntax nodes to HIR expression
+/// IDs. This is needed to go from e.g. a position in a file to the HIR
+/// expression containing it; but for type inference etc., we want to operate on
+/// a structure that is agnostic to the actual positions of expressions in the
+/// file, so that we don't recompute types whenever some whitespace is typed.
+///
+/// One complication here is that, due to macro expansion, a single `Body` might
+/// be spread across several files. So, for each ExprId and PatId, we record
+/// both the HirFileId and the position inside the file. However, we only store
+/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
+/// this properly for macros.
+#[derive(Default, Debug, Eq, PartialEq)]
+pub struct BodySourceMap {
+ pub self_param: Option<InFile<SelfParamPtr>>,
+ pub store: ExpressionStoreSourceMap,
+}
+
+impl ops::Deref for BodySourceMap {
+ type Target = ExpressionStoreSourceMap;
+
+ fn deref(&self) -> &Self::Target {
+ &self.store
+ }
+}
+
+impl Body {
+ pub(crate) fn body_with_source_map_query(
+ db: &dyn DefDatabase,
+ def: DefWithBodyId,
+ ) -> (Arc<Body>, Arc<BodySourceMap>) {
+ let _p = tracing::info_span!("body_with_source_map_query").entered();
+ let mut params = None;
+
+ let mut is_async_fn = false;
+ let InFile { file_id, value: body } = {
+ match def {
+ DefWithBodyId::FunctionId(f) => {
+ let data = db.function_data(f);
+ let f = f.lookup(db);
+ let src = f.source(db);
+ params = src.value.param_list().map(move |param_list| {
+ let item_tree = f.id.item_tree(db);
+ let func = &item_tree[f.id.value];
+ let krate = f.container.module(db).krate;
+ let crate_graph = db.crate_graph();
+ (
+ param_list,
+ (0..func.params.len()).map(move |idx| {
+ item_tree
+ .attrs(
+ db,
+ krate,
+ AttrOwner::Param(
+ f.id.value,
+ Idx::from_raw(RawIdx::from(idx as u32)),
+ ),
+ )
+ .is_cfg_enabled(&crate_graph[krate].cfg_options)
+ }),
+ )
+ });
+ is_async_fn = data.is_async();
+ src.map(|it| it.body().map(ast::Expr::from))
+ }
+ DefWithBodyId::ConstId(c) => {
+ let c = c.lookup(db);
+ let src = c.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::StaticId(s) => {
+ let s = s.lookup(db);
+ let src = s.source(db);
+ src.map(|it| it.body())
+ }
+ DefWithBodyId::VariantId(v) => {
+ let s = v.lookup(db);
+ let src = s.source(db);
+ src.map(|it| it.expr())
+ }
+ DefWithBodyId::InTypeConstId(c) => c.lookup(db).id.map(|_| c.source(db).expr()),
+ }
+ };
+ let module = def.module(db);
+ let expander = Expander::new(db, file_id, module);
+ let (body, mut source_map) =
+ lower::lower_body(db, def, expander, params, body, module.krate, is_async_fn);
+ source_map.store.shrink_to_fit();
+
+ (Arc::new(body), Arc::new(source_map))
+ }
+
+ pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> {
+ db.body_with_source_map(def).0
+ }
+
+ pub fn pretty_print(
+ &self,
+ db: &dyn DefDatabase,
+ owner: DefWithBodyId,
+ edition: Edition,
+ ) -> String {
+ pretty::print_body_hir(db, self, owner, edition)
+ }
+
+ pub fn pretty_print_expr(
+ &self,
+ db: &dyn DefDatabase,
+ owner: DefWithBodyId,
+ expr: ExprId,
+ edition: Edition,
+ ) -> String {
+ pretty::print_expr_hir(db, self, owner, expr, edition)
+ }
+
+ pub fn pretty_print_pat(
+ &self,
+ db: &dyn DefDatabase,
+ owner: DefWithBodyId,
+ pat: PatId,
+ oneline: bool,
+ edition: Edition,
+ ) -> String {
+ pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
+ }
+}
+
+impl BodySourceMap {
+ pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> {
+ self.self_param
+ }
+}