Skip to main content

hir_def/expr_store/
body.rs

1//! Defines `Body`: a lowered representation of functions, statics and
2//! consts.
3use std::ops;
4
5use base_db::SourceDatabase;
6use hir_expand::{InFile, Lookup};
7use span::Edition;
8use syntax::{SyntaxNodePtr, ast};
9use triomphe::Arc;
10
11use crate::{
12    DefWithBodyId, ExpressionStoreOwnerId, HasModule,
13    expr_store::{
14        ExpressionStore, ExpressionStoreSourceMap, SelfParamPtr, lower::lower_body, pretty,
15    },
16    hir::{BindingId, ExprId, PatId},
17    src::HasSource,
18};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub struct Param<Id> {
22    /// The id of the formal parameter. In analysis, you want to use this: it has a special status as a parameter,
23    /// while [`user_written`][Self::user_written] is just a local variable.
24    pub formal: Id,
25    /// The id that corresponds to the pattern the user wrote. In IDE, you usually want to use this.
26    ///
27    /// It will be different than [`formal`][Self::formal] for coroutine fns (async fn etc.), because we apply
28    /// a desugaring for those that copies the parameter into a local variable.
29    pub user_written: Id,
30}
31
32impl<Id: Copy> Param<Id> {
33    pub(crate) fn new(id: Id) -> Self {
34        Self { formal: id, user_written: id }
35    }
36}
37
38/// The body of an item (function, const etc.).
39#[derive(Debug, Eq, PartialEq)]
40pub struct Body {
41    pub store: ExpressionStore,
42    /// The patterns for the function's parameters. While the parameter types are
43    /// part of the function signature, the patterns are not (they don't change
44    /// the external type of the function).
45    ///
46    /// If this `Body` is for the body of a constant, this will just be
47    /// empty.
48    pub params: Box<[Param<PatId>]>,
49    pub self_param: Option<Param<BindingId>>,
50}
51
52impl ops::Deref for Body {
53    type Target = ExpressionStore;
54
55    #[inline]
56    fn deref(&self) -> &Self::Target {
57        &self.store
58    }
59}
60
61/// An item body together with the mapping from syntax nodes to HIR expression
62/// IDs. This is needed to go from e.g. a position in a file to the HIR
63/// expression containing it; but for type inference etc., we want to operate on
64/// a structure that is agnostic to the actual positions of expressions in the
65/// file, so that we don't recompute types whenever some whitespace is typed.
66///
67/// One complication here is that, due to macro expansion, a single `Body` might
68/// be spread across several files. So, for each ExprId and PatId, we record
69/// both the HirFileId and the position inside the file. However, we only store
70/// AST -> ExprId mapping for non-macro files, as it is not clear how to handle
71/// this properly for macros.
72#[derive(Default, Debug, Eq, PartialEq)]
73pub struct BodySourceMap {
74    pub self_param: Option<InFile<SelfParamPtr>>,
75    pub store: ExpressionStoreSourceMap,
76}
77
78impl ops::Deref for BodySourceMap {
79    type Target = ExpressionStoreSourceMap;
80
81    #[inline]
82    fn deref(&self) -> &Self::Target {
83        &self.store
84    }
85}
86
87#[salsa::tracked]
88impl Body {
89    #[salsa::tracked(lru = 512, returns(ref))]
90    pub fn with_source_map(
91        db: &dyn SourceDatabase,
92        def: DefWithBodyId,
93    ) -> (Arc<Body>, BodySourceMap) {
94        let _p = tracing::info_span!("body_with_source_map_query").entered();
95        let mut params = None;
96
97        let mut is_async_fn = false;
98        let mut is_gen_fn = false;
99        let (InFile { file_id, value: body }, syntax_node) = {
100            match def {
101                DefWithBodyId::FunctionId(f) => {
102                    let f = f.lookup(db);
103                    let src = f.source(db);
104                    params = src.value.param_list();
105                    is_async_fn = src.value.async_token().is_some();
106                    is_gen_fn = src.value.gen_token().is_some();
107                    let syntax_node = SyntaxNodePtr::new(src.syntax().value);
108                    (src.map(|it| it.body().map(ast::Expr::from)), syntax_node)
109                }
110                DefWithBodyId::ConstId(c) => {
111                    let c = c.lookup(db);
112                    let src = c.source(db);
113                    let syntax_node = SyntaxNodePtr::new(src.syntax().value);
114                    (src.map(|it| it.body()), syntax_node)
115                }
116                DefWithBodyId::StaticId(s) => {
117                    let s = s.lookup(db);
118                    let src = s.source(db);
119                    let syntax_node = SyntaxNodePtr::new(src.syntax().value);
120                    (src.map(|it| it.body()), syntax_node)
121                }
122                DefWithBodyId::VariantId(v) => {
123                    let s = v.lookup(db);
124                    let src = s.source(db);
125                    let syntax_node = SyntaxNodePtr::new(src.syntax().value);
126                    (src.map(|it| it.const_arg()?.expr()), syntax_node)
127                }
128            }
129        };
130        let module = def.module(db);
131        let (body, source_map) =
132            lower_body(db, def, syntax_node, file_id, module, params, body, is_async_fn, is_gen_fn);
133
134        (Arc::new(body), source_map)
135    }
136
137    #[salsa::tracked(returns(deref))]
138    pub fn of(db: &dyn SourceDatabase, def: DefWithBodyId) -> Arc<Body> {
139        Self::with_source_map(db, def).0.clone()
140    }
141}
142
143impl Body {
144    pub fn root_expr(&self) -> ExprId {
145        // A `Body` can also contain root expressions that aren't the body (in the param patterns),
146        // but the body always come last.
147        self.store.expr_roots().next_back().unwrap()
148    }
149
150    /// Returns `true` if this is the formal or user-written self param.
151    #[inline]
152    pub fn is_any_self_param(&self, binding: BindingId) -> bool {
153        self.self_param.is_some_and(|self_param| {
154            self_param.formal == binding || self_param.user_written == binding
155        })
156    }
157
158    pub fn pretty_print(
159        &self,
160        db: &dyn SourceDatabase,
161        owner: DefWithBodyId,
162        edition: Edition,
163    ) -> String {
164        pretty::print_body_hir(db, self, owner, edition)
165    }
166
167    pub fn pretty_print_expr(
168        &self,
169        db: &dyn SourceDatabase,
170        owner: DefWithBodyId,
171        expr: ExprId,
172        edition: Edition,
173    ) -> String {
174        pretty::print_expr_hir(db, self, owner.into(), expr, edition)
175    }
176
177    pub fn pretty_print_pat(
178        &self,
179        db: &dyn SourceDatabase,
180        owner: ExpressionStoreOwnerId,
181        pat: PatId,
182        oneline: bool,
183        edition: Edition,
184    ) -> String {
185        pretty::print_pat_hir(db, self, owner, pat, oneline, edition)
186    }
187}
188
189impl BodySourceMap {
190    pub fn self_param_syntax(&self) -> Option<InFile<SelfParamPtr>> {
191        self.self_param
192    }
193}