aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/annotations.rs87
-rw-r--r--crates/ide/src/goto_implementation.rs130
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/runnables.rs60
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 @@
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/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 @@
1use hir::{Crate, Impl, Semantics}; 1use hir::{Impl, Semantics};
2use ide_db::RootDatabase; 2use ide_db::{
3use syntax::{algo::find_node_at_offset, ast, AstNode}; 3 defs::{Definition, NameClass, NameRefClass},
4 RootDatabase,
5};
6use syntax::{ast, AstNode};
4 7
5use crate::{display::TryToNav, FilePosition, NavigationTarget, RangeInfo}; 8use 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
41fn 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
63fn impls_for_trait( 54fn 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()) 58fn 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#"
224struct Foo;
225
226type Bar$0 = Foo;
227
228impl Foo {}
229 //^^^
230impl Bar {}
231 //^^^
232"#,
233 );
234 }
235
236 #[test]
237 fn goto_implementation_adt_generic() {
238 check(
239 r#"
240struct Foo$0<T>;
241
242impl<T> Foo<T> {}
243 //^^^^^^
244impl 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
255fn foo(_: bool$0) {{}}
256//- /libcore.rs crate:core
257#[lang = "bool"]
258impl 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
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