mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 13:25:09 +00:00
Merge #897
897: Add basic const/static type inference r=flodiebold a=vipentti This adds basic const/static type inference discussed in #887. Currently the inference does not work for const/static declared inside a block. In addition the inference does not work inside the bodies of const/static. Co-authored-by: Ville Penttinen <villem.penttinen@gmail.com>
This commit is contained in:
commit
7c9acf2f83
11 changed files with 206 additions and 20 deletions
|
@ -554,11 +554,26 @@ impl Const {
|
||||||
self.id.module(db)
|
self.id.module(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self, db: &impl HirDatabase) -> Arc<ConstSignature> {
|
||||||
|
db.const_signature(*self)
|
||||||
|
}
|
||||||
|
|
||||||
/// The containing impl block, if this is a method.
|
/// The containing impl block, if this is a method.
|
||||||
pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option<ImplBlock> {
|
pub fn impl_block(&self, db: &impl PersistentHirDatabase) -> Option<ImplBlock> {
|
||||||
let module_impls = db.impls_in_module(self.module(db));
|
let module_impls = db.impls_in_module(self.module(db));
|
||||||
ImplBlock::containing(module_impls, (*self).into())
|
ImplBlock::containing(module_impls, (*self).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: move to a more general type for 'body-having' items
|
||||||
|
/// Builds a resolver for code inside this item.
|
||||||
|
pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
|
// take the outer scope...
|
||||||
|
let r = self
|
||||||
|
.impl_block(db)
|
||||||
|
.map(|ib| ib.resolver(db))
|
||||||
|
.unwrap_or_else(|| self.module(db).resolver(db));
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Docs for Const {
|
impl Docs for Const {
|
||||||
|
@ -567,6 +582,23 @@ impl Docs for Const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The declared signature of a const.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ConstSignature {
|
||||||
|
pub(crate) name: Name,
|
||||||
|
pub(crate) type_ref: TypeRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstSignature {
|
||||||
|
pub fn name(&self) -> &Name {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn type_ref(&self) -> &TypeRef {
|
||||||
|
&self.type_ref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub struct Static {
|
pub struct Static {
|
||||||
pub(crate) id: StaticId,
|
pub(crate) id: StaticId,
|
||||||
|
@ -580,6 +612,16 @@ impl Static {
|
||||||
pub fn module(&self, db: &impl PersistentHirDatabase) -> Module {
|
pub fn module(&self, db: &impl PersistentHirDatabase) -> Module {
|
||||||
self.id.module(db)
|
self.id.module(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn signature(&self, db: &impl HirDatabase) -> Arc<ConstSignature> {
|
||||||
|
db.static_signature(*self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a resolver for code inside this item.
|
||||||
|
pub fn resolver(&self, db: &impl HirDatabase) -> Resolver {
|
||||||
|
// take the outer scope...
|
||||||
|
self.module(db).resolver(db)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Docs for Static {
|
impl Docs for Static {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
mod krate; // `crate` is invalid ident :(
|
mod krate; // `crate` is invalid ident :(
|
||||||
|
mod konst; // `const` is invalid ident :(
|
||||||
mod module;
|
mod module;
|
||||||
pub(crate) mod function;
|
pub(crate) mod function;
|
||||||
|
|
37
crates/ra_hir/src/code_model_impl/konst.rs
Normal file
37
crates/ra_hir/src/code_model_impl/konst.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ra_syntax::ast::{self, NameOwner};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Name, AsName, Const, ConstSignature, Static,
|
||||||
|
type_ref::{TypeRef},
|
||||||
|
PersistentHirDatabase,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn const_signature_for<N: NameOwner>(
|
||||||
|
node: &N,
|
||||||
|
type_ref: Option<&ast::TypeRef>,
|
||||||
|
) -> Arc<ConstSignature> {
|
||||||
|
let name = node.name().map(|n| n.as_name()).unwrap_or_else(Name::missing);
|
||||||
|
let type_ref = TypeRef::from_ast_opt(type_ref);
|
||||||
|
let sig = ConstSignature { name, type_ref };
|
||||||
|
Arc::new(sig)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConstSignature {
|
||||||
|
pub(crate) fn const_signature_query(
|
||||||
|
db: &impl PersistentHirDatabase,
|
||||||
|
konst: Const,
|
||||||
|
) -> Arc<ConstSignature> {
|
||||||
|
let (_, node) = konst.source(db);
|
||||||
|
const_signature_for(&*node, node.type_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn static_signature_query(
|
||||||
|
db: &impl PersistentHirDatabase,
|
||||||
|
konst: Static,
|
||||||
|
) -> Arc<ConstSignature> {
|
||||||
|
let (_, node) = konst.source(db);
|
||||||
|
const_signature_for(&*node, node.type_ref())
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ use crate::{
|
||||||
SourceFileItems, SourceItemId, Crate, Module, HirInterner,
|
SourceFileItems, SourceItemId, Crate, Module, HirInterner,
|
||||||
Function, FnSignature, ExprScopes, TypeAlias,
|
Function, FnSignature, ExprScopes, TypeAlias,
|
||||||
Struct, Enum, StructField,
|
Struct, Enum, StructField,
|
||||||
|
Const, ConstSignature, Static,
|
||||||
macros::MacroExpansion,
|
macros::MacroExpansion,
|
||||||
module_tree::ModuleTree,
|
module_tree::ModuleTree,
|
||||||
nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}},
|
nameres::{ItemMap, lower::{LoweredModule, ImportSourceMap}},
|
||||||
|
@ -82,6 +83,12 @@ pub trait PersistentHirDatabase: SourceDatabase + AsRef<HirInterner> {
|
||||||
|
|
||||||
#[salsa::invoke(crate::type_alias::type_alias_ref_query)]
|
#[salsa::invoke(crate::type_alias::type_alias_ref_query)]
|
||||||
fn type_alias_ref(&self, typ: TypeAlias) -> Arc<TypeRef>;
|
fn type_alias_ref(&self, typ: TypeAlias) -> Arc<TypeRef>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::ConstSignature::const_signature_query)]
|
||||||
|
fn const_signature(&self, konst: Const) -> Arc<ConstSignature>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::ConstSignature::static_signature_query)]
|
||||||
|
fn static_signature(&self, konst: Static) -> Arc<ConstSignature>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::query_group(HirDatabaseStorage)]
|
#[salsa::query_group(HirDatabaseStorage)]
|
||||||
|
|
|
@ -71,6 +71,6 @@ pub use self::code_model_api::{
|
||||||
Struct, Enum, EnumVariant,
|
Struct, Enum, EnumVariant,
|
||||||
Function, FnSignature,
|
Function, FnSignature,
|
||||||
StructField, FieldSource,
|
StructField, FieldSource,
|
||||||
Static, Const,
|
Static, Const, ConstSignature,
|
||||||
Trait, TypeAlias,
|
Trait, TypeAlias,
|
||||||
};
|
};
|
||||||
|
|
|
@ -393,17 +393,22 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
// Attempt to find an impl_item for the type which has a name matching
|
// Attempt to find an impl_item for the type which has a name matching
|
||||||
// the current segment
|
// the current segment
|
||||||
log::debug!("looking for path segment: {:?}", segment);
|
log::debug!("looking for path segment: {:?}", segment);
|
||||||
let item = ty.iterate_impl_items(self.db, |item| match item {
|
let item: crate::ModuleDef = ty.iterate_impl_items(self.db, |item| match item {
|
||||||
crate::ImplItem::Method(func) => {
|
crate::ImplItem::Method(func) => {
|
||||||
let sig = func.signature(self.db);
|
let sig = func.signature(self.db);
|
||||||
if segment.name == *sig.name() {
|
if segment.name == *sig.name() {
|
||||||
return Some(func);
|
return Some(func.into());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Resolve associated const
|
crate::ImplItem::Const(konst) => {
|
||||||
crate::ImplItem::Const(_) => None,
|
let sig = konst.signature(self.db);
|
||||||
|
if segment.name == *sig.name() {
|
||||||
|
return Some(konst.into());
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Resolve associated types
|
// TODO: Resolve associated types
|
||||||
crate::ImplItem::TypeAlias(_) => None,
|
crate::ImplItem::TypeAlias(_) => None,
|
||||||
|
@ -477,9 +482,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let ty = self.insert_type_vars(ty.apply_substs(substs));
|
let ty = self.insert_type_vars(ty.apply_substs(substs));
|
||||||
(ty, Some(var.into()))
|
(ty, Some(var.into()))
|
||||||
}
|
}
|
||||||
TypableDef::TypeAlias(_) | TypableDef::Function(_) | TypableDef::Enum(_) => {
|
TypableDef::TypeAlias(_)
|
||||||
(Ty::Unknown, None)
|
| TypableDef::Function(_)
|
||||||
}
|
| TypableDef::Enum(_)
|
||||||
|
| TypableDef::Const(_)
|
||||||
|
| TypableDef::Static(_) => (Ty::Unknown, None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ use std::sync::Arc;
|
||||||
use crate::{
|
use crate::{
|
||||||
Function, Struct, StructField, Enum, EnumVariant, Path, Name,
|
Function, Struct, StructField, Enum, EnumVariant, Path, Name,
|
||||||
ModuleDef, TypeAlias,
|
ModuleDef, TypeAlias,
|
||||||
|
Const, Static,
|
||||||
HirDatabase,
|
HirDatabase,
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
name::KnownName,
|
name::KnownName,
|
||||||
|
@ -125,6 +126,7 @@ impl Ty {
|
||||||
TypableDef::Enum(e) => e.generic_params(db),
|
TypableDef::Enum(e) => e.generic_params(db),
|
||||||
TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db),
|
TypableDef::EnumVariant(var) => var.parent_enum(db).generic_params(db),
|
||||||
TypableDef::TypeAlias(t) => t.generic_params(db),
|
TypableDef::TypeAlias(t) => t.generic_params(db),
|
||||||
|
TypableDef::Const(_) | TypableDef::Static(_) => GenericParams::default().into(),
|
||||||
};
|
};
|
||||||
let parent_param_count = def_generics.count_parent_params();
|
let parent_param_count = def_generics.count_parent_params();
|
||||||
substs.extend((0..parent_param_count).map(|_| Ty::Unknown));
|
substs.extend((0..parent_param_count).map(|_| Ty::Unknown));
|
||||||
|
@ -163,6 +165,8 @@ impl Ty {
|
||||||
TypableDef::Function(_)
|
TypableDef::Function(_)
|
||||||
| TypableDef::Struct(_)
|
| TypableDef::Struct(_)
|
||||||
| TypableDef::Enum(_)
|
| TypableDef::Enum(_)
|
||||||
|
| TypableDef::Const(_)
|
||||||
|
| TypableDef::Static(_)
|
||||||
| TypableDef::TypeAlias(_) => last,
|
| TypableDef::TypeAlias(_) => last,
|
||||||
TypableDef::EnumVariant(_) => {
|
TypableDef::EnumVariant(_) => {
|
||||||
// the generic args for an enum variant may be either specified
|
// the generic args for an enum variant may be either specified
|
||||||
|
@ -197,12 +201,16 @@ pub(crate) fn type_for_def(db: &impl HirDatabase, def: TypableDef, ns: Namespace
|
||||||
(TypableDef::Enum(e), Namespace::Types) => type_for_enum(db, e),
|
(TypableDef::Enum(e), Namespace::Types) => type_for_enum(db, e),
|
||||||
(TypableDef::EnumVariant(v), Namespace::Values) => type_for_enum_variant_constructor(db, v),
|
(TypableDef::EnumVariant(v), Namespace::Values) => type_for_enum_variant_constructor(db, v),
|
||||||
(TypableDef::TypeAlias(t), Namespace::Types) => type_for_type_alias(db, t),
|
(TypableDef::TypeAlias(t), Namespace::Types) => type_for_type_alias(db, t),
|
||||||
|
(TypableDef::Const(c), Namespace::Values) => type_for_const(db, c),
|
||||||
|
(TypableDef::Static(c), Namespace::Values) => type_for_static(db, c),
|
||||||
|
|
||||||
// 'error' cases:
|
// 'error' cases:
|
||||||
(TypableDef::Function(_), Namespace::Types) => Ty::Unknown,
|
(TypableDef::Function(_), Namespace::Types) => Ty::Unknown,
|
||||||
(TypableDef::Enum(_), Namespace::Values) => Ty::Unknown,
|
(TypableDef::Enum(_), Namespace::Values) => Ty::Unknown,
|
||||||
(TypableDef::EnumVariant(_), Namespace::Types) => Ty::Unknown,
|
(TypableDef::EnumVariant(_), Namespace::Types) => Ty::Unknown,
|
||||||
(TypableDef::TypeAlias(_), Namespace::Values) => Ty::Unknown,
|
(TypableDef::TypeAlias(_), Namespace::Values) => Ty::Unknown,
|
||||||
|
(TypableDef::Const(_), Namespace::Types) => Ty::Unknown,
|
||||||
|
(TypableDef::Static(_), Namespace::Types) => Ty::Unknown,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -233,6 +241,22 @@ fn type_for_fn(db: &impl HirDatabase, def: Function) -> Ty {
|
||||||
Ty::FnDef { def: def.into(), sig, name, substs }
|
Ty::FnDef { def: def.into(), sig, name, substs }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build the declared type of a const.
|
||||||
|
fn type_for_const(db: &impl HirDatabase, def: Const) -> Ty {
|
||||||
|
let signature = def.signature(db);
|
||||||
|
let resolver = def.resolver(db);
|
||||||
|
|
||||||
|
Ty::from_hir(db, &resolver, signature.type_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the declared type of a static.
|
||||||
|
fn type_for_static(db: &impl HirDatabase, def: Static) -> Ty {
|
||||||
|
let signature = def.signature(db);
|
||||||
|
let resolver = def.resolver(db);
|
||||||
|
|
||||||
|
Ty::from_hir(db, &resolver, signature.type_ref())
|
||||||
|
}
|
||||||
|
|
||||||
/// Build the type of a tuple struct constructor.
|
/// Build the type of a tuple struct constructor.
|
||||||
fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty {
|
fn type_for_struct_constructor(db: &impl HirDatabase, def: Struct) -> Ty {
|
||||||
let var_data = def.variant_data(db);
|
let var_data = def.variant_data(db);
|
||||||
|
@ -318,8 +342,10 @@ pub enum TypableDef {
|
||||||
Enum(Enum),
|
Enum(Enum),
|
||||||
EnumVariant(EnumVariant),
|
EnumVariant(EnumVariant),
|
||||||
TypeAlias(TypeAlias),
|
TypeAlias(TypeAlias),
|
||||||
|
Const(Const),
|
||||||
|
Static(Static),
|
||||||
}
|
}
|
||||||
impl_froms!(TypableDef: Function, Struct, Enum, EnumVariant, TypeAlias);
|
impl_froms!(TypableDef: Function, Struct, Enum, EnumVariant, TypeAlias, Const, Static);
|
||||||
|
|
||||||
impl From<ModuleDef> for Option<TypableDef> {
|
impl From<ModuleDef> for Option<TypableDef> {
|
||||||
fn from(def: ModuleDef) -> Option<TypableDef> {
|
fn from(def: ModuleDef) -> Option<TypableDef> {
|
||||||
|
@ -329,10 +355,9 @@ impl From<ModuleDef> for Option<TypableDef> {
|
||||||
ModuleDef::Enum(e) => e.into(),
|
ModuleDef::Enum(e) => e.into(),
|
||||||
ModuleDef::EnumVariant(v) => v.into(),
|
ModuleDef::EnumVariant(v) => v.into(),
|
||||||
ModuleDef::TypeAlias(t) => t.into(),
|
ModuleDef::TypeAlias(t) => t.into(),
|
||||||
ModuleDef::Const(_)
|
ModuleDef::Const(v) => v.into(),
|
||||||
| ModuleDef::Static(_)
|
ModuleDef::Static(v) => v.into(),
|
||||||
| ModuleDef::Module(_)
|
ModuleDef::Module(_) | ModuleDef::Trait(_) => return None,
|
||||||
| ModuleDef::Trait(_) => return None,
|
|
||||||
};
|
};
|
||||||
Some(res)
|
Some(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
---
|
---
|
||||||
created: "2019-02-21T21:51:46.497925200Z"
|
created: "2019-02-25T08:36:33.885804400Z"
|
||||||
creator: insta@0.6.3
|
creator: insta@0.6.3
|
||||||
source: crates/ra_hir/src/ty/tests.rs
|
source: crates/ra_hir/src/ty/tests.rs
|
||||||
expression: "&result"
|
expression: "&result"
|
||||||
---
|
---
|
||||||
[227; 305) '{ ...:ID; }': ()
|
[227; 305) '{ ...:ID; }': ()
|
||||||
[237; 238) 'x': [unknown]
|
[237; 238) 'x': u32
|
||||||
[241; 252) 'Struct::FOO': [unknown]
|
[241; 252) 'Struct::FOO': u32
|
||||||
[262; 263) 'y': [unknown]
|
[262; 263) 'y': u32
|
||||||
[266; 275) 'Enum::BAR': [unknown]
|
[266; 275) 'Enum::BAR': u32
|
||||||
[285; 286) 'z': [unknown]
|
[285; 286) 'z': u32
|
||||||
[289; 302) 'TraitTest::ID': [unknown]
|
[289; 302) 'TraitTest::ID': u32
|
||||||
|
|
||||||
|
|
14
crates/ra_hir/src/ty/snapshots/tests__infer_const.snap
Normal file
14
crates/ra_hir/src/ty/snapshots/tests__infer_const.snap
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
---
|
||||||
|
created: "2019-02-25T07:26:34.115351100Z"
|
||||||
|
creator: insta@0.6.3
|
||||||
|
source: crates/ra_hir/src/ty/tests.rs
|
||||||
|
expression: "&result"
|
||||||
|
---
|
||||||
|
[95; 213) '{ ...NST; }': ()
|
||||||
|
[138; 139) 'x': [unknown]
|
||||||
|
[142; 153) 'LOCAL_CONST': [unknown]
|
||||||
|
[163; 164) 'z': u32
|
||||||
|
[167; 179) 'GLOBAL_CONST': u32
|
||||||
|
[189; 191) 'id': u32
|
||||||
|
[194; 210) 'Foo::A..._CONST': u32
|
||||||
|
|
16
crates/ra_hir/src/ty/snapshots/tests__infer_static.snap
Normal file
16
crates/ra_hir/src/ty/snapshots/tests__infer_static.snap
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
---
|
||||||
|
created: "2019-02-25T08:20:17.807316Z"
|
||||||
|
creator: insta@0.6.3
|
||||||
|
source: crates/ra_hir/src/ty/tests.rs
|
||||||
|
expression: "&result"
|
||||||
|
---
|
||||||
|
[85; 280) '{ ...MUT; }': ()
|
||||||
|
[173; 174) 'x': [unknown]
|
||||||
|
[177; 189) 'LOCAL_STATIC': [unknown]
|
||||||
|
[199; 200) 'y': [unknown]
|
||||||
|
[203; 219) 'LOCAL_...IC_MUT': [unknown]
|
||||||
|
[229; 230) 'z': u32
|
||||||
|
[233; 246) 'GLOBAL_STATIC': u32
|
||||||
|
[256; 257) 'w': u32
|
||||||
|
[260; 277) 'GLOBAL...IC_MUT': u32
|
||||||
|
|
|
@ -1006,6 +1006,43 @@ mod foo {
|
||||||
assert_eq!("i128", type_at_pos(&db, pos));
|
assert_eq!("i128", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_const() {
|
||||||
|
check_inference(
|
||||||
|
"infer_const",
|
||||||
|
r#"
|
||||||
|
struct Foo;
|
||||||
|
impl Foo { const ASSOC_CONST: u32 = 0; }
|
||||||
|
const GLOBAL_CONST: u32 = 101;
|
||||||
|
fn test() {
|
||||||
|
const LOCAL_CONST: u32 = 99;
|
||||||
|
let x = LOCAL_CONST;
|
||||||
|
let z = GLOBAL_CONST;
|
||||||
|
let id = Foo::ASSOC_CONST;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_static() {
|
||||||
|
check_inference(
|
||||||
|
"infer_static",
|
||||||
|
r#"
|
||||||
|
static GLOBAL_STATIC: u32 = 101;
|
||||||
|
static mut GLOBAL_STATIC_MUT: u32 = 101;
|
||||||
|
fn test() {
|
||||||
|
static LOCAL_STATIC: u32 = 99;
|
||||||
|
static mut LOCAL_STATIC_MUT: u32 = 99;
|
||||||
|
let x = LOCAL_STATIC;
|
||||||
|
let y = LOCAL_STATIC_MUT;
|
||||||
|
let z = GLOBAL_STATIC;
|
||||||
|
let w = GLOBAL_STATIC_MUT;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
fn type_at_pos(db: &MockDatabase, pos: FilePosition) -> String {
|
||||||
let func = source_binder::function_from_position(db, pos).unwrap();
|
let func = source_binder::function_from_position(db, pos).unwrap();
|
||||||
let body_syntax_mapping = func.body_syntax_mapping(db);
|
let body_syntax_mapping = func.body_syntax_mapping(db);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue