aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/introduce_variable.rs81
-rw-r--r--crates/ra_batch/Cargo.toml20
-rw-r--r--crates/ra_batch/src/lib.rs151
-rw-r--r--crates/ra_cli/Cargo.toml6
-rw-r--r--crates/ra_cli/src/analysis_stats.rs100
-rw-r--r--crates/ra_cli/src/main.rs11
-rw-r--r--crates/ra_db/src/input.rs20
-rw-r--r--crates/ra_hir/src/code_model_api.rs34
-rw-r--r--crates/ra_hir/src/expr.rs23
-rw-r--r--crates/ra_hir/src/marks.rs1
-rw-r--r--crates/ra_hir/src/ty.rs125
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_1.snap13
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_2.snap14
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_3.snap13
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_4.snap16
-rw-r--r--crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_5.snap30
-rw-r--r--crates/ra_hir/src/ty/tests.rs89
-rw-r--r--crates/ra_lsp_server/Cargo.toml2
-rw-r--r--crates/ra_lsp_server/src/project_model.rs42
-rw-r--r--crates/ra_lsp_server/src/server_world.rs93
-rw-r--r--crates/ra_project_model/Cargo.toml21
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs (renamed from crates/ra_lsp_server/src/project_model/cargo_workspace.rs)13
-rw-r--r--crates/ra_project_model/src/lib.rs130
-rw-r--r--crates/ra_project_model/src/sysroot.rs (renamed from crates/ra_lsp_server/src/project_model/sysroot.rs)17
-rw-r--r--crates/ra_syntax/Cargo.toml2
-rw-r--r--crates/ra_syntax/src/ast/generated.rs37
-rw-r--r--crates/ra_syntax/src/grammar.ron3
-rw-r--r--crates/ra_syntax/src/grammar/patterns.rs41
-rw-r--r--crates/ra_syntax/src/syntax_kinds/generated.rs2
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0055_literal_pattern.txt22
-rw-r--r--crates/ra_syntax/tests/data/parser/inline/ok/0058_range_pat.txt30
-rw-r--r--crates/ra_syntax/tests/data/parser/ok/0035_weird_exprs.txt10
-rw-r--r--crates/ra_vfs/Cargo.toml2
-rw-r--r--crates/ra_vfs/src/lib.rs5
34 files changed, 1003 insertions, 216 deletions
diff --git a/crates/ra_assists/src/introduce_variable.rs b/crates/ra_assists/src/introduce_variable.rs
index 934d1d6b3..954b97b05 100644
--- a/crates/ra_assists/src/introduce_variable.rs
+++ b/crates/ra_assists/src/introduce_variable.rs
@@ -44,7 +44,22 @@ pub(crate) fn introduce_variable(ctx: AssistCtx<impl HirDatabase>) -> Option<Ass
44 edit.replace(expr.syntax().range(), buf); 44 edit.replace(expr.syntax().range(), buf);
45 } else { 45 } else {
46 buf.push_str(";"); 46 buf.push_str(";");
47 indent.text().push_to(&mut buf); 47
48 // We want to maintain the indent level,
49 // but we do not want to duplicate possible
50 // extra newlines in the indent block
51 for chunk in indent.text().chunks() {
52 if chunk.starts_with("\r\n") {
53 buf.push_str("\r\n");
54 buf.push_str(chunk.trim_start_matches("\r\n"));
55 } else if chunk.starts_with("\n") {
56 buf.push_str("\n");
57 buf.push_str(chunk.trim_start_matches("\n"));
58 } else {
59 buf.push_str(chunk);
60 }
61 }
62
48 edit.target(expr.syntax().range()); 63 edit.target(expr.syntax().range());
49 edit.replace(expr.syntax().range(), "var_name".to_string()); 64 edit.replace(expr.syntax().range(), "var_name".to_string());
50 edit.insert(anchor_stmt.range().start(), buf); 65 edit.insert(anchor_stmt.range().start(), buf);
@@ -345,6 +360,70 @@ fn foo() -> u32 {
345 } 360 }
346 361
347 #[test] 362 #[test]
363 fn test_introduce_var_does_not_add_extra_whitespace() {
364 check_assist(
365 introduce_variable,
366 "
367fn foo() -> u32 {
368
369
370 r<|>eturn 2 + 2;
371}
372",
373 "
374fn foo() -> u32 {
375
376
377 let <|>var_name = 2 + 2;
378 return var_name;
379}
380",
381 );
382
383 check_assist(
384 introduce_variable,
385 "
386fn foo() -> u32 {
387
388 r<|>eturn 2 + 2;
389}
390",
391 "
392fn foo() -> u32 {
393
394 let <|>var_name = 2 + 2;
395 return var_name;
396}
397",
398 );
399
400 check_assist(
401 introduce_variable,
402 "
403fn foo() -> u32 {
404 let foo = 1;
405
406 // bar
407
408
409 r<|>eturn 2 + 2;
410}
411",
412 "
413fn foo() -> u32 {
414 let foo = 1;
415
416 // bar
417
418
419 let <|>var_name = 2 + 2;
420 return var_name;
421}
422",
423 );
424 }
425
426 #[test]
348 fn test_introduce_var_break() { 427 fn test_introduce_var_break() {
349 check_assist( 428 check_assist(
350 introduce_variable, 429 introduce_variable,
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]
2edition = "2018"
3name = "ra_batch"
4version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"]
6
7[dependencies]
8log = "0.4.5"
9rustc-hash = "1.0"
10
11failure = "0.1.4"
12
13ra_syntax = { path = "../ra_syntax" }
14ra_db = { path = "../ra_db" }
15ra_hir = { path = "../ra_hir" }
16ra_vfs = { path = "../ra_vfs" }
17ra_project_model = { path = "../ra_project_model" }
18
19[dev-dependencies]
20test_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 @@
1use std::sync::Arc;
2use std::path::Path;
3use std::collections::HashSet;
4
5use rustc_hash::FxHashMap;
6
7use ra_db::{
8 CrateGraph, FileId, SourceRoot, SourceRootId, SourceDatabase, salsa,
9};
10use ra_hir::{db, HirInterner};
11use ra_project_model::ProjectWorkspace;
12use ra_vfs::{Vfs, VfsChange};
13
14type 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)]
22pub struct BatchDatabase {
23 runtime: salsa::Runtime<BatchDatabase>,
24 interner: Arc<HirInterner>,
25}
26
27impl salsa::Database for BatchDatabase {
28 fn salsa_runtime(&self) -> &salsa::Runtime<BatchDatabase> {
29 &self.runtime
30 }
31}
32
33impl AsRef<HirInterner> for BatchDatabase {
34 fn as_ref(&self) -> &HirInterner {
35 &self.interner
36 }
37}
38
39fn vfs_file_to_id(f: ra_vfs::VfsFile) -> FileId {
40 FileId(f.0.into())
41}
42fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
43 SourceRootId(r.0.into())
44}
45
46impl 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)]
130mod 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
9clap = "2.32.0" 9clap = "2.32.0"
10failure = "0.1.4" 10failure = "0.1.4"
11join_to_string = "0.1.1" 11join_to_string = "0.1.1"
12flexi_logger = "0.10.0"
13indicatif = "0.11.0"
14
12ra_syntax = { path = "../ra_syntax" } 15ra_syntax = { path = "../ra_syntax" }
13ra_ide_api_light = { path = "../ra_ide_api_light" } 16ra_ide_api_light = { path = "../ra_ide_api_light" }
14tools = { path = "../tools" } 17tools = { path = "../tools" }
18ra_batch = { path = "../ra_batch" }
19ra_hir = { path = "../ra_hir" }
20ra_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 @@
1use std::collections::HashSet;
2
3use ra_db::SourceDatabase;
4use ra_batch::BatchDatabase;
5use ra_hir::{Crate, ModuleDef, Ty, ImplItem};
6use ra_syntax::AstNode;
7
8use crate::Result;
9
10pub 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 @@
1mod analysis_stats;
2
1use std::{fs, io::Read, path::Path, time::Instant}; 3use std::{fs, io::Read, path::Path, time::Instant};
2 4
3use clap::{App, Arg, SubCommand}; 5use clap::{App, Arg, SubCommand};
@@ -5,10 +7,12 @@ use join_to_string::join;
5use ra_ide_api_light::{extend_selection, file_structure, syntax_tree}; 7use ra_ide_api_light::{extend_selection, file_structure, syntax_tree};
6use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode}; 8use ra_syntax::{SourceFile, TextRange, TreeArc, AstNode};
7use tools::collect_tests; 9use tools::collect_tests;
10use flexi_logger::Logger;
8 11
9type Result<T> = ::std::result::Result<T, failure::Error>; 12type Result<T> = ::std::result::Result<T, failure::Error>;
10 13
11fn main() -> Result<()> { 14fn 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 614325a0f..8decc65c5 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -91,6 +91,7 @@ impl CrateGraph {
91 assert!(prev.is_none()); 91 assert!(prev.is_none());
92 crate_id 92 crate_id
93 } 93 }
94
94 pub fn add_dep( 95 pub fn add_dep(
95 &mut self, 96 &mut self,
96 from: CrateId, 97 from: CrateId,
@@ -102,22 +103,41 @@ impl CrateGraph {
102 } 103 }
103 Ok(self.arena.get_mut(&from).unwrap().add_dep(name, to)) 104 Ok(self.arena.get_mut(&from).unwrap().add_dep(name, to))
104 } 105 }
106
105 pub fn is_empty(&self) -> bool { 107 pub fn is_empty(&self) -> bool {
106 self.arena.is_empty() 108 self.arena.is_empty()
107 } 109 }
110
108 pub fn crate_root(&self, crate_id: CrateId) -> FileId { 111 pub fn crate_root(&self, crate_id: CrateId) -> FileId {
109 self.arena[&crate_id].file_id 112 self.arena[&crate_id].file_id
110 } 113 }
114
115 // TODO: this only finds one crate with the given root; we could have multiple
111 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> {
112 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)?;
113 Some(crate_id) 118 Some(crate_id)
114 } 119 }
120
115 pub fn dependencies<'a>( 121 pub fn dependencies<'a>(
116 &'a self, 122 &'a self,
117 crate_id: CrateId, 123 crate_id: CrateId,
118 ) -> impl Iterator<Item = &'a Dependency> + 'a { 124 ) -> impl Iterator<Item = &'a Dependency> + 'a {
119 self.arena[&crate_id].dependencies.iter() 125 self.arena[&crate_id].dependencies.iter()
120 } 126 }
127
128 /// Extends this crate graph by adding a complete disjoint second crate
129 /// graph.
130 pub fn extend(&mut self, other: CrateGraph) {
131 let start = self.arena.len() as u32;
132 self.arena.extend(other.arena.into_iter().map(|(id, mut data)| {
133 let new_id = CrateId(id.0 + start);
134 for dep in &mut data.dependencies {
135 dep.crate_id = CrateId(dep.crate_id.0 + start);
136 }
137 (new_id, data)
138 }));
139 }
140
121 fn dfs_find(&self, target: CrateId, from: CrateId, visited: &mut FxHashSet<CrateId>) -> bool { 141 fn dfs_find(&self, target: CrateId, from: CrateId, visited: &mut FxHashSet<CrateId>) -> bool {
122 if !visited.insert(from) { 142 if !visited.insert(from) {
123 return false; 143 return false;
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 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use relative_path::RelativePathBuf; 3use relative_path::RelativePathBuf;
4use ra_db::{CrateId, FileId}; 4use ra_db::{CrateId, FileId, SourceRootId};
5use ra_syntax::{ast::self, TreeArc, SyntaxNode}; 5use ra_syntax::{ast::self, TreeArc, SyntaxNode};
6 6
7use crate::{ 7use 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
173impl Docs for Module { 203impl Docs for Module {
diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs
index 4e61d87ff..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 }
@@ -831,18 +839,18 @@ impl ExprCollector {
831 p.field_pat_list().expect("every struct should have a field list"); 839 p.field_pat_list().expect("every struct should have a field list");
832 let mut fields: Vec<_> = field_pat_list 840 let mut fields: Vec<_> = field_pat_list
833 .bind_pats() 841 .bind_pats()
834 .map(|bind_pat| { 842 .filter_map(|bind_pat| {
835 let ast_pat = ast::Pat::cast(bind_pat.syntax()).expect("bind pat is a pat"); 843 let ast_pat = ast::Pat::cast(bind_pat.syntax()).expect("bind pat is a pat");
836 let pat = self.collect_pat(ast_pat); 844 let pat = self.collect_pat(ast_pat);
837 let name = bind_pat.name().expect("bind pat has a name").as_name(); 845 let name = bind_pat.name()?.as_name();
838 FieldPat { name, pat } 846 Some(FieldPat { name, pat })
839 }) 847 })
840 .collect(); 848 .collect();
841 let iter = field_pat_list.field_pats().map(|f| { 849 let iter = field_pat_list.field_pats().filter_map(|f| {
842 let ast_pat = f.pat().expect("field pat always contains a pattern"); 850 let ast_pat = f.pat()?;
843 let pat = self.collect_pat(ast_pat); 851 let pat = self.collect_pat(ast_pat);
844 let name = f.name().expect("field pats always have a name").as_name(); 852 let name = f.name()?.as_name();
845 FieldPat { name, pat } 853 Some(FieldPat { name, pat })
846 }); 854 });
847 fields.extend(iter); 855 fields.extend(iter);
848 856
@@ -850,6 +858,7 @@ impl ExprCollector {
850 } 858 }
851 859
852 // TODO: implement 860 // TODO: implement
861 ast::PatKind::LiteralPat(_) => Pat::Missing,
853 ast::PatKind::SlicePat(_) | ast::PatKind::RangePat(_) => Pat::Missing, 862 ast::PatKind::SlicePat(_) | ast::PatKind::RangePat(_) => Pat::Missing,
854 }; 863 };
855 let syntax_ptr = SyntaxNodePtr::new(pat.syntax()); 864 let syntax_ptr = SyntaxNodePtr::new(pat.syntax());
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index d704c3adb..aba0c9968 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -3,4 +3,5 @@ test_utils::marks!(
3 item_map_enum_importing 3 item_map_enum_importing
4 type_var_cycles_resolve_completely 4 type_var_cycles_resolve_completely
5 type_var_cycles_resolve_as_possible 5 type_var_cycles_resolve_as_possible
6 type_var_resolves_to_int_var
6); 7);
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 453520bbe..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
@@ -879,11 +930,22 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
879 ty 930 ty
880 } 931 }
881 932
882 fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs) -> bool { 933 fn unify_substs(&mut self, substs1: &Substs, substs2: &Substs, depth: usize) -> bool {
883 substs1.0.iter().zip(substs2.0.iter()).all(|(t1, t2)| self.unify(t1, t2)) 934 substs1.0.iter().zip(substs2.0.iter()).all(|(t1, t2)| self.unify_inner(t1, t2, depth))
884 } 935 }
885 936
886 fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool { 937 fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
938 self.unify_inner(ty1, ty2, 0)
939 }
940
941 fn unify_inner(&mut self, ty1: &Ty, ty2: &Ty, depth: usize) -> bool {
942 if depth > 1000 {
943 // prevent stackoverflows
944 panic!("infinite recursion in unification");
945 }
946 if ty1 == ty2 {
947 return true;
948 }
887 // try to resolve type vars first 949 // try to resolve type vars first
888 let ty1 = self.resolve_ty_shallow(ty1); 950 let ty1 = self.resolve_ty_shallow(ty1);
889 let ty2 = self.resolve_ty_shallow(ty2); 951 let ty2 = self.resolve_ty_shallow(ty2);
@@ -904,13 +966,15 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
904 ( 966 (
905 Ty::Adt { def_id: def_id1, substs: substs1, .. }, 967 Ty::Adt { def_id: def_id1, substs: substs1, .. },
906 Ty::Adt { def_id: def_id2, substs: substs2, .. }, 968 Ty::Adt { def_id: def_id2, substs: substs2, .. },
907 ) if def_id1 == def_id2 => self.unify_substs(substs1, substs2), 969 ) if def_id1 == def_id2 => self.unify_substs(substs1, substs2, depth + 1),
908 (Ty::Slice(t1), Ty::Slice(t2)) => self.unify(t1, t2), 970 (Ty::Slice(t1), Ty::Slice(t2)) => self.unify_inner(t1, t2, depth + 1),
909 (Ty::RawPtr(t1, m1), Ty::RawPtr(t2, m2)) if m1 == m2 => self.unify(t1, t2), 971 (Ty::RawPtr(t1, m1), Ty::RawPtr(t2, m2)) if m1 == m2 => {
910 (Ty::Ref(t1, m1), Ty::Ref(t2, m2)) if m1 == m2 => self.unify(t1, t2), 972 self.unify_inner(t1, t2, depth + 1)
973 }
974 (Ty::Ref(t1, m1), Ty::Ref(t2, m2)) if m1 == m2 => self.unify_inner(t1, t2, depth + 1),
911 (Ty::FnPtr(sig1), Ty::FnPtr(sig2)) if sig1 == sig2 => true, 975 (Ty::FnPtr(sig1), Ty::FnPtr(sig2)) if sig1 == sig2 => true,
912 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => { 976 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => {
913 ts1.iter().zip(ts2.iter()).all(|(t1, t2)| self.unify(t1, t2)) 977 ts1.iter().zip(ts2.iter()).all(|(t1, t2)| self.unify_inner(t1, t2, depth + 1))
914 } 978 }
915 (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) 979 (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2)))
916 | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2))) 980 | (Ty::Infer(InferTy::IntVar(tv1)), Ty::Infer(InferTy::IntVar(tv2)))
@@ -989,19 +1053,30 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
989 /// If `ty` is a type variable with known type, returns that type; 1053 /// If `ty` is a type variable with known type, returns that type;
990 /// otherwise, return ty. 1054 /// otherwise, return ty.
991 fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> { 1055 fn resolve_ty_shallow<'b>(&mut self, ty: &'b Ty) -> Cow<'b, Ty> {
992 match ty { 1056 let mut ty = Cow::Borrowed(ty);
993 Ty::Infer(tv) => { 1057 // The type variable could resolve to a int/float variable. Hence try
994 let inner = tv.to_inner(); 1058 // resolving up to three times; each type of variable shouldn't occur
995 match self.var_unification_table.probe_value(inner).known() { 1059 // more than once
996 Some(known_ty) => { 1060 for i in 0..3 {
997 // The known_ty can't be a type var itself 1061 if i > 0 {
998 Cow::Owned(known_ty.clone()) 1062 tested_by!(type_var_resolves_to_int_var);
1063 }
1064 match &*ty {
1065 Ty::Infer(tv) => {
1066 let inner = tv.to_inner();
1067 match self.var_unification_table.probe_value(inner).known() {
1068 Some(known_ty) => {
1069 // The known_ty can't be a type var itself
1070 ty = Cow::Owned(known_ty.clone());
1071 }
1072 _ => return ty,
999 } 1073 }
1000 _ => Cow::Borrowed(ty),
1001 } 1074 }
1075 _ => return ty,
1002 } 1076 }
1003 _ => Cow::Borrowed(ty),
1004 } 1077 }
1078 log::error!("Inference variable still not resolved: {:?}", ty);
1079 ty
1005 } 1080 }
1006 1081
1007 /// Resolves the type completely; type variables without known type are 1082 /// Resolves the type completely; type variables without known type are
@@ -1185,17 +1260,21 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
1185 self.infer_path_expr(&resolver, &path).unwrap_or(Ty::Unknown) 1260 self.infer_path_expr(&resolver, &path).unwrap_or(Ty::Unknown)
1186 } 1261 }
1187 Pat::Bind { mode, name: _name, subpat } => { 1262 Pat::Bind { mode, name: _name, subpat } => {
1188 let subty = if let Some(subpat) = subpat { 1263 let inner_ty = if let Some(subpat) = subpat {
1189 self.infer_pat(*subpat, expected) 1264 self.infer_pat(*subpat, expected)
1190 } else { 1265 } else {
1191 expected.clone() 1266 expected.clone()
1192 }; 1267 };
1268 let inner_ty = self.insert_type_vars_shallow(inner_ty);
1193 1269
1194 match mode { 1270 let bound_ty = match mode {
1195 BindingAnnotation::Ref => Ty::Ref(subty.into(), Mutability::Shared), 1271 BindingAnnotation::Ref => Ty::Ref(inner_ty.clone().into(), Mutability::Shared),
1196 BindingAnnotation::RefMut => Ty::Ref(subty.into(), Mutability::Mut), 1272 BindingAnnotation::RefMut => Ty::Ref(inner_ty.clone().into(), Mutability::Mut),
1197 BindingAnnotation::Mutable | BindingAnnotation::Unannotated => subty, 1273 BindingAnnotation::Mutable | BindingAnnotation::Unannotated => inner_ty.clone(),
1198 } 1274 };
1275 let bound_ty = self.resolve_ty_as_possible(&mut vec![], bound_ty);
1276 self.write_pat_ty(pat, bound_ty);
1277 return inner_ty;
1199 } 1278 }
1200 _ => Ty::Unknown, 1279 _ => Ty::Unknown,
1201 }; 1280 };
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_1.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_1.snap
new file mode 100644
index 000000000..4b99788e4
--- /dev/null
+++ b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_1.snap
@@ -0,0 +1,13 @@
1---
2created: "2019-02-09T16:56:24.803326529Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&result"
6---
7[54; 139) '{ ... } }': ()
8[60; 137) 'match ... }': ()
9[66; 83) 'someth...nknown': Maybe<[unknown]>
10[94; 124) 'Maybe:...thing)': Maybe<[unknown]>
11[106; 123) 'ref mu...ething': &mut [unknown]
12[128; 130) '()': ()
13
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_2.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_2.snap
new file mode 100644
index 000000000..fd0b39b7d
--- /dev/null
+++ b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_2.snap
@@ -0,0 +1,14 @@
1---
2created: "2019-02-09T17:03:11.974225590Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&result"
6---
7[23; 53) '{ ...n']; }': ()
8[29; 50) '&[0, b...b'\n']': &[u8]
9[30; 50) '[0, b'...b'\n']': [u8]
10[31; 32) '0': u8
11[34; 39) 'b'\n'': u8
12[41; 42) '1': u8
13[44; 49) 'b'\n'': u8
14
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_3.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_3.snap
new file mode 100644
index 000000000..d15b77e17
--- /dev/null
+++ b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_3.snap
@@ -0,0 +1,13 @@
1---
2created: "2019-02-09T18:02:37.377591660Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&result"
6---
7[18; 102) '{ ... } }': ()
8[24; 100) 'match ... }': ()
9[42; 88) 'SizeSk...tail }': [unknown]
10[76; 80) 'true': [unknown]
11[82; 86) 'tail': [unknown]
12[92; 94) '{}': ()
13
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_4.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_4.snap
new file mode 100644
index 000000000..fb31883ce
--- /dev/null
+++ b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_4.snap
@@ -0,0 +1,16 @@
1---
2created: "2019-02-09T19:55:39.712470520Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&result"
6---
7[25; 110) '{ ... } }': ()
8[31; 108) 'match ... }': ()
9[37; 42) '*self': [unknown]
10[38; 42) 'self': [unknown]
11[53; 95) 'Borrow...), ..}': [unknown]
12[74; 77) 'box': [unknown]
13[78; 87) 'Primitive': [unknown]
14[88; 89) 'p': [unknown]
15[99; 101) '{}': ()
16
diff --git a/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_5.snap b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_5.snap
new file mode 100644
index 000000000..6bbf59fb6
--- /dev/null
+++ b/crates/ra_hir/src/ty/snapshots/tests__infer_std_crash_5.snap
@@ -0,0 +1,30 @@
1---
2created: "2019-02-09T20:28:37.294693728Z"
3creator: [email protected]
4source: crates/ra_hir/src/ty/tests.rs
5expression: "&result"
6---
7[27; 323) '{ ... } }': ()
8[33; 321) 'for co... }': ()
9[37; 44) 'content': &[unknown]
10[48; 61) 'doesnt_matter': [unknown]
11[62; 321) '{ ... }': ()
12[76; 80) 'name': &&[unknown]
13[83; 167) 'if doe... }': &&[unknown]
14[86; 99) 'doesnt_matter': bool
15[100; 129) '{ ... }': &&[unknown]
16[114; 119) 'first': &&[unknown]
17[135; 167) '{ ... }': &&[unknown]
18[149; 157) '&content': &&[unknown]
19[150; 157) 'content': &[unknown]
20[182; 189) 'content': &&[unknown]
21[192; 314) 'if ICE... }': &&[unknown]
22[195; 232) 'ICE_RE..._VALUE': [unknown]
23[195; 248) 'ICE_RE...&name)': bool
24[242; 247) '&name': &&&[unknown]
25[243; 247) 'name': &&[unknown]
26[249; 277) '{ ... }': &&[unknown]
27[263; 267) 'name': &&[unknown]
28[283; 314) '{ ... }': &[unknown]
29[297; 304) 'content': &[unknown]
30
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 2621d1b55..e64fd2749 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -630,6 +630,95 @@ fn test() {
630 ); 630 );
631} 631}
632 632
633#[test]
634fn infer_std_crash_1() {
635 // caused stack overflow, taken from std
636 check_inference(
637 "infer_std_crash_1",
638 r#"
639enum Maybe<T> {
640 Real(T),
641 Fake,
642}
643
644fn write() {
645 match something_unknown {
646 Maybe::Real(ref mut something) => (),
647 }
648}
649"#,
650 );
651}
652
653#[test]
654fn infer_std_crash_2() {
655 covers!(type_var_resolves_to_int_var);
656 // caused "equating two type variables, ...", taken from std
657 check_inference(
658 "infer_std_crash_2",
659 r#"
660fn test_line_buffer() {
661 &[0, b'\n', 1, b'\n'];
662}
663"#,
664 );
665}
666
667#[test]
668fn infer_std_crash_3() {
669 // taken from rustc
670 check_inference(
671 "infer_std_crash_3",
672 r#"
673pub fn compute() {
674 match _ {
675 SizeSkeleton::Pointer { non_zero: true, tail } => {}
676 }
677}
678"#,
679 );
680}
681
682#[test]
683fn infer_std_crash_4() {
684 // taken from rustc
685 check_inference(
686 "infer_std_crash_4",
687 r#"
688pub fn primitive_type() {
689 match *self {
690 BorrowedRef { type_: box Primitive(p), ..} => {},
691 }
692}
693"#,
694 );
695}
696
697#[test]
698fn infer_std_crash_5() {
699 // taken from rustc
700 check_inference(
701 "infer_std_crash_5",
702 r#"
703fn extra_compiler_flags() {
704 for content in doesnt_matter {
705 let name = if doesnt_matter {
706 first
707 } else {
708 &content
709 };
710
711 let content = if ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.contains(&name) {
712 name
713 } else {
714 content
715 };
716 }
717}
718"#,
719 );
720}
721
633fn infer(content: &str) -> String { 722fn infer(content: &str) -> String {
634 let (db, _, file_id) = MockDatabase::with_single_file(content); 723 let (db, _, file_id) = MockDatabase::with_single_file(content);
635 let source_file = db.parse(file_id); 724 let source_file = db.parse(file_id);
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index bb92747f2..f46d77893 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -19,7 +19,6 @@ url_serde = "0.2.0"
19lsp-types = "0.55.0" 19lsp-types = "0.55.0"
20walkdir = "2.2.7" 20walkdir = "2.2.7"
21im = "12.0.0" 21im = "12.0.0"
22cargo_metadata = "0.7.0"
23rustc-hash = "1.0" 22rustc-hash = "1.0"
24parking_lot = "0.7.0" 23parking_lot = "0.7.0"
25 24
@@ -30,6 +29,7 @@ ra_ide_api = { path = "../ra_ide_api" }
30ra_arena = { path = "../ra_arena" } 29ra_arena = { path = "../ra_arena" }
31gen_lsp_server = { path = "../gen_lsp_server" } 30gen_lsp_server = { path = "../gen_lsp_server" }
32ra_vfs = { path = "../ra_vfs" } 31ra_vfs = { path = "../ra_vfs" }
32ra_project_model = { path = "../ra_project_model" }
33 33
34[dev-dependencies] 34[dev-dependencies]
35tempfile = "3" 35tempfile = "3"
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs
index fd5875a0a..6800eb138 100644
--- a/crates/ra_lsp_server/src/project_model.rs
+++ b/crates/ra_lsp_server/src/project_model.rs
@@ -1,34 +1,13 @@
1mod cargo_workspace; 1use std::path::PathBuf;
2mod sysroot;
3 2
4use std::path::{Path, PathBuf};
5
6use failure::bail;
7use thread_worker::{WorkerHandle, Worker}; 3use thread_worker::{WorkerHandle, Worker};
8 4
9use crate::Result; 5use crate::Result;
10 6
11pub use crate::project_model::{ 7pub use ra_project_model::{
12 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind}, 8 ProjectWorkspace, CargoWorkspace, Package, Target, TargetKind, Sysroot,
13 sysroot::Sysroot,
14}; 9};
15 10
16#[derive(Debug, Clone)]
17pub struct ProjectWorkspace {
18 pub(crate) cargo: CargoWorkspace,
19 pub(crate) sysroot: Sysroot,
20}
21
22impl ProjectWorkspace {
23 pub fn discover(path: &Path) -> Result<ProjectWorkspace> {
24 let cargo_toml = find_cargo_toml(path)?;
25 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?;
26 let sysroot = Sysroot::discover(&cargo_toml)?;
27 let res = ProjectWorkspace { cargo, sysroot };
28 Ok(res)
29 }
30}
31
32pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerHandle) { 11pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerHandle) {
33 thread_worker::spawn::<PathBuf, Result<ProjectWorkspace>, _>( 12 thread_worker::spawn::<PathBuf, Result<ProjectWorkspace>, _>(
34 "workspace loader", 13 "workspace loader",
@@ -42,18 +21,3 @@ pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerH
42 }, 21 },
43 ) 22 )
44} 23}
45
46fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
47 if path.ends_with("Cargo.toml") {
48 return Ok(path.to_path_buf());
49 }
50 let mut curr = Some(path);
51 while let Some(path) = curr {
52 let candidate = path.join("Cargo.toml");
53 if candidate.exists() {
54 return Ok(candidate);
55 }
56 curr = path.parent();
57 }
58 bail!("can't find Cargo.toml at {}", path.display())
59}
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index 02f2a37a8..4a68c019f 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -9,13 +9,12 @@ use ra_ide_api::{
9 SourceRootId 9 SourceRootId
10}; 10};
11use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot}; 11use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot};
12use rustc_hash::FxHashMap;
13use relative_path::RelativePathBuf; 12use relative_path::RelativePathBuf;
14use parking_lot::RwLock; 13use parking_lot::RwLock;
15use failure::format_err; 14use failure::format_err;
16 15
17use crate::{ 16use crate::{
18 project_model::{ProjectWorkspace, TargetKind}, 17 project_model::ProjectWorkspace,
19 Result, 18 Result,
20}; 19};
21 20
@@ -48,97 +47,21 @@ impl ServerWorldState {
48 roots.push(krate.root_dir(&ws.sysroot).to_path_buf()) 47 roots.push(krate.root_dir(&ws.sysroot).to_path_buf())
49 } 48 }
50 } 49 }
51 roots.sort();
52 roots.dedup();
53 let roots_to_scan = roots.len();
54 let (mut vfs, roots) = Vfs::new(roots); 50 let (mut vfs, roots) = Vfs::new(roots);
51 let roots_to_scan = roots.len();
55 for r in roots { 52 for r in roots {
56 let is_local = vfs.root2path(r).starts_with(&root); 53 let is_local = vfs.root2path(r).starts_with(&root);
57 change.add_root(SourceRootId(r.0.into()), is_local); 54 change.add_root(SourceRootId(r.0.into()), is_local);
58 } 55 }
59 56
57 // Create crate graph from all the workspaces
60 let mut crate_graph = CrateGraph::default(); 58 let mut crate_graph = CrateGraph::default();
59 let mut load = |path: &std::path::Path| {
60 let vfs_file = vfs.load(path);
61 vfs_file.map(|f| FileId(f.0.into()))
62 };
61 for ws in workspaces.iter() { 63 for ws in workspaces.iter() {
62 // First, load std 64 crate_graph.extend(ws.to_crate_graph(&mut load));
63 let mut sysroot_crates = FxHashMap::default();
64 for krate in ws.sysroot.crates() {
65 if let Some(file_id) = vfs.load(krate.root(&ws.sysroot)) {
66 let file_id = FileId(file_id.0.into());
67 sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
68 }
69 }
70 for from in ws.sysroot.crates() {
71 for to in from.deps(&ws.sysroot) {
72 let name = to.name(&ws.sysroot);
73 if let (Some(&from), Some(&to)) =
74 (sysroot_crates.get(&from), sysroot_crates.get(&to))
75 {
76 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
77 log::error!("cyclic dependency between sysroot crates")
78 }
79 }
80 }
81 }
82
83 let libstd = ws.sysroot.std().and_then(|it| sysroot_crates.get(&it).map(|&it| it));
84
85 let mut pkg_to_lib_crate = FxHashMap::default();
86 let mut pkg_crates = FxHashMap::default();
87 // Next, create crates for each package, target pair
88 for pkg in ws.cargo.packages() {
89 let mut lib_tgt = None;
90 for tgt in pkg.targets(&ws.cargo) {
91 let root = tgt.root(&ws.cargo);
92 if let Some(file_id) = vfs.load(root) {
93 let file_id = FileId(file_id.0.into());
94 let crate_id = crate_graph.add_crate_root(file_id);
95 if tgt.kind(&ws.cargo) == TargetKind::Lib {
96 lib_tgt = Some(crate_id);
97 pkg_to_lib_crate.insert(pkg, crate_id);
98 }
99 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
100 }
101 }
102
103 // Set deps to the std and to the lib target of the current package
104 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
105 if let Some(to) = lib_tgt {
106 if to != from {
107 if let Err(_) =
108 crate_graph.add_dep(from, pkg.name(&ws.cargo).into(), to)
109 {
110 log::error!(
111 "cyclic dependency between targets of {}",
112 pkg.name(&ws.cargo)
113 )
114 }
115 }
116 }
117 if let Some(std) = libstd {
118 if let Err(_) = crate_graph.add_dep(from, "std".into(), std) {
119 log::error!("cyclic dependency on std for {}", pkg.name(&ws.cargo))
120 }
121 }
122 }
123 }
124
125 // Now add a dep ednge from all targets of upstream to the lib
126 // target of downstream.
127 for pkg in ws.cargo.packages() {
128 for dep in pkg.dependencies(&ws.cargo) {
129 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
130 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
131 if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) {
132 log::error!(
133 "cyclic dependency {} -> {}",
134 pkg.name(&ws.cargo),
135 dep.pkg.name(&ws.cargo)
136 )
137 }
138 }
139 }
140 }
141 }
142 } 65 }
143 change.set_crate_graph(crate_graph); 66 change.set_crate_graph(crate_graph);
144 67
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml
new file mode 100644
index 000000000..90f8b8398
--- /dev/null
+++ b/crates/ra_project_model/Cargo.toml
@@ -0,0 +1,21 @@
1[package]
2edition = "2018"
3name = "ra_project_model"
4version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"]
6
7[dependencies]
8log = "0.4.5"
9rustc-hash = "1.0"
10
11failure = "0.1.4"
12
13walkdir = "2.2.7"
14
15cargo_metadata = "0.7.0"
16
17ra_arena = { path = "../ra_arena" }
18ra_db = { path = "../ra_db" }
19
20[dev-dependencies]
21test_utils = { path = "../test_utils" }
diff --git a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index 3b76389d2..8adf463a6 100644
--- a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -1,17 +1,16 @@
1use std::path::{Path, PathBuf}; 1use std::path::{Path, PathBuf};
2 2
3use cargo_metadata::{MetadataCommand, CargoOpt}; 3use cargo_metadata::{MetadataCommand, CargoOpt};
4use ra_syntax::SmolStr;
5use ra_arena::{Arena, RawId, impl_arena_id}; 4use ra_arena::{Arena, RawId, impl_arena_id};
6use rustc_hash::FxHashMap; 5use rustc_hash::FxHashMap;
7use failure::format_err; 6use failure::format_err;
8 7
9use crate::Result; 8use crate::Result;
10 9
11/// `CargoWorksapce` represents the logical structure of, well, a Cargo 10/// `CargoWorkspace` represents the logical structure of, well, a Cargo
12/// workspace. It pretty closely mirrors `cargo metadata` output. 11/// workspace. It pretty closely mirrors `cargo metadata` output.
13/// 12///
14/// Note that internally, rust analyzer uses a differnet structure: 13/// Note that internally, rust analyzer uses a different structure:
15/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, 14/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
16/// while this knows about `Pacakges` & `Targets`: purely cargo-related 15/// while this knows about `Pacakges` & `Targets`: purely cargo-related
17/// concepts. 16/// concepts.
@@ -31,7 +30,7 @@ impl_arena_id!(Target);
31 30
32#[derive(Debug, Clone)] 31#[derive(Debug, Clone)]
33struct PackageData { 32struct PackageData {
34 name: SmolStr, 33 name: String,
35 manifest: PathBuf, 34 manifest: PathBuf,
36 targets: Vec<Target>, 35 targets: Vec<Target>,
37 is_member: bool, 36 is_member: bool,
@@ -41,13 +40,13 @@ struct PackageData {
41#[derive(Debug, Clone)] 40#[derive(Debug, Clone)]
42pub struct PackageDependency { 41pub struct PackageDependency {
43 pub pkg: Package, 42 pub pkg: Package,
44 pub name: SmolStr, 43 pub name: String,
45} 44}
46 45
47#[derive(Debug, Clone)] 46#[derive(Debug, Clone)]
48struct TargetData { 47struct TargetData {
49 pkg: Package, 48 pkg: Package,
50 name: SmolStr, 49 name: String,
51 root: PathBuf, 50 root: PathBuf,
52 kind: TargetKind, 51 kind: TargetKind,
53} 52}
@@ -162,9 +161,11 @@ impl CargoWorkspace {
162 161
163 Ok(CargoWorkspace { packages, targets }) 162 Ok(CargoWorkspace { packages, targets })
164 } 163 }
164
165 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a { 165 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
166 self.packages.iter().map(|(id, _pkg)| id) 166 self.packages.iter().map(|(id, _pkg)| id)
167 } 167 }
168
168 pub fn target_by_root(&self, root: &Path) -> Option<Target> { 169 pub fn target_by_root(&self, root: &Path) -> Option<Target> {
169 self.packages().filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)).next() 170 self.packages().filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)).next()
170 } 171 }
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
new file mode 100644
index 000000000..3b1e07149
--- /dev/null
+++ b/crates/ra_project_model/src/lib.rs
@@ -0,0 +1,130 @@
1mod cargo_workspace;
2mod sysroot;
3
4use std::path::{Path, PathBuf};
5
6use failure::bail;
7use rustc_hash::FxHashMap;
8
9use ra_db::{CrateGraph, FileId};
10
11pub use crate::{
12 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind},
13 sysroot::Sysroot,
14};
15
16// TODO use proper error enum
17pub type Result<T> = ::std::result::Result<T, ::failure::Error>;
18
19#[derive(Debug, Clone)]
20pub struct ProjectWorkspace {
21 pub cargo: CargoWorkspace,
22 pub sysroot: Sysroot,
23}
24
25impl ProjectWorkspace {
26 pub fn discover(path: &Path) -> Result<ProjectWorkspace> {
27 let cargo_toml = find_cargo_toml(path)?;
28 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?;
29 let sysroot = Sysroot::discover(&cargo_toml)?;
30 let res = ProjectWorkspace { cargo, sysroot };
31 Ok(res)
32 }
33
34 pub fn to_crate_graph(&self, load: &mut dyn FnMut(&Path) -> Option<FileId>) -> CrateGraph {
35 let mut crate_graph = CrateGraph::default();
36 let mut sysroot_crates = FxHashMap::default();
37 for krate in self.sysroot.crates() {
38 if let Some(file_id) = load(krate.root(&self.sysroot)) {
39 sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
40 }
41 }
42 for from in self.sysroot.crates() {
43 for to in from.deps(&self.sysroot) {
44 let name = to.name(&self.sysroot);
45 if let (Some(&from), Some(&to)) =
46 (sysroot_crates.get(&from), sysroot_crates.get(&to))
47 {
48 if let Err(_) = crate_graph.add_dep(from, name.into(), to) {
49 log::error!("cyclic dependency between sysroot crates")
50 }
51 }
52 }
53 }
54
55 let libstd = self.sysroot.std().and_then(|it| sysroot_crates.get(&it).map(|&it| it));
56
57 let mut pkg_to_lib_crate = FxHashMap::default();
58 let mut pkg_crates = FxHashMap::default();
59 // Next, create crates for each package, target pair
60 for pkg in self.cargo.packages() {
61 let mut lib_tgt = None;
62 for tgt in pkg.targets(&self.cargo) {
63 let root = tgt.root(&self.cargo);
64 if let Some(file_id) = load(root) {
65 let crate_id = crate_graph.add_crate_root(file_id);
66 if tgt.kind(&self.cargo) == TargetKind::Lib {
67 lib_tgt = Some(crate_id);
68 pkg_to_lib_crate.insert(pkg, crate_id);
69 }
70 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
71 }
72 }
73
74 // Set deps to the std and to the lib target of the current package
75 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
76 if let Some(to) = lib_tgt {
77 if to != from {
78 if let Err(_) = crate_graph.add_dep(from, pkg.name(&self.cargo).into(), to)
79 {
80 log::error!(
81 "cyclic dependency between targets of {}",
82 pkg.name(&self.cargo)
83 )
84 }
85 }
86 }
87 if let Some(std) = libstd {
88 if let Err(_) = crate_graph.add_dep(from, "std".into(), std) {
89 log::error!("cyclic dependency on std for {}", pkg.name(&self.cargo))
90 }
91 }
92 }
93 }
94
95 // Now add a dep ednge from all targets of upstream to the lib
96 // target of downstream.
97 for pkg in self.cargo.packages() {
98 for dep in pkg.dependencies(&self.cargo) {
99 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
100 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
101 if let Err(_) = crate_graph.add_dep(from, dep.name.clone().into(), to) {
102 log::error!(
103 "cyclic dependency {} -> {}",
104 pkg.name(&self.cargo),
105 dep.pkg.name(&self.cargo)
106 )
107 }
108 }
109 }
110 }
111 }
112
113 crate_graph
114 }
115}
116
117fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
118 if path.ends_with("Cargo.toml") {
119 return Ok(path.to_path_buf());
120 }
121 let mut curr = Some(path);
122 while let Some(path) = curr {
123 let candidate = path.join("Cargo.toml");
124 if candidate.exists() {
125 return Ok(candidate);
126 }
127 curr = path.parent();
128 }
129 bail!("can't find Cargo.toml at {}", path.display())
130}
diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_project_model/src/sysroot.rs
index 49210ac7a..8b87aa7bd 100644
--- a/crates/ra_lsp_server/src/project_model/sysroot.rs
+++ b/crates/ra_project_model/src/sysroot.rs
@@ -3,7 +3,6 @@ use std::{
3 process::Command, 3 process::Command,
4}; 4};
5 5
6use ra_syntax::SmolStr;
7use ra_arena::{Arena, RawId, impl_arena_id}; 6use ra_arena::{Arena, RawId, impl_arena_id};
8 7
9use crate::Result; 8use crate::Result;
@@ -19,21 +18,21 @@ impl_arena_id!(SysrootCrate);
19 18
20#[derive(Debug, Clone)] 19#[derive(Debug, Clone)]
21struct SysrootCrateData { 20struct SysrootCrateData {
22 name: SmolStr, 21 name: String,
23 root: PathBuf, 22 root: PathBuf,
24 deps: Vec<SysrootCrate>, 23 deps: Vec<SysrootCrate>,
25} 24}
26 25
27impl Sysroot { 26impl Sysroot {
28 pub(crate) fn std(&self) -> Option<SysrootCrate> { 27 pub fn std(&self) -> Option<SysrootCrate> {
29 self.by_name("std") 28 self.by_name("std")
30 } 29 }
31 30
32 pub(crate) fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a { 31 pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a {
33 self.crates.iter().map(|(id, _data)| id) 32 self.crates.iter().map(|(id, _data)| id)
34 } 33 }
35 34
36 pub(super) fn discover(cargo_toml: &Path) -> Result<Sysroot> { 35 pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
37 let rustc_output = Command::new("rustc") 36 let rustc_output = Command::new("rustc")
38 .current_dir(cargo_toml.parent().unwrap()) 37 .current_dir(cargo_toml.parent().unwrap())
39 .args(&["--print", "sysroot"]) 38 .args(&["--print", "sysroot"])
@@ -80,16 +79,16 @@ impl Sysroot {
80} 79}
81 80
82impl SysrootCrate { 81impl SysrootCrate {
83 pub(crate) fn name(self, sysroot: &Sysroot) -> &SmolStr { 82 pub fn name(self, sysroot: &Sysroot) -> &str {
84 &sysroot.crates[self].name 83 &sysroot.crates[self].name
85 } 84 }
86 pub(crate) fn root(self, sysroot: &Sysroot) -> &Path { 85 pub fn root(self, sysroot: &Sysroot) -> &Path {
87 sysroot.crates[self].root.as_path() 86 sysroot.crates[self].root.as_path()
88 } 87 }
89 pub(crate) fn root_dir(self, sysroot: &Sysroot) -> &Path { 88 pub fn root_dir(self, sysroot: &Sysroot) -> &Path {
90 self.root(sysroot).parent().unwrap() 89 self.root(sysroot).parent().unwrap()
91 } 90 }
92 pub(crate) fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a { 91 pub fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
93 sysroot.crates[self].deps.iter().map(|&it| it) 92 sysroot.crates[self].deps.iter().map(|&it| it)
94 } 93 }
95} 94}
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 0ec8492aa..7c7a85a75 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -15,7 +15,7 @@ drop_bomb = "0.1.4"
15parking_lot = "0.7.0" 15parking_lot = "0.7.0"
16rowan = "0.3.3" 16rowan = "0.3.3"
17 17
18# ideally, `serde` should be enabled by `ra_lsp_serder`, but we enable it here 18# ideally, `serde` should be enabled by `ra_lsp_server`, but we enable it here
19# to reduce number of compilations 19# to reduce number of compilations
20text_unit = { version = "0.1.6", features = ["serde"] } 20text_unit = { version = "0.1.6", features = ["serde"] }
21smol_str = { version = "0.1.9", features = ["serde"] } 21smol_str = { version = "0.1.9", features = ["serde"] }
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 60314d245..256277609 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -1821,6 +1821,38 @@ impl LiteralExpr {
1821 1821
1822impl LiteralExpr {} 1822impl LiteralExpr {}
1823 1823
1824// LiteralPat
1825#[derive(Debug, PartialEq, Eq, Hash)]
1826#[repr(transparent)]
1827pub struct LiteralPat {
1828 pub(crate) syntax: SyntaxNode,
1829}
1830unsafe impl TransparentNewType for LiteralPat {
1831 type Repr = rowan::SyntaxNode<RaTypes>;
1832}
1833
1834impl AstNode for LiteralPat {
1835 fn cast(syntax: &SyntaxNode) -> Option<&Self> {
1836 match syntax.kind() {
1837 LITERAL_PAT => Some(LiteralPat::from_repr(syntax.into_repr())),
1838 _ => None,
1839 }
1840 }
1841 fn syntax(&self) -> &SyntaxNode { &self.syntax }
1842}
1843
1844impl ToOwned for LiteralPat {
1845 type Owned = TreeArc<LiteralPat>;
1846 fn to_owned(&self) -> TreeArc<LiteralPat> { TreeArc::cast(self.syntax.to_owned()) }
1847}
1848
1849
1850impl LiteralPat {
1851 pub fn literal(&self) -> Option<&Literal> {
1852 super::child_opt(self)
1853 }
1854}
1855
1824// LoopExpr 1856// LoopExpr
1825#[derive(Debug, PartialEq, Eq, Hash)] 1857#[derive(Debug, PartialEq, Eq, Hash)]
1826#[repr(transparent)] 1858#[repr(transparent)]
@@ -2594,6 +2626,7 @@ pub enum PatKind<'a> {
2594 TuplePat(&'a TuplePat), 2626 TuplePat(&'a TuplePat),
2595 SlicePat(&'a SlicePat), 2627 SlicePat(&'a SlicePat),
2596 RangePat(&'a RangePat), 2628 RangePat(&'a RangePat),
2629 LiteralPat(&'a LiteralPat),
2597} 2630}
2598 2631
2599impl AstNode for Pat { 2632impl AstNode for Pat {
@@ -2607,7 +2640,8 @@ impl AstNode for Pat {
2607 | TUPLE_STRUCT_PAT 2640 | TUPLE_STRUCT_PAT
2608 | TUPLE_PAT 2641 | TUPLE_PAT
2609 | SLICE_PAT 2642 | SLICE_PAT
2610 | RANGE_PAT => Some(Pat::from_repr(syntax.into_repr())), 2643 | RANGE_PAT
2644 | LITERAL_PAT => Some(Pat::from_repr(syntax.into_repr())),
2611 _ => None, 2645 _ => None,
2612 } 2646 }
2613 } 2647 }
@@ -2631,6 +2665,7 @@ impl Pat {
2631 TUPLE_PAT => PatKind::TuplePat(TuplePat::cast(&self.syntax).unwrap()), 2665 TUPLE_PAT => PatKind::TuplePat(TuplePat::cast(&self.syntax).unwrap()),
2632 SLICE_PAT => PatKind::SlicePat(SlicePat::cast(&self.syntax).unwrap()), 2666 SLICE_PAT => PatKind::SlicePat(SlicePat::cast(&self.syntax).unwrap()),
2633 RANGE_PAT => PatKind::RangePat(RangePat::cast(&self.syntax).unwrap()), 2667 RANGE_PAT => PatKind::RangePat(RangePat::cast(&self.syntax).unwrap()),
2668 LITERAL_PAT => PatKind::LiteralPat(LiteralPat::cast(&self.syntax).unwrap()),
2634 _ => unreachable!(), 2669 _ => unreachable!(),
2635 } 2670 }
2636 } 2671 }
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 046db5885..d428bc595 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -161,6 +161,7 @@ Grammar(
161 "TUPLE_PAT", 161 "TUPLE_PAT",
162 "SLICE_PAT", 162 "SLICE_PAT",
163 "RANGE_PAT", 163 "RANGE_PAT",
164 "LITERAL_PAT",
164 165
165 // atoms 166 // atoms
166 "TUPLE_EXPR", 167 "TUPLE_EXPR",
@@ -524,6 +525,7 @@ Grammar(
524 "TuplePat": ( collections: [["args", "Pat"]] ), 525 "TuplePat": ( collections: [["args", "Pat"]] ),
525 "SlicePat": (), 526 "SlicePat": (),
526 "RangePat": (), 527 "RangePat": (),
528 "LiteralPat": (options: ["Literal"]),
527 529
528 "Pat": ( 530 "Pat": (
529 enum: [ 531 enum: [
@@ -536,6 +538,7 @@ Grammar(
536 "TuplePat", 538 "TuplePat",
537 "SlicePat", 539 "SlicePat",
538 "RangePat", 540 "RangePat",
541 "LiteralPat",
539 ], 542 ],
540 ), 543 ),
541 544
diff --git a/crates/ra_syntax/src/grammar/patterns.rs b/crates/ra_syntax/src/grammar/patterns.rs
index f3f400ae0..9d7da639d 100644
--- a/crates/ra_syntax/src/grammar/patterns.rs
+++ b/crates/ra_syntax/src/grammar/patterns.rs
@@ -43,21 +43,8 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
43 return Some(path_pat(p)); 43 return Some(path_pat(p));
44 } 44 }
45 45
46 // test literal_pattern 46 if is_literal_pat_start(p) {
47 // fn main() { 47 return Some(literal_pat(p));
48 // match () {
49 // -1 => (),
50 // 92 => (),
51 // 'c' => (),
52 // "hello" => (),
53 // }
54 // }
55 if p.at(MINUS) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER) {
56 p.bump();
57 }
58
59 if let Some(m) = expressions::literal(p) {
60 return Some(m);
61 } 48 }
62 49
63 let m = match la0 { 50 let m = match la0 {
@@ -73,6 +60,30 @@ fn atom_pat(p: &mut Parser, recovery_set: TokenSet) -> Option<CompletedMarker> {
73 Some(m) 60 Some(m)
74} 61}
75 62
63fn is_literal_pat_start(p: &mut Parser) -> bool {
64 p.at(MINUS) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER)
65 || p.at_ts(expressions::LITERAL_FIRST)
66}
67
68// test literal_pattern
69// fn main() {
70// match () {
71// -1 => (),
72// 92 => (),
73// 'c' => (),
74// "hello" => (),
75// }
76// }
77fn literal_pat(p: &mut Parser) -> CompletedMarker {
78 assert!(is_literal_pat_start(p));
79 let m = p.start();
80 if p.at(MINUS) {
81 p.bump();
82 }
83 expressions::literal(p);
84 m.complete(p, LITERAL_PAT)
85}
86
76// test path_part 87// test path_part
77// fn foo() { 88// fn foo() {
78// let foo::Bar = (); 89// let foo::Bar = ();
diff --git a/crates/ra_syntax/src/syntax_kinds/generated.rs b/crates/ra_syntax/src/syntax_kinds/generated.rs
index fea513458..266b95bbb 100644
--- a/crates/ra_syntax/src/syntax_kinds/generated.rs
+++ b/crates/ra_syntax/src/syntax_kinds/generated.rs
@@ -157,6 +157,7 @@ pub enum SyntaxKind {
157 TUPLE_PAT, 157 TUPLE_PAT,
158 SLICE_PAT, 158 SLICE_PAT,
159 RANGE_PAT, 159 RANGE_PAT,
160 LITERAL_PAT,
160 TUPLE_EXPR, 161 TUPLE_EXPR,
161 ARRAY_EXPR, 162 ARRAY_EXPR,
162 PAREN_EXPR, 163 PAREN_EXPR,
@@ -493,6 +494,7 @@ impl SyntaxKind {
493 TUPLE_PAT => &SyntaxInfo { name: "TUPLE_PAT" }, 494 TUPLE_PAT => &SyntaxInfo { name: "TUPLE_PAT" },
494 SLICE_PAT => &SyntaxInfo { name: "SLICE_PAT" }, 495 SLICE_PAT => &SyntaxInfo { name: "SLICE_PAT" },
495 RANGE_PAT => &SyntaxInfo { name: "RANGE_PAT" }, 496 RANGE_PAT => &SyntaxInfo { name: "RANGE_PAT" },
497 LITERAL_PAT => &SyntaxInfo { name: "LITERAL_PAT" },
496 TUPLE_EXPR => &SyntaxInfo { name: "TUPLE_EXPR" }, 498 TUPLE_EXPR => &SyntaxInfo { name: "TUPLE_EXPR" },
497 ARRAY_EXPR => &SyntaxInfo { name: "ARRAY_EXPR" }, 499 ARRAY_EXPR => &SyntaxInfo { name: "ARRAY_EXPR" },
498 PAREN_EXPR => &SyntaxInfo { name: "PAREN_EXPR" }, 500 PAREN_EXPR => &SyntaxInfo { name: "PAREN_EXPR" },
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0055_literal_pattern.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0055_literal_pattern.txt
index 71d62eaba..51c5ab7f2 100644
--- a/crates/ra_syntax/tests/data/parser/inline/ok/0055_literal_pattern.txt
+++ b/crates/ra_syntax/tests/data/parser/inline/ok/0055_literal_pattern.txt
@@ -22,9 +22,10 @@ SOURCE_FILE@[0; 113)
22 L_CURLY@[25; 26) 22 L_CURLY@[25; 26)
23 WHITESPACE@[26; 35) 23 WHITESPACE@[26; 35)
24 MATCH_ARM@[35; 43) 24 MATCH_ARM@[35; 43)
25 MINUS@[35; 36) 25 LITERAL_PAT@[35; 37)
26 LITERAL@[36; 37) 26 MINUS@[35; 36)
27 INT_NUMBER@[36; 37) "1" 27 LITERAL@[36; 37)
28 INT_NUMBER@[36; 37) "1"
28 WHITESPACE@[37; 38) 29 WHITESPACE@[37; 38)
29 FAT_ARROW@[38; 40) 30 FAT_ARROW@[38; 40)
30 WHITESPACE@[40; 41) 31 WHITESPACE@[40; 41)
@@ -34,8 +35,9 @@ SOURCE_FILE@[0; 113)
34 COMMA@[43; 44) 35 COMMA@[43; 44)
35 WHITESPACE@[44; 53) 36 WHITESPACE@[44; 53)
36 MATCH_ARM@[53; 61) 37 MATCH_ARM@[53; 61)
37 LITERAL@[53; 55) 38 LITERAL_PAT@[53; 55)
38 INT_NUMBER@[53; 55) "92" 39 LITERAL@[53; 55)
40 INT_NUMBER@[53; 55) "92"
39 WHITESPACE@[55; 56) 41 WHITESPACE@[55; 56)
40 FAT_ARROW@[56; 58) 42 FAT_ARROW@[56; 58)
41 WHITESPACE@[58; 59) 43 WHITESPACE@[58; 59)
@@ -45,8 +47,9 @@ SOURCE_FILE@[0; 113)
45 COMMA@[61; 62) 47 COMMA@[61; 62)
46 WHITESPACE@[62; 71) 48 WHITESPACE@[62; 71)
47 MATCH_ARM@[71; 80) 49 MATCH_ARM@[71; 80)
48 LITERAL@[71; 74) 50 LITERAL_PAT@[71; 74)
49 CHAR@[71; 74) 51 LITERAL@[71; 74)
52 CHAR@[71; 74)
50 WHITESPACE@[74; 75) 53 WHITESPACE@[74; 75)
51 FAT_ARROW@[75; 77) 54 FAT_ARROW@[75; 77)
52 WHITESPACE@[77; 78) 55 WHITESPACE@[77; 78)
@@ -56,8 +59,9 @@ SOURCE_FILE@[0; 113)
56 COMMA@[80; 81) 59 COMMA@[80; 81)
57 WHITESPACE@[81; 90) 60 WHITESPACE@[81; 90)
58 MATCH_ARM@[90; 103) 61 MATCH_ARM@[90; 103)
59 LITERAL@[90; 97) 62 LITERAL_PAT@[90; 97)
60 STRING@[90; 97) 63 LITERAL@[90; 97)
64 STRING@[90; 97)
61 WHITESPACE@[97; 98) 65 WHITESPACE@[97; 98)
62 FAT_ARROW@[98; 100) 66 FAT_ARROW@[98; 100)
63 WHITESPACE@[100; 101) 67 WHITESPACE@[100; 101)
diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0058_range_pat.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0058_range_pat.txt
index d47f38903..de54c49e6 100644
--- a/crates/ra_syntax/tests/data/parser/inline/ok/0058_range_pat.txt
+++ b/crates/ra_syntax/tests/data/parser/inline/ok/0058_range_pat.txt
@@ -22,13 +22,15 @@ SOURCE_FILE@[0; 112)
22 WHITESPACE@[26; 35) 22 WHITESPACE@[26; 35)
23 MATCH_ARM@[35; 50) 23 MATCH_ARM@[35; 50)
24 RANGE_PAT@[35; 44) 24 RANGE_PAT@[35; 44)
25 LITERAL@[35; 36) 25 LITERAL_PAT@[35; 36)
26 INT_NUMBER@[35; 36) "0" 26 LITERAL@[35; 36)
27 INT_NUMBER@[35; 36) "0"
27 WHITESPACE@[36; 37) 28 WHITESPACE@[36; 37)
28 DOTDOTDOT@[37; 40) 29 DOTDOTDOT@[37; 40)
29 WHITESPACE@[40; 41) 30 WHITESPACE@[40; 41)
30 LITERAL@[41; 44) 31 LITERAL_PAT@[41; 44)
31 INT_NUMBER@[41; 44) "100" 32 LITERAL@[41; 44)
33 INT_NUMBER@[41; 44) "100"
32 WHITESPACE@[44; 45) 34 WHITESPACE@[44; 45)
33 FAT_ARROW@[45; 47) 35 FAT_ARROW@[45; 47)
34 WHITESPACE@[47; 48) 36 WHITESPACE@[47; 48)
@@ -39,13 +41,15 @@ SOURCE_FILE@[0; 112)
39 WHITESPACE@[51; 60) 41 WHITESPACE@[51; 60)
40 MATCH_ARM@[60; 77) 42 MATCH_ARM@[60; 77)
41 RANGE_PAT@[60; 71) 43 RANGE_PAT@[60; 71)
42 LITERAL@[60; 63) 44 LITERAL_PAT@[60; 63)
43 INT_NUMBER@[60; 63) "101" 45 LITERAL@[60; 63)
46 INT_NUMBER@[60; 63) "101"
44 WHITESPACE@[63; 64) 47 WHITESPACE@[63; 64)
45 DOTDOTEQ@[64; 67) 48 DOTDOTEQ@[64; 67)
46 WHITESPACE@[67; 68) 49 WHITESPACE@[67; 68)
47 LITERAL@[68; 71) 50 LITERAL_PAT@[68; 71)
48 INT_NUMBER@[68; 71) "200" 51 LITERAL@[68; 71)
52 INT_NUMBER@[68; 71) "200"
49 WHITESPACE@[71; 72) 53 WHITESPACE@[71; 72)
50 FAT_ARROW@[72; 74) 54 FAT_ARROW@[72; 74)
51 WHITESPACE@[74; 75) 55 WHITESPACE@[74; 75)
@@ -56,13 +60,15 @@ SOURCE_FILE@[0; 112)
56 WHITESPACE@[78; 87) 60 WHITESPACE@[78; 87)
57 MATCH_ARM@[87; 102) 61 MATCH_ARM@[87; 102)
58 RANGE_PAT@[87; 97) 62 RANGE_PAT@[87; 97)
59 LITERAL@[87; 90) 63 LITERAL_PAT@[87; 90)
60 INT_NUMBER@[87; 90) "200" 64 LITERAL@[87; 90)
65 INT_NUMBER@[87; 90) "200"
61 WHITESPACE@[90; 91) 66 WHITESPACE@[90; 91)
62 DOTDOT@[91; 93) 67 DOTDOT@[91; 93)
63 WHITESPACE@[93; 94) 68 WHITESPACE@[93; 94)
64 LITERAL@[94; 97) 69 LITERAL_PAT@[94; 97)
65 INT_NUMBER@[94; 97) "301" 70 LITERAL@[94; 97)
71 INT_NUMBER@[94; 97) "301"
66 FAT_ARROW@[97; 99) 72 FAT_ARROW@[97; 99)
67 WHITESPACE@[99; 100) 73 WHITESPACE@[99; 100)
68 TUPLE_EXPR@[100; 102) 74 TUPLE_EXPR@[100; 102)
diff --git a/crates/ra_syntax/tests/data/parser/ok/0035_weird_exprs.txt b/crates/ra_syntax/tests/data/parser/ok/0035_weird_exprs.txt
index 20932a837..f3987c3b7 100644
--- a/crates/ra_syntax/tests/data/parser/ok/0035_weird_exprs.txt
+++ b/crates/ra_syntax/tests/data/parser/ok/0035_weird_exprs.txt
@@ -456,8 +456,9 @@ SOURCE_FILE@[0; 3813)
456 L_CURLY@[930; 931) 456 L_CURLY@[930; 931)
457 WHITESPACE@[931; 952) 457 WHITESPACE@[931; 952)
458 MATCH_ARM@[952; 1147) 458 MATCH_ARM@[952; 1147)
459 LITERAL@[952; 953) 459 LITERAL_PAT@[952; 953)
460 INT_NUMBER@[952; 953) "1" 460 LITERAL@[952; 953)
461 INT_NUMBER@[952; 953) "1"
461 WHITESPACE@[953; 954) 462 WHITESPACE@[953; 954)
462 FAT_ARROW@[954; 956) 463 FAT_ARROW@[954; 956)
463 WHITESPACE@[956; 957) 464 WHITESPACE@[956; 957)
@@ -1080,8 +1081,9 @@ SOURCE_FILE@[0; 3813)
1080 L_CURLY@[1853; 1854) 1081 L_CURLY@[1853; 1854)
1081 WHITESPACE@[1854; 1855) 1082 WHITESPACE@[1854; 1855)
1082 MATCH_ARM@[1855; 1863) 1083 MATCH_ARM@[1855; 1863)
1083 LITERAL@[1855; 1856) 1084 LITERAL_PAT@[1855; 1856)
1084 INT_NUMBER@[1855; 1856) "1" 1085 LITERAL@[1855; 1856)
1086 INT_NUMBER@[1855; 1856) "1"
1085 WHITESPACE@[1856; 1857) 1087 WHITESPACE@[1856; 1857)
1086 FAT_ARROW@[1857; 1859) 1088 FAT_ARROW@[1857; 1859)
1087 WHITESPACE@[1859; 1860) 1089 WHITESPACE@[1859; 1860)
diff --git a/crates/ra_vfs/Cargo.toml b/crates/ra_vfs/Cargo.toml
index 383381d2a..01109a9a7 100644
--- a/crates/ra_vfs/Cargo.toml
+++ b/crates/ra_vfs/Cargo.toml
@@ -10,7 +10,7 @@ relative-path = "0.4.0"
10rustc-hash = "1.0" 10rustc-hash = "1.0"
11crossbeam-channel = "0.3.5" 11crossbeam-channel = "0.3.5"
12log = "0.4.6" 12log = "0.4.6"
13notify = "4.0.7" 13notify = "4.0.9"
14drop_bomb = "0.1.0" 14drop_bomb = "0.1.0"
15parking_lot = "0.7.0" 15parking_lot = "0.7.0"
16 16
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 {