mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
Introduce Semantics::visit_file_defs
This commit is contained in:
parent
5138baf2ac
commit
a1c96e04be
@ -2,10 +2,12 @@
|
|||||||
|
|
||||||
mod source_to_def;
|
mod source_to_def;
|
||||||
|
|
||||||
use std::{cell::RefCell, fmt, iter::successors};
|
use std::{cell::RefCell, collections::VecDeque, fmt, iter::successors};
|
||||||
|
|
||||||
use base_db::{FileId, FileRange};
|
use base_db::{FileId, FileRange};
|
||||||
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
|
nameres::ModuleSource,
|
||||||
resolver::{self, HasResolver, Resolver, TypeNs},
|
resolver::{self, HasResolver, Resolver, TypeNs},
|
||||||
AsMacroCall, FunctionId, TraitId, VariantId,
|
AsMacroCall, FunctionId, TraitId, VariantId,
|
||||||
};
|
};
|
||||||
@ -155,6 +157,28 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||||||
self.imp.ancestors_at_offset_with_macros(node, offset)
|
self.imp.ancestors_at_offset_with_macros(node, offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Iterates all `ModuleDef`s and `Impl` blocks of the given file.
|
||||||
|
pub fn visit_file_defs(&self, file_id: FileId, cb: &mut dyn FnMut(Either<ModuleDef, Impl>)) {
|
||||||
|
let module = match self.to_module_def(file_id) {
|
||||||
|
Some(it) => it,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
let mut defs: VecDeque<_> = module.declarations(self.db).into();
|
||||||
|
while let Some(def) = defs.pop_front() {
|
||||||
|
if let ModuleDef::Module(submodule) = def {
|
||||||
|
if let ModuleSource::Module(_) = submodule.definition_source(self.db).value {
|
||||||
|
defs.extend(submodule.declarations(self.db));
|
||||||
|
submodule
|
||||||
|
.impl_defs(self.db)
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|impl_| cb(Either::Right(impl_)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(Either::Left(def));
|
||||||
|
}
|
||||||
|
module.impl_defs(self.db).into_iter().for_each(|impl_| cb(Either::Right(impl_)));
|
||||||
|
}
|
||||||
|
|
||||||
/// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
|
/// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*,
|
||||||
/// search up until it is of the target AstNode type
|
/// search up until it is of the target AstNode type
|
||||||
pub fn find_node_at_offset_with_macros<N: AstNode>(
|
pub fn find_node_at_offset_with_macros<N: AstNode>(
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use hir::Semantics;
|
use either::Either;
|
||||||
|
use hir::{HasSource, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{FileId, FilePosition, FileRange, SourceDatabase},
|
base_db::{FileId, FilePosition, FileRange},
|
||||||
RootDatabase, SymbolKind,
|
RootDatabase,
|
||||||
};
|
};
|
||||||
use syntax::TextRange;
|
use syntax::{ast::NameOwner, AstNode, TextRange, TextSize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
file_structure::file_structure,
|
|
||||||
fn_references::find_all_methods,
|
fn_references::find_all_methods,
|
||||||
goto_implementation::goto_implementation,
|
goto_implementation::goto_implementation,
|
||||||
references::find_all_refs,
|
references::find_all_refs,
|
||||||
runnables::{runnables, Runnable},
|
runnables::{runnables, Runnable},
|
||||||
NavigationTarget, RunnableKind, StructureNodeKind,
|
NavigationTarget, RunnableKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Feature: Annotations
|
// Feature: Annotations
|
||||||
@ -75,41 +75,56 @@ pub(crate) fn annotations(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file_structure(&db.parse(file_id).tree())
|
Semantics::new(db).visit_file_defs(file_id, &mut |def| match def {
|
||||||
.into_iter()
|
Either::Left(def) => {
|
||||||
.filter(|node| {
|
let node = match def {
|
||||||
matches!(
|
hir::ModuleDef::Const(konst) => {
|
||||||
node.kind,
|
konst.source(db).and_then(|node| range_and_position_of(&node.value))
|
||||||
StructureNodeKind::SymbolKind(SymbolKind::Trait)
|
}
|
||||||
| StructureNodeKind::SymbolKind(SymbolKind::Struct)
|
hir::ModuleDef::Trait(trait_) => {
|
||||||
| StructureNodeKind::SymbolKind(SymbolKind::Enum)
|
trait_.source(db).and_then(|node| range_and_position_of(&node.value))
|
||||||
| StructureNodeKind::SymbolKind(SymbolKind::Union)
|
}
|
||||||
| StructureNodeKind::SymbolKind(SymbolKind::Const)
|
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
|
||||||
)
|
strukt.source(db).and_then(|node| range_and_position_of(&node.value))
|
||||||
})
|
}
|
||||||
.for_each(|node| {
|
hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => {
|
||||||
if config.annotate_impls
|
enum_.source(db).and_then(|node| range_and_position_of(&node.value))
|
||||||
&& node.kind != StructureNodeKind::SymbolKind(SymbolKind::Const)
|
}
|
||||||
{
|
hir::ModuleDef::Adt(hir::Adt::Union(union)) => {
|
||||||
|
union.source(db).and_then(|node| range_and_position_of(&node.value))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let (offset, range) = match node {
|
||||||
|
Some(node) => node,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if config.annotate_impls && !matches!(def, hir::ModuleDef::Const(_)) {
|
||||||
annotations.push(Annotation {
|
annotations.push(Annotation {
|
||||||
range: node.node_range,
|
range,
|
||||||
kind: AnnotationKind::HasImpls {
|
kind: AnnotationKind::HasImpls {
|
||||||
position: FilePosition { file_id, offset: node.navigation_range.start() },
|
position: FilePosition { file_id, offset },
|
||||||
|
data: None,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if config.annotate_references {
|
||||||
|
annotations.push(Annotation {
|
||||||
|
range,
|
||||||
|
kind: AnnotationKind::HasReferences {
|
||||||
|
position: FilePosition { file_id, offset },
|
||||||
data: None,
|
data: None,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.annotate_references {
|
fn range_and_position_of(node: &dyn NameOwner) -> Option<(TextSize, TextRange)> {
|
||||||
annotations.push(Annotation {
|
Some((node.name()?.syntax().text_range().start(), node.syntax().text_range()))
|
||||||
range: node.node_range,
|
|
||||||
kind: AnnotationKind::HasReferences {
|
|
||||||
position: FilePosition { file_id, offset: node.navigation_range.start() },
|
|
||||||
data: None,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
Either::Right(_) => (),
|
||||||
|
});
|
||||||
|
|
||||||
if config.annotate_method_references {
|
if config.annotate_method_references {
|
||||||
annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
|
annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
|
||||||
@ -936,4 +951,19 @@ mod tests {
|
|||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_no_annotations_outside_module_tree() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
//- /foo.rs
|
||||||
|
struct Foo;
|
||||||
|
//- /lib.rs
|
||||||
|
// this file comes last since `check` checks the first file only
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
[]
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use std::fmt;
|
|||||||
|
|
||||||
use ast::NameOwner;
|
use ast::NameOwner;
|
||||||
use cfg::CfgExpr;
|
use cfg::CfgExpr;
|
||||||
|
use either::Either;
|
||||||
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
|
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
|
||||||
use ide_assists::utils::test_related_attribute;
|
use ide_assists::utils::test_related_attribute;
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
@ -102,13 +103,27 @@ impl Runnable {
|
|||||||
// |===
|
// |===
|
||||||
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
||||||
let sema = Semantics::new(db);
|
let sema = Semantics::new(db);
|
||||||
let module = match sema.to_module_def(file_id) {
|
|
||||||
None => return Vec::new(),
|
|
||||||
Some(it) => it,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
runnables_mod(&sema, &mut res, module);
|
sema.visit_file_defs(file_id, &mut |def| match def {
|
||||||
|
Either::Left(def) => {
|
||||||
|
let runnable = match def {
|
||||||
|
hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
|
||||||
|
hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
res.extend(runnable.or_else(|| module_def_doctest(&sema, def)))
|
||||||
|
}
|
||||||
|
Either::Right(impl_) => {
|
||||||
|
res.extend(impl_.items(db).into_iter().filter_map(|assoc| match assoc {
|
||||||
|
hir::AssocItem::Function(it) => {
|
||||||
|
runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
|
||||||
|
}
|
||||||
|
hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()),
|
||||||
|
hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
});
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,39 +226,6 @@ fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Optio
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) {
|
|
||||||
acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| {
|
|
||||||
let runnable = match def {
|
|
||||||
hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
|
|
||||||
hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
runnable.or_else(|| module_def_doctest(&sema, def))
|
|
||||||
}));
|
|
||||||
|
|
||||||
acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
|
|
||||||
|def| match def {
|
|
||||||
hir::AssocItem::Function(it) => {
|
|
||||||
runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
|
|
||||||
}
|
|
||||||
hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()),
|
|
||||||
hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()),
|
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
for def in module.declarations(sema.db) {
|
|
||||||
if let hir::ModuleDef::Module(submodule) = def {
|
|
||||||
match submodule.definition_source(sema.db).value {
|
|
||||||
hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
|
|
||||||
hir::ModuleSource::SourceFile(_) => {
|
|
||||||
cov_mark::hit!(dont_recurse_in_outline_submodules)
|
|
||||||
}
|
|
||||||
hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
|
pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
|
||||||
let func = def.source(sema.db)?;
|
let func = def.source(sema.db)?;
|
||||||
let name_string = def.name(sema.db).to_string();
|
let name_string = def.name(sema.db).to_string();
|
||||||
@ -1178,7 +1160,6 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dont_recurse_in_outline_submodules() {
|
fn dont_recurse_in_outline_submodules() {
|
||||||
cov_mark::check!(dont_recurse_in_outline_submodules);
|
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
|
Loading…
Reference in New Issue
Block a user