diff options
Diffstat (limited to 'crates/ide/src')
-rw-r--r-- | crates/ide/src/annotations.rs | 87 | ||||
-rw-r--r-- | crates/ide/src/goto_implementation.rs | 130 | ||||
-rw-r--r-- | crates/ide/src/inlay_hints.rs | 2 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 60 |
4 files changed, 163 insertions, 116 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 @@ | |||
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 | helpers::visit_file_defs, |
6 | RootDatabase, | ||
5 | }; | 7 | }; |
6 | use syntax::TextRange; | 8 | use syntax::{ast::NameOwner, AstNode, TextRange, TextSize}; |
7 | 9 | ||
8 | use crate::{ | 10 | use 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 | ||
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 | } | ||
939 | } | 970 | } |
diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 3990305fc..f4d7c14a6 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs | |||
@@ -1,6 +1,9 @@ | |||
1 | use hir::{Crate, Impl, Semantics}; | 1 | use hir::{Impl, Semantics}; |
2 | use ide_db::RootDatabase; | 2 | use ide_db::{ |
3 | use syntax::{algo::find_node_at_offset, ast, AstNode}; | 3 | defs::{Definition, NameClass, NameRefClass}, |
4 | RootDatabase, | ||
5 | }; | ||
6 | use syntax::{ast, AstNode}; | ||
4 | 7 | ||
5 | use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo}; | 8 | use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo}; |
6 | 9 | ||
@@ -21,55 +24,42 @@ pub(crate) fn goto_implementation( | |||
21 | let source_file = sema.parse(position.file_id); | 24 | let source_file = sema.parse(position.file_id); |
22 | let syntax = source_file.syntax().clone(); | 25 | let syntax = source_file.syntax().clone(); |
23 | 26 | ||
24 | let krate = sema.to_module_def(position.file_id)?.krate(); | 27 | let node = sema.find_node_at_offset_with_descend(&syntax, position.offset)?; |
25 | 28 | let def = match &node { | |
26 | if let Some(nominal_def) = find_node_at_offset::<ast::Adt>(&syntax, position.offset) { | 29 | ast::NameLike::Name(name) => { |
27 | return Some(RangeInfo::new( | 30 | NameClass::classify(&sema, name).map(|class| class.referenced_or_defined(sema.db)) |
28 | nominal_def.syntax().text_range(), | 31 | } |
29 | impls_for_def(&sema, &nominal_def, krate)?, | 32 | ast::NameLike::NameRef(name_ref) => { |
30 | )); | 33 | NameRefClass::classify(&sema, name_ref).map(|class| class.referenced(sema.db)) |
31 | } else if let Some(trait_def) = find_node_at_offset::<ast::Trait>(&syntax, position.offset) { | 34 | } |
32 | return Some(RangeInfo::new( | 35 | ast::NameLike::Lifetime(_) => None, |
33 | trait_def.syntax().text_range(), | 36 | }?; |
34 | impls_for_trait(&sema, &trait_def, krate)?, | 37 | let def = match def { |
35 | )); | 38 | Definition::ModuleDef(def) => def, |
36 | } | 39 | _ => return None, |
37 | |||
38 | None | ||
39 | } | ||
40 | |||
41 | fn impls_for_def( | ||
42 | sema: &Semantics<RootDatabase>, | ||
43 | node: &ast::Adt, | ||
44 | krate: Crate, | ||
45 | ) -> Option<Vec<NavigationTarget>> { | ||
46 | let ty = match node { | ||
47 | ast::Adt::Struct(def) => sema.to_def(def)?.ty(sema.db), | ||
48 | ast::Adt::Enum(def) => sema.to_def(def)?.ty(sema.db), | ||
49 | ast::Adt::Union(def) => sema.to_def(def)?.ty(sema.db), | ||
50 | }; | 40 | }; |
51 | 41 | let navs = match def { | |
52 | let impls = Impl::all_in_crate(sema.db, krate); | 42 | hir::ModuleDef::Trait(trait_) => impls_for_trait(&sema, trait_), |
53 | 43 | hir::ModuleDef::Adt(adt) => impls_for_ty(&sema, adt.ty(sema.db)), | |
54 | Some( | 44 | hir::ModuleDef::TypeAlias(alias) => impls_for_ty(&sema, alias.ty(sema.db)), |
55 | impls | 45 | hir::ModuleDef::BuiltinType(builtin) => { |
56 | .into_iter() | 46 | let module = sema.to_module_def(position.file_id)?; |
57 | .filter(|impl_def| ty.is_equal_for_find_impls(&impl_def.target_ty(sema.db))) | 47 | impls_for_ty(&sema, builtin.ty(sema.db, module)) |
58 | .filter_map(|imp| imp.try_to_nav(sema.db)) | 48 | } |
59 | .collect(), | 49 | _ => return None, |
60 | ) | 50 | }; |
51 | Some(RangeInfo { range: node.syntax().text_range(), info: navs }) | ||
61 | } | 52 | } |
62 | 53 | ||
63 | fn impls_for_trait( | 54 | fn impls_for_ty(sema: &Semantics<RootDatabase>, ty: hir::Type) -> Vec<NavigationTarget> { |
64 | sema: &Semantics<RootDatabase>, | 55 | Impl::all_for_type(sema.db, ty).into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect() |
65 | node: &ast::Trait, | 56 | } |
66 | krate: Crate, | ||
67 | ) -> Option<Vec<NavigationTarget>> { | ||
68 | let tr = sema.to_def(node)?; | ||
69 | |||
70 | let impls = Impl::for_trait(sema.db, krate, tr); | ||
71 | 57 | ||
72 | Some(impls.into_iter().filter_map(|imp| imp.try_to_nav(sema.db)).collect()) | 58 | fn impls_for_trait(sema: &Semantics<RootDatabase>, trait_: hir::Trait) -> Vec<NavigationTarget> { |
59 | Impl::all_for_trait(sema.db, trait_) | ||
60 | .into_iter() | ||
61 | .filter_map(|imp| imp.try_to_nav(sema.db)) | ||
62 | .collect() | ||
73 | } | 63 | } |
74 | 64 | ||
75 | #[cfg(test)] | 65 | #[cfg(test)] |
@@ -226,4 +216,48 @@ macro Copy {} | |||
226 | "#, | 216 | "#, |
227 | ); | 217 | ); |
228 | } | 218 | } |
219 | |||
220 | #[test] | ||
221 | fn goto_implementation_type_alias() { | ||
222 | check( | ||
223 | r#" | ||
224 | struct Foo; | ||
225 | |||
226 | type Bar$0 = Foo; | ||
227 | |||
228 | impl Foo {} | ||
229 | //^^^ | ||
230 | impl Bar {} | ||
231 | //^^^ | ||
232 | "#, | ||
233 | ); | ||
234 | } | ||
235 | |||
236 | #[test] | ||
237 | fn goto_implementation_adt_generic() { | ||
238 | check( | ||
239 | r#" | ||
240 | struct Foo$0<T>; | ||
241 | |||
242 | impl<T> Foo<T> {} | ||
243 | //^^^^^^ | ||
244 | impl Foo<str> {} | ||
245 | //^^^^^^^^ | ||
246 | "#, | ||
247 | ); | ||
248 | } | ||
249 | |||
250 | #[test] | ||
251 | fn goto_implementation_builtin() { | ||
252 | check( | ||
253 | r#" | ||
254 | //- /lib.rs crate:main deps:core | ||
255 | fn foo(_: bool$0) {{}} | ||
256 | //- /libcore.rs crate:core | ||
257 | #[lang = "bool"] | ||
258 | impl bool {} | ||
259 | //^^^^ | ||
260 | "#, | ||
261 | ); | ||
262 | } | ||
229 | } | 263 | } |
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 4ceb20742..16c04eeee 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs | |||
@@ -219,7 +219,7 @@ fn hint_iterator( | |||
219 | let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) | 219 | let strukt = std::iter::successors(Some(ty.clone()), |ty| ty.remove_ref()) |
220 | .last() | 220 | .last() |
221 | .and_then(|strukt| strukt.as_adt())?; | 221 | .and_then(|strukt| strukt.as_adt())?; |
222 | let krate = strukt.krate(db)?; | 222 | let krate = strukt.krate(db); |
223 | if krate != famous_defs.core()? { | 223 | if krate != famous_defs.core()? { |
224 | return None; | 224 | return None; |
225 | } | 225 | } |
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 | ||
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::{ |
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 | // |=== |
103 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 105 | pub(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 | ||
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> { | 230 | pub(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 |