aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/annotations.rs87
-rw-r--r--crates/ide/src/runnables.rs60
-rw-r--r--crates/ide_db/src/helpers.rs28
3 files changed, 108 insertions, 67 deletions
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs
index 8e0a8fd8d..72492f826 100644
--- a/crates/ide/src/annotations.rs
+++ b/crates/ide/src/annotations.rs
@@ -1,17 +1,18 @@
1use hir::Semantics; 1use either::Either;
2use hir::{HasSource, Semantics};
2use ide_db::{ 3use ide_db::{
3 base_db::{FileId, FilePosition, FileRange, SourceDatabase}, 4 base_db::{FileId, FilePosition, FileRange},
4 RootDatabase, SymbolKind, 5 helpers::visit_file_defs,
6 RootDatabase,
5}; 7};
6use syntax::TextRange; 8use syntax::{ast::NameOwner, AstNode, TextRange, TextSize};
7 9
8use crate::{ 10use crate::{
9 file_structure::file_structure,
10 fn_references::find_all_methods, 11 fn_references::find_all_methods,
11 goto_implementation::goto_implementation, 12 goto_implementation::goto_implementation,
12 references::find_all_refs, 13 references::find_all_refs,
13 runnables::{runnables, Runnable}, 14 runnables::{runnables, Runnable},
14 NavigationTarget, RunnableKind, StructureNodeKind, 15 NavigationTarget, RunnableKind,
15}; 16};
16 17
17// Feature: Annotations 18// Feature: Annotations
@@ -75,41 +76,56 @@ pub(crate) fn annotations(
75 } 76 }
76 } 77 }
77 78
78 file_structure(&db.parse(file_id).tree()) 79 visit_file_defs(&Semantics::new(db), file_id, &mut |def| match def {
79 .into_iter() 80 Either::Left(def) => {
80 .filter(|node| { 81 let node = match def {
81 matches!( 82 hir::ModuleDef::Const(konst) => {
82 node.kind, 83 konst.source(db).and_then(|node| range_and_position_of(&node.value))
83 StructureNodeKind::SymbolKind(SymbolKind::Trait) 84 }
84 | StructureNodeKind::SymbolKind(SymbolKind::Struct) 85 hir::ModuleDef::Trait(trait_) => {
85 | StructureNodeKind::SymbolKind(SymbolKind::Enum) 86 trait_.source(db).and_then(|node| range_and_position_of(&node.value))
86 | StructureNodeKind::SymbolKind(SymbolKind::Union) 87 }
87 | StructureNodeKind::SymbolKind(SymbolKind::Const) 88 hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => {
88 ) 89 strukt.source(db).and_then(|node| range_and_position_of(&node.value))
89 }) 90 }
90 .for_each(|node| { 91 hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => {
91 if config.annotate_impls 92 enum_.source(db).and_then(|node| range_and_position_of(&node.value))
92 && node.kind != StructureNodeKind::SymbolKind(SymbolKind::Const) 93 }
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(_)) {
94 annotations.push(Annotation { 105 annotations.push(Annotation {
95 range: node.node_range, 106 range,
96 kind: AnnotationKind::HasImpls { 107 kind: AnnotationKind::HasImpls {
97 position: FilePosition { file_id, offset: node.navigation_range.start() }, 108 position: FilePosition { file_id, offset },
98 data: None, 109 data: None,
99 }, 110 },
100 }); 111 });
101 } 112 }
102
103 if config.annotate_references { 113 if config.annotate_references {
104 annotations.push(Annotation { 114 annotations.push(Annotation {
105 range: node.node_range, 115 range,
106 kind: AnnotationKind::HasReferences { 116 kind: AnnotationKind::HasReferences {
107 position: FilePosition { file_id, offset: node.navigation_range.start() }, 117 position: FilePosition { file_id, offset },
108 data: None, 118 data: None,
109 }, 119 },
110 }); 120 });
111 } 121 }
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 });
113 129
114 if config.annotate_method_references { 130 if config.annotate_method_references {
115 annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { 131 annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation {
@@ -936,4 +952,19 @@ mod tests {
936 "#]], 952 "#]],
937 ); 953 );
938 } 954 }
955
956 #[test]
957 fn test_no_annotations_outside_module_tree() {
958 check(
959 r#"
960//- /foo.rs
961struct Foo;
962//- /lib.rs
963// this file comes last since `check` checks the first file only
964"#,
965 expect![[r#"
966 []
967 "#]],
968 );
969 }
939} 970}
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 27d35de5b..0c7a8fbf8 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -2,11 +2,13 @@ use std::fmt;
2 2
3use ast::NameOwner; 3use ast::NameOwner;
4use cfg::CfgExpr; 4use cfg::CfgExpr;
5use either::Either;
5use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; 6use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics};
6use ide_assists::utils::test_related_attribute; 7use ide_assists::utils::test_related_attribute;
7use ide_db::{ 8use ide_db::{
8 base_db::{FilePosition, FileRange}, 9 base_db::{FilePosition, FileRange},
9 defs::Definition, 10 defs::Definition,
11 helpers::visit_file_defs,
10 search::SearchScope, 12 search::SearchScope,
11 RootDatabase, SymbolKind, 13 RootDatabase, SymbolKind,
12}; 14};
@@ -102,13 +104,27 @@ impl Runnable {
102// |=== 104// |===
103pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 105pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
104 let sema = Semantics::new(db); 106 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 };
109 107
110 let mut res = Vec::new(); 108 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 });
112 res 128 res
113} 129}
114 130
@@ -211,39 +227,6 @@ fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Optio
211 }) 227 })
212} 228}
213 229
214fn 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
247pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 230pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
248 let func = def.source(sema.db)?; 231 let func = def.source(sema.db)?;
249 let name_string = def.name(sema.db).to_string(); 232 let name_string = def.name(sema.db).to_string();
@@ -1178,7 +1161,6 @@ mod tests {
1178 1161
1179 #[test] 1162 #[test]
1180 fn dont_recurse_in_outline_submodules() { 1163 fn dont_recurse_in_outline_submodules() {
1181 cov_mark::check!(dont_recurse_in_outline_submodules);
1182 check( 1164 check(
1183 r#" 1165 r#"
1184//- /lib.rs 1166//- /lib.rs
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs
index 3c95d3cff..9992a92bd 100644
--- a/crates/ide_db/src/helpers.rs
+++ b/crates/ide_db/src/helpers.rs
@@ -2,6 +2,10 @@
2pub mod insert_use; 2pub mod insert_use;
3pub mod import_assets; 3pub mod import_assets;
4 4
5use std::collections::VecDeque;
6
7use base_db::FileId;
8use either::Either;
5use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait}; 9use hir::{Crate, Enum, ItemInNs, MacroDef, Module, ModuleDef, Name, ScopeDef, Semantics, Trait};
6use syntax::ast::{self, make}; 10use syntax::ast::{self, make};
7 11
@@ -39,6 +43,30 @@ pub fn mod_path_to_ast(path: &hir::ModPath) -> ast::Path {
39 make::path_from_segments(segments, is_abs) 43 make::path_from_segments(segments, is_abs)
40} 44}
41 45
46/// Iterates all `ModuleDef`s and `Impl` blocks of the given file.
47pub 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
42/// Helps with finding well-know things inside the standard library. This is 70/// Helps with finding well-know things inside the standard library. This is
43/// somewhat similar to the known paths infra inside hir, but it different; We 71/// somewhat similar to the known paths infra inside hir, but it different; We
44/// want to make sure that IDE specific paths don't become interesting inside 72/// want to make sure that IDE specific paths don't become interesting inside