diff options
-rw-r--r-- | crates/ide/src/runnables.rs | 67 | ||||
-rw-r--r-- | docs/dev/style.md | 37 |
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 | }; |
12 | use test_utils::mark; | ||
12 | 13 | ||
13 | use crate::{ | 14 | use crate::{ |
14 | display::{ToNav, TryToNav}, | 15 | display::{ToNav, TryToNav}, |
@@ -96,28 +97,26 @@ impl Runnable { | |||
96 | pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> { | 97 | pub(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 | ||
106 | fn runnables_mod(sema: &Semantics<RootDatabase>, module: hir::Module) -> Vec<Runnable> { | 109 | fn 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 | ||
138 | pub(crate) fn runnable_fn(sema: &Semantics<RootDatabase>, def: hir::Function) -> Option<Runnable> { | 139 | pub(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)] |
327 | mod tests { | 328 | mod 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 | ||
1063 | mod m; | ||
1064 | //- /m.rs | ||
1065 | mod 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 | ||
283 | Use `Vec::new` rather than `vec![]`. **Rationale:** uniformity, strength | ||
284 | reduction. | ||
285 | |||
283 | ## Functions Over Objects | 286 | ## Functions Over Objects |
284 | 287 | ||
285 | Avoid creating "doer" objects. | 288 | Avoid creating "doer" objects. |
@@ -418,12 +421,44 @@ fn frobnicate(s: &str) { | |||
418 | **Rationale:** reveals the costs. | 421 | **Rationale:** reveals the costs. |
419 | It is also more efficient when the caller already owns the allocation. | 422 | It is also more efficient when the caller already owns the allocation. |
420 | 423 | ||
421 | ## Collection types | 424 | ## Collection Types |
422 | 425 | ||
423 | Prefer `rustc_hash::FxHashMap` and `rustc_hash::FxHashSet` instead of the ones in `std::collections`. | 426 | Prefer `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 | |||
432 | When writing a recursive function to compute a sets of things, use an accumulator parameter instead of returning a fresh collection. | ||
433 | Accumulator goes first in the list of arguments. | ||
434 | |||
435 | ```rust | ||
436 | // GOOD | ||
437 | pub fn reachable_nodes(node: Node) -> FxHashSet<Node> { | ||
438 | let mut res = FxHashSet::default(); | ||
439 | go(&mut res, node); | ||
440 | res | ||
441 | } | ||
442 | fn 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 | ||
450 | pub 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 |