aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ide/src/runnables.rs67
-rw-r--r--docs/dev/style.md37
2 files changed, 81 insertions, 23 deletions
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 8976f1080..47a85dc45 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -9,6 +9,7 @@ use syntax::{
9 ast::{self, AstNode, AttrsOwner}, 9 ast::{self, AstNode, AttrsOwner},
10 match_ast, SyntaxNode, 10 match_ast, SyntaxNode,
11}; 11};
12use test_utils::mark;
12 13
13use crate::{ 14use crate::{
14 display::{ToNav, TryToNav}, 15 display::{ToNav, TryToNav},
@@ -96,28 +97,26 @@ impl Runnable {
96pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { 97pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
97 let sema = Semantics::new(db); 98 let sema = Semantics::new(db);
98 let module = match sema.to_module_def(file_id) { 99 let module = match sema.to_module_def(file_id) {
99 None => return vec![], 100 None => return Vec::new(),
100 Some(it) => it, 101 Some(it) => it,
101 }; 102 };
102 103
103 runnables_mod(&sema, module) 104 let mut res = Vec::new();
105 runnables_mod(&sema, &mut res, module);
106 res
104} 107}
105 108
106fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Runnable> { 109fn runnables_mod(sema: &Semantics<RootDatabase>, acc: &mut Vec<Runnable>, module: hir::Module) {
107 let mut res: Vec<Runnable> = module 110 acc.extend(module.declarations(sema.db).into_iter().filter_map(|def| {
108 .declarations(sema.db) 111 let runnable = match def {
109 .into_iter() 112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it),
110 .filter_map(|def| { 113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it),
111 let runnable = match def { 114 _ => None,
112 hir::ModuleDef::Module(it) => runnable_mod(&sema, it), 115 };
113 hir::ModuleDef::Function(it) => runnable_fn(&sema, it), 116 runnable.or_else(|| module_def_doctest(&sema, def))
114 _ => None, 117 }));
115 };
116 runnable.or_else(|| module_def_doctest(&sema, def))
117 })
118 .collect();
119 118
120 res.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map( 119 acc.extend(module.impl_defs(sema.db).into_iter().flat_map(|it| it.items(sema.db)).filter_map(
121 |def| match def { 120 |def| match def {
122 hir::AssocItem::Function(it) => { 121 hir::AssocItem::Function(it) => {
123 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into())) 122 runnable_fn(&sema, it).or_else(|| module_def_doctest(&sema, it.into()))
@@ -127,12 +126,14 @@ fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Run
127 }, 126 },
128 )); 127 ));
129 128
130 res.extend(module.declarations(sema.db).into_iter().flat_map(|def| match def { 129 for def in module.declarations(sema.db) {
131 hir::ModuleDef::Module(it) => runnables_mod(sema, it), 130 if let hir::ModuleDef::Module(submodule) = def {
132 _ => vec![], 131 match submodule.definition_source(sema.db).value {
133 })); 132 hir::ModuleSource::Module(_) => runnables_mod(sema, acc, submodule),
134 133 hir::ModuleSource::SourceFile(_) => mark::hit!(dont_recurse_in_outline_submodules),
135 res 134 }
135 }
136 }
136} 137}
137 138
138pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { 139pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> {
@@ -326,6 +327,7 @@ fn has_test_function_or_multiple_test_submodules(
326#[cfg(test)] 327#[cfg(test)]
327mod tests { 328mod tests {
328 use expect_test::{expect, Expect}; 329 use expect_test::{expect, Expect};
330 use test_utils::mark;
329 331
330 use crate::fixture; 332 use crate::fixture;
331 333
@@ -1050,4 +1052,25 @@ mod tests {
1050 "#]], 1052 "#]],
1051 ); 1053 );
1052 } 1054 }
1055
1056 #[test]
1057 fn dont_recurse_in_outline_submodules() {
1058 mark::check!(dont_recurse_in_outline_submodules);
1059 check(
1060 r#"
1061//- /lib.rs
1062$0
1063mod m;
1064//- /m.rs
1065mod tests {
1066 #[test]
1067 fn t() {}
1068}
1069"#,
1070 &[],
1071 expect![[r#"
1072 []
1073 "#]],
1074 );
1075 }
1053} 1076}
diff --git a/docs/dev/style.md b/docs/dev/style.md
index 21330948b..389649398 100644
--- a/docs/dev/style.md
+++ b/docs/dev/style.md
@@ -280,6 +280,9 @@ Prefer `Default` even it has to be implemented manually.
280 280
281**Rationale:** less typing in the common case, uniformity. 281**Rationale:** less typing in the common case, uniformity.
282 282
283Use `Vec::new` rather than `vec![]`. **Rationale:** uniformity, strength
284reduction.
285
283## Functions Over Objects 286## Functions Over Objects
284 287
285Avoid creating "doer" objects. 288Avoid creating "doer" objects.
@@ -418,12 +421,44 @@ fn frobnicate(s: &str) {
418**Rationale:** reveals the costs. 421**Rationale:** reveals the costs.
419It is also more efficient when the caller already owns the allocation. 422It is also more efficient when the caller already owns the allocation.
420 423
421## Collection types 424## Collection Types
422 425
423Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`. 426Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`.
424 427
425**Rationale:** they use a hasher that's significantly faster and using them consistently will reduce code size by some small amount. 428**Rationale:** they use a hasher that's significantly faster and using them consistently will reduce code size by some small amount.
426 429
430## Avoid Intermediate Collections
431
432When writing a recursive function to compute a sets of things, use an accumulator parameter instead of returning a fresh collection.
433Accumulator goes first in the list of arguments.
434
435```rust
436// GOOD
437pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
438 let mut res = FxHashSet::default();
439 go(&mut res, node);
440 res
441}
442fn go(acc: &mut FxHashSet<Node>, node: Node) {
443 acc.insert(node);
444 for n in node.neighbors() {
445 go(acc, n);
446 }
447}
448
449// BAD
450pub fn reachable_nodes(node: Node) -> FxHashSet<Node> {
451 let mut res = FxHashSet::default();
452 res.insert(node);
453 for n in node.neighbors() {
454 res.extend(reachable_nodes(n));
455 }
456 res
457}
458```
459
460**Rational:** re-use allocations, accumulator style is more concise for complex cases.
461
427# Style 462# Style
428 463
429## Order of Imports 464## Order of Imports