Initial support for lang items.
This commit is contained in:
parent
d55f1136d6
commit
e85ee60c42
11 changed files with 204 additions and 41 deletions
|
@ -16,6 +16,7 @@ use crate::{
|
||||||
generics::{GenericParams, GenericDef},
|
generics::{GenericParams, GenericDef},
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
traits::TraitData, Trait, ty::TraitRef,
|
traits::TraitData, Trait, ty::TraitRef,
|
||||||
|
lang_item::LangItems,
|
||||||
ids
|
ids
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -100,6 +101,9 @@ pub trait DefDatabase: SourceDatabase {
|
||||||
|
|
||||||
#[salsa::invoke(crate::ConstSignature::static_signature_query)]
|
#[salsa::invoke(crate::ConstSignature::static_signature_query)]
|
||||||
fn static_signature(&self, konst: Static) -> Arc<ConstSignature>;
|
fn static_signature(&self, konst: Static) -> Arc<ConstSignature>;
|
||||||
|
|
||||||
|
#[salsa::invoke(crate::lang_item::LangItems::lang_items_query)]
|
||||||
|
fn lang_items(&self, krate: Crate) -> Arc<LangItems>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[salsa::query_group(HirDatabaseStorage)]
|
#[salsa::query_group(HirDatabaseStorage)]
|
||||||
|
|
102
crates/ra_hir/src/lang_item.rs
Normal file
102
crates/ra_hir/src/lang_item.rs
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
use ra_syntax::{SmolStr, ast::AttrsOwner};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
Crate, DefDatabase, Enum, Function, HirDatabase, ImplBlock, Module, Static, Struct, Trait
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum LangItemTarget {
|
||||||
|
Enum(Enum),
|
||||||
|
Function(Function),
|
||||||
|
Impl(ImplBlock),
|
||||||
|
Static(Static),
|
||||||
|
Struct(Struct),
|
||||||
|
Trait(Trait),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LangItemTarget {
|
||||||
|
pub(crate) fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
|
||||||
|
match self {
|
||||||
|
LangItemTarget::Enum(e) => e.module(db).krate(db),
|
||||||
|
LangItemTarget::Function(f) => f.module(db).krate(db),
|
||||||
|
LangItemTarget::Impl(i) => i.module().krate(db),
|
||||||
|
LangItemTarget::Static(s) => s.module(db).krate(db),
|
||||||
|
LangItemTarget::Struct(s) => s.module(db).krate(db),
|
||||||
|
LangItemTarget::Trait(t) => t.module(db).krate(db),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct LangItems {
|
||||||
|
items: FxHashMap<SmolStr, LangItemTarget>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LangItems {
|
||||||
|
pub fn target<'a>(&'a self, item: &str) -> Option<&'a LangItemTarget> {
|
||||||
|
self.items.get(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Salsa query. This will query a specific crate for lang items.
|
||||||
|
pub(crate) fn lang_items_query(db: &impl DefDatabase, krate: Crate) -> Arc<LangItems> {
|
||||||
|
let mut lang_items = LangItems { items: FxHashMap::default() };
|
||||||
|
|
||||||
|
if let Some(module) = krate.root_module(db) {
|
||||||
|
lang_items.collect_lang_items_recursive(db, &module);
|
||||||
|
}
|
||||||
|
|
||||||
|
Arc::new(lang_items)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_lang_items_recursive(&mut self, db: &impl DefDatabase, module: &Module) {
|
||||||
|
// Look for impl targets
|
||||||
|
let (impl_blocks, source_map) = db.impls_in_module_with_source_map(module.clone());
|
||||||
|
let source = module.definition_source(db).1;
|
||||||
|
for (impl_id, _) in impl_blocks.impls.iter() {
|
||||||
|
let impl_block = source_map.get(&source, impl_id);
|
||||||
|
let lang_item_name = impl_block
|
||||||
|
.attrs()
|
||||||
|
.filter_map(|a| a.as_key_value())
|
||||||
|
.filter(|(key, _)| key == "lang")
|
||||||
|
.map(|(_, val)| val)
|
||||||
|
.nth(0);
|
||||||
|
if let Some(lang_item_name) = lang_item_name {
|
||||||
|
let imp = ImplBlock::from_id(*module, impl_id);
|
||||||
|
self.items.entry(lang_item_name).or_insert(LangItemTarget::Impl(imp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME we should look for the other lang item targets (traits, structs, ...)
|
||||||
|
|
||||||
|
// Look for lang items in the children
|
||||||
|
for child in module.children(db) {
|
||||||
|
self.collect_lang_items_recursive(db, &child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Look for a lang item, starting from the specified crate and recursively traversing its
|
||||||
|
/// dependencies.
|
||||||
|
pub(crate) fn lang_item_lookup(
|
||||||
|
db: &impl DefDatabase,
|
||||||
|
start_krate: Crate,
|
||||||
|
item: &str,
|
||||||
|
) -> Option<LangItemTarget> {
|
||||||
|
let lang_items = db.lang_items(start_krate);
|
||||||
|
let start_krate_target = lang_items.items.get(item);
|
||||||
|
if start_krate_target.is_some() {
|
||||||
|
start_krate_target.map(|t| *t)
|
||||||
|
} else {
|
||||||
|
for dep in start_krate.dependencies(db) {
|
||||||
|
let dep_krate = dep.krate;
|
||||||
|
let dep_target = lang_item_lookup(db, dep_krate, item);
|
||||||
|
if dep_target.is_some() {
|
||||||
|
return dep_target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -36,6 +36,7 @@ mod type_ref;
|
||||||
mod ty;
|
mod ty;
|
||||||
mod impl_block;
|
mod impl_block;
|
||||||
mod expr;
|
mod expr;
|
||||||
|
mod lang_item;
|
||||||
mod generics;
|
mod generics;
|
||||||
mod docs;
|
mod docs;
|
||||||
mod resolve;
|
mod resolve;
|
||||||
|
|
|
@ -202,6 +202,10 @@ impl CrateDefMap {
|
||||||
Arc::new(def_map)
|
Arc::new(def_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn krate(&self) -> Crate {
|
||||||
|
self.krate
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn root(&self) -> CrateModuleId {
|
pub(crate) fn root(&self) -> CrateModuleId {
|
||||||
self.root
|
self.root
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,7 @@ impl Resolver {
|
||||||
.flatten()
|
.flatten()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> {
|
pub(crate) fn module(&self) -> Option<(&CrateDefMap, CrateModuleId)> {
|
||||||
self.scopes.iter().rev().find_map(|scope| match scope {
|
self.scopes.iter().rev().find_map(|scope| match scope {
|
||||||
Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),
|
Scope::ModuleScope(m) => Some((&*m.crate_def_map, m.module_id)),
|
||||||
|
|
||||||
|
|
|
@ -462,6 +462,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let remaining_index = remaining_index.unwrap_or(path.segments.len());
|
let remaining_index = remaining_index.unwrap_or(path.segments.len());
|
||||||
let mut actual_def_ty: Option<Ty> = None;
|
let mut actual_def_ty: Option<Ty> = None;
|
||||||
|
|
||||||
|
let krate = resolver.module().map(|t| t.0.krate());
|
||||||
// resolve intermediate segments
|
// resolve intermediate segments
|
||||||
for (i, segment) in path.segments[remaining_index..].iter().enumerate() {
|
for (i, segment) in path.segments[remaining_index..].iter().enumerate() {
|
||||||
let ty = match resolved {
|
let ty = match resolved {
|
||||||
|
@ -500,9 +501,11 @@ 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);
|
||||||
|
|
||||||
actual_def_ty = Some(ty.clone());
|
actual_def_ty = Some(ty.clone());
|
||||||
|
|
||||||
let item: crate::ModuleDef = ty.iterate_impl_items(self.db, |item| {
|
let item: crate::ModuleDef = krate.and_then(|k| {
|
||||||
|
ty.iterate_impl_items(self.db, k, |item| {
|
||||||
let matching_def: Option<crate::ModuleDef> = match item {
|
let matching_def: Option<crate::ModuleDef> = match item {
|
||||||
crate::ImplItem::Method(func) => {
|
crate::ImplItem::Method(func) => {
|
||||||
let sig = func.signature(self.db);
|
let sig = func.signature(self.db);
|
||||||
|
@ -532,6 +535,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
}
|
}
|
||||||
None => None,
|
None => None,
|
||||||
}
|
}
|
||||||
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
resolved = Resolution::Def(item.into());
|
resolved = Resolution::Def(item.into());
|
||||||
|
|
|
@ -14,6 +14,8 @@ use crate::{
|
||||||
resolve::Resolver,
|
resolve::Resolver,
|
||||||
traits::TraitItem,
|
traits::TraitItem,
|
||||||
generics::HasGenericParams,
|
generics::HasGenericParams,
|
||||||
|
lang_item::lang_item_lookup,
|
||||||
|
ty::primitive::{UncertainIntTy, UncertainFloatTy}
|
||||||
};
|
};
|
||||||
use super::{TraitRef, Substs};
|
use super::{TraitRef, Substs};
|
||||||
|
|
||||||
|
@ -110,10 +112,33 @@ impl CrateImplBlocks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn def_crate(db: &impl HirDatabase, ty: &Ty) -> Option<Crate> {
|
/// Rudimentary check whether an impl exists for a given type and trait; this
|
||||||
|
/// will actually be done by chalk.
|
||||||
|
pub(crate) fn implements(db: &impl HirDatabase, trait_ref: TraitRef) -> bool {
|
||||||
|
// FIXME use all trait impls in the whole crate graph
|
||||||
|
let krate = trait_ref.trait_.module(db).krate(db);
|
||||||
|
let krate = match krate {
|
||||||
|
Some(krate) => krate,
|
||||||
|
None => return false,
|
||||||
|
};
|
||||||
|
let crate_impl_blocks = db.impls_in_crate(krate);
|
||||||
|
let mut impl_blocks = crate_impl_blocks.lookup_impl_blocks_for_trait(&trait_ref.trait_);
|
||||||
|
impl_blocks.any(|impl_block| &impl_block.target_ty(db) == trait_ref.self_ty())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn def_crate(db: &impl HirDatabase, cur_krate: Crate, ty: &Ty) -> Option<Crate> {
|
||||||
match ty {
|
match ty {
|
||||||
Ty::Apply(a_ty) => match a_ty.ctor {
|
Ty::Apply(a_ty) => match a_ty.ctor {
|
||||||
TypeCtor::Adt(def_id) => def_id.krate(db),
|
TypeCtor::Adt(def_id) => def_id.krate(db),
|
||||||
|
TypeCtor::Bool => lang_item_lookup(db, cur_krate, "bool")?.krate(db),
|
||||||
|
TypeCtor::Char => lang_item_lookup(db, cur_krate, "char")?.krate(db),
|
||||||
|
TypeCtor::Float(UncertainFloatTy::Known(f)) => {
|
||||||
|
lang_item_lookup(db, cur_krate, f.ty_to_string())?.krate(db)
|
||||||
|
}
|
||||||
|
TypeCtor::Int(UncertainIntTy::Known(i)) => {
|
||||||
|
lang_item_lookup(db, cur_krate, i.ty_to_string())?.krate(db)
|
||||||
|
}
|
||||||
|
TypeCtor::Str => lang_item_lookup(db, cur_krate, "str")?.krate(db),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -150,8 +175,11 @@ impl Ty {
|
||||||
// find in the end takes &self, we still do the autoderef step (just as
|
// find in the end takes &self, we still do the autoderef step (just as
|
||||||
// rustc does an autoderef and then autoref again).
|
// rustc does an autoderef and then autoref again).
|
||||||
|
|
||||||
|
let krate = resolver.module().map(|t| t.0.krate())?;
|
||||||
for derefed_ty in self.autoderef(db) {
|
for derefed_ty in self.autoderef(db) {
|
||||||
if let Some(result) = derefed_ty.iterate_inherent_methods(db, name, &mut callback) {
|
if let Some(result) =
|
||||||
|
derefed_ty.iterate_inherent_methods(db, name, krate, &mut callback)
|
||||||
|
{
|
||||||
return Some(result);
|
return Some(result);
|
||||||
}
|
}
|
||||||
if let Some(result) =
|
if let Some(result) =
|
||||||
|
@ -208,9 +236,10 @@ impl Ty {
|
||||||
&self,
|
&self,
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
name: Option<&Name>,
|
name: Option<&Name>,
|
||||||
|
krate: Crate,
|
||||||
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
mut callback: impl FnMut(&Ty, Function) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let krate = match def_crate(db, self) {
|
let krate = match def_crate(db, krate, self) {
|
||||||
Some(krate) => krate,
|
Some(krate) => krate,
|
||||||
None => return None,
|
None => return None,
|
||||||
};
|
};
|
||||||
|
@ -239,9 +268,10 @@ impl Ty {
|
||||||
pub fn iterate_impl_items<T>(
|
pub fn iterate_impl_items<T>(
|
||||||
self,
|
self,
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
|
krate: Crate,
|
||||||
mut callback: impl FnMut(ImplItem) -> Option<T>,
|
mut callback: impl FnMut(ImplItem) -> Option<T>,
|
||||||
) -> Option<T> {
|
) -> Option<T> {
|
||||||
let krate = def_crate(db, &self)?;
|
let krate = def_crate(db, krate, &self)?;
|
||||||
let impls = db.impls_in_crate(krate);
|
let impls = db.impls_in_crate(krate);
|
||||||
|
|
||||||
for impl_block in impls.lookup_impl_blocks(&self) {
|
for impl_block in impls.lookup_impl_blocks(&self) {
|
||||||
|
|
|
@ -38,7 +38,9 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
}
|
}
|
||||||
hir::ModuleDef::Struct(s) => {
|
hir::ModuleDef::Struct(s) => {
|
||||||
let ty = s.ty(ctx.db);
|
let ty = s.ty(ctx.db);
|
||||||
ty.iterate_impl_items(ctx.db, |item| {
|
let krate = ctx.module.and_then(|m| m.krate(ctx.db));
|
||||||
|
krate.map_or((), |krate| {
|
||||||
|
ty.iterate_impl_items(ctx.db, krate, |item| {
|
||||||
match item {
|
match item {
|
||||||
hir::ImplItem::Method(func) => {
|
hir::ImplItem::Method(func) => {
|
||||||
let sig = func.signature(ctx.db);
|
let sig = func.signature(ctx.db);
|
||||||
|
@ -51,6 +53,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) {
|
||||||
}
|
}
|
||||||
None::<()>
|
None::<()>
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -65,6 +65,20 @@ impl ast::Attr {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_key_value(&self) -> Option<(SmolStr, SmolStr)> {
|
||||||
|
let tt = self.value()?;
|
||||||
|
let tt_node = tt.syntax();
|
||||||
|
let attr = tt_node.children_with_tokens().nth(1)?;
|
||||||
|
if attr.kind() == IDENT {
|
||||||
|
let key = attr.as_token()?.text().clone();
|
||||||
|
let val_node = tt_node.children_with_tokens().find(|t| t.kind() == STRING)?;
|
||||||
|
let val = val_node.as_token()?.text().trim_start_matches("\"").trim_end_matches("\"");
|
||||||
|
Some((key, SmolStr::new(val)))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
|
|
@ -1325,6 +1325,7 @@ impl ToOwned for ImplBlock {
|
||||||
|
|
||||||
|
|
||||||
impl ast::TypeParamsOwner for ImplBlock {}
|
impl ast::TypeParamsOwner for ImplBlock {}
|
||||||
|
impl ast::AttrsOwner for ImplBlock {}
|
||||||
impl ImplBlock {
|
impl ImplBlock {
|
||||||
pub fn item_list(&self) -> Option<&ItemList> {
|
pub fn item_list(&self) -> Option<&ItemList> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
|
|
|
@ -341,7 +341,7 @@ Grammar(
|
||||||
],
|
],
|
||||||
options: ["TypeRef"]
|
options: ["TypeRef"]
|
||||||
),
|
),
|
||||||
"ImplBlock": (options: ["ItemList"], traits: ["TypeParamsOwner"]),
|
"ImplBlock": (options: ["ItemList"], traits: ["TypeParamsOwner", "AttrsOwner"]),
|
||||||
|
|
||||||
"ParenType": (options: ["TypeRef"]),
|
"ParenType": (options: ["TypeRef"]),
|
||||||
"TupleType": ( collections: [["fields", "TypeRef"]] ),
|
"TupleType": ( collections: [["fields", "TypeRef"]] ),
|
||||||
|
|
Loading…
Reference in a new issue