diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-10 11:42:42 +0000 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-02-10 11:42:42 +0000 |
commit | 8e4be2708635818aa3e210f0e39fb871cc433004 (patch) | |
tree | 569110cbb504c0516b136c414610b0f2edbe5044 /crates | |
parent | 01b15c9fc2ce128149872ffe02de022bdb157286 (diff) | |
parent | 2e9194a621ccb33872d6189ecc30a83c17e6e33a (diff) |
Merge #774
774: Batch crate & command r=matklad a=flodiebold
This adds a new crate, `ra_batch`, which is intended for scenarios where you're loading a workspace once and then running some analyses using the HIR API. Also, it adds a command to `ra_cli` which uses that to type-check all crates in a workspace and print some statistics:
E.g. in rust-analyzer:
```
> $ time target/release/ra_cli analysis-stats
Database loaded, 21 roots
Crates in this dir: 28
Total modules found: 231
Total declarations: 3694
Total functions: 2408
Total expressions: 47017
Expressions of unknown type: 19826 (42%)
Expressions of partially unknown type: 4482 (9%)
target/release/ra_cli analysis-stats 3,23s user 0,60s system 100% cpu 3,821 total
```
Or in rust-lang/rust:
```
> $ time ../opensource/rust-analyzer/target/release/ra_cli analysis-stats
Database loaded, 77 roots
Crates in this dir: 130
Total modules found: 1820
Total declarations: 35038
Total functions: 25914
Total expressions: 753678
Expressions of unknown type: 337975 (44%)
Expressions of partially unknown type: 92314 (12%)
../opensource/rust-analyzer/target/release/ra_cli analysis-stats 13,45s user 2,08s system 100% cpu 15,477 total
```
~This still needs a test. Type-checking all of rust-analyzer sadly takes almost a minute when compiled in debug mode :sweat_smile: So I'll need to add something simpler (maybe just looking at a few modules).~
Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_batch/Cargo.toml | 20 | ||||
-rw-r--r-- | crates/ra_batch/src/lib.rs | 151 | ||||
-rw-r--r-- | crates/ra_cli/Cargo.toml | 6 | ||||
-rw-r--r-- | crates/ra_cli/src/analysis_stats.rs | 100 | ||||
-rw-r--r-- | crates/ra_cli/src/main.rs | 11 | ||||
-rw-r--r-- | crates/ra_db/src/input.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir/src/code_model_api.rs | 34 | ||||
-rw-r--r-- | crates/ra_hir/src/expr.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir/src/ty.rs | 53 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/server_world.rs | 4 | ||||
-rw-r--r-- | crates/ra_vfs/src/lib.rs | 5 |
11 files changed, 387 insertions, 6 deletions
diff --git a/crates/ra_batch/Cargo.toml b/crates/ra_batch/Cargo.toml new file mode 100644 index 000000000..30a0749c7 --- /dev/null +++ b/crates/ra_batch/Cargo.toml | |||
@@ -0,0 +1,20 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_batch" | ||
4 | version = "0.1.0" | ||
5 | authors = ["Aleksey Kladov <[email protected]>"] | ||
6 | |||
7 | [dependencies] | ||
8 | log = "0.4.5" | ||
9 | rustc-hash = "1.0" | ||
10 | |||
11 | failure = "0.1.4" | ||
12 | |||
13 | ra_syntax = { path = "../ra_syntax" } | ||
14 | ra_db = { path = "../ra_db" } | ||
15 | ra_hir = { path = "../ra_hir" } | ||
16 | ra_vfs = { path = "../ra_vfs" } | ||
17 | ra_project_model = { path = "../ra_project_model" } | ||
18 | |||
19 | [dev-dependencies] | ||
20 | test_utils = { path = "../test_utils" } | ||
diff --git a/crates/ra_batch/src/lib.rs b/crates/ra_batch/src/lib.rs new file mode 100644 index 000000000..837fff4dc --- /dev/null +++ b/crates/ra_batch/src/lib.rs | |||
@@ -0,0 +1,151 @@ | |||
1 | use std::sync::Arc; | ||
2 | use std::path::Path; | ||
3 | use std::collections::HashSet; | ||
4 | |||
5 | use rustc_hash::FxHashMap; | ||
6 | |||
7 | use ra_db::{ | ||
8 | CrateGraph, FileId, SourceRoot, SourceRootId, SourceDatabase, salsa, | ||
9 | }; | ||
10 | use ra_hir::{db, HirInterner}; | ||
11 | use ra_project_model::ProjectWorkspace; | ||
12 | use ra_vfs::{Vfs, VfsChange}; | ||
13 | |||
14 | type Result<T> = std::result::Result<T, failure::Error>; | ||
15 | |||
16 | #[salsa::database( | ||
17 | ra_db::SourceDatabaseStorage, | ||
18 | db::HirDatabaseStorage, | ||
19 | db::PersistentHirDatabaseStorage | ||
20 | )] | ||
21 | #[derive(Debug)] | ||
22 | pub struct BatchDatabase { | ||
23 | runtime: salsa::Runtime<BatchDatabase>, | ||
24 | interner: Arc<HirInterner>, | ||
25 | } | ||
26 | |||
27 | impl salsa::Database for BatchDatabase { | ||
28 | fn salsa_runtime(&self) -> &salsa::Runtime<BatchDatabase> { | ||
29 | &self.runtime | ||
30 | } | ||
31 | } | ||
32 | |||
33 | impl AsRef<HirInterner> for BatchDatabase { | ||
34 | fn as_ref(&self) -> &HirInterner { | ||
35 | &self.interner | ||
36 | } | ||
37 | } | ||
38 | |||
39 | fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId { | ||
40 | FileId(f.0.into()) | ||
41 | } | ||
42 | fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId { | ||
43 | SourceRootId(r.0.into()) | ||
44 | } | ||
45 | |||
46 | impl BatchDatabase { | ||
47 | pub fn load(crate_graph: CrateGraph, vfs: &mut Vfs) -> BatchDatabase { | ||
48 | let mut db = | ||
49 | BatchDatabase { runtime: salsa::Runtime::default(), interner: Default::default() }; | ||
50 | db.set_crate_graph(Arc::new(crate_graph)); | ||
51 | |||
52 | // wait until Vfs has loaded all roots | ||
53 | let receiver = vfs.task_receiver().clone(); | ||
54 | let mut roots_loaded = HashSet::new(); | ||
55 | for task in receiver { | ||
56 | vfs.handle_task(task); | ||
57 | let mut done = false; | ||
58 | for change in vfs.commit_changes() { | ||
59 | match change { | ||
60 | VfsChange::AddRoot { root, files } => { | ||
61 | let source_root_id = vfs_root_to_id(root); | ||
62 | log::debug!( | ||
63 | "loaded source root {:?} with path {:?}", | ||
64 | source_root_id, | ||
65 | vfs.root2path(root) | ||
66 | ); | ||
67 | let mut file_map = FxHashMap::default(); | ||
68 | for (vfs_file, path, text) in files { | ||
69 | let file_id = vfs_file_to_id(vfs_file); | ||
70 | db.set_file_text(file_id, text); | ||
71 | db.set_file_relative_path(file_id, path.clone()); | ||
72 | db.set_file_source_root(file_id, source_root_id); | ||
73 | file_map.insert(path, file_id); | ||
74 | } | ||
75 | let source_root = SourceRoot { files: file_map }; | ||
76 | db.set_source_root(source_root_id, Arc::new(source_root)); | ||
77 | roots_loaded.insert(source_root_id); | ||
78 | if roots_loaded.len() == vfs.num_roots() { | ||
79 | done = true; | ||
80 | } | ||
81 | } | ||
82 | VfsChange::AddFile { .. } | ||
83 | | VfsChange::RemoveFile { .. } | ||
84 | | VfsChange::ChangeFile { .. } => { | ||
85 | // We just need the first scan, so just ignore these | ||
86 | } | ||
87 | } | ||
88 | } | ||
89 | if done { | ||
90 | break; | ||
91 | } | ||
92 | } | ||
93 | |||
94 | db | ||
95 | } | ||
96 | |||
97 | pub fn load_cargo(root: impl AsRef<Path>) -> Result<(BatchDatabase, Vec<SourceRootId>)> { | ||
98 | let root = root.as_ref().canonicalize()?; | ||
99 | let ws = ProjectWorkspace::discover(root.as_ref())?; | ||
100 | let mut roots = Vec::new(); | ||
101 | roots.push(root.clone()); | ||
102 | for pkg in ws.cargo.packages() { | ||
103 | roots.push(pkg.root(&ws.cargo).to_path_buf()); | ||
104 | } | ||
105 | for krate in ws.sysroot.crates() { | ||
106 | roots.push(krate.root_dir(&ws.sysroot).to_path_buf()) | ||
107 | } | ||
108 | let (mut vfs, roots) = Vfs::new(roots); | ||
109 | let mut load = |path: &Path| { | ||
110 | let vfs_file = vfs.load(path); | ||
111 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | ||
112 | vfs_file.map(vfs_file_to_id) | ||
113 | }; | ||
114 | let crate_graph = ws.to_crate_graph(&mut load); | ||
115 | log::debug!("crate graph: {:?}", crate_graph); | ||
116 | |||
117 | let local_roots = roots | ||
118 | .into_iter() | ||
119 | .filter(|r| vfs.root2path(*r).starts_with(&root)) | ||
120 | .map(vfs_root_to_id) | ||
121 | .collect(); | ||
122 | |||
123 | let db = BatchDatabase::load(crate_graph, &mut vfs); | ||
124 | let _ = vfs.shutdown(); | ||
125 | Ok((db, local_roots)) | ||
126 | } | ||
127 | } | ||
128 | |||
129 | #[cfg(test)] | ||
130 | mod tests { | ||
131 | use ra_hir::Crate; | ||
132 | use super::*; | ||
133 | |||
134 | #[test] | ||
135 | fn test_loading_rust_analyzer() { | ||
136 | let mut path = std::env::current_exe().unwrap(); | ||
137 | while !path.join("Cargo.toml").is_file() { | ||
138 | path = path.parent().unwrap().to_owned(); | ||
139 | } | ||
140 | let (db, roots) = BatchDatabase::load_cargo(path).unwrap(); | ||
141 | let mut num_crates = 0; | ||
142 | for root in roots { | ||
143 | for _krate in Crate::source_root_crates(&db, root) { | ||
144 | num_crates += 1; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | // RA has quite a few crates, but the exact count doesn't matter | ||
149 | assert!(num_crates > 20); | ||
150 | } | ||
151 | } | ||
diff --git a/crates/ra_cli/Cargo.toml b/crates/ra_cli/Cargo.toml index eb1722d5e..641ac5cbd 100644 --- a/crates/ra_cli/Cargo.toml +++ b/crates/ra_cli/Cargo.toml | |||
@@ -9,6 +9,12 @@ publish = false | |||
9 | clap = "2.32.0" | 9 | clap = "2.32.0" |
10 | failure = "0.1.4" | 10 | failure = "0.1.4" |
11 | join_to_string = "0.1.1" | 11 | join_to_string = "0.1.1" |
12 | flexi_logger = "0.10.0" | ||
13 | indicatif = "0.11.0" | ||
14 | |||
12 | ra_syntax = { path = "../ra_syntax" } | 15 | ra_syntax = { path = "../ra_syntax" } |
13 | ra_ide_api_light = { path = "../ra_ide_api_light" } | 16 | ra_ide_api_light = { path = "../ra_ide_api_light" } |
14 | tools = { path = "../tools" } | 17 | tools = { path = "../tools" } |
18 | ra_batch = { path = "../ra_batch" } | ||
19 | ra_hir = { path = "../ra_hir" } | ||
20 | ra_db = { path = "../ra_db" } | ||
diff --git a/crates/ra_cli/src/analysis_stats.rs b/crates/ra_cli/src/analysis_stats.rs new file mode 100644 index 000000000..a46ac974d --- /dev/null +++ b/crates/ra_cli/src/analysis_stats.rs | |||
@@ -0,0 +1,100 @@ | |||
1 | use std::collections::HashSet; | ||
2 | |||
3 | use ra_db::SourceDatabase; | ||
4 | use ra_batch::BatchDatabase; | ||
5 | use ra_hir::{Crate, ModuleDef, Ty, ImplItem}; | ||
6 | use ra_syntax::AstNode; | ||
7 | |||
8 | use crate::Result; | ||
9 | |||
10 | pub fn run(verbose: bool) -> Result<()> { | ||
11 | let (db, roots) = BatchDatabase::load_cargo(".")?; | ||
12 | println!("Database loaded, {} roots", roots.len()); | ||
13 | let mut num_crates = 0; | ||
14 | let mut visited_modules = HashSet::new(); | ||
15 | let mut visit_queue = Vec::new(); | ||
16 | for root in roots { | ||
17 | for krate in Crate::source_root_crates(&db, root) { | ||
18 | num_crates += 1; | ||
19 | let module = krate.root_module(&db).expect("crate in source root without root module"); | ||
20 | visit_queue.push(module); | ||
21 | } | ||
22 | } | ||
23 | println!("Crates in this dir: {}", num_crates); | ||
24 | let mut num_decls = 0; | ||
25 | let mut funcs = Vec::new(); | ||
26 | while let Some(module) = visit_queue.pop() { | ||
27 | if visited_modules.insert(module) { | ||
28 | visit_queue.extend(module.children(&db)); | ||
29 | |||
30 | for decl in module.declarations(&db) { | ||
31 | num_decls += 1; | ||
32 | match decl { | ||
33 | ModuleDef::Function(f) => funcs.push(f), | ||
34 | _ => {} | ||
35 | } | ||
36 | } | ||
37 | |||
38 | for impl_block in module.impl_blocks(&db) { | ||
39 | for item in impl_block.items() { | ||
40 | num_decls += 1; | ||
41 | match item { | ||
42 | ImplItem::Method(f) => funcs.push(*f), | ||
43 | _ => {} | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | println!("Total modules found: {}", visited_modules.len()); | ||
50 | println!("Total declarations: {}", num_decls); | ||
51 | println!("Total functions: {}", funcs.len()); | ||
52 | let bar = indicatif::ProgressBar::new(funcs.len() as u64); | ||
53 | bar.tick(); | ||
54 | let mut num_exprs = 0; | ||
55 | let mut num_exprs_unknown = 0; | ||
56 | let mut num_exprs_partially_unknown = 0; | ||
57 | for f in funcs { | ||
58 | if verbose { | ||
59 | let (file_id, source) = f.source(&db); | ||
60 | let original_file = file_id.original_file(&db); | ||
61 | let path = db.file_relative_path(original_file); | ||
62 | let syntax_range = source.syntax().range(); | ||
63 | let name = f.name(&db); | ||
64 | println!("{} ({:?} {})", name, path, syntax_range); | ||
65 | } | ||
66 | let body = f.body(&db); | ||
67 | let inference_result = f.infer(&db); | ||
68 | for (expr_id, _) in body.exprs() { | ||
69 | let ty = &inference_result[expr_id]; | ||
70 | num_exprs += 1; | ||
71 | if let Ty::Unknown = ty { | ||
72 | num_exprs_unknown += 1; | ||
73 | } else { | ||
74 | let mut is_partially_unknown = false; | ||
75 | ty.walk(&mut |ty| { | ||
76 | if let Ty::Unknown = ty { | ||
77 | is_partially_unknown = true; | ||
78 | } | ||
79 | }); | ||
80 | if is_partially_unknown { | ||
81 | num_exprs_partially_unknown += 1; | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | bar.inc(1); | ||
86 | } | ||
87 | bar.finish_and_clear(); | ||
88 | println!("Total expressions: {}", num_exprs); | ||
89 | println!( | ||
90 | "Expressions of unknown type: {} ({}%)", | ||
91 | num_exprs_unknown, | ||
92 | (num_exprs_unknown * 100 / num_exprs) | ||
93 | ); | ||
94 | println!( | ||
95 | "Expressions of partially unknown type: {} ({}%)", | ||
96 | num_exprs_partially_unknown, | ||
97 | (num_exprs_partially_unknown * 100 / num_exprs) | ||
98 | ); | ||
99 | Ok(()) | ||
100 | } | ||
diff --git a/crates/ra_cli/src/main.rs b/crates/ra_cli/src/main.rs index a4debeb48..72e6ae4d5 100644 --- a/crates/ra_cli/src/main.rs +++ b/crates/ra_cli/src/main.rs | |||
@@ -1,3 +1,5 @@ | |||
1 | mod analysis_stats; | ||
2 | |||
1 | use std::{fs, io::Read, path::Path, time::Instant}; | 3 | use std::{fs, io::Read, path::Path, time::Instant}; |
2 | 4 | ||
3 | use clap::{App, Arg, SubCommand}; | 5 | use clap::{App, Arg, SubCommand}; |
@@ -5,10 +7,12 @@ use join_to_string::join; | |||
5 | use ra_ide_api_light::{extend_selection, file_structure, syntax_tree}; | 7 | use ra_ide_api_light::{extend_selection, file_structure, syntax_tree}; |
6 | use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode}; | 8 | use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode}; |
7 | use tools::collect_tests; | 9 | use tools::collect_tests; |
10 | use flexi_logger::Logger; | ||
8 | 11 | ||
9 | type Result<T> = ::std::result::Result<T, failure::Error>; | 12 | type Result<T> = ::std::result::Result<T, failure::Error>; |
10 | 13 | ||
11 | fn main() -> Result<()> { | 14 | fn main() -> Result<()> { |
15 | Logger::with_env().start()?; | ||
12 | let matches = App::new("ra-cli") | 16 | let matches = App::new("ra-cli") |
13 | .setting(clap::AppSettings::SubcommandRequiredElseHelp) | 17 | .setting(clap::AppSettings::SubcommandRequiredElseHelp) |
14 | .subcommand( | 18 | .subcommand( |
@@ -23,6 +27,9 @@ fn main() -> Result<()> { | |||
23 | .arg(Arg::with_name("start")) | 27 | .arg(Arg::with_name("start")) |
24 | .arg(Arg::with_name("end")), | 28 | .arg(Arg::with_name("end")), |
25 | ) | 29 | ) |
30 | .subcommand( | ||
31 | SubCommand::with_name("analysis-stats").arg(Arg::with_name("verbose").short("v")), | ||
32 | ) | ||
26 | .get_matches(); | 33 | .get_matches(); |
27 | match matches.subcommand() { | 34 | match matches.subcommand() { |
28 | ("parse", Some(matches)) => { | 35 | ("parse", Some(matches)) => { |
@@ -56,6 +63,10 @@ fn main() -> Result<()> { | |||
56 | let sels = selections(&file, start, end); | 63 | let sels = selections(&file, start, end); |
57 | println!("{}", sels) | 64 | println!("{}", sels) |
58 | } | 65 | } |
66 | ("analysis-stats", Some(matches)) => { | ||
67 | let verbose = matches.is_present("verbose"); | ||
68 | analysis_stats::run(verbose)?; | ||
69 | } | ||
59 | _ => unreachable!(), | 70 | _ => unreachable!(), |
60 | } | 71 | } |
61 | Ok(()) | 72 | Ok(()) |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index 405634fe0..8decc65c5 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -112,6 +112,7 @@ impl CrateGraph { | |||
112 | self.arena[&crate_id].file_id | 112 | self.arena[&crate_id].file_id |
113 | } | 113 | } |
114 | 114 | ||
115 | // TODO: this only finds one crate with the given root; we could have multiple | ||
115 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { | 116 | pub fn crate_id_for_crate_root(&self, file_id: FileId) -> Option<CrateId> { |
116 | let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?; | 117 | let (&crate_id, _) = self.arena.iter().find(|(_crate_id, data)| data.file_id == file_id)?; |
117 | Some(crate_id) | 118 | Some(crate_id) |
diff --git a/crates/ra_hir/src/code_model_api.rs b/crates/ra_hir/src/code_model_api.rs index cafc5279d..19f103855 100644 --- a/crates/ra_hir/src/code_model_api.rs +++ b/crates/ra_hir/src/code_model_api.rs | |||
@@ -1,7 +1,7 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use relative_path::RelativePathBuf; | 3 | use relative_path::RelativePathBuf; |
4 | use ra_db::{CrateId, FileId}; | 4 | use ra_db::{CrateId, FileId, SourceRootId}; |
5 | use ra_syntax::{ast::self, TreeArc, SyntaxNode}; | 5 | use ra_syntax::{ast::self, TreeArc, SyntaxNode}; |
6 | 6 | ||
7 | use crate::{ | 7 | use crate::{ |
@@ -16,7 +16,7 @@ use crate::{ | |||
16 | docs::{Documentation, Docs, docs_from_ast}, | 16 | docs::{Documentation, Docs, docs_from_ast}, |
17 | module_tree::ModuleId, | 17 | module_tree::ModuleId, |
18 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, | 18 | ids::{FunctionId, StructId, EnumId, AstItemDef, ConstId, StaticId, TraitId, TypeId}, |
19 | impl_block::ImplId, | 19 | impl_block::{ImplId, ImplBlock}, |
20 | resolve::Resolver, | 20 | resolve::Resolver, |
21 | }; | 21 | }; |
22 | 22 | ||
@@ -44,6 +44,15 @@ impl Crate { | |||
44 | pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> { | 44 | pub fn root_module(&self, db: &impl PersistentHirDatabase) -> Option<Module> { |
45 | self.root_module_impl(db) | 45 | self.root_module_impl(db) |
46 | } | 46 | } |
47 | |||
48 | // TODO: should this be in source_binder? | ||
49 | pub fn source_root_crates( | ||
50 | db: &impl PersistentHirDatabase, | ||
51 | source_root: SourceRootId, | ||
52 | ) -> Vec<Crate> { | ||
53 | let crate_ids = db.source_root_crates(source_root); | ||
54 | crate_ids.iter().map(|&crate_id| Crate { crate_id }).collect() | ||
55 | } | ||
47 | } | 56 | } |
48 | 57 | ||
49 | #[derive(Debug)] | 58 | #[derive(Debug)] |
@@ -168,6 +177,27 @@ impl Module { | |||
168 | let item_map = db.item_map(self.krate); | 177 | let item_map = db.item_map(self.krate); |
169 | Resolver::default().push_module_scope(item_map, *self) | 178 | Resolver::default().push_module_scope(item_map, *self) |
170 | } | 179 | } |
180 | |||
181 | pub fn declarations(self, db: &impl HirDatabase) -> Vec<ModuleDef> { | ||
182 | let (lowered_module, _) = db.lower_module(self); | ||
183 | lowered_module | ||
184 | .declarations | ||
185 | .values() | ||
186 | .cloned() | ||
187 | .flat_map(|per_ns| { | ||
188 | per_ns.take_types().into_iter().chain(per_ns.take_values().into_iter()) | ||
189 | }) | ||
190 | .collect() | ||
191 | } | ||
192 | |||
193 | pub fn impl_blocks(self, db: &impl HirDatabase) -> Vec<ImplBlock> { | ||
194 | let module_impl_blocks = db.impls_in_module(self); | ||
195 | module_impl_blocks | ||
196 | .impls | ||
197 | .iter() | ||
198 | .map(|(impl_id, _)| ImplBlock::from_id(module_impl_blocks.clone(), impl_id)) | ||
199 | .collect() | ||
200 | } | ||
171 | } | 201 | } |
172 | 202 | ||
173 | impl Docs for Module { | 203 | impl Docs for Module { |
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index b30e11abb..4e73590d0 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs | |||
@@ -70,6 +70,14 @@ impl Body { | |||
70 | self.owner | 70 | self.owner |
71 | } | 71 | } |
72 | 72 | ||
73 | pub fn exprs(&self) -> impl Iterator<Item = (ExprId, &Expr)> { | ||
74 | self.exprs.iter() | ||
75 | } | ||
76 | |||
77 | pub fn pats(&self) -> impl Iterator<Item = (PatId, &Pat)> { | ||
78 | self.pats.iter() | ||
79 | } | ||
80 | |||
73 | pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc<BodySyntaxMapping> { | 81 | pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc<BodySyntaxMapping> { |
74 | db.body_syntax_mapping(self.owner) | 82 | db.body_syntax_mapping(self.owner) |
75 | } | 83 | } |
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index 7203a8a10..2dc1de41a 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs | |||
@@ -449,6 +449,49 @@ impl Ty { | |||
449 | Ty::Tuple(Arc::new([])) | 449 | Ty::Tuple(Arc::new([])) |
450 | } | 450 | } |
451 | 451 | ||
452 | pub fn walk(&self, f: &mut impl FnMut(&Ty)) { | ||
453 | f(self); | ||
454 | match self { | ||
455 | Ty::Slice(t) | Ty::Array(t) => t.walk(f), | ||
456 | Ty::RawPtr(t, _) => t.walk(f), | ||
457 | Ty::Ref(t, _) => t.walk(f), | ||
458 | Ty::Tuple(ts) => { | ||
459 | for t in ts.iter() { | ||
460 | t.walk(f); | ||
461 | } | ||
462 | } | ||
463 | Ty::FnPtr(sig) => { | ||
464 | for input in &sig.input { | ||
465 | input.walk(f); | ||
466 | } | ||
467 | sig.output.walk(f); | ||
468 | } | ||
469 | Ty::FnDef { substs, sig, .. } => { | ||
470 | for input in &sig.input { | ||
471 | input.walk(f); | ||
472 | } | ||
473 | sig.output.walk(f); | ||
474 | for t in substs.0.iter() { | ||
475 | t.walk(f); | ||
476 | } | ||
477 | } | ||
478 | Ty::Adt { substs, .. } => { | ||
479 | for t in substs.0.iter() { | ||
480 | t.walk(f); | ||
481 | } | ||
482 | } | ||
483 | Ty::Bool | ||
484 | | Ty::Char | ||
485 | | Ty::Int(_) | ||
486 | | Ty::Float(_) | ||
487 | | Ty::Str | ||
488 | | Ty::Never | ||
489 | | Ty::Param { .. } | ||
490 | | Ty::Infer(_) | ||
491 | | Ty::Unknown => {} | ||
492 | } | ||
493 | } | ||
494 | |||
452 | fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { | 495 | fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) { |
453 | f(self); | 496 | f(self); |
454 | match self { | 497 | match self { |
@@ -491,7 +534,15 @@ impl Ty { | |||
491 | } | 534 | } |
492 | substs.0 = v.into(); | 535 | substs.0 = v.into(); |
493 | } | 536 | } |
494 | _ => {} | 537 | Ty::Bool |
538 | | Ty::Char | ||
539 | | Ty::Int(_) | ||
540 | | Ty::Float(_) | ||
541 | | Ty::Str | ||
542 | | Ty::Never | ||
543 | | Ty::Param { .. } | ||
544 | | Ty::Infer(_) | ||
545 | | Ty::Unknown => {} | ||
495 | } | 546 | } |
496 | } | 547 | } |
497 | 548 | ||
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index f97d240fa..4a68c019f 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs | |||
@@ -47,10 +47,8 @@ impl ServerWorldState { | |||
47 | roots.push(krate.root_dir(&ws.sysroot).to_path_buf()) | 47 | roots.push(krate.root_dir(&ws.sysroot).to_path_buf()) |
48 | } | 48 | } |
49 | } | 49 | } |
50 | roots.sort(); | ||
51 | roots.dedup(); | ||
52 | let roots_to_scan = roots.len(); | ||
53 | let (mut vfs, roots) = Vfs::new(roots); | 50 | let (mut vfs, roots) = Vfs::new(roots); |
51 | let roots_to_scan = roots.len(); | ||
54 | for r in roots { | 52 | for r in roots { |
55 | let is_local = vfs.root2path(r).starts_with(&root); | 53 | let is_local = vfs.root2path(r).starts_with(&root); |
56 | change.add_root(SourceRootId(r.0.into()), is_local); | 54 | change.add_root(SourceRootId(r.0.into()), is_local); |
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs index 6b4eb6842..3805be570 100644 --- a/crates/ra_vfs/src/lib.rs +++ b/crates/ra_vfs/src/lib.rs | |||
@@ -94,6 +94,7 @@ impl Roots { | |||
94 | let mut roots = Arena::default(); | 94 | let mut roots = Arena::default(); |
95 | // A hack to make nesting work. | 95 | // A hack to make nesting work. |
96 | paths.sort_by_key(|it| Reverse(it.as_os_str().len())); | 96 | paths.sort_by_key(|it| Reverse(it.as_os_str().len())); |
97 | paths.dedup(); | ||
97 | for (i, path) in paths.iter().enumerate() { | 98 | for (i, path) in paths.iter().enumerate() { |
98 | let nested_roots = paths[..i] | 99 | let nested_roots = paths[..i] |
99 | .iter() | 100 | .iter() |
@@ -181,6 +182,10 @@ impl Vfs { | |||
181 | None | 182 | None |
182 | } | 183 | } |
183 | 184 | ||
185 | pub fn num_roots(&self) -> usize { | ||
186 | self.roots.len() | ||
187 | } | ||
188 | |||
184 | pub fn load(&mut self, path: &Path) -> Option<VfsFile> { | 189 | pub fn load(&mut self, path: &Path) -> Option<VfsFile> { |
185 | if let Some((root, rel_path, file)) = self.find_root(path) { | 190 | if let Some((root, rel_path, file)) = self.find_root(path) { |
186 | return if let Some(file) = file { | 191 | return if let Some(file) = file { |