Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/has_source.rs')
-rw-r--r--crates/hir/src/has_source.rs90
1 files changed, 72 insertions, 18 deletions
diff --git a/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs
index 6f427d728b..09c5b1cca7 100644
--- a/crates/hir/src/has_source.rs
+++ b/crates/hir/src/has_source.rs
@@ -7,26 +7,40 @@ use hir_def::{
src::{HasChildSource, HasSource as _},
};
use hir_expand::{EditionedFileId, HirFileId, InFile};
-use hir_ty::db::InternedClosure;
-use syntax::ast;
+use hir_ty::{db::InternedClosure, next_solver::AnyImplId};
+use syntax::{AstNode, ast};
use tt::TextRange;
use crate::{
- Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
+ Adt, AnyFunctionId, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static,
Struct, Trait, TypeAlias, TypeOrConstParam, Union, Variant, VariantDef, db::HirDatabase,
};
-pub trait HasSource {
- type Ast;
+pub trait HasSource: Sized {
+ type Ast: AstNode;
/// Fetches the definition's source node.
- /// Using [`crate::Semantics::source`] is preferred when working with [`crate::Semantics`],
+ /// Using [`crate::SemanticsImpl::source`] is preferred when working with [`crate::Semantics`],
/// as that caches the parsed file in the semantics' cache.
///
/// The current some implementations can return `InFile` instead of `Option<InFile>`.
/// But we made this method `Option` to support rlib in the future
/// by <https://github.com/rust-lang/rust-analyzer/issues/6913>
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>>;
+
+ /// Fetches the source node, along with its full range.
+ ///
+ /// The reason for the separate existence of this method is that some things, notably builtin derive impls,
+ /// do not really have a source node, at least not of the correct type. But we still can trace them
+ /// to source code (the derive producing them). So this method will return the range if it is supported,
+ /// and if the node is supported too it will return it as well.
+ fn source_with_range(
+ self,
+ db: &dyn HirDatabase,
+ ) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
+ let source = self.source(db)?;
+ Some(source.map(|node| (node.syntax().text_range(), Some(node))))
+ }
}
/// NB: Module is !HasSource, because it has two source nodes at the same time:
@@ -35,23 +49,23 @@ impl Module {
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
pub fn definition_source(self, db: &dyn HirDatabase) -> InFile<ModuleSource> {
let def_map = self.id.def_map(db);
- def_map[self.id.local_id].definition_source(db)
+ def_map[self.id].definition_source(db)
}
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
pub fn definition_source_range(self, db: &dyn HirDatabase) -> InFile<TextRange> {
let def_map = self.id.def_map(db);
- def_map[self.id.local_id].definition_source_range(db)
+ def_map[self.id].definition_source_range(db)
}
pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId {
let def_map = self.id.def_map(db);
- def_map[self.id.local_id].definition_source_file_id()
+ def_map[self.id].definition_source_file_id()
}
pub fn is_mod_rs(self, db: &dyn HirDatabase) -> bool {
let def_map = self.id.def_map(db);
- match def_map[self.id.local_id].origin {
+ match def_map[self.id].origin {
ModuleOrigin::File { is_mod_rs, .. } => is_mod_rs,
_ => false,
}
@@ -59,7 +73,7 @@ impl Module {
pub fn as_source_file_id(self, db: &dyn HirDatabase) -> Option<EditionedFileId> {
let def_map = self.id.def_map(db);
- match def_map[self.id.local_id].origin {
+ match def_map[self.id].origin {
ModuleOrigin::File { definition, .. } | ModuleOrigin::CrateRoot { definition, .. } => {
Some(definition)
}
@@ -69,21 +83,21 @@ impl Module {
pub fn is_inline(self, db: &dyn HirDatabase) -> bool {
let def_map = self.id.def_map(db);
- def_map[self.id.local_id].origin.is_inline()
+ def_map[self.id].origin.is_inline()
}
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
/// `None` for the crate root.
pub fn declaration_source(self, db: &dyn HirDatabase) -> Option<InFile<ast::Module>> {
let def_map = self.id.def_map(db);
- def_map[self.id.local_id].declaration_source(db)
+ def_map[self.id].declaration_source(db)
}
/// Returns a text range which declares this module, either a `mod foo;` or a `mod foo {}`.
/// `None` for the crate root.
pub fn declaration_source_range(self, db: &dyn HirDatabase) -> Option<InFile<TextRange>> {
let def_map = self.id.def_map(db);
- def_map[self.id.local_id].declaration_source_range(db)
+ def_map[self.id].declaration_source_range(db)
}
}
@@ -146,7 +160,30 @@ impl HasSource for Variant {
impl HasSource for Function {
type Ast = ast::Fn;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
- Some(self.id.lookup(db).source(db))
+ match self.id {
+ AnyFunctionId::FunctionId(id) => Some(id.loc(db).source(db)),
+ // When calling `source()`, we use the trait method source, but when calling `source_with_range()`,
+ // we return `None` as the syntax node source. This is relying on the assumption that if you are calling
+ // `source_with_range()` (e.g. in navigation) you're prepared to deal with no source node, while if
+ // you call `source()` maybe you don't - therefore we fall back to the trait method, to not lose features.
+ AnyFunctionId::BuiltinDeriveImplMethod { method, impl_ } => method
+ .trait_method(db, impl_)
+ .and_then(|trait_method| Function::from(trait_method).source(db)),
+ }
+ }
+
+ fn source_with_range(
+ self,
+ db: &dyn HirDatabase,
+ ) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
+ match self.id {
+ AnyFunctionId::FunctionId(id) => Some(
+ id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
+ ),
+ AnyFunctionId::BuiltinDeriveImplMethod { impl_, .. } => {
+ Some(impl_.loc(db).source(db).map(|range| (range, None)))
+ }
+ }
}
}
impl HasSource for Const {
@@ -190,7 +227,24 @@ impl HasSource for Macro {
impl HasSource for Impl {
type Ast = ast::Impl;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
- Some(self.id.lookup(db).source(db))
+ match self.id {
+ AnyImplId::ImplId(id) => Some(id.loc(db).source(db)),
+ AnyImplId::BuiltinDeriveImplId(_) => None,
+ }
+ }
+
+ fn source_with_range(
+ self,
+ db: &dyn HirDatabase,
+ ) -> Option<InFile<(TextRange, Option<Self::Ast>)>> {
+ match self.id {
+ AnyImplId::ImplId(id) => Some(
+ id.loc(db).source(db).map(|source| (source.syntax().text_range(), Some(source))),
+ ),
+ AnyImplId::BuiltinDeriveImplId(impl_) => {
+ Some(impl_.loc(db).source(db).map(|range| (range, None)))
+ }
+ }
}
}
@@ -224,7 +278,7 @@ impl HasSource for Param<'_> {
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
match self.func {
Callee::Def(CallableDefId::FunctionId(func)) => {
- let InFile { file_id, value } = Function { id: func }.source(db)?;
+ let InFile { file_id, value } = Function::from(func).source(db)?;
let params = value.param_list()?;
if let Some(self_param) = params.self_param() {
if let Some(idx) = self.idx.checked_sub(1) {
@@ -261,7 +315,7 @@ impl HasSource for SelfParam {
type Ast = ast::SelfParam;
fn source(self, db: &dyn HirDatabase) -> Option<InFile<Self::Ast>> {
- let InFile { file_id, value } = Function::from(self.func).source(db)?;
+ let InFile { file_id, value } = self.func.source(db)?;
value
.param_list()
.and_then(|params| params.self_param())