Introduce Semantics::visit_file_defs

This commit is contained in:
Lukas Wirth 2021-03-14 16:12:38 +01:00
parent 5138baf2ac
commit a1c96e04be
3 changed files with 108 additions and 73 deletions

View File

@ -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>(

View File

@ -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#"
[]
"#]],
);
}
} }

View File

@ -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