Skip to content

Commit b245e8d

Browse files
bors[bot]Veykril
andauthored
Merge #8015
8015: Introduce Semantics::visit_file_defs r=matklad a=Veykril See #3538 (comment) Co-authored-by: Lukas Wirth <[email protected]>
2 parents 0ac7a19 + 41745f4 commit b245e8d

File tree

3 files changed

+108
-67
lines changed

3 files changed

+108
-67
lines changed

crates/ide/src/annotations.rs

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
use hir::Semantics;
1+
use either::Either;
2+
use hir::{HasSource, Semantics};
23
use ide_db::{
3-
base_db::{FileId, FilePosition, FileRange, SourceDatabase},
4-
RootDatabase, SymbolKind,
4+
base_db::{FileId, FilePosition, FileRange},
5+
helpers::visit_file_defs,
6+
RootDatabase,
57
};
6-
use syntax::TextRange;
8+
use syntax::{ast::NameOwner, AstNode, TextRange, TextSize};
79

810
use crate::{
9-
file_structure::file_structure,
1011
fn_references::find_all_methods,
1112
goto_implementation::goto_implementation,
1213
references::find_all_refs,
1314
runnables::{runnables, Runnable},
14-
NavigationTarget, RunnableKind, StructureNodeKind,
15+
NavigationTarget, RunnableKind,
1516
};
1617

1718
// Feature: Annotations
@@ -75,41 +76,56 @@ pub(crate) fn annotations(
7576
}
7677
}
7778

78-
file_structure(&db.parse(file_id).tree())
79-
.into_iter()
80-
.filter(|node| {
81-
matches!(
82-
node.kind,
83-
StructureNodeKind::SymbolKind(SymbolKind::Trait)
84-
| StructureNodeKind::SymbolKind(SymbolKind::Struct)
85-
| StructureNodeKind::SymbolKind(SymbolKind::Enum)
86-
| StructureNodeKind::SymbolKind(SymbolKind::Union)
87-
| StructureNodeKind::SymbolKind(SymbolKind::Const)
88-
)
89-
})
90-
.for_each(|node| {
91-
if config.annotate_impls
92-
&& node.kind != StructureNodeKind::SymbolKind(SymbolKind::Const)
93-
{
79+
visit_file_defs(&Semantics::new(db), file_id, &mut |def| match def {
80+
Either::Left(def) => {
81+
let node = match def {
82+
hir::ModuleDef::Const(konst) => {
83+
konst.source(db).and_then(|node| range_and_position_of(&node.value))
84+
}
85+
hir::ModuleDef::Trait(trait_) => {
86+
trait_.source(db).and_then(|node| range_and_position_of(&node.value))
87+
}
88+
hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
89+
strukt.source(db).and_then(|node| range_and_position_of(&node.value))
90+
}
91+
hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => {
92+
enum_.source(db).and_then(|node| range_and_position_of(&node.value))
93+
}
94+
hir::ModuleDef::Adt(hir::Adt::Union(union)) => {
95+
union.source(db).and_then(|node| range_and_position_of(&node.value))
96+
}
97+
_ => None,
98+
};
99+
let (offset, range) = match node {
100+
Some(node) => node,
101+
None => return,
102+
};
103+
104+
if config.annotate_impls && !matches!(def, hir::ModuleDef::Const(_)) {
94105
annotations.push(Annotation {
95-
range: node.node_range,
106+
range,
96107
kind: AnnotationKind::HasImpls {
97-
position: FilePosition { file_id, offset: node.navigation_range.start() },
108+
position: FilePosition { file_id, offset },
98109
data: None,
99110
},
100111
});
101112
}
102-
103113
if config.annotate_references {
104114
annotations.push(Annotation {
105-
range: node.node_range,
115+
range,
106116
kind: AnnotationKind::HasReferences {
107-
position: FilePosition { file_id, offset: node.navigation_range.start() },
117+
position: FilePosition { file_id, offset },
108118
data: None,
109119
},
110120
});
111121
}
112-
});
122+
123+
fn range_and_position_of(node: &dyn NameOwner) -> Option<(TextSize, TextRange)> {
124+
Some((node.name()?.syntax().text_range().start(), node.syntax().text_range()))
125+
}
126+
}
127+
Either::Right(_) => (),
128+
});
113129

114130
if config.annotate_method_references {
115131
annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
@@ -936,4 +952,19 @@ mod tests {
936952
"#]],
937953
);
938954
}
955+
956+
#[test]
957+
fn test_no_annotations_outside_module_tree() {
958+
check(
959+
r#"
960+
//- /foo.rs
961+
struct Foo;
962+
//- /lib.rs
963+
// this file comes last since `check` checks the first file only
964+
"#,
965+
expect![[r#"
966+
[]
967+
"#]],
968+
);
969+
}
939970
}

crates/ide/src/runnables.rs

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@ use std::fmt;
22

33
use ast::NameOwner;
44
use cfg::CfgExpr;
5+
use either::Either;
56
use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
67
use ide_assists::utils::test_related_attribute;
78
use ide_db::{
89
base_db::{FilePosition, FileRange},
910
defs::Definition,
11+
helpers::visit_file_defs,
1012
search::SearchScope,
1113
RootDatabase, SymbolKind,
1214
};
@@ -102,13 +104,27 @@ impl Runnable {
102104
// |===
103105
pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
104106
let sema = Semantics::new(db);
105-
let module = match sema.to_module_def(file_id) {
106-
None => return Vec::new(),
107-
Some(it) => it,
108-
};
109107

110108
let mut res = Vec::new();
111-
runnables_mod(&sema, &mut res, module);
109+
visit_file_defs(&sema, file_id, &mut |def| match def {
110+
Either::Left(def) => {
111+
let runnable = match def {
112+
hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
113+
hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
114+
_ => None,
115+
};
116+
res.extend(runnable.or_else(|| module_def_doctest(&sema, def)))
117+
}
118+
Either::Right(impl_) => {
119+
res.extend(impl_.items(db).into_iter().filter_map(|assoc| match assoc {
120+
hir::AssocItem::Function(it) => {
121+
runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
122+
}
123+
hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()),
124+
hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()),
125+
}))
126+
}
127+
});
112128
res
113129
}
114130

@@ -211,39 +227,6 @@ fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Optio
211227
})
212228
}
213229

214-
fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) {
215-
acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| {
216-
let runnable = match def {
217-
hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
218-
hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
219-
_ => None,
220-
};
221-
runnable.or_else(|| module_def_doctest(&sema, def))
222-
}));
223-
224-
acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
225-
|def| match def {
226-
hir::AssocItem::Function(it) => {
227-
runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
228-
}
229-
hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()),
230-
hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()),
231-
},
232-
));
233-
234-
for def in module.declarations(sema.db) {
235-
if let hir::ModuleDef::Module(submodule) = def {
236-
match submodule.definition_source(sema.db).value {
237-
hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
238-
hir::ModuleSource::SourceFile(_) => {
239-
cov_mark::hit!(dont_recurse_in_outline_submodules)
240-
}
241-
hir::ModuleSource::BlockExpr(_) => {} // inner items aren't runnable
242-
}
243-
}
244-
}
245-
}
246-
247230
pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
248231
let func = def.source(sema.db)?;
249232
let name_string = def.name(sema.db).to_string();
@@ -1178,7 +1161,6 @@ mod tests {
11781161

11791162
#[test]
11801163
fn dont_recurse_in_outline_submodules() {
1181-
cov_mark::check!(dont_recurse_in_outline_submodules);
11821164
check(
11831165
r#"
11841166
//- /lib.rs

crates/ide_db/src/helpers.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
pub mod insert_use;
33
pub mod import_assets;
44

5+
use std::collections::VecDeque;
6+
7+
use base_db::FileId;
8+
use either::Either;
59
use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
610
use syntax::ast::{self, make};
711

@@ -39,6 +43,30 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
3943
make::path_from_segments(segments, is_abs)
4044
}
4145

46+
/// Iterates all `ModuleDef`s and `Impl` blocks of the given file.
47+
pub fn visit_file_defs(
48+
sema: &Semantics<RootDatabase>,
49+
file_id: FileId,
50+
cb: &mut dyn FnMut(Either<hir::ModuleDef, hir::Impl>),
51+
) {
52+
let db = sema.db;
53+
let module = match sema.to_module_def(file_id) {
54+
Some(it) => it,
55+
None => return,
56+
};
57+
let mut defs: VecDeque<_> = module.declarations(db).into();
58+
while let Some(def) = defs.pop_front() {
59+
if let ModuleDef::Module(submodule) = def {
60+
if let hir::ModuleSource::Module(_) = submodule.definition_source(db).value {
61+
defs.extend(submodule.declarations(db));
62+
submodule.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_)));
63+
}
64+
}
65+
cb(Either::Left(def));
66+
}
67+
module.impl_defs(db).into_iter().for_each(|impl_| cb(Either::Right(impl_)));
68+
}
69+
4270
/// Helps with finding well-know things inside the standard library. This is
4371
/// somewhat similar to the known paths infra inside hir, but it different; We
4472
/// want to make sure that IDE specific paths don't become interesting inside

0 commit comments

Comments
 (0)