diff options
-rw-r--r-- | crates/hir/src/semantics.rs | 26 | ||||
-rw-r--r-- | crates/ide/src/annotations.rs | 86 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 59 |
3 files changed, 103 insertions, 68 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 945638cc5..2a0a36de4 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs | |||
@@ -2,10 +2,12 @@ | |||
2 | 2 | ||
3 | mod source_to_def; | 3 | mod source_to_def; |
4 | 4 | ||
5 | use std::{cell::RefCell, fmt, iter::successors}; | 5 | use std::{cell::RefCell, collections::VecDeque, fmt, iter::successors}; |
6 | 6 | ||
7 | use base_db::{FileId, FileRange}; | 7 | use base_db::{FileId, FileRange}; |
8 | use either::Either; | ||
8 | use hir_def::{ | 9 | use hir_def::{ |
10 | nameres::ModuleSource, | ||
9 | resolver::{self, HasResolver, Resolver, TypeNs}, | 11 | resolver::{self, HasResolver, Resolver, TypeNs}, |
10 | AsMacroCall, FunctionId, TraitId, VariantId, | 12 | AsMacroCall, FunctionId, TraitId, VariantId, |
11 | }; | 13 | }; |
@@ -155,6 +157,28 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { | |||
155 | self.imp.ancestors_at_offset_with_macros(node, offset) | 157 | self.imp.ancestors_at_offset_with_macros(node, offset) |
156 | } | 158 | } |
157 | 159 | ||
160 | /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. | ||
161 | pub fn visit_file_defs(&self, file_id: FileId, cb: &mut dyn FnMut(Either<ModuleDef, Impl>)) { | ||
162 | let module = match self.to_module_def(file_id) { | ||
163 | Some(it) => it, | ||
164 | None => return, | ||
165 | }; | ||
166 | let mut defs: VecDeque<_> = module.declarations(self.db).into(); | ||
167 | while let Some(def) = defs.pop_front() { | ||
168 | if let ModuleDef::Module(submodule) = def { | ||
169 | if let ModuleSource::Module(_) = submodule.definition_source(self.db).value { | ||
170 | defs.extend(submodule.declarations(self.db)); | ||
171 | submodule | ||
172 | .impl_defs(self.db) | ||
173 | .into_iter() | ||
174 | .for_each(|impl_| cb(Either::Right(impl_))); | ||
175 | } | ||
176 | } | ||
177 | cb(Either::Left(def)); | ||
178 | } | ||
179 | module.impl_defs(self.db).into_iter().for_each(|impl_| cb(Either::Right(impl_))); | ||
180 | } | ||
181 | |||
158 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, | 182 | /// Find a AstNode by offset inside SyntaxNode, if it is inside *Macrofile*, |
159 | /// search up until it is of the target AstNode type | 183 | /// search up until it is of the target AstNode type |
160 | pub fn find_node_at_offset_with_macros<N: AstNode>( | 184 | pub fn find_node_at_offset_with_macros<N: AstNode>( |
diff --git a/crates/ide/src/annotations.rs b/crates/ide/src/annotations.rs index 8e0a8fd8d..c3422ce70 100644 --- a/crates/ide/src/annotations.rs +++ b/crates/ide/src/annotations.rs | |||
@@ -1,17 +1,17 @@ | |||
1 | use hir::Semantics; | 1 | use either::Either; |
2 | use hir::{HasSource, Semantics}; | ||
2 | use ide_db::{ | 3 | use ide_db::{ |
3 | base_db::{FileId, FilePosition, FileRange, SourceDatabase}, | 4 | base_db::{FileId, FilePosition, FileRange}, |
4 | RootDatabase, SymbolKind, | 5 | RootDatabase, |
5 | }; | 6 | }; |
6 | use syntax::TextRange; | 7 | use syntax::{ast::NameOwner, AstNode, TextRange, TextSize}; |
7 | 8 | ||
8 | use crate::{ | 9 | use crate::{ |
9 | file_structure::file_structure, | ||
10 | fn_references::find_all_methods, | 10 | fn_references::find_all_methods, |
11 | goto_implementation::goto_implementation, | 11 | goto_implementation::goto_implementation, |
12 | references::find_all_refs, | 12 | references::find_all_refs, |
13 | runnables::{runnables, Runnable}, | 13 | runnables::{runnables, Runnable}, |
14 | NavigationTarget, RunnableKind, StructureNodeKind, | 14 | NavigationTarget, RunnableKind, |
15 | }; | 15 | }; |
16 | 16 | ||
17 | // Feature: Annotations | 17 | // Feature: Annotations |
@@ -75,41 +75,56 @@ pub(crate) fn annotations( | |||
75 | } | 75 | } |
76 | } | 76 | } |
77 | 77 | ||
78 | file_structure(&db.parse(file_id).tree()) | 78 | Semantics::new(db).visit_file_defs(file_id, &mut |def| match def { |
79 | .into_iter() | 79 | Either::Left(def) => { |
80 | .filter(|node| { | 80 | let node = match def { |
81 | matches!( | 81 | hir::ModuleDef::Const(konst) => { |
82 | node.kind, | 82 | konst.source(db).and_then(|node| range_and_position_of(&node.value)) |
83 | StructureNodeKind::SymbolKind(SymbolKind::Trait) | 83 | } |
84 | | StructureNodeKind::SymbolKind(SymbolKind::Struct) | 84 | hir::ModuleDef::Trait(trait_) => { |
85 | | StructureNodeKind::SymbolKind(SymbolKind::Enum) | 85 | trait_.source(db).and_then(|node| range_and_position_of(&node.value)) |
86 | | StructureNodeKind::SymbolKind(SymbolKind::Union) | 86 | } |
87 | | StructureNodeKind::SymbolKind(SymbolKind::Const) | 87 | hir::ModuleDef::Adt(hir::Adt::Struct(strukt)) => { |
88 | ) | 88 | strukt.source(db).and_then(|node| range_and_position_of(&node.value)) |
89 | }) | 89 | } |
90 | .for_each(|node| { | 90 | hir::ModuleDef::Adt(hir::Adt::Enum(enum_)) => { |
91 | if config.annotate_impls | 91 | enum_.source(db).and_then(|node| range_and_position_of(&node.value)) |
92 | && node.kind != StructureNodeKind::SymbolKind(SymbolKind::Const) | 92 | } |
93 | { | 93 | hir::ModuleDef::Adt(hir::Adt::Union(union)) => { |
94 | union.source(db).and_then(|node| range_and_position_of(&node.value)) | ||
95 | } | ||
96 | _ => None, | ||
97 | }; | ||
98 | let (offset, range) = match node { | ||
99 | Some(node) => node, | ||
100 | None => return, | ||
101 | }; | ||
102 | |||
103 | if config.annotate_impls && !matches!(def, hir::ModuleDef::Const(_)) { | ||
94 | annotations.push(Annotation { | 104 | annotations.push(Annotation { |
95 | range: node.node_range, | 105 | range, |
96 | kind: AnnotationKind::HasImpls { | 106 | kind: AnnotationKind::HasImpls { |
97 | position: FilePosition { file_id, offset: node.navigation_range.start() }, | 107 | position: FilePosition { file_id, offset }, |
98 | data: None, | 108 | data: None, |
99 | }, | 109 | }, |
100 | }); | 110 | }); |
101 | } | 111 | } |
102 | |||
103 | if config.annotate_references { | 112 | if config.annotate_references { |
104 | annotations.push(Annotation { | 113 | annotations.push(Annotation { |
105 | range: node.node_range, | 114 | range, |
106 | kind: AnnotationKind::HasReferences { | 115 | kind: AnnotationKind::HasReferences { |
107 | position: FilePosition { file_id, offset: node.navigation_range.start() }, | 116 | position: FilePosition { file_id, offset }, |
108 | data: None, | 117 | data: None, |
109 | }, | 118 | }, |
110 | }); | 119 | }); |
111 | } | 120 | } |
112 | }); | 121 | |
122 | fn range_and_position_of(node: &dyn NameOwner) -> Option<(TextSize, TextRange)> { | ||
123 | Some((node.name()?.syntax().text_range().start(), node.syntax().text_range())) | ||
124 | } | ||
125 | } | ||
126 | Either::Right(_) => (), | ||
127 | }); | ||
113 | 128 | ||
114 | if config.annotate_method_references { | 129 | if config.annotate_method_references { |
115 | annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { | 130 | annotations.extend(find_all_methods(db, file_id).into_iter().map(|method| Annotation { |
@@ -936,4 +951,19 @@ mod tests { | |||
936 | "#]], | 951 | "#]], |
937 | ); | 952 | ); |
938 | } | 953 | } |
954 | |||
955 | #[test] | ||
956 | fn test_no_annotations_outside_module_tree() { | ||
957 | check( | ||
958 | r#" | ||
959 | //- /foo.rs | ||
960 | struct Foo; | ||
961 | //- /lib.rs | ||
962 | // this file comes last since `check` checks the first file only | ||
963 | "#, | ||
964 | expect![[r#" | ||
965 | [] | ||
966 | "#]], | ||
967 | ); | ||
968 | } | ||
939 | } | 969 | } |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 27d35de5b..17454f270 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -2,6 +2,7 @@ use std::fmt; | |||
2 | 2 | ||
3 | use ast::NameOwner; | 3 | use ast::NameOwner; |
4 | use cfg::CfgExpr; | 4 | use cfg::CfgExpr; |
5 | use either::Either; | ||
5 | use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; | 6 | use hir::{AsAssocItem, HasAttrs, HasSource, HirDisplay, Semantics}; |
6 | use ide_assists::utils::test_related_attribute; | 7 | use ide_assists::utils::test_related_attribute; |
7 | use ide_db::{ | 8 | use ide_db::{ |
@@ -102,13 +103,27 @@ impl Runnable { | |||
102 | // |=== | 103 | // |=== |
103 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 104 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { |
104 | let sema = Semantics::new(db); | 105 | 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 | 106 | ||
110 | let mut res = Vec::new(); | 107 | let mut res = Vec::new(); |
111 | runnables_mod(&sema, &mut res, module); | 108 | sema.visit_file_defs(file_id, &mut |def| match def { |
109 | Either::Left(def) => { | ||
110 | let runnable = match def { | ||
111 | hir::ModuleDef::Module(it) => runnable_mod(&sema, it), | ||
112 | hir::ModuleDef::Function(it) => runnable_fn(&sema, it), | ||
113 | _ => None, | ||
114 | }; | ||
115 | res.extend(runnable.or_else(|| module_def_doctest(&sema, def))) | ||
116 | } | ||
117 | Either::Right(impl_) => { | ||
118 | res.extend(impl_.items(db).into_iter().filter_map(|assoc| match assoc { | ||
119 | hir::AssocItem::Function(it) => { | ||
120 | runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) | ||
121 | } | ||
122 | hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()), | ||
123 | hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()), | ||
124 | })) | ||
125 | } | ||
126 | }); | ||
112 | res | 127 | res |
113 | } | 128 | } |
114 | 129 | ||
@@ -211,39 +226,6 @@ fn parent_test_module(sema: &Semantics<RootDatabase>, fn_def: &ast::Fn) -> Optio | |||
211 | }) | 226 | }) |
212 | } | 227 | } |
213 | 228 | ||
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 | |||
247 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { | 229 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { |
248 | let func = def.source(sema.db)?; | 230 | let func = def.source(sema.db)?; |
249 | let name_string = def.name(sema.db).to_string(); | 231 | let name_string = def.name(sema.db).to_string(); |
@@ -1178,7 +1160,6 @@ mod tests { | |||
1178 | 1160 | ||
1179 | #[test] | 1161 | #[test] |
1180 | fn dont_recurse_in_outline_submodules() { | 1162 | fn dont_recurse_in_outline_submodules() { |
1181 | cov_mark::check!(dont_recurse_in_outline_submodules); | ||
1182 | check( | 1163 | check( |
1183 | r#" | 1164 | r#" |
1184 | //- /lib.rs | 1165 | //- /lib.rs |