aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/assists/src/handlers/qualify_path.rs2
-rw-r--r--crates/base_db/src/input.rs2
-rw-r--r--crates/completion/src/completions/flyimport.rs12
-rw-r--r--crates/completion/src/render.rs64
-rw-r--r--crates/completion/src/render/const_.rs5
-rw-r--r--crates/completion/src/render/function.rs4
-rw-r--r--crates/completion/src/render/type_alias.rs5
-rw-r--r--crates/completion/src/test_utils.rs3
-rw-r--r--crates/hir/src/code_model.rs19
-rw-r--r--crates/ide/src/hover.rs7
-rw-r--r--crates/ide/src/runnables.rs202
-rw-r--r--crates/mbe/src/syntax_bridge.rs26
-rw-r--r--crates/parser/src/event.rs5
-rw-r--r--crates/stdx/src/lib.rs2
14 files changed, 237 insertions, 121 deletions
diff --git a/crates/assists/src/handlers/qualify_path.rs b/crates/assists/src/handlers/qualify_path.rs
index af8a11d03..b0b0d31b4 100644
--- a/crates/assists/src/handlers/qualify_path.rs
+++ b/crates/assists/src/handlers/qualify_path.rs
@@ -1,6 +1,6 @@
1use std::iter; 1use std::iter;
2 2
3use hir::AsName; 3use hir::{AsAssocItem, AsName};
4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast}; 4use ide_db::helpers::{import_assets::ImportCandidate, mod_path_to_ast};
5use ide_db::RootDatabase; 5use ide_db::RootDatabase;
6use syntax::{ 6use syntax::{
diff --git a/crates/base_db/src/input.rs b/crates/base_db/src/input.rs
index 2dd8fbe67..b5f7e4200 100644
--- a/crates/base_db/src/input.rs
+++ b/crates/base_db/src/input.rs
@@ -178,7 +178,7 @@ pub struct CrateData {
178 pub root_file_id: FileId, 178 pub root_file_id: FileId,
179 pub edition: Edition, 179 pub edition: Edition,
180 /// A name used in the package's project declaration: for Cargo projects, 180 /// A name used in the package's project declaration: for Cargo projects,
181 /// it's [package].name, can be different for other project types or even 181 /// its `[package].name` can be different for other project types or even
182 /// absent (a dummy crate for the code snippet, for example). 182 /// absent (a dummy crate for the code snippet, for example).
183 /// 183 ///
184 /// For purposes of analysis, crates are anonymous (only names in 184 /// For purposes of analysis, crates are anonymous (only names in
diff --git a/crates/completion/src/completions/flyimport.rs b/crates/completion/src/completions/flyimport.rs
index 47e797ac8..dc0b38a16 100644
--- a/crates/completion/src/completions/flyimport.rs
+++ b/crates/completion/src/completions/flyimport.rs
@@ -48,7 +48,7 @@
48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding 48//! Note that having this flag set to `true` does not guarantee that the feature is enabled: your client needs to have the corredponding
49//! capability enabled. 49//! capability enabled.
50 50
51use hir::{ModPath, ScopeDef}; 51use hir::{AsAssocItem, ModPath, ScopeDef};
52use ide_db::helpers::{ 52use ide_db::helpers::{
53 import_assets::{ImportAssets, ImportCandidate}, 53 import_assets::{ImportAssets, ImportCandidate},
54 insert_use::ImportScope, 54 insert_use::ImportScope,
@@ -601,11 +601,12 @@ fn main() {
601 } 601 }
602 602
603 #[test] 603 #[test]
604 fn zero_input_assoc_item_completion() { 604 fn zero_input_deprecated_assoc_item_completion() {
605 check( 605 check(
606 r#" 606 r#"
607//- /lib.rs crate:dep 607//- /lib.rs crate:dep
608pub mod test_mod { 608pub mod test_mod {
609 #[deprecated]
609 pub trait TestTrait { 610 pub trait TestTrait {
610 const SPECIAL_CONST: u8; 611 const SPECIAL_CONST: u8;
611 type HumbleType; 612 type HumbleType;
@@ -628,7 +629,7 @@ fn main() {
628} 629}
629 "#, 630 "#,
630 expect![[r#" 631 expect![[r#"
631 me random_method() (dep::test_mod::TestTrait) fn random_method(&self) 632 me random_method() (dep::test_mod::TestTrait) fn random_method(&self) DEPRECATED
632 "#]], 633 "#]],
633 ); 634 );
634 635
@@ -636,6 +637,7 @@ fn main() {
636 r#" 637 r#"
637//- /lib.rs crate:dep 638//- /lib.rs crate:dep
638pub mod test_mod { 639pub mod test_mod {
640 #[deprecated]
639 pub trait TestTrait { 641 pub trait TestTrait {
640 const SPECIAL_CONST: u8; 642 const SPECIAL_CONST: u8;
641 type HumbleType; 643 type HumbleType;
@@ -657,8 +659,8 @@ fn main() {
657} 659}
658"#, 660"#,
659 expect![[r#" 661 expect![[r#"
660 ct SPECIAL_CONST (dep::test_mod::TestTrait) 662 ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
661 fn weird_function() (dep::test_mod::TestTrait) fn weird_function() 663 fn weird_function() (dep::test_mod::TestTrait) fn weird_function() DEPRECATED
662 "#]], 664 "#]],
663 ); 665 );
664 } 666 }
diff --git a/crates/completion/src/render.rs b/crates/completion/src/render.rs
index 4b3c9702a..4f622d28a 100644
--- a/crates/completion/src/render.rs
+++ b/crates/completion/src/render.rs
@@ -10,7 +10,9 @@ pub(crate) mod type_alias;
10 10
11mod builder_ext; 11mod builder_ext;
12 12
13use hir::{Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type}; 13use hir::{
14 AsAssocItem, Documentation, HasAttrs, HirDisplay, ModuleDef, Mutability, ScopeDef, Type,
15};
14use ide_db::{helpers::SnippetCap, RootDatabase}; 16use ide_db::{helpers::SnippetCap, RootDatabase};
15use syntax::TextRange; 17use syntax::TextRange;
16use test_utils::mark; 18use test_utils::mark;
@@ -87,7 +89,24 @@ impl<'a> RenderContext<'a> {
87 } 89 }
88 90
89 fn is_deprecated(&self, node: impl HasAttrs) -> bool { 91 fn is_deprecated(&self, node: impl HasAttrs) -> bool {
90 node.attrs(self.db()).by_key("deprecated").exists() 92 let attrs = node.attrs(self.db());
93 attrs.by_key("deprecated").exists() || attrs.by_key("rustc_deprecated").exists()
94 }
95
96 fn is_deprecated_assoc_item(&self, as_assoc_item: impl AsAssocItem) -> bool {
97 let db = self.db();
98 let assoc = match as_assoc_item.as_assoc_item(db) {
99 Some(assoc) => assoc,
100 None => return false,
101 };
102
103 let is_assoc_deprecated = match assoc {
104 hir::AssocItem::Function(it) => self.is_deprecated(it),
105 hir::AssocItem::Const(it) => self.is_deprecated(it),
106 hir::AssocItem::TypeAlias(it) => self.is_deprecated(it),
107 };
108 is_assoc_deprecated
109 || assoc.containing_trait(db).map(|trait_| self.is_deprecated(trait_)).unwrap_or(false)
91 } 110 }
92 111
93 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> { 112 fn docs(&self, node: impl HasAttrs) -> Option<Documentation> {
@@ -206,8 +225,6 @@ impl<'a> Render<'a> {
206 } 225 }
207 }; 226 };
208 227
209 let docs = self.docs(resolution);
210
211 let mut item = 228 let mut item =
212 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone()); 229 CompletionItem::new(completion_kind, self.ctx.source_range(), local_name.clone());
213 if let ScopeDef::Local(local) = resolution { 230 if let ScopeDef::Local(local) = resolution {
@@ -253,13 +270,14 @@ impl<'a> Render<'a> {
253 } 270 }
254 } 271 }
255 272
256 let item = item 273 Some(
257 .kind(kind) 274 item.kind(kind)
258 .add_import(import_to_add) 275 .add_import(import_to_add)
259 .set_documentation(docs) 276 .set_ref_match(ref_match)
260 .set_ref_match(ref_match) 277 .set_documentation(self.docs(resolution))
261 .build(); 278 .set_deprecated(self.is_deprecated(resolution))
262 Some(item) 279 .build(),
280 )
263 } 281 }
264 282
265 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> { 283 fn docs(&self, resolution: &ScopeDef) -> Option<Documentation> {
@@ -275,6 +293,16 @@ impl<'a> Render<'a> {
275 _ => None, 293 _ => None,
276 } 294 }
277 } 295 }
296
297 fn is_deprecated(&self, resolution: &ScopeDef) -> bool {
298 match resolution {
299 ScopeDef::ModuleDef(it) => self.ctx.is_deprecated_assoc_item(*it),
300 ScopeDef::MacroDef(it) => self.ctx.is_deprecated(*it),
301 ScopeDef::GenericParam(it) => self.ctx.is_deprecated(*it),
302 ScopeDef::AdtSelfType(it) => self.ctx.is_deprecated(*it),
303 _ => false,
304 }
305 }
278} 306}
279 307
280fn compute_score_from_active( 308fn compute_score_from_active(
@@ -485,7 +513,7 @@ fn main() { let _: m::Spam = S$0 }
485 r#" 513 r#"
486#[deprecated] 514#[deprecated]
487fn something_deprecated() {} 515fn something_deprecated() {}
488#[deprecated(since = "1.0.0")] 516#[rustc_deprecated(since = "1.0.0")]
489fn something_else_deprecated() {} 517fn something_else_deprecated() {}
490 518
491fn main() { som$0 } 519fn main() { som$0 }
@@ -494,8 +522,8 @@ fn main() { som$0 }
494 [ 522 [
495 CompletionItem { 523 CompletionItem {
496 label: "main()", 524 label: "main()",
497 source_range: 121..124, 525 source_range: 127..130,
498 delete: 121..124, 526 delete: 127..130,
499 insert: "main()$0", 527 insert: "main()$0",
500 kind: Function, 528 kind: Function,
501 lookup: "main", 529 lookup: "main",
@@ -503,8 +531,8 @@ fn main() { som$0 }
503 }, 531 },
504 CompletionItem { 532 CompletionItem {
505 label: "something_deprecated()", 533 label: "something_deprecated()",
506 source_range: 121..124, 534 source_range: 127..130,
507 delete: 121..124, 535 delete: 127..130,
508 insert: "something_deprecated()$0", 536 insert: "something_deprecated()$0",
509 kind: Function, 537 kind: Function,
510 lookup: "something_deprecated", 538 lookup: "something_deprecated",
@@ -513,8 +541,8 @@ fn main() { som$0 }
513 }, 541 },
514 CompletionItem { 542 CompletionItem {
515 label: "something_else_deprecated()", 543 label: "something_else_deprecated()",
516 source_range: 121..124, 544 source_range: 127..130,
517 delete: 121..124, 545 delete: 127..130,
518 insert: "something_else_deprecated()$0", 546 insert: "something_else_deprecated()$0",
519 kind: Function, 547 kind: Function,
520 lookup: "something_else_deprecated", 548 lookup: "something_else_deprecated",
diff --git a/crates/completion/src/render/const_.rs b/crates/completion/src/render/const_.rs
index ce924f309..e46452d4e 100644
--- a/crates/completion/src/render/const_.rs
+++ b/crates/completion/src/render/const_.rs
@@ -38,7 +38,10 @@ impl<'a> ConstRender<'a> {
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::Const) 39 .kind(CompletionItemKind::Const)
40 .set_documentation(self.ctx.docs(self.const_)) 40 .set_documentation(self.ctx.docs(self.const_))
41 .set_deprecated(self.ctx.is_deprecated(self.const_)) 41 .set_deprecated(
42 self.ctx.is_deprecated(self.const_)
43 || self.ctx.is_deprecated_assoc_item(self.const_),
44 )
42 .detail(detail) 45 .detail(detail)
43 .build(); 46 .build();
44 47
diff --git a/crates/completion/src/render/function.rs b/crates/completion/src/render/function.rs
index f5b0ce3e3..8f4c66211 100644
--- a/crates/completion/src/render/function.rs
+++ b/crates/completion/src/render/function.rs
@@ -44,7 +44,9 @@ impl<'a> FunctionRender<'a> {
44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone()) 44 CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), self.name.clone())
45 .kind(self.kind()) 45 .kind(self.kind())
46 .set_documentation(self.ctx.docs(self.func)) 46 .set_documentation(self.ctx.docs(self.func))
47 .set_deprecated(self.ctx.is_deprecated(self.func)) 47 .set_deprecated(
48 self.ctx.is_deprecated(self.func) || self.ctx.is_deprecated_assoc_item(self.func),
49 )
48 .detail(self.detail()) 50 .detail(self.detail())
49 .add_call_parens(self.ctx.completion, self.name, params) 51 .add_call_parens(self.ctx.completion, self.name, params)
50 .add_import(import_to_add) 52 .add_import(import_to_add)
diff --git a/crates/completion/src/render/type_alias.rs b/crates/completion/src/render/type_alias.rs
index 69b445b9c..29287143a 100644
--- a/crates/completion/src/render/type_alias.rs
+++ b/crates/completion/src/render/type_alias.rs
@@ -38,7 +38,10 @@ impl<'a> TypeAliasRender<'a> {
38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name) 38 let item = CompletionItem::new(CompletionKind::Reference, self.ctx.source_range(), name)
39 .kind(CompletionItemKind::TypeAlias) 39 .kind(CompletionItemKind::TypeAlias)
40 .set_documentation(self.ctx.docs(self.type_alias)) 40 .set_documentation(self.ctx.docs(self.type_alias))
41 .set_deprecated(self.ctx.is_deprecated(self.type_alias)) 41 .set_deprecated(
42 self.ctx.is_deprecated(self.type_alias)
43 || self.ctx.is_deprecated_assoc_item(self.type_alias),
44 )
42 .detail(detail) 45 .detail(detail)
43 .build(); 46 .build();
44 47
diff --git a/crates/completion/src/test_utils.rs b/crates/completion/src/test_utils.rs
index 3faf861b9..baff83305 100644
--- a/crates/completion/src/test_utils.rs
+++ b/crates/completion/src/test_utils.rs
@@ -83,6 +83,9 @@ pub(crate) fn completion_list_with_config(
83 let width = label_width.saturating_sub(monospace_width(it.label())); 83 let width = label_width.saturating_sub(monospace_width(it.label()));
84 format_to!(buf, "{:width$} {}", "", detail, width = width); 84 format_to!(buf, "{:width$} {}", "", detail, width = width);
85 } 85 }
86 if it.deprecated() {
87 format_to!(buf, " DEPRECATED");
88 }
86 format_to!(buf, "\n"); 89 format_to!(buf, "\n");
87 buf 90 buf
88 }) 91 })
diff --git a/crates/hir/src/code_model.rs b/crates/hir/src/code_model.rs
index 6f48322db..a4141e111 100644
--- a/crates/hir/src/code_model.rs
+++ b/crates/hir/src/code_model.rs
@@ -272,15 +272,6 @@ impl ModuleDef {
272 272
273 hir_ty::diagnostics::validate_module_item(db, module.id.krate, id, sink) 273 hir_ty::diagnostics::validate_module_item(db, module.id.krate, id, sink)
274 } 274 }
275
276 pub fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
277 match self {
278 ModuleDef::Function(f) => f.as_assoc_item(db),
279 ModuleDef::Const(c) => c.as_assoc_item(db),
280 ModuleDef::TypeAlias(t) => t.as_assoc_item(db),
281 _ => None,
282 }
283 }
284} 275}
285 276
286impl Module { 277impl Module {
@@ -1060,6 +1051,16 @@ impl AsAssocItem for TypeAlias {
1060 as_assoc_item(db, AssocItem::TypeAlias, self.id) 1051 as_assoc_item(db, AssocItem::TypeAlias, self.id)
1061 } 1052 }
1062} 1053}
1054impl AsAssocItem for ModuleDef {
1055 fn as_assoc_item(self, db: &dyn HirDatabase) -> Option<AssocItem> {
1056 match self {
1057 ModuleDef::Function(it) => it.as_assoc_item(db),
1058 ModuleDef::Const(it) => it.as_assoc_item(db),
1059 ModuleDef::TypeAlias(it) => it.as_assoc_item(db),
1060 _ => None,
1061 }
1062 }
1063}
1063fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem> 1064fn as_assoc_item<ID, DEF, CTOR, AST>(db: &dyn HirDatabase, ctor: CTOR, id: ID) -> Option<AssocItem>
1064where 1065where
1065 ID: Lookup<Data = AssocItemLoc<AST>>, 1066 ID: Lookup<Data = AssocItemLoc<AST>>,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 2024acd94..44ebdbd35 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -182,12 +182,7 @@ fn runnable_action(
182) -> Option<HoverAction> { 182) -> Option<HoverAction> {
183 match def { 183 match def {
184 Definition::ModuleDef(it) => match it { 184 Definition::ModuleDef(it) => match it {
185 ModuleDef::Module(it) => match it.definition_source(sema.db).value { 185 ModuleDef::Module(it) => runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it)),
186 ModuleSource::Module(it) => {
187 runnable_mod(&sema, it).map(|it| HoverAction::Runnable(it))
188 }
189 _ => None,
190 },
191 ModuleDef::Function(func) => { 186 ModuleDef::Function(func) => {
192 let src = func.source(sema.db)?; 187 let src = func.source(sema.db)?;
193 if src.file_id != file_id.into() { 188 if src.file_id != file_id.into() {
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index f5ee7de86..8976f1080 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -6,7 +6,7 @@ use hir::{AsAssocItem, HasAttrs, HasSource, Semantics};
6use ide_db::{defs::Definition, RootDatabase}; 6use ide_db::{defs::Definition, RootDatabase};
7use itertools::Itertools; 7use itertools::Itertools;
8use syntax::{ 8use syntax::{
9 ast::{self, AstNode, AttrsOwner, ModuleItemOwner}, 9 ast::{self, AstNode, AttrsOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12 12
@@ -95,27 +95,44 @@ impl Runnable {
95// |=== 95// |===
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 97 let sema = Semantics::new(db);
98 let source_file = sema.parse(file_id); 98 let module = match sema.to_module_def(file_id) {
99 source_file 99 None => return vec![],
100 .syntax() 100 Some(it) => it,
101 .descendants() 101 };
102 .filter_map(|item| { 102
103 let runnable = match_ast! { 103 runnables_mod(&sema, module)
104 match item { 104}
105 ast::Fn(func) => { 105
106 let def = sema.to_def(&func)?; 106fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Runnable> {
107 runnable_fn(&sema, def) 107 let mut res: Vec<Runnable> = module
108 }, 108 .declarations(sema.db)
109 ast::Module(it) => runnable_mod(&sema, it), 109 .into_iter()
110 _ => None, 110 .filter_map(|def| {
111 } 111 let runnable = match def {
112 }; 112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
113 runnable.or_else(|| match doc_owner_to_def(&sema, item)? { 113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
114 Definition::ModuleDef(def) => module_def_doctest(&sema, def),
115 _ => None, 114 _ => None,
116 }) 115 };
116 runnable.or_else(|| module_def_doctest(&sema, def))
117 }) 117 })
118 .collect() 118 .collect();
119
120 res.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
121 |def| match def {
122 hir::AssocItem::Function(it) => {
123 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
124 }
125 hir::AssocItem::Const(it) => module_def_doctest(&sema, it.into()),
126 hir::AssocItem::TypeAlias(it) => module_def_doctest(&sema, it.into()),
127 },
128 ));
129
130 res.extend(module.declarations(sema.db).into_iter().flat_map(|def| match def {
131 hir::ModuleDef::Module(it) => runnables_mod(sema, it),
132 _ => vec![],
133 }));
134
135 res
119} 136}
120 137
121pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 138pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -150,26 +167,16 @@ pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) ->
150 Some(Runnable { nav, kind, cfg }) 167 Some(Runnable { nav, kind, cfg })
151} 168}
152 169
153pub(crate) fn runnable_mod( 170pub(crate) fn runnable_mod(sema: &Semantics<RootDatabase>, def: hir::Module) -> Option<Runnable> {
154 sema: &Semantics<RootDatabase>, 171 if !has_test_function_or_multiple_test_submodules(sema, &def) {
155 module: ast::Module,
156) -> Option<Runnable> {
157 if !has_test_function_or_multiple_test_submodules(&module) {
158 return None; 172 return None;
159 } 173 }
160 let module_def = sema.to_def(&module)?; 174 let path =
161 175 def.path_to_root(sema.db).into_iter().rev().filter_map(|it| it.name(sema.db)).join("::");
162 let path = module_def
163 .path_to_root(sema.db)
164 .into_iter()
165 .rev()
166 .filter_map(|it| it.name(sema.db))
167 .join("::");
168 176
169 let def = sema.to_def(&module)?;
170 let attrs = def.attrs(sema.db); 177 let attrs = def.attrs(sema.db);
171 let cfg = attrs.cfg(); 178 let cfg = attrs.cfg();
172 let nav = module_def.to_nav(sema.db); 179 let nav = def.to_nav(sema.db);
173 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg }) 180 Some(Runnable { nav, kind: RunnableKind::TestMod { path }, cfg })
174} 181}
175 182
@@ -289,30 +296,31 @@ fn has_runnable_doc_test(attrs: &hir::Attrs) -> bool {
289 296
290// We could create runnables for modules with number_of_test_submodules > 0, 297// We could create runnables for modules with number_of_test_submodules > 0,
291// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already 298// but that bloats the runnables for no real benefit, since all tests can be run by the submodule already
292fn has_test_function_or_multiple_test_submodules(module: &ast::Module) -> bool { 299fn has_test_function_or_multiple_test_submodules(
293 if let Some(item_list) = module.item_list() { 300 sema: &Semantics<RootDatabase>,
294 let mut number_of_test_submodules = 0; 301 module: &hir::Module,
295 302) -> bool {
296 for item in item_list.items() { 303 let mut number_of_test_submodules = 0;
297 match item { 304
298 ast::Item::Fn(f) => { 305 for item in module.declarations(sema.db) {
299 if test_related_attribute(&f).is_some() { 306 match item {
307 hir::ModuleDef::Function(f) => {
308 if let Some(it) = f.source(sema.db) {
309 if test_related_attribute(&it.value).is_some() {
300 return true; 310 return true;
301 } 311 }
302 } 312 }
303 ast::Item::Module(submodule) => { 313 }
304 if has_test_function_or_multiple_test_submodules(&submodule) { 314 hir::ModuleDef::Module(submodule) => {
305 number_of_test_submodules += 1; 315 if has_test_function_or_multiple_test_submodules(sema, &submodule) {
306 } 316 number_of_test_submodules += 1;
307 } 317 }
308 _ => (),
309 } 318 }
319 _ => (),
310 } 320 }
311
312 number_of_test_submodules > 1
313 } else {
314 false
315 } 321 }
322
323 number_of_test_submodules > 1
316} 324}
317 325
318#[cfg(test)] 326#[cfg(test)]
@@ -753,6 +761,21 @@ mod root_tests {
753 file_id: FileId( 761 file_id: FileId(
754 0, 762 0,
755 ), 763 ),
764 full_range: 202..286,
765 focus_range: 206..220,
766 name: "nested_tests_2",
767 kind: Module,
768 },
769 kind: TestMod {
770 path: "root_tests::nested_tests_0::nested_tests_2",
771 },
772 cfg: None,
773 },
774 Runnable {
775 nav: NavigationTarget {
776 file_id: FileId(
777 0,
778 ),
756 full_range: 84..126, 779 full_range: 84..126,
757 focus_range: 107..121, 780 focus_range: 107..121,
758 name: "nested_test_11", 781 name: "nested_test_11",
@@ -793,21 +816,6 @@ mod root_tests {
793 file_id: FileId( 816 file_id: FileId(
794 0, 817 0,
795 ), 818 ),
796 full_range: 202..286,
797 focus_range: 206..220,
798 name: "nested_tests_2",
799 kind: Module,
800 },
801 kind: TestMod {
802 path: "root_tests::nested_tests_0::nested_tests_2",
803 },
804 cfg: None,
805 },
806 Runnable {
807 nav: NavigationTarget {
808 file_id: FileId(
809 0,
810 ),
811 full_range: 235..276, 819 full_range: 235..276,
812 focus_range: 258..271, 820 focus_range: 258..271,
813 name: "nested_test_2", 821 name: "nested_test_2",
@@ -982,4 +990,64 @@ impl Foo {
982 "#]], 990 "#]],
983 ); 991 );
984 } 992 }
993
994 #[test]
995 fn test_runnables_in_macro() {
996 check(
997 r#"
998//- /lib.rs
999$0
1000macro_rules! gen {
1001 () => {
1002 #[test]
1003 fn foo_test() {
1004 }
1005 }
1006}
1007mod tests {
1008 gen!();
1009}
1010"#,
1011 &[&TEST, &TEST],
1012 expect![[r#"
1013 [
1014 Runnable {
1015 nav: NavigationTarget {
1016 file_id: FileId(
1017 0,
1018 ),
1019 full_range: 90..115,
1020 focus_range: 94..99,
1021 name: "tests",
1022 kind: Module,
1023 },
1024 kind: TestMod {
1025 path: "tests",
1026 },
1027 cfg: None,
1028 },
1029 Runnable {
1030 nav: NavigationTarget {
1031 file_id: FileId(
1032 0,
1033 ),
1034 full_range: 106..113,
1035 focus_range: 106..113,
1036 name: "foo_test",
1037 kind: Function,
1038 },
1039 kind: Test {
1040 test_id: Path(
1041 "tests::foo_test",
1042 ),
1043 attr: TestAttr {
1044 ignore: false,
1045 },
1046 },
1047 cfg: None,
1048 },
1049 ]
1050 "#]],
1051 );
1052 }
985} 1053}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 2a41d8167..51002e7b8 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -16,16 +16,18 @@ use crate::ExpandError;
16#[derive(Debug, PartialEq, Eq, Clone, Copy)] 16#[derive(Debug, PartialEq, Eq, Clone, Copy)]
17pub enum TokenTextRange { 17pub enum TokenTextRange {
18 Token(TextRange), 18 Token(TextRange),
19 Delimiter(TextRange, TextRange), 19 Delimiter(TextRange),
20} 20}
21 21
22impl TokenTextRange { 22impl TokenTextRange {
23 pub fn by_kind(self, kind: SyntaxKind) -> Option<TextRange> { 23 pub fn by_kind(self, kind: SyntaxKind) -> Option<TextRange> {
24 match self { 24 match self {
25 TokenTextRange::Token(it) => Some(it), 25 TokenTextRange::Token(it) => Some(it),
26 TokenTextRange::Delimiter(open, close) => match kind { 26 TokenTextRange::Delimiter(it) => match kind {
27 T!['{'] | T!['('] | T!['['] => Some(open), 27 T!['{'] | T!['('] | T!['['] => Some(TextRange::at(it.start(), 1.into())),
28 T!['}'] | T![')'] | T![']'] => Some(close), 28 T!['}'] | T![')'] | T![']'] => {
29 Some(TextRange::at(it.end() - TextSize::of('}'), 1.into()))
30 }
29 _ => None, 31 _ => None,
30 }, 32 },
31 } 33 }
@@ -114,8 +116,10 @@ impl TokenMap {
114 pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> { 116 pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> {
115 let &(token_id, _) = self.entries.iter().find(|(_, range)| match range { 117 let &(token_id, _) = self.entries.iter().find(|(_, range)| match range {
116 TokenTextRange::Token(it) => *it == relative_range, 118 TokenTextRange::Token(it) => *it == relative_range,
117 TokenTextRange::Delimiter(open, close) => { 119 TokenTextRange::Delimiter(it) => {
118 *open == relative_range || *close == relative_range 120 let open = TextRange::at(it.start(), 1.into());
121 let close = TextRange::at(it.end() - TextSize::of('}'), 1.into());
122 open == relative_range || close == relative_range
119 } 123 }
120 })?; 124 })?;
121 Some(token_id) 125 Some(token_id)
@@ -137,15 +141,17 @@ impl TokenMap {
137 close_relative_range: TextRange, 141 close_relative_range: TextRange,
138 ) -> usize { 142 ) -> usize {
139 let res = self.entries.len(); 143 let res = self.entries.len();
140 self.entries 144 let cover = open_relative_range.cover(close_relative_range);
141 .push((token_id, TokenTextRange::Delimiter(open_relative_range, close_relative_range))); 145
146 self.entries.push((token_id, TokenTextRange::Delimiter(cover)));
142 res 147 res
143 } 148 }
144 149
145 fn update_close_delim(&mut self, idx: usize, close_relative_range: TextRange) { 150 fn update_close_delim(&mut self, idx: usize, close_relative_range: TextRange) {
146 let (_, token_text_range) = &mut self.entries[idx]; 151 let (_, token_text_range) = &mut self.entries[idx];
147 if let TokenTextRange::Delimiter(dim, _) = token_text_range { 152 if let TokenTextRange::Delimiter(dim) = token_text_range {
148 *token_text_range = TokenTextRange::Delimiter(*dim, close_relative_range); 153 let cover = dim.cover(close_relative_range);
154 *token_text_range = TokenTextRange::Delimiter(cover);
149 } 155 }
150 } 156 }
151 157
diff --git a/crates/parser/src/event.rs b/crates/parser/src/event.rs
index a7d06a815..903668892 100644
--- a/crates/parser/src/event.rs
+++ b/crates/parser/src/event.rs
@@ -38,14 +38,16 @@ pub(crate) enum Event {
38 /// 38 ///
39 /// The events for it would look like this: 39 /// The events for it would look like this:
40 /// 40 ///
41 /// 41 /// ```text
42 /// START(PATH) IDENT('foo') FINISH START(PATH) T![::] IDENT('bar') FINISH 42 /// START(PATH) IDENT('foo') FINISH START(PATH) T![::] IDENT('bar') FINISH
43 /// | /\ 43 /// | /\
44 /// | | 44 /// | |
45 /// +------forward-parent------+ 45 /// +------forward-parent------+
46 /// ```
46 /// 47 ///
47 /// And the tree would look like this 48 /// And the tree would look like this
48 /// 49 ///
50 /// ```text
49 /// +--PATH---------+ 51 /// +--PATH---------+
50 /// | | | 52 /// | | |
51 /// | | | 53 /// | | |
@@ -54,6 +56,7 @@ pub(crate) enum Event {
54 /// PATH 56 /// PATH
55 /// | 57 /// |
56 /// 'foo' 58 /// 'foo'
59 /// ```
57 /// 60 ///
58 /// See also `CompletedMarker::precede`. 61 /// See also `CompletedMarker::precede`.
59 Start { 62 Start {
diff --git a/crates/stdx/src/lib.rs b/crates/stdx/src/lib.rs
index 1ff2559bb..73b121f8a 100644
--- a/crates/stdx/src/lib.rs
+++ b/crates/stdx/src/lib.rs
@@ -134,8 +134,10 @@ impl<'a> Iterator for LinesWithEnds<'a> {
134 134
135/// Returns `idx` such that: 135/// Returns `idx` such that:
136/// 136///
137/// ```text
137/// ∀ x in slice[..idx]: pred(x) 138/// ∀ x in slice[..idx]: pred(x)
138/// && ∀ x in slice[idx..]: !pred(x) 139/// && ∀ x in slice[idx..]: !pred(x)
140/// ```
139/// 141///
140/// https://github.com/rust-lang/rust/issues/73831 142/// https://github.com/rust-lang/rust/issues/73831
141pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize 143pub fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize