aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/assists/auto_import.rs46
-rw-r--r--crates/ra_cargo_watch/Cargo.toml1
-rw-r--r--crates/ra_cargo_watch/src/lib.rs44
-rw-r--r--crates/ra_hir/src/db.rs22
-rw-r--r--crates/ra_hir_def/src/find_path.rs99
-rw-r--r--crates/ra_hir_def/src/marks.rs1
-rw-r--r--crates/ra_hir_def/src/nameres.rs40
-rw-r--r--crates/ra_hir_def/src/nameres/tests.rs43
-rw-r--r--crates/ra_hir_expand/src/name.rs2
-rw-r--r--crates/ra_hir_ty/src/lib.rs2
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs96
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs8
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs70
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs2
-rw-r--r--crates/ra_ide/src/change.rs51
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs92
-rw-r--r--crates/ra_prof/src/lib.rs117
-rw-r--r--crates/test_utils/src/lib.rs82
-rw-r--r--crates/test_utils/src/marks.rs2
19 files changed, 505 insertions, 315 deletions
diff --git a/crates/ra_assists/src/assists/auto_import.rs b/crates/ra_assists/src/assists/auto_import.rs
index 9163cc662..69126a1c9 100644
--- a/crates/ra_assists/src/assists/auto_import.rs
+++ b/crates/ra_assists/src/assists/auto_import.rs
@@ -1,8 +1,8 @@
1use hir::db::HirDatabase; 1use hir::db::HirDatabase;
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, AstNode}, 3 ast::{self, AstNode},
4 SmolStr, SyntaxElement, 4 SmolStr,
5 SyntaxKind::{NAME_REF, USE_ITEM}, 5 SyntaxKind::USE_ITEM,
6 SyntaxNode, 6 SyntaxNode,
7}; 7};
8 8
@@ -32,25 +32,28 @@ pub(crate) fn auto_import<F: ImportsLocator>(
32 ctx: AssistCtx<impl HirDatabase>, 32 ctx: AssistCtx<impl HirDatabase>,
33 imports_locator: &mut F, 33 imports_locator: &mut F,
34) -> Option<Assist> { 34) -> Option<Assist> {
35 let path: ast::Path = ctx.find_node_at_offset()?; 35 let path_to_import: ast::Path = ctx.find_node_at_offset()?;
36 let module = path.syntax().ancestors().find_map(ast::Module::cast); 36 let path_to_import_syntax = path_to_import.syntax();
37 if path_to_import_syntax.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() {
38 return None;
39 }
40
41 let module = path_to_import_syntax.ancestors().find_map(ast::Module::cast);
37 let position = match module.and_then(|it| it.item_list()) { 42 let position = match module.and_then(|it| it.item_list()) {
38 Some(item_list) => item_list.syntax().clone(), 43 Some(item_list) => item_list.syntax().clone(),
39 None => { 44 None => {
40 let current_file = path.syntax().ancestors().find_map(ast::SourceFile::cast)?; 45 let current_file = path_to_import_syntax.ancestors().find_map(ast::SourceFile::cast)?;
41 current_file.syntax().clone() 46 current_file.syntax().clone()
42 } 47 }
43 }; 48 };
44 let source_analyzer = ctx.source_analyzer(&position, None); 49 let source_analyzer = ctx.source_analyzer(&position, None);
45 let module_with_name_to_import = source_analyzer.module()?; 50 let module_with_name_to_import = source_analyzer.module()?;
46 let path_to_import = ctx.covering_element().ancestors().find_map(ast::Path::cast)?;
47 if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() { 51 if source_analyzer.resolve_path(ctx.db, &path_to_import).is_some() {
48 return None; 52 return None;
49 } 53 }
50 54
51 let name_to_import = &find_applicable_name_ref(ctx.covering_element())?.syntax().to_string();
52 let proposed_imports = imports_locator 55 let proposed_imports = imports_locator
53 .find_imports(&name_to_import.to_string()) 56 .find_imports(&path_to_import_syntax.to_string())
54 .into_iter() 57 .into_iter()
55 .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def)) 58 .filter_map(|module_def| module_with_name_to_import.find_use_path(ctx.db, module_def))
56 .filter(|use_path| !use_path.segments.is_empty()) 59 .filter(|use_path| !use_path.segments.is_empty())
@@ -64,26 +67,11 @@ pub(crate) fn auto_import<F: ImportsLocator>(
64 ctx.add_assist_group(AssistId("auto_import"), "auto import", || { 67 ctx.add_assist_group(AssistId("auto_import"), "auto import", || {
65 proposed_imports 68 proposed_imports
66 .into_iter() 69 .into_iter()
67 .map(|import| import_to_action(import, &position, &path_to_import.syntax())) 70 .map(|import| import_to_action(import, &position, &path_to_import_syntax))
68 .collect() 71 .collect()
69 }) 72 })
70} 73}
71 74
72fn find_applicable_name_ref(element: SyntaxElement) -> Option<ast::NameRef> {
73 if element.ancestors().find(|ancestor| ancestor.kind() == USE_ITEM).is_some() {
74 None
75 } else if element.kind() == NAME_REF {
76 Some(element.as_node().cloned().and_then(ast::NameRef::cast)?)
77 } else {
78 let parent = element.parent()?;
79 if parent.kind() == NAME_REF {
80 Some(ast::NameRef::cast(parent)?)
81 } else {
82 None
83 }
84 }
85}
86
87fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { 75fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder {
88 let mut action_builder = ActionBuilder::default(); 76 let mut action_builder = ActionBuilder::default();
89 action_builder.label(format!("Import `{}`", &import)); 77 action_builder.label(format!("Import `{}`", &import));
@@ -110,16 +98,16 @@ mod tests {
110 auto_import, 98 auto_import,
111 TestImportsLocator::new, 99 TestImportsLocator::new,
112 r" 100 r"
113 PubStruct<|> 101 <|>PubStruct
114 102
115 pub mod PubMod { 103 pub mod PubMod {
116 pub struct PubStruct; 104 pub struct PubStruct;
117 } 105 }
118 ", 106 ",
119 r" 107 r"
120 use PubMod::PubStruct; 108 <|>use PubMod::PubStruct;
121 109
122 PubStruct<|> 110 PubStruct
123 111
124 pub mod PubMod { 112 pub mod PubMod {
125 pub struct PubStruct; 113 pub struct PubStruct;
@@ -134,7 +122,7 @@ mod tests {
134 auto_import, 122 auto_import,
135 TestImportsLocator::new, 123 TestImportsLocator::new,
136 r" 124 r"
137 PubStruct<|> 125 PubSt<|>ruct
138 126
139 pub mod PubMod1 { 127 pub mod PubMod1 {
140 pub struct PubStruct; 128 pub struct PubStruct;
@@ -149,7 +137,7 @@ mod tests {
149 r" 137 r"
150 use PubMod1::PubStruct; 138 use PubMod1::PubStruct;
151 139
152 PubStruct<|> 140 PubSt<|>ruct
153 141
154 pub mod PubMod1 { 142 pub mod PubMod1 {
155 pub struct PubStruct; 143 pub struct PubStruct;
diff --git a/crates/ra_cargo_watch/Cargo.toml b/crates/ra_cargo_watch/Cargo.toml
index 49e06e0d3..dd814fc9d 100644
--- a/crates/ra_cargo_watch/Cargo.toml
+++ b/crates/ra_cargo_watch/Cargo.toml
@@ -11,6 +11,7 @@ log = "0.4.3"
11cargo_metadata = "0.9.1" 11cargo_metadata = "0.9.1"
12jod-thread = "0.1.0" 12jod-thread = "0.1.0"
13parking_lot = "0.10.0" 13parking_lot = "0.10.0"
14serde_json = "1.0.45"
14 15
15[dev-dependencies] 16[dev-dependencies]
16insta = "0.13.0" 17insta = "0.13.0"
diff --git a/crates/ra_cargo_watch/src/lib.rs b/crates/ra_cargo_watch/src/lib.rs
index e7b700c10..ea7ddc86b 100644
--- a/crates/ra_cargo_watch/src/lib.rs
+++ b/crates/ra_cargo_watch/src/lib.rs
@@ -9,7 +9,7 @@ use lsp_types::{
9}; 9};
10use std::{ 10use std::{
11 collections::HashMap, 11 collections::HashMap,
12 io::BufReader, 12 io::{BufRead, BufReader},
13 path::PathBuf, 13 path::PathBuf,
14 process::{Command, Stdio}, 14 process::{Command, Stdio},
15 sync::Arc, 15 sync::Arc,
@@ -216,8 +216,10 @@ impl CheckWatcherThread {
216 self.last_update_req.take(); 216 self.last_update_req.take();
217 task_send.send(CheckTask::ClearDiagnostics).unwrap(); 217 task_send.send(CheckTask::ClearDiagnostics).unwrap();
218 218
219 // By replacing the watcher, we drop the previous one which 219 // Replace with a dummy watcher first so we drop the original and wait for completion
220 // causes it to shut down automatically. 220 std::mem::replace(&mut self.watcher, WatchThread::dummy());
221
222 // Then create the actual new watcher
221 self.watcher = WatchThread::new(&self.options, &self.workspace_root); 223 self.watcher = WatchThread::new(&self.options, &self.workspace_root);
222 } 224 }
223 } 225 }
@@ -348,17 +350,45 @@ impl WatchThread {
348 // which will break out of the loop, and continue the shutdown 350 // which will break out of the loop, and continue the shutdown
349 let _ = message_send.send(CheckEvent::Begin); 351 let _ = message_send.send(CheckEvent::Begin);
350 352
351 for message in 353 // We manually read a line at a time, instead of using serde's
352 cargo_metadata::parse_messages(BufReader::new(command.stdout.take().unwrap())) 354 // stream deserializers, because the deserializer cannot recover
353 { 355 // from an error, resulting in it getting stuck, because we try to
356 // be resillient against failures.
357 //
358 // Because cargo only outputs one JSON object per line, we can
359 // simply skip a line if it doesn't parse, which just ignores any
360 // erroneus output.
361 let stdout = BufReader::new(command.stdout.take().unwrap());
362 for line in stdout.lines() {
363 let line = match line {
364 Ok(line) => line,
365 Err(err) => {
366 log::error!("Couldn't read line from cargo: {}", err);
367 continue;
368 }
369 };
370
371 let message = serde_json::from_str::<cargo_metadata::Message>(&line);
354 let message = match message { 372 let message = match message {
355 Ok(message) => message, 373 Ok(message) => message,
356 Err(err) => { 374 Err(err) => {
357 log::error!("Invalid json from cargo check, ignoring: {}", err); 375 log::error!(
376 "Invalid json from cargo check, ignoring ({}): {:?} ",
377 err,
378 line
379 );
358 continue; 380 continue;
359 } 381 }
360 }; 382 };
361 383
384 // Skip certain kinds of messages to only spend time on what's useful
385 match &message {
386 Message::CompilerArtifact(artifact) if artifact.fresh => continue,
387 Message::BuildScriptExecuted(_) => continue,
388 Message::Unknown => continue,
389 _ => {}
390 }
391
362 match message_send.send(CheckEvent::Msg(message)) { 392 match message_send.send(CheckEvent::Msg(message)) {
363 Ok(()) => {} 393 Ok(()) => {}
364 Err(_err) => { 394 Err(_err) => {
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index e6079b88d..a77bf6de6 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -1,20 +1,24 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3pub use hir_def::db::{ 3pub use hir_def::db::{
4 BodyQuery, BodyWithSourceMapQuery, ComputeCrateDefMapQuery, ConstDataQuery, 4 AttrsQuery, BodyQuery, BodyWithSourceMapQuery, ComputeCrateDefMapQuery, ConstDataQuery,
5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery, 5 CrateLangItemsQuery, DefDatabase, DefDatabaseStorage, DocumentationQuery, EnumDataQuery,
6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternDatabase, 6 ExprScopesQuery, FunctionDataQuery, GenericParamsQuery, ImplDataQuery, InternConstQuery,
7 InternDatabaseStorage, LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, 7 InternDatabase, InternDatabaseStorage, InternEnumQuery, InternFunctionQuery, InternImplQuery,
8 StructDataQuery, TraitDataQuery, TypeAliasDataQuery, 8 InternStaticQuery, InternStructQuery, InternTraitQuery, InternTypeAliasQuery, InternUnionQuery,
9 LangItemQuery, ModuleLangItemsQuery, RawItemsQuery, StaticDataQuery, StructDataQuery,
10 TraitDataQuery, TypeAliasDataQuery, UnionDataQuery,
9}; 11};
10pub use hir_expand::db::{ 12pub use hir_expand::db::{
11 AstDatabase, AstDatabaseStorage, AstIdMapQuery, MacroArgQuery, MacroDefQuery, MacroExpandQuery, 13 AstDatabase, AstDatabaseStorage, AstIdMapQuery, InternMacroQuery, MacroArgQuery, MacroDefQuery,
12 ParseMacroQuery, 14 MacroExpandQuery, ParseMacroQuery,
13}; 15};
14pub use hir_ty::db::{ 16pub use hir_ty::db::{
15 AssociatedTyDataQuery, CallableItemSignatureQuery, DoInferQuery, FieldTypesQuery, 17 AssociatedTyDataQuery, AssociatedTyValueQuery, CallableItemSignatureQuery, DoInferQuery,
16 GenericDefaultsQuery, GenericPredicatesQuery, HirDatabase, HirDatabaseStorage, ImplDatumQuery, 18 FieldTypesQuery, GenericDefaultsQuery, GenericPredicatesForParamQuery, GenericPredicatesQuery,
17 ImplsForTraitQuery, ImplsInCrateQuery, StructDatumQuery, TraitDatumQuery, TraitSolveQuery, 19 HirDatabase, HirDatabaseStorage, ImplDatumQuery, ImplSelfTyQuery, ImplTraitQuery,
20 ImplsForTraitQuery, ImplsInCrateQuery, InternAssocTyValueQuery, InternChalkImplQuery,
21 InternTypeCtorQuery, StructDatumQuery, TraitDatumQuery, TraitSolveQuery, TraitSolverQuery,
18 TyQuery, ValueTyQuery, 22 TyQuery, ValueTyQuery,
19}; 23};
20 24
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index 8cc2fb160..43b9b124a 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -7,10 +7,39 @@ use crate::{
7 visibility::Visibility, 7 visibility::Visibility,
8 CrateId, ModuleDefId, ModuleId, 8 CrateId, ModuleDefId, ModuleId,
9}; 9};
10use hir_expand::name::Name; 10use hir_expand::name::{known, Name};
11use test_utils::tested_by;
11 12
12const MAX_PATH_LEN: usize = 15; 13const MAX_PATH_LEN: usize = 15;
13 14
15impl ModPath {
16 fn starts_with_std(&self) -> bool {
17 self.segments.first().filter(|&first_segment| first_segment == &known::std).is_some()
18 }
19
20 // When std library is present, paths starting with `std::`
21 // should be preferred over paths starting with `core::` and `alloc::`
22 fn should_start_with_std(&self) -> bool {
23 self.segments
24 .first()
25 .filter(|&first_segment| {
26 first_segment == &known::alloc || first_segment == &known::core
27 })
28 .is_some()
29 }
30
31 fn len(&self) -> usize {
32 self.segments.len()
33 + match self.kind {
34 PathKind::Plain => 0,
35 PathKind::Super(i) => i as usize,
36 PathKind::Crate => 1,
37 PathKind::Abs => 0,
38 PathKind::DollarCrate(_) => 1,
39 }
40 }
41}
42
14// FIXME: handle local items 43// FIXME: handle local items
15 44
16/// Find a path that can be used to refer to a certain item. This can depend on 45/// Find a path that can be used to refer to a certain item. This can depend on
@@ -112,23 +141,27 @@ fn find_path_inner(
112 Some(path) => path, 141 Some(path) => path,
113 }; 142 };
114 path.segments.push(name); 143 path.segments.push(name);
115 if path_len(&path) < best_path_len { 144
116 best_path_len = path_len(&path); 145 let new_path =
117 best_path = Some(path); 146 if let Some(best_path) = best_path { select_best_path(best_path, path) } else { path };
118 } 147 best_path_len = new_path.len();
148 best_path = Some(new_path);
119 } 149 }
120 best_path 150 best_path
121} 151}
122 152
123fn path_len(path: &ModPath) -> usize { 153fn select_best_path(old_path: ModPath, new_path: ModPath) -> ModPath {
124 path.segments.len() 154 if old_path.starts_with_std() && new_path.should_start_with_std() {
125 + match path.kind { 155 tested_by!(prefer_std_paths);
126 PathKind::Plain => 0, 156 old_path
127 PathKind::Super(i) => i as usize, 157 } else if new_path.starts_with_std() && old_path.should_start_with_std() {
128 PathKind::Crate => 1, 158 tested_by!(prefer_std_paths);
129 PathKind::Abs => 0, 159 new_path
130 PathKind::DollarCrate(_) => 1, 160 } else if new_path.len() < old_path.len() {
131 } 161 new_path
162 } else {
163 old_path
164 }
132} 165}
133 166
134fn find_importable_locations( 167fn find_importable_locations(
@@ -201,6 +234,7 @@ mod tests {
201 use hir_expand::hygiene::Hygiene; 234 use hir_expand::hygiene::Hygiene;
202 use ra_db::fixture::WithFixture; 235 use ra_db::fixture::WithFixture;
203 use ra_syntax::ast::AstNode; 236 use ra_syntax::ast::AstNode;
237 use test_utils::covers;
204 238
205 /// `code` needs to contain a cursor marker; checks that `find_path` for the 239 /// `code` needs to contain a cursor marker; checks that `find_path` for the
206 /// item the `path` refers to returns that same path when called from the 240 /// item the `path` refers to returns that same path when called from the
@@ -452,4 +486,41 @@ mod tests {
452 "#; 486 "#;
453 check_found_path(code, "crate::foo::S"); 487 check_found_path(code, "crate::foo::S");
454 } 488 }
489
490 #[test]
491 fn prefer_std_paths_over_alloc() {
492 covers!(prefer_std_paths);
493 let code = r#"
494 //- /main.rs crate:main deps:alloc,std
495 <|>
496
497 //- /std.rs crate:std deps:alloc
498 pub mod sync {
499 pub use alloc::sync::Arc;
500 }
501
502 //- /zzz.rs crate:alloc
503 pub mod sync {
504 pub struct Arc;
505 }
506 "#;
507 check_found_path(code, "std::sync::Arc");
508 }
509
510 #[test]
511 fn prefer_shorter_paths_if_not_alloc() {
512 let code = r#"
513 //- /main.rs crate:main deps:megaalloc,std
514 <|>
515
516 //- /std.rs crate:std deps:megaalloc
517 pub mod sync {
518 pub use megaalloc::sync::Arc;
519 }
520
521 //- /zzz.rs crate:megaalloc
522 pub struct Arc;
523 "#;
524 check_found_path(code, "megaalloc::Arc");
525 }
455} 526}
diff --git a/crates/ra_hir_def/src/marks.rs b/crates/ra_hir_def/src/marks.rs
index 457ba4abe..daa49d5f1 100644
--- a/crates/ra_hir_def/src/marks.rs
+++ b/crates/ra_hir_def/src/marks.rs
@@ -13,4 +13,5 @@ test_utils::marks!(
13 macro_dollar_crate_self 13 macro_dollar_crate_self
14 macro_dollar_crate_other 14 macro_dollar_crate_other
15 infer_resolve_while_let 15 infer_resolve_while_let
16 prefer_std_paths
16); 17);
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index 27c12e46c..852304dd0 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -229,6 +229,46 @@ impl CrateDefMap {
229 self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow); 229 self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path, shadow);
230 (res.resolved_def, res.segment_index) 230 (res.resolved_def, res.segment_index)
231 } 231 }
232
233 // FIXME: this can use some more human-readable format (ideally, an IR
234 // even), as this should be a great debugging aid.
235 pub fn dump(&self) -> String {
236 let mut buf = String::new();
237 go(&mut buf, self, "\ncrate", self.root);
238 return buf.trim().to_string();
239
240 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: LocalModuleId) {
241 *buf += path;
242 *buf += "\n";
243
244 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
245 entries.sort_by_key(|(name, _)| name.clone());
246
247 for (name, def) in entries {
248 *buf += &format!("{}:", name);
249
250 if def.types.is_some() {
251 *buf += " t";
252 }
253 if def.values.is_some() {
254 *buf += " v";
255 }
256 if def.macros.is_some() {
257 *buf += " m";
258 }
259 if def.is_none() {
260 *buf += " _";
261 }
262
263 *buf += "\n";
264 }
265
266 for (name, child) in map.modules[module].children.iter() {
267 let path = path.to_string() + &format!("::{}", name);
268 go(buf, map, &path, *child);
269 }
270 }
271 }
232} 272}
233 273
234impl ModuleData { 274impl ModuleData {
diff --git a/crates/ra_hir_def/src/nameres/tests.rs b/crates/ra_hir_def/src/nameres/tests.rs
index 78bcdc850..82f0f835c 100644
--- a/crates/ra_hir_def/src/nameres/tests.rs
+++ b/crates/ra_hir_def/src/nameres/tests.rs
@@ -10,11 +10,10 @@ use insta::assert_snapshot;
10use ra_db::{fixture::WithFixture, SourceDatabase}; 10use ra_db::{fixture::WithFixture, SourceDatabase};
11use test_utils::covers; 11use test_utils::covers;
12 12
13use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId}; 13use crate::{db::DefDatabase, nameres::*, test_db::TestDB};
14 14
15fn def_map(fixture: &str) -> String { 15fn def_map(fixture: &str) -> String {
16 let dm = compute_crate_def_map(fixture); 16 compute_crate_def_map(fixture).dump()
17 render_crate_def_map(&dm)
18} 17}
19 18
20fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> { 19fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
@@ -23,44 +22,6 @@ fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> {
23 db.crate_def_map(krate) 22 db.crate_def_map(krate)
24} 23}
25 24
26fn render_crate_def_map(map: &CrateDefMap) -> String {
27 let mut buf = String::new();
28 go(&mut buf, map, "\ncrate", map.root);
29 return buf.trim().to_string();
30
31 fn go(buf: &mut String, map: &CrateDefMap, path: &str, module: LocalModuleId) {
32 *buf += path;
33 *buf += "\n";
34
35 let mut entries: Vec<_> = map.modules[module].scope.resolutions().collect();
36 entries.sort_by_key(|(name, _)| name.clone());
37
38 for (name, def) in entries {
39 *buf += &format!("{}:", name);
40
41 if def.types.is_some() {
42 *buf += " t";
43 }
44 if def.values.is_some() {
45 *buf += " v";
46 }
47 if def.macros.is_some() {
48 *buf += " m";
49 }
50 if def.is_none() {
51 *buf += " _";
52 }
53
54 *buf += "\n";
55 }
56
57 for (name, child) in map.modules[module].children.iter() {
58 let path = path.to_string() + &format!("::{}", name);
59 go(buf, map, &path, *child);
60 }
61 }
62}
63
64#[test] 25#[test]
65fn crate_def_map_smoke_test() { 26fn crate_def_map_smoke_test() {
66 let map = def_map( 27 let map = def_map(
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index b3fa1efba..b2e10f445 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -141,6 +141,8 @@ pub mod known {
141 macro_rules, 141 macro_rules,
142 // Components of known path (value or mod name) 142 // Components of known path (value or mod name)
143 std, 143 std,
144 core,
145 alloc,
144 iter, 146 iter,
145 ops, 147 ops,
146 future, 148 future,
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index 908e4862d..08d501ccd 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -847,7 +847,7 @@ impl HirDisplay for ApplicationTy {
847 } 847 }
848 TypeCtor::Array => { 848 TypeCtor::Array => {
849 let t = self.parameters.as_single(); 849 let t = self.parameters.as_single();
850 write!(f, "[{};_]", t.display(f.db))?; 850 write!(f, "[{}; _]", t.display(f.db))?;
851 } 851 }
852 TypeCtor::RawPtr(m) => { 852 TypeCtor::RawPtr(m) => {
853 let t = self.parameters.as_single(); 853 let t = self.parameters.as_single();
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 7e99a42ed..76a1b46c0 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -71,42 +71,42 @@ fn test2() {
71 [82; 93) '{ loop {} }': T 71 [82; 93) '{ loop {} }': T
72 [84; 91) 'loop {}': ! 72 [84; 91) 'loop {}': !
73 [89; 91) '{}': () 73 [89; 91) '{}': ()
74 [122; 133) '{ loop {} }': *mut [T;_] 74 [122; 133) '{ loop {} }': *mut [T; _]
75 [124; 131) 'loop {}': ! 75 [124; 131) 'loop {}': !
76 [129; 131) '{}': () 76 [129; 131) '{}': ()
77 [160; 173) '{ gen() }': *mut [U] 77 [160; 173) '{ gen() }': *mut [U]
78 [166; 169) 'gen': fn gen<U>() -> *mut [T;_] 78 [166; 169) 'gen': fn gen<U>() -> *mut [T; _]
79 [166; 171) 'gen()': *mut [U;_] 79 [166; 171) 'gen()': *mut [U; _]
80 [186; 420) '{ ...rr); }': () 80 [186; 420) '{ ...rr); }': ()
81 [196; 199) 'arr': &[u8;_] 81 [196; 199) 'arr': &[u8; _]
82 [212; 216) '&[1]': &[u8;_] 82 [212; 216) '&[1]': &[u8; _]
83 [213; 216) '[1]': [u8;_] 83 [213; 216) '[1]': [u8; _]
84 [214; 215) '1': u8 84 [214; 215) '1': u8
85 [227; 228) 'a': &[u8] 85 [227; 228) 'a': &[u8]
86 [237; 240) 'arr': &[u8;_] 86 [237; 240) 'arr': &[u8; _]
87 [250; 251) 'b': u8 87 [250; 251) 'b': u8
88 [254; 255) 'f': fn f<u8>(&[T]) -> T 88 [254; 255) 'f': fn f<u8>(&[T]) -> T
89 [254; 260) 'f(arr)': u8 89 [254; 260) 'f(arr)': u8
90 [256; 259) 'arr': &[u8;_] 90 [256; 259) 'arr': &[u8; _]
91 [270; 271) 'c': &[u8] 91 [270; 271) 'c': &[u8]
92 [280; 287) '{ arr }': &[u8] 92 [280; 287) '{ arr }': &[u8]
93 [282; 285) 'arr': &[u8;_] 93 [282; 285) 'arr': &[u8; _]
94 [297; 298) 'd': u8 94 [297; 298) 'd': u8
95 [301; 302) 'g': fn g<u8>(S<&[T]>) -> T 95 [301; 302) 'g': fn g<u8>(S<&[T]>) -> T
96 [301; 316) 'g(S { a: arr })': u8 96 [301; 316) 'g(S { a: arr })': u8
97 [303; 315) 'S { a: arr }': S<&[u8]> 97 [303; 315) 'S { a: arr }': S<&[u8]>
98 [310; 313) 'arr': &[u8;_] 98 [310; 313) 'arr': &[u8; _]
99 [326; 327) 'e': [&[u8];_] 99 [326; 327) 'e': [&[u8]; _]
100 [341; 346) '[arr]': [&[u8];_] 100 [341; 346) '[arr]': [&[u8]; _]
101 [342; 345) 'arr': &[u8;_] 101 [342; 345) 'arr': &[u8; _]
102 [356; 357) 'f': [&[u8];_] 102 [356; 357) 'f': [&[u8]; _]
103 [371; 379) '[arr; 2]': [&[u8];_] 103 [371; 379) '[arr; 2]': [&[u8]; _]
104 [372; 375) 'arr': &[u8;_] 104 [372; 375) 'arr': &[u8; _]
105 [377; 378) '2': usize 105 [377; 378) '2': usize
106 [389; 390) 'g': (&[u8], &[u8]) 106 [389; 390) 'g': (&[u8], &[u8])
107 [407; 417) '(arr, arr)': (&[u8], &[u8]) 107 [407; 417) '(arr, arr)': (&[u8], &[u8])
108 [408; 411) 'arr': &[u8;_] 108 [408; 411) 'arr': &[u8; _]
109 [413; 416) 'arr': &[u8;_] 109 [413; 416) 'arr': &[u8; _]
110 "### 110 "###
111 ); 111 );
112} 112}
@@ -122,8 +122,8 @@ fn test() {
122 @r###" 122 @r###"
123 [11; 40) '{ ...[1]; }': () 123 [11; 40) '{ ...[1]; }': ()
124 [21; 22) 'x': &[i32] 124 [21; 22) 'x': &[i32]
125 [33; 37) '&[1]': &[i32;_] 125 [33; 37) '&[1]': &[i32; _]
126 [34; 37) '[1]': [i32;_] 126 [34; 37) '[1]': [i32; _]
127 [35; 36) '1': i32 127 [35; 36) '1': i32
128 "###); 128 "###);
129} 129}
@@ -159,22 +159,22 @@ fn test(a: A<[u8; 2]>, b: B<[u8; 2]>, c: C<[u8; 2]>) {
159 [334; 335) 'x': C<[T]> 159 [334; 335) 'x': C<[T]>
160 [355; 360) '{ x }': C<[T]> 160 [355; 360) '{ x }': C<[T]>
161 [357; 358) 'x': C<[T]> 161 [357; 358) 'x': C<[T]>
162 [370; 371) 'a': A<[u8;_]> 162 [370; 371) 'a': A<[u8; _]>
163 [385; 386) 'b': B<[u8;_]> 163 [385; 386) 'b': B<[u8; _]>
164 [400; 401) 'c': C<[u8;_]> 164 [400; 401) 'c': C<[u8; _]>
165 [415; 481) '{ ...(c); }': () 165 [415; 481) '{ ...(c); }': ()
166 [425; 426) 'd': A<[{unknown}]> 166 [425; 426) 'd': A<[{unknown}]>
167 [429; 433) 'foo1': fn foo1<{unknown}>(A<[T]>) -> A<[T]> 167 [429; 433) 'foo1': fn foo1<{unknown}>(A<[T]>) -> A<[T]>
168 [429; 436) 'foo1(a)': A<[{unknown}]> 168 [429; 436) 'foo1(a)': A<[{unknown}]>
169 [434; 435) 'a': A<[u8;_]> 169 [434; 435) 'a': A<[u8; _]>
170 [446; 447) 'e': B<[u8]> 170 [446; 447) 'e': B<[u8]>
171 [450; 454) 'foo2': fn foo2<u8>(B<[T]>) -> B<[T]> 171 [450; 454) 'foo2': fn foo2<u8>(B<[T]>) -> B<[T]>
172 [450; 457) 'foo2(b)': B<[u8]> 172 [450; 457) 'foo2(b)': B<[u8]>
173 [455; 456) 'b': B<[u8;_]> 173 [455; 456) 'b': B<[u8; _]>
174 [467; 468) 'f': C<[u8]> 174 [467; 468) 'f': C<[u8]>
175 [471; 475) 'foo3': fn foo3<u8>(C<[T]>) -> C<[T]> 175 [471; 475) 'foo3': fn foo3<u8>(C<[T]>) -> C<[T]>
176 [471; 478) 'foo3(c)': C<[u8]> 176 [471; 478) 'foo3(c)': C<[u8]>
177 [476; 477) 'c': C<[u8;_]> 177 [476; 477) 'c': C<[u8; _]>
178 "### 178 "###
179 ); 179 );
180} 180}
@@ -204,12 +204,12 @@ fn test() {
204 [72; 97) '{ ... }': &[i32] 204 [72; 97) '{ ... }': &[i32]
205 [82; 85) 'foo': fn foo<i32>(&[T]) -> &[T] 205 [82; 85) 'foo': fn foo<i32>(&[T]) -> &[T]
206 [82; 91) 'foo(&[1])': &[i32] 206 [82; 91) 'foo(&[1])': &[i32]
207 [86; 90) '&[1]': &[i32;_] 207 [86; 90) '&[1]': &[i32; _]
208 [87; 90) '[1]': [i32;_] 208 [87; 90) '[1]': [i32; _]
209 [88; 89) '1': i32 209 [88; 89) '1': i32
210 [103; 123) '{ ... }': &[i32;_] 210 [103; 123) '{ ... }': &[i32; _]
211 [113; 117) '&[1]': &[i32;_] 211 [113; 117) '&[1]': &[i32; _]
212 [114; 117) '[1]': [i32;_] 212 [114; 117) '[1]': [i32; _]
213 [115; 116) '1': i32 213 [115; 116) '1': i32
214 "### 214 "###
215 ); 215 );
@@ -237,15 +237,15 @@ fn test() {
237 [60; 61) 'x': &[i32] 237 [60; 61) 'x': &[i32]
238 [64; 123) 'if tru... }': &[i32] 238 [64; 123) 'if tru... }': &[i32]
239 [67; 71) 'true': bool 239 [67; 71) 'true': bool
240 [72; 92) '{ ... }': &[i32;_] 240 [72; 92) '{ ... }': &[i32; _]
241 [82; 86) '&[1]': &[i32;_] 241 [82; 86) '&[1]': &[i32; _]
242 [83; 86) '[1]': [i32;_] 242 [83; 86) '[1]': [i32; _]
243 [84; 85) '1': i32 243 [84; 85) '1': i32
244 [98; 123) '{ ... }': &[i32] 244 [98; 123) '{ ... }': &[i32]
245 [108; 111) 'foo': fn foo<i32>(&[T]) -> &[T] 245 [108; 111) 'foo': fn foo<i32>(&[T]) -> &[T]
246 [108; 117) 'foo(&[1])': &[i32] 246 [108; 117) 'foo(&[1])': &[i32]
247 [112; 116) '&[1]': &[i32;_] 247 [112; 116) '&[1]': &[i32; _]
248 [113; 116) '[1]': [i32;_] 248 [113; 116) '[1]': [i32; _]
249 [114; 115) '1': i32 249 [114; 115) '1': i32
250 "### 250 "###
251 ); 251 );
@@ -277,16 +277,16 @@ fn test(i: i32) {
277 [88; 89) '2': i32 277 [88; 89) '2': i32
278 [93; 96) 'foo': fn foo<i32>(&[T]) -> &[T] 278 [93; 96) 'foo': fn foo<i32>(&[T]) -> &[T]
279 [93; 102) 'foo(&[2])': &[i32] 279 [93; 102) 'foo(&[2])': &[i32]
280 [97; 101) '&[2]': &[i32;_] 280 [97; 101) '&[2]': &[i32; _]
281 [98; 101) '[2]': [i32;_] 281 [98; 101) '[2]': [i32; _]
282 [99; 100) '2': i32 282 [99; 100) '2': i32
283 [112; 113) '1': i32 283 [112; 113) '1': i32
284 [117; 121) '&[1]': &[i32;_] 284 [117; 121) '&[1]': &[i32; _]
285 [118; 121) '[1]': [i32;_] 285 [118; 121) '[1]': [i32; _]
286 [119; 120) '1': i32 286 [119; 120) '1': i32
287 [131; 132) '_': i32 287 [131; 132) '_': i32
288 [136; 140) '&[3]': &[i32;_] 288 [136; 140) '&[3]': &[i32; _]
289 [137; 140) '[3]': [i32;_] 289 [137; 140) '[3]': [i32; _]
290 [138; 139) '3': i32 290 [138; 139) '3': i32
291 "### 291 "###
292 ); 292 );
@@ -316,18 +316,18 @@ fn test(i: i32) {
316 [70; 147) 'match ... }': &[i32] 316 [70; 147) 'match ... }': &[i32]
317 [76; 77) 'i': i32 317 [76; 77) 'i': i32
318 [88; 89) '1': i32 318 [88; 89) '1': i32
319 [93; 97) '&[1]': &[i32;_] 319 [93; 97) '&[1]': &[i32; _]
320 [94; 97) '[1]': [i32;_] 320 [94; 97) '[1]': [i32; _]
321 [95; 96) '1': i32 321 [95; 96) '1': i32
322 [107; 108) '2': i32 322 [107; 108) '2': i32
323 [112; 115) 'foo': fn foo<i32>(&[T]) -> &[T] 323 [112; 115) 'foo': fn foo<i32>(&[T]) -> &[T]
324 [112; 121) 'foo(&[2])': &[i32] 324 [112; 121) 'foo(&[2])': &[i32]
325 [116; 120) '&[2]': &[i32;_] 325 [116; 120) '&[2]': &[i32; _]
326 [117; 120) '[2]': [i32;_] 326 [117; 120) '[2]': [i32; _]
327 [118; 119) '2': i32 327 [118; 119) '2': i32
328 [131; 132) '_': i32 328 [131; 132) '_': i32
329 [136; 140) '&[3]': &[i32;_] 329 [136; 140) '&[3]': &[i32; _]
330 [137; 140) '[3]': [i32;_] 330 [137; 140) '[3]': [i32; _]
331 [138; 139) '3': i32 331 [138; 139) '3': i32
332 "### 332 "###
333 ); 333 );
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index aa948dcbf..02bab6dbe 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -102,7 +102,7 @@ fn test() {
102 [11; 48) '{ ...&y]; }': () 102 [11; 48) '{ ...&y]; }': ()
103 [21; 22) 'y': &{unknown} 103 [21; 22) 'y': &{unknown}
104 [25; 32) 'unknown': &{unknown} 104 [25; 32) 'unknown': &{unknown}
105 [38; 45) '[y, &y]': [&&{unknown};_] 105 [38; 45) '[y, &y]': [&&{unknown}; _]
106 [39; 40) 'y': &{unknown} 106 [39; 40) 'y': &{unknown}
107 [42; 44) '&y': &&{unknown} 107 [42; 44) '&y': &&{unknown}
108 [43; 44) 'y': &{unknown} 108 [43; 44) 'y': &{unknown}
@@ -128,7 +128,7 @@ fn test() {
128 [25; 32) 'unknown': &&{unknown} 128 [25; 32) 'unknown': &&{unknown}
129 [42; 43) 'y': &&{unknown} 129 [42; 43) 'y': &&{unknown}
130 [46; 53) 'unknown': &&{unknown} 130 [46; 53) 'unknown': &&{unknown}
131 [59; 77) '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown});_] 131 [59; 77) '[(x, y..., &x)]': [(&&&{unknown}, &&&{unknown}); _]
132 [60; 66) '(x, y)': (&&&{unknown}, &&&{unknown}) 132 [60; 66) '(x, y)': (&&&{unknown}, &&&{unknown})
133 [61; 62) 'x': &&{unknown} 133 [61; 62) 'x': &&{unknown}
134 [64; 65) 'y': &&{unknown} 134 [64; 65) 'y': &&{unknown}
@@ -180,8 +180,8 @@ fn test_line_buffer() {
180"#), 180"#),
181 @r###" 181 @r###"
182 [23; 53) '{ ...n']; }': () 182 [23; 53) '{ ...n']; }': ()
183 [29; 50) '&[0, b...b'\n']': &[u8;_] 183 [29; 50) '&[0, b...b'\n']': &[u8; _]
184 [30; 50) '[0, b'...b'\n']': [u8;_] 184 [30; 50) '[0, b'...b'\n']': [u8; _]
185 [31; 32) '0': u8 185 [31; 32) '0': u8
186 [34; 39) 'b'\n'': u8 186 [34; 39) 'b'\n'': u8
187 [41; 42) '1': u8 187 [41; 42) '1': u8
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index b7204ec00..fdab9c187 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -28,7 +28,7 @@ mod boxed {
28 28
29"#, 29"#,
30 ); 30 );
31 assert_eq!("(Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32;_]>)", type_at_pos(&db, pos)); 31 assert_eq!("(Box<i32>, Box<Box<i32>>, Box<&i32>, Box<[i32; _]>)", type_at_pos(&db, pos));
32} 32}
33 33
34#[test] 34#[test]
@@ -1061,55 +1061,55 @@ fn test(x: &str, y: isize) {
1061 [9; 10) 'x': &str 1061 [9; 10) 'x': &str
1062 [18; 19) 'y': isize 1062 [18; 19) 'y': isize
1063 [28; 293) '{ ... []; }': () 1063 [28; 293) '{ ... []; }': ()
1064 [38; 39) 'a': [&str;_] 1064 [38; 39) 'a': [&str; _]
1065 [42; 45) '[x]': [&str;_] 1065 [42; 45) '[x]': [&str; _]
1066 [43; 44) 'x': &str 1066 [43; 44) 'x': &str
1067 [55; 56) 'b': [[&str;_];_] 1067 [55; 56) 'b': [[&str; _]; _]
1068 [59; 65) '[a, a]': [[&str;_];_] 1068 [59; 65) '[a, a]': [[&str; _]; _]
1069 [60; 61) 'a': [&str;_] 1069 [60; 61) 'a': [&str; _]
1070 [63; 64) 'a': [&str;_] 1070 [63; 64) 'a': [&str; _]
1071 [75; 76) 'c': [[[&str;_];_];_] 1071 [75; 76) 'c': [[[&str; _]; _]; _]
1072 [79; 85) '[b, b]': [[[&str;_];_];_] 1072 [79; 85) '[b, b]': [[[&str; _]; _]; _]
1073 [80; 81) 'b': [[&str;_];_] 1073 [80; 81) 'b': [[&str; _]; _]
1074 [83; 84) 'b': [[&str;_];_] 1074 [83; 84) 'b': [[&str; _]; _]
1075 [96; 97) 'd': [isize;_] 1075 [96; 97) 'd': [isize; _]
1076 [100; 112) '[y, 1, 2, 3]': [isize;_] 1076 [100; 112) '[y, 1, 2, 3]': [isize; _]
1077 [101; 102) 'y': isize 1077 [101; 102) 'y': isize
1078 [104; 105) '1': isize 1078 [104; 105) '1': isize
1079 [107; 108) '2': isize 1079 [107; 108) '2': isize
1080 [110; 111) '3': isize 1080 [110; 111) '3': isize
1081 [122; 123) 'd': [isize;_] 1081 [122; 123) 'd': [isize; _]
1082 [126; 138) '[1, y, 2, 3]': [isize;_] 1082 [126; 138) '[1, y, 2, 3]': [isize; _]
1083 [127; 128) '1': isize 1083 [127; 128) '1': isize
1084 [130; 131) 'y': isize 1084 [130; 131) 'y': isize
1085 [133; 134) '2': isize 1085 [133; 134) '2': isize
1086 [136; 137) '3': isize 1086 [136; 137) '3': isize
1087 [148; 149) 'e': [isize;_] 1087 [148; 149) 'e': [isize; _]
1088 [152; 155) '[y]': [isize;_] 1088 [152; 155) '[y]': [isize; _]
1089 [153; 154) 'y': isize 1089 [153; 154) 'y': isize
1090 [165; 166) 'f': [[isize;_];_] 1090 [165; 166) 'f': [[isize; _]; _]
1091 [169; 175) '[d, d]': [[isize;_];_] 1091 [169; 175) '[d, d]': [[isize; _]; _]
1092 [170; 171) 'd': [isize;_] 1092 [170; 171) 'd': [isize; _]
1093 [173; 174) 'd': [isize;_] 1093 [173; 174) 'd': [isize; _]
1094 [185; 186) 'g': [[isize;_];_] 1094 [185; 186) 'g': [[isize; _]; _]
1095 [189; 195) '[e, e]': [[isize;_];_] 1095 [189; 195) '[e, e]': [[isize; _]; _]
1096 [190; 191) 'e': [isize;_] 1096 [190; 191) 'e': [isize; _]
1097 [193; 194) 'e': [isize;_] 1097 [193; 194) 'e': [isize; _]
1098 [206; 207) 'h': [i32;_] 1098 [206; 207) 'h': [i32; _]
1099 [210; 216) '[1, 2]': [i32;_] 1099 [210; 216) '[1, 2]': [i32; _]
1100 [211; 212) '1': i32 1100 [211; 212) '1': i32
1101 [214; 215) '2': i32 1101 [214; 215) '2': i32
1102 [226; 227) 'i': [&str;_] 1102 [226; 227) 'i': [&str; _]
1103 [230; 240) '["a", "b"]': [&str;_] 1103 [230; 240) '["a", "b"]': [&str; _]
1104 [231; 234) '"a"': &str 1104 [231; 234) '"a"': &str
1105 [236; 239) '"b"': &str 1105 [236; 239) '"b"': &str
1106 [251; 252) 'b': [[&str;_];_] 1106 [251; 252) 'b': [[&str; _]; _]
1107 [255; 265) '[a, ["b"]]': [[&str;_];_] 1107 [255; 265) '[a, ["b"]]': [[&str; _]; _]
1108 [256; 257) 'a': [&str;_] 1108 [256; 257) 'a': [&str; _]
1109 [259; 264) '["b"]': [&str;_] 1109 [259; 264) '["b"]': [&str; _]
1110 [260; 263) '"b"': &str 1110 [260; 263) '"b"': &str
1111 [275; 276) 'x': [u8;_] 1111 [275; 276) 'x': [u8; _]
1112 [288; 290) '[]': [u8;_] 1112 [288; 290) '[]': [u8; _]
1113 "### 1113 "###
1114 ); 1114 );
1115} 1115}
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index bc20a49cc..a6ac18f86 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -479,7 +479,7 @@ fn indexing_arrays() {
479 @r###" 479 @r###"
480 [10; 26) '{ &mut...[2]; }': () 480 [10; 26) '{ &mut...[2]; }': ()
481 [12; 23) '&mut [9][2]': &mut {unknown} 481 [12; 23) '&mut [9][2]': &mut {unknown}
482 [17; 20) '[9]': [i32;_] 482 [17; 20) '[9]': [i32; _]
483 [17; 23) '[9][2]': {unknown} 483 [17; 23) '[9][2]': {unknown}
484 [18; 19) '9': i32 484 [18; 19) '9': i32
485 [21; 22) '2': i32 485 [21; 22) '2': i32
diff --git a/crates/ra_ide/src/change.rs b/crates/ra_ide/src/change.rs
index ce617840c..45a58690b 100644
--- a/crates/ra_ide/src/change.rs
+++ b/crates/ra_ide/src/change.rs
@@ -301,45 +301,74 @@ impl RootDatabase {
301 )*} 301 )*}
302 } 302 }
303 sweep_each_query![ 303 sweep_each_query![
304 // SourceDatabase
304 ra_db::ParseQuery 305 ra_db::ParseQuery
305 ra_db::SourceRootCratesQuery 306 ra_db::SourceRootCratesQuery
307
308 // AstDatabase
306 hir::db::AstIdMapQuery 309 hir::db::AstIdMapQuery
307 hir::db::ParseMacroQuery 310 hir::db::InternMacroQuery
308 hir::db::MacroDefQuery
309 hir::db::MacroArgQuery 311 hir::db::MacroArgQuery
312 hir::db::MacroDefQuery
313 hir::db::ParseMacroQuery
310 hir::db::MacroExpandQuery 314 hir::db::MacroExpandQuery
315
316 // DefDatabase
317 hir::db::RawItemsQuery
318 hir::db::ComputeCrateDefMapQuery
311 hir::db::StructDataQuery 319 hir::db::StructDataQuery
320 hir::db::UnionDataQuery
312 hir::db::EnumDataQuery 321 hir::db::EnumDataQuery
322 hir::db::ImplDataQuery
313 hir::db::TraitDataQuery 323 hir::db::TraitDataQuery
314 hir::db::RawItemsQuery
315 hir::db::ComputeCrateDefMapQuery
316 hir::db::GenericParamsQuery
317 hir::db::FunctionDataQuery
318 hir::db::TypeAliasDataQuery 324 hir::db::TypeAliasDataQuery
325 hir::db::FunctionDataQuery
319 hir::db::ConstDataQuery 326 hir::db::ConstDataQuery
320 hir::db::StaticDataQuery 327 hir::db::StaticDataQuery
328 hir::db::BodyWithSourceMapQuery
329 hir::db::BodyQuery
330 hir::db::ExprScopesQuery
331 hir::db::GenericParamsQuery
332 hir::db::AttrsQuery
321 hir::db::ModuleLangItemsQuery 333 hir::db::ModuleLangItemsQuery
322 hir::db::CrateLangItemsQuery 334 hir::db::CrateLangItemsQuery
323 hir::db::LangItemQuery 335 hir::db::LangItemQuery
324 hir::db::DocumentationQuery 336 hir::db::DocumentationQuery
325 hir::db::ExprScopesQuery 337
338 // InternDatabase
339 hir::db::InternFunctionQuery
340 hir::db::InternStructQuery
341 hir::db::InternUnionQuery
342 hir::db::InternEnumQuery
343 hir::db::InternConstQuery
344 hir::db::InternStaticQuery
345 hir::db::InternTraitQuery
346 hir::db::InternTypeAliasQuery
347 hir::db::InternImplQuery
348
349 // HirDatabase
326 hir::db::DoInferQuery 350 hir::db::DoInferQuery
327 hir::db::TyQuery 351 hir::db::TyQuery
328 hir::db::ValueTyQuery 352 hir::db::ValueTyQuery
353 hir::db::ImplSelfTyQuery
354 hir::db::ImplTraitQuery
329 hir::db::FieldTypesQuery 355 hir::db::FieldTypesQuery
330 hir::db::CallableItemSignatureQuery 356 hir::db::CallableItemSignatureQuery
357 hir::db::GenericPredicatesForParamQuery
331 hir::db::GenericPredicatesQuery 358 hir::db::GenericPredicatesQuery
332 hir::db::GenericDefaultsQuery 359 hir::db::GenericDefaultsQuery
333 hir::db::BodyWithSourceMapQuery
334 hir::db::BodyQuery
335 hir::db::ImplsInCrateQuery 360 hir::db::ImplsInCrateQuery
336 hir::db::ImplsForTraitQuery 361 hir::db::ImplsForTraitQuery
362 hir::db::TraitSolverQuery
363 hir::db::InternTypeCtorQuery
364 hir::db::InternChalkImplQuery
365 hir::db::InternAssocTyValueQuery
337 hir::db::AssociatedTyDataQuery 366 hir::db::AssociatedTyDataQuery
367 hir::db::AssociatedTyValueQuery
368 hir::db::TraitSolveQuery
338 hir::db::TraitDatumQuery 369 hir::db::TraitDatumQuery
339 hir::db::StructDatumQuery 370 hir::db::StructDatumQuery
340 hir::db::ImplDatumQuery 371 hir::db::ImplDatumQuery
341 hir::db::ImplDataQuery
342 hir::db::TraitSolveQuery
343 ]; 372 ];
344 acc.sort_by_key(|it| std::cmp::Reverse(it.1)); 373 acc.sort_by_key(|it| std::cmp::Reverse(it.1));
345 acc 374 acc
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 15bf519c9..d850ded37 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -5,11 +5,18 @@ mod handlers;
5mod subscriptions; 5mod subscriptions;
6pub(crate) mod pending_requests; 6pub(crate) mod pending_requests;
7 7
8use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant}; 8use std::{
9 env,
10 error::Error,
11 fmt, panic,
12 path::PathBuf,
13 sync::Arc,
14 time::{Duration, Instant},
15};
9 16
10use crossbeam_channel::{select, unbounded, RecvError, Sender}; 17use crossbeam_channel::{select, unbounded, RecvError, Sender};
11use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
12use lsp_types::{ClientCapabilities, NumberOrString, Url}; 19use lsp_types::{ClientCapabilities, NumberOrString};
13use ra_cargo_watch::{CheckOptions, CheckTask}; 20use ra_cargo_watch::{CheckOptions, CheckTask};
14use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; 21use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId};
15use ra_prof::profile; 22use ra_prof::profile;
@@ -352,7 +359,7 @@ fn loop_turn(
352 world_state.maybe_collect_garbage(); 359 world_state.maybe_collect_garbage();
353 loop_state.in_flight_libraries -= 1; 360 loop_state.in_flight_libraries -= 1;
354 } 361 }
355 Event::CheckWatcher(task) => on_check_task(task, world_state, task_sender)?, 362 Event::CheckWatcher(task) => on_check_task(pool, task, world_state, task_sender)?,
356 Event::Msg(msg) => match msg { 363 Event::Msg(msg) => match msg {
357 Message::Request(req) => on_request( 364 Message::Request(req) => on_request(
358 world_state, 365 world_state,
@@ -425,6 +432,19 @@ fn loop_turn(
425 loop_state.subscriptions.subscriptions(), 432 loop_state.subscriptions.subscriptions(),
426 ) 433 )
427 } 434 }
435
436 let loop_duration = loop_start.elapsed();
437 if loop_duration > Duration::from_millis(100) {
438 log::error!("overly long loop turn: {:?}", loop_duration);
439 if env::var("RA_PROFILE").is_ok() {
440 show_message(
441 req::MessageType::Error,
442 format!("overly long loop turn: {:?}", loop_duration),
443 &connection.sender,
444 );
445 }
446 }
447
428 Ok(()) 448 Ok(())
429} 449}
430 450
@@ -452,7 +472,7 @@ fn on_request(
452 world: &mut WorldState, 472 world: &mut WorldState,
453 pending_requests: &mut PendingRequests, 473 pending_requests: &mut PendingRequests,
454 pool: &ThreadPool, 474 pool: &ThreadPool,
455 sender: &Sender<Task>, 475 task_sender: &Sender<Task>,
456 msg_sender: &Sender<Message>, 476 msg_sender: &Sender<Message>,
457 request_received: Instant, 477 request_received: Instant,
458 req: Request, 478 req: Request,
@@ -461,7 +481,7 @@ fn on_request(
461 req: Some(req), 481 req: Some(req),
462 pool, 482 pool,
463 world, 483 world,
464 sender, 484 task_sender,
465 msg_sender, 485 msg_sender,
466 pending_requests, 486 pending_requests,
467 request_received, 487 request_received,
@@ -602,31 +622,23 @@ fn on_notification(
602} 622}
603 623
604fn on_check_task( 624fn on_check_task(
625 pool: &ThreadPool,
605 task: CheckTask, 626 task: CheckTask,
606 world_state: &mut WorldState, 627 world_state: &mut WorldState,
607 task_sender: &Sender<Task>, 628 task_sender: &Sender<Task>,
608) -> Result<()> { 629) -> Result<()> {
609 match task { 630 let urls = match task {
610 CheckTask::ClearDiagnostics => { 631 CheckTask::ClearDiagnostics => {
611 let state = Arc::get_mut(&mut world_state.check_watcher.state) 632 let state = Arc::get_mut(&mut world_state.check_watcher.state)
612 .expect("couldn't get check watcher state as mutable"); 633 .expect("couldn't get check watcher state as mutable");
613 let cleared_files = state.clear(); 634 state.clear()
614
615 // Send updated diagnostics for each cleared file
616 for url in cleared_files {
617 publish_diagnostics_for_url(&url, world_state, task_sender)?;
618 }
619 } 635 }
620 636
621 CheckTask::AddDiagnostic(url, diagnostic) => { 637 CheckTask::AddDiagnostic(url, diagnostic) => {
622 let state = Arc::get_mut(&mut world_state.check_watcher.state) 638 let state = Arc::get_mut(&mut world_state.check_watcher.state)
623 .expect("couldn't get check watcher state as mutable"); 639 .expect("couldn't get check watcher state as mutable");
624 state.add_diagnostic_with_fixes(url.clone(), diagnostic); 640 state.add_diagnostic_with_fixes(url.clone(), diagnostic);
625 641 vec![url]
626 // We manually send a diagnostic update when the watcher asks
627 // us to, to avoid the issue of having to change the file to
628 // receive updated diagnostics.
629 publish_diagnostics_for_url(&url, world_state, task_sender)?;
630 } 642 }
631 643
632 CheckTask::Status(progress) => { 644 CheckTask::Status(progress) => {
@@ -636,22 +648,30 @@ fn on_check_task(
636 }; 648 };
637 let not = notification_new::<req::Progress>(params); 649 let not = notification_new::<req::Progress>(params);
638 task_sender.send(Task::Notify(not)).unwrap(); 650 task_sender.send(Task::Notify(not)).unwrap();
651 Vec::new()
639 } 652 }
640 } 653 };
641 Ok(()) 654
642} 655 let subscriptions = urls
656 .into_iter()
657 .map(|url| {
658 let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?;
659 Ok(world_state.vfs.read().path2file(&path).map(|it| FileId(it.0)))
660 })
661 .filter_map(|res| res.transpose())
662 .collect::<Result<Vec<_>>>()?;
663
664 // We manually send a diagnostic update when the watcher asks
665 // us to, to avoid the issue of having to change the file to
666 // receive updated diagnostics.
667 update_file_notifications_on_threadpool(
668 pool,
669 world_state.snapshot(),
670 false,
671 task_sender.clone(),
672 subscriptions,
673 );
643 674
644fn publish_diagnostics_for_url(
645 url: &Url,
646 world_state: &WorldState,
647 task_sender: &Sender<Task>,
648) -> Result<()> {
649 let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?;
650 if let Some(file_id) = world_state.vfs.read().path2file(&path) {
651 let params = handlers::publish_diagnostics(&world_state.snapshot(), FileId(file_id.0))?;
652 let not = notification_new::<req::PublishDiagnostics>(params);
653 task_sender.send(Task::Notify(not)).unwrap();
654 }
655 Ok(()) 675 Ok(())
656} 676}
657 677
@@ -661,7 +681,7 @@ struct PoolDispatcher<'a> {
661 world: &'a mut WorldState, 681 world: &'a mut WorldState,
662 pending_requests: &'a mut PendingRequests, 682 pending_requests: &'a mut PendingRequests,
663 msg_sender: &'a Sender<Message>, 683 msg_sender: &'a Sender<Message>,
664 sender: &'a Sender<Task>, 684 task_sender: &'a Sender<Task>,
665 request_received: Instant, 685 request_received: Instant,
666} 686}
667 687
@@ -708,7 +728,7 @@ impl<'a> PoolDispatcher<'a> {
708 728
709 self.pool.execute({ 729 self.pool.execute({
710 let world = self.world.snapshot(); 730 let world = self.world.snapshot();
711 let sender = self.sender.clone(); 731 let sender = self.task_sender.clone();
712 move || { 732 move || {
713 let result = f(world, params); 733 let result = f(world, params);
714 let task = result_to_task::<R>(id, result); 734 let task = result_to_task::<R>(id, result);
@@ -786,7 +806,7 @@ fn update_file_notifications_on_threadpool(
786 pool: &ThreadPool, 806 pool: &ThreadPool,
787 world: WorldSnapshot, 807 world: WorldSnapshot,
788 publish_decorations: bool, 808 publish_decorations: bool,
789 sender: Sender<Task>, 809 task_sender: Sender<Task>,
790 subscriptions: Vec<FileId>, 810 subscriptions: Vec<FileId>,
791) { 811) {
792 log::trace!("updating notifications for {:?}", subscriptions); 812 log::trace!("updating notifications for {:?}", subscriptions);
@@ -802,7 +822,7 @@ fn update_file_notifications_on_threadpool(
802 } 822 }
803 Ok(params) => { 823 Ok(params) => {
804 let not = notification_new::<req::PublishDiagnostics>(params); 824 let not = notification_new::<req::PublishDiagnostics>(params);
805 sender.send(Task::Notify(not)).unwrap(); 825 task_sender.send(Task::Notify(not)).unwrap();
806 } 826 }
807 } 827 }
808 } 828 }
@@ -815,7 +835,7 @@ fn update_file_notifications_on_threadpool(
815 } 835 }
816 Ok(params) => { 836 Ok(params) => {
817 let not = notification_new::<req::PublishDecorations>(params); 837 let not = notification_new::<req::PublishDecorations>(params);
818 sender.send(Task::Notify(not)).unwrap(); 838 task_sender.send(Task::Notify(not)).unwrap();
819 } 839 }
820 } 840 }
821 } 841 }
diff --git a/crates/ra_prof/src/lib.rs b/crates/ra_prof/src/lib.rs
index c7973ddf4..4a49e9f95 100644
--- a/crates/ra_prof/src/lib.rs
+++ b/crates/ra_prof/src/lib.rs
@@ -6,9 +6,9 @@ mod google_cpu_profiler;
6 6
7use std::{ 7use std::{
8 cell::RefCell, 8 cell::RefCell,
9 collections::BTreeMap,
9 collections::HashSet, 10 collections::HashSet,
10 io::{stderr, Write}, 11 io::{stderr, Write},
11 iter::repeat,
12 mem, 12 mem,
13 sync::{ 13 sync::{
14 atomic::{AtomicBool, Ordering}, 14 atomic::{AtomicBool, Ordering},
@@ -17,7 +17,6 @@ use std::{
17 time::{Duration, Instant}, 17 time::{Duration, Instant},
18}; 18};
19 19
20use itertools::Itertools;
21use once_cell::sync::Lazy; 20use once_cell::sync::Lazy;
22 21
23pub use crate::memory_usage::{Bytes, MemoryUsage}; 22pub use crate::memory_usage::{Bytes, MemoryUsage};
@@ -216,7 +215,7 @@ impl Drop for Profiler {
216 // (otherwise we could print `0ms` despite user's `>0` filter when 215 // (otherwise we could print `0ms` despite user's `>0` filter when
217 // `duration` is just a few nanos). 216 // `duration` is just a few nanos).
218 if duration.as_millis() > longer_than.as_millis() { 217 if duration.as_millis() > longer_than.as_millis() {
219 print(0, &stack.messages, &mut stdout.lock(), longer_than, None); 218 print(&stack.messages, longer_than, &mut stdout.lock());
220 } 219 }
221 stack.messages.clear(); 220 stack.messages.clear();
222 } 221 }
@@ -227,59 +226,85 @@ impl Drop for Profiler {
227 } 226 }
228} 227}
229 228
230fn print( 229fn print(msgs: &[Message], longer_than: Duration, out: &mut impl Write) {
231 lvl: usize,
232 msgs: &[Message],
233 out: &mut impl Write,
234 longer_than: Duration,
235 total: Option<Duration>,
236) {
237 if msgs.is_empty() { 230 if msgs.is_empty() {
238 return; 231 return;
239 } 232 }
240 // The index of the first element that will be included in the slice when we recurse. 233 let children_map = idx_to_children(msgs);
241 let mut next_start = 0; 234 let root_idx = msgs.len() - 1;
242 let indent = repeat(" ").take(lvl).collect::<String>(); 235 print_for_idx(root_idx, &children_map, msgs, longer_than, out);
243 // We output hierarchy for long calls, but sum up all short calls 236}
244 let mut short = Vec::new(); 237
238fn print_for_idx(
239 current_idx: usize,
240 children_map: &[Vec<usize>],
241 msgs: &[Message],
242 longer_than: Duration,
243 out: &mut impl Write,
244) {
245 let current = &msgs[current_idx];
246 let current_indent = " ".repeat(current.level);
247 writeln!(out, "{}{:5}ms - {}", current_indent, current.duration.as_millis(), current.message)
248 .expect("printing profiling info");
249
250 let longer_than_millis = longer_than.as_millis();
251 let children_indices = &children_map[current_idx];
245 let mut accounted_for = Duration::default(); 252 let mut accounted_for = Duration::default();
246 for (i, &Message { level, duration, message: ref msg }) in msgs.iter().enumerate() { 253 let mut short_children = BTreeMap::new(); // Use `BTreeMap` to get deterministic output.
247 if level != lvl {
248 continue;
249 }
250 accounted_for += duration;
251 if duration.as_millis() > longer_than.as_millis() {
252 writeln!(out, "{}{:5}ms - {}", indent, duration.as_millis(), msg)
253 .expect("printing profiling info to stdout");
254 254
255 print(lvl + 1, &msgs[next_start..i], out, longer_than, Some(duration)); 255 for child_idx in children_indices.iter() {
256 let child = &msgs[*child_idx];
257 if child.duration.as_millis() > longer_than_millis {
258 print_for_idx(*child_idx, children_map, msgs, longer_than, out);
256 } else { 259 } else {
257 short.push((msg, duration)) 260 let pair = short_children.entry(&child.message).or_insert((Duration::default(), 0));
261 pair.0 += child.duration;
262 pair.1 += 1;
258 } 263 }
264 accounted_for += child.duration;
265 }
259 266
260 next_start = i + 1; 267 for (child_msg, (duration, count)) in short_children.iter() {
268 let millis = duration.as_millis();
269 writeln!(out, " {}{:5}ms - {} ({} calls)", current_indent, millis, child_msg, count)
270 .expect("printing profiling info");
261 } 271 }
262 short.sort_by_key(|(msg, _time)| *msg); 272
263 for (msg, entires) in short.iter().group_by(|(msg, _time)| msg).into_iter() { 273 let unaccounted_millis = (current.duration - accounted_for).as_millis();
264 let mut count = 0; 274 if !children_indices.is_empty()
265 let mut total_duration = Duration::default(); 275 && unaccounted_millis > 0
266 entires.for_each(|(_msg, time)| { 276 && unaccounted_millis > longer_than_millis
267 count += 1; 277 {
268 total_duration += *time; 278 writeln!(out, " {}{:5}ms - ???", current_indent, unaccounted_millis)
269 }); 279 .expect("printing profiling info");
270 writeln!(out, "{}{:5}ms - {} ({} calls)", indent, total_duration.as_millis(), msg, count)
271 .expect("printing profiling info to stdout");
272 } 280 }
281}
273 282
274 if let Some(total) = total { 283/// Returns a mapping from an index in the `msgs` to the vector with the indices of its children.
275 if let Some(unaccounted) = total.checked_sub(accounted_for) { 284///
276 let unaccounted_millis = unaccounted.as_millis(); 285/// This assumes that the entries in `msgs` are in the order of when the calls to `profile` finish.
277 if unaccounted_millis > longer_than.as_millis() && unaccounted_millis > 0 { 286/// In other words, a postorder of the call graph. In particular, the root is the last element of
278 writeln!(out, "{}{:5}ms - ???", indent, unaccounted_millis) 287/// `msgs`.
279 .expect("printing profiling info to stdout"); 288fn idx_to_children(msgs: &[Message]) -> Vec<Vec<usize>> {
280 } 289 // Initialize with the index of the root; `msgs` and `ancestors` should be never empty.
290 assert!(!msgs.is_empty());
291 let mut ancestors = vec![msgs.len() - 1];
292 let mut result: Vec<Vec<usize>> = vec![vec![]; msgs.len()];
293 for (idx, msg) in msgs[..msgs.len() - 1].iter().enumerate().rev() {
294 // We need to find the parent of the current message, i.e., the last ancestor that has a
295 // level lower than the current message.
296 while msgs[*ancestors.last().unwrap()].level >= msg.level {
297 ancestors.pop();
281 } 298 }
299 result[*ancestors.last().unwrap()].push(idx);
300 ancestors.push(idx);
301 }
302 // Note that above we visited all children from the last to the first one. Let's reverse vectors
303 // to get the more natural order where the first element is the first child.
304 for vec in result.iter_mut() {
305 vec.reverse();
282 } 306 }
307 result
283} 308}
284 309
285/// Prints backtrace to stderr, useful for debugging. 310/// Prints backtrace to stderr, useful for debugging.
@@ -388,7 +413,7 @@ mod tests {
388 Message { level: 1, duration: Duration::from_nanos(2), message: "bar".to_owned() }, 413 Message { level: 1, duration: Duration::from_nanos(2), message: "bar".to_owned() },
389 Message { level: 0, duration: Duration::from_millis(1), message: "foo".to_owned() }, 414 Message { level: 0, duration: Duration::from_millis(1), message: "foo".to_owned() },
390 ]; 415 ];
391 print(0, &msgs, &mut result, Duration::from_millis(0), Some(Duration::from_millis(1))); 416 print(&msgs, Duration::from_millis(0), &mut result);
392 // The calls to `bar` are so short that they'll be rounded to 0ms and should get collapsed 417 // The calls to `bar` are so short that they'll be rounded to 0ms and should get collapsed
393 // when printing. 418 // when printing.
394 assert_eq!( 419 assert_eq!(
@@ -404,7 +429,7 @@ mod tests {
404 Message { level: 1, duration: Duration::from_millis(2), message: "bar".to_owned() }, 429 Message { level: 1, duration: Duration::from_millis(2), message: "bar".to_owned() },
405 Message { level: 0, duration: Duration::from_millis(5), message: "foo".to_owned() }, 430 Message { level: 0, duration: Duration::from_millis(5), message: "foo".to_owned() },
406 ]; 431 ];
407 print(0, &msgs, &mut result, Duration::from_millis(0), Some(Duration::from_millis(1))); 432 print(&msgs, Duration::from_millis(0), &mut result);
408 assert_eq!( 433 assert_eq!(
409 std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(), 434 std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
410 vec![ 435 vec![
@@ -426,7 +451,7 @@ mod tests {
426 Message { level: 1, duration: Duration::from_millis(4), message: "bar".to_owned() }, 451 Message { level: 1, duration: Duration::from_millis(4), message: "bar".to_owned() },
427 Message { level: 0, duration: Duration::from_millis(9), message: "foo".to_owned() }, 452 Message { level: 0, duration: Duration::from_millis(9), message: "foo".to_owned() },
428 ]; 453 ];
429 print(0, &msgs, &mut result, Duration::from_millis(0), None); 454 print(&msgs, Duration::from_millis(0), &mut result);
430 assert_eq!( 455 assert_eq!(
431 std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(), 456 std::str::from_utf8(&result).unwrap().lines().collect::<Vec<_>>(),
432 vec![ 457 vec![
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 659f77b71..336c594a6 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -21,6 +21,12 @@ pub use difference::Changeset as __Changeset;
21 21
22pub const CURSOR_MARKER: &str = "<|>"; 22pub const CURSOR_MARKER: &str = "<|>";
23 23
24/// Asserts that two strings are equal, otherwise displays a rich diff between them.
25///
26/// The diff shows changes from the "original" left string to the "actual" right string.
27///
28/// All arguments starting from and including the 3rd one are passed to
29/// `eprintln!()` macro in case of text inequality.
24#[macro_export] 30#[macro_export]
25macro_rules! assert_eq_text { 31macro_rules! assert_eq_text {
26 ($left:expr, $right:expr) => { 32 ($left:expr, $right:expr) => {
@@ -42,6 +48,7 @@ macro_rules! assert_eq_text {
42 }}; 48 }};
43} 49}
44 50
51/// Infallible version of `try_extract_offset()`.
45pub fn extract_offset(text: &str) -> (TextUnit, String) { 52pub fn extract_offset(text: &str) -> (TextUnit, String) {
46 match try_extract_offset(text) { 53 match try_extract_offset(text) {
47 None => panic!("text should contain cursor marker"), 54 None => panic!("text should contain cursor marker"),
@@ -49,6 +56,8 @@ pub fn extract_offset(text: &str) -> (TextUnit, String) {
49 } 56 }
50} 57}
51 58
59/// Returns the offset of the first occurence of `<|>` marker and the copy of `text`
60/// without the marker.
52fn try_extract_offset(text: &str) -> Option<(TextUnit, String)> { 61fn try_extract_offset(text: &str) -> Option<(TextUnit, String)> {
53 let cursor_pos = text.find(CURSOR_MARKER)?; 62 let cursor_pos = text.find(CURSOR_MARKER)?;
54 let mut new_text = String::with_capacity(text.len() - CURSOR_MARKER.len()); 63 let mut new_text = String::with_capacity(text.len() - CURSOR_MARKER.len());
@@ -58,6 +67,7 @@ fn try_extract_offset(text: &str) -> Option<(TextUnit, String)> {
58 Some((cursor_pos, new_text)) 67 Some((cursor_pos, new_text))
59} 68}
60 69
70/// Infallible version of `try_extract_range()`.
61pub fn extract_range(text: &str) -> (TextRange, String) { 71pub fn extract_range(text: &str) -> (TextRange, String) {
62 match try_extract_range(text) { 72 match try_extract_range(text) {
63 None => panic!("text should contain cursor marker"), 73 None => panic!("text should contain cursor marker"),
@@ -65,6 +75,8 @@ pub fn extract_range(text: &str) -> (TextRange, String) {
65 } 75 }
66} 76}
67 77
78/// Returns `TextRange` between the first two markers `<|>...<|>` and the copy
79/// of `text` without both of these markers.
68fn try_extract_range(text: &str) -> Option<(TextRange, String)> { 80fn try_extract_range(text: &str) -> Option<(TextRange, String)> {
69 let (start, text) = try_extract_offset(text)?; 81 let (start, text) = try_extract_offset(text)?;
70 let (end, text) = try_extract_offset(&text)?; 82 let (end, text) = try_extract_offset(&text)?;
@@ -85,6 +97,11 @@ impl From<RangeOrOffset> for TextRange {
85 } 97 }
86} 98}
87 99
100/// Extracts `TextRange` or `TextUnit` depending on the amount of `<|>` markers
101/// found in `text`.
102///
103/// # Panics
104/// Panics if no `<|>` marker is present in the `text`.
88pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) { 105pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) {
89 if let Some((range, text)) = try_extract_range(text) { 106 if let Some((range, text)) = try_extract_range(text) {
90 return (RangeOrOffset::Range(range), text); 107 return (RangeOrOffset::Range(range), text);
@@ -93,7 +110,7 @@ pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) {
93 (RangeOrOffset::Offset(offset), text) 110 (RangeOrOffset::Offset(offset), text)
94} 111}
95 112
96/// Extracts ranges, marked with `<tag> </tag>` paris from the `text` 113/// Extracts ranges, marked with `<tag> </tag>` pairs from the `text`
97pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { 114pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) {
98 let open = format!("<{}>", tag); 115 let open = format!("<{}>", tag);
99 let close = format!("</{}>", tag); 116 let close = format!("</{}>", tag);
@@ -127,9 +144,9 @@ pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) {
127 (ranges, res) 144 (ranges, res)
128} 145}
129 146
147/// Inserts `<|>` marker into the `text` at `offset`.
130pub fn add_cursor(text: &str, offset: TextUnit) -> String { 148pub fn add_cursor(text: &str, offset: TextUnit) -> String {
131 let offset: u32 = offset.into(); 149 let offset: usize = offset.to_usize();
132 let offset: usize = offset as usize;
133 let mut res = String::new(); 150 let mut res = String::new();
134 res.push_str(&text[..offset]); 151 res.push_str(&text[..offset]);
135 res.push_str("<|>"); 152 res.push_str("<|>");
@@ -152,19 +169,6 @@ pub struct FixtureEntry {
152/// // - other meta 169/// // - other meta
153/// ``` 170/// ```
154pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { 171pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
155 let mut res = Vec::new();
156 let mut buf = String::new();
157 let mut meta: Option<&str> = None;
158
159 macro_rules! flush {
160 () => {
161 if let Some(meta) = meta {
162 res.push(FixtureEntry { meta: meta.to_string(), text: buf.clone() });
163 buf.clear();
164 }
165 };
166 };
167
168 let margin = fixture 172 let margin = fixture
169 .lines() 173 .lines()
170 .filter(|it| it.trim_start().starts_with("//-")) 174 .filter(|it| it.trim_start().starts_with("//-"))
@@ -172,7 +176,7 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
172 .next() 176 .next()
173 .expect("empty fixture"); 177 .expect("empty fixture");
174 178
175 let lines = fixture 179 let mut lines = fixture
176 .split('\n') // don't use `.lines` to not drop `\r\n` 180 .split('\n') // don't use `.lines` to not drop `\r\n`
177 .filter_map(|line| { 181 .filter_map(|line| {
178 if line.len() >= margin { 182 if line.len() >= margin {
@@ -184,17 +188,16 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
184 } 188 }
185 }); 189 });
186 190
187 for line in lines { 191 let mut res: Vec<FixtureEntry> = Vec::new();
192 for line in lines.by_ref() {
188 if line.starts_with("//-") { 193 if line.starts_with("//-") {
189 flush!(); 194 let meta = line["//-".len()..].trim().to_string();
190 buf.clear(); 195 res.push(FixtureEntry { meta, text: String::new() })
191 meta = Some(line["//-".len()..].trim()); 196 } else if let Some(entry) = res.last_mut() {
192 continue; 197 entry.text.push_str(line);
198 entry.text.push('\n');
193 } 199 }
194 buf.push_str(line);
195 buf.push('\n');
196 } 200 }
197 flush!();
198 res 201 res
199} 202}
200 203
@@ -236,11 +239,10 @@ fn lines_match_works() {
236 assert!(!lines_match("b", "cb")); 239 assert!(!lines_match("b", "cb"));
237} 240}
238 241
239// Compares JSON object for approximate equality. 242/// Compares JSON object for approximate equality.
240// You can use `[..]` wildcard in strings (useful for OS dependent things such 243/// You can use `[..]` wildcard in strings (useful for OS dependent things such
241// as paths). You can use a `"{...}"` string literal as a wildcard for 244/// as paths). You can use a `"{...}"` string literal as a wildcard for
242// arbitrary nested JSON (useful for parts of object emitted by other programs 245/// arbitrary nested JSON. Arrays are sorted before comparison.
243// (e.g. rustc) rather than Cargo itself). Arrays are sorted before comparison.
244pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { 246pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> {
245 use serde_json::Value::*; 247 use serde_json::Value::*;
246 match (expected, actual) { 248 match (expected, actual) {
@@ -286,6 +288,14 @@ pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a
286 } 288 }
287} 289}
288 290
291/// Calls callback `f` with input code and file paths of all `.rs` files from `test_data_dir`
292/// subdirectories defined by `paths`.
293///
294/// If the content of the matching `.txt` file differs from the output of `f()`
295/// the test will fail.
296///
297/// If there is no matching `.txt` file it will be created and filled with the
298/// output of `f()`, but the test will fail.
289pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], f: F) 299pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], f: F)
290where 300where
291 F: Fn(&str, &Path) -> String, 301 F: Fn(&str, &Path) -> String,
@@ -307,6 +317,7 @@ where
307 } 317 }
308} 318}
309 319
320/// Collects all `.rs` files from `test_data_dir` subdirectories defined by `paths`.
310pub fn collect_tests(test_data_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> { 321pub fn collect_tests(test_data_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> {
311 paths 322 paths
312 .iter() 323 .iter()
@@ -321,6 +332,7 @@ pub fn collect_tests(test_data_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, Stri
321 .collect() 332 .collect()
322} 333}
323 334
335/// Collects paths to all `.rs` files from `dir` in a sorted `Vec<PathBuf>`.
324fn test_from_dir(dir: &Path) -> Vec<PathBuf> { 336fn test_from_dir(dir: &Path) -> Vec<PathBuf> {
325 let mut acc = Vec::new(); 337 let mut acc = Vec::new();
326 for file in fs::read_dir(&dir).unwrap() { 338 for file in fs::read_dir(&dir).unwrap() {
@@ -334,6 +346,7 @@ fn test_from_dir(dir: &Path) -> Vec<PathBuf> {
334 acc 346 acc
335} 347}
336 348
349/// Returns the path to the root directory of `rust-analyzer` project.
337pub fn project_dir() -> PathBuf { 350pub fn project_dir() -> PathBuf {
338 let dir = env!("CARGO_MANIFEST_DIR"); 351 let dir = env!("CARGO_MANIFEST_DIR");
339 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned() 352 PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
@@ -356,6 +369,9 @@ pub fn read_text(path: &Path) -> String {
356 .replace("\r\n", "\n") 369 .replace("\r\n", "\n")
357} 370}
358 371
372/// Returns `false` if slow tests should not run, otherwise returns `true` and
373/// also creates a file at `./target/.slow_tests_cookie` which serves as a flag
374/// that slow tests did run.
359pub fn skip_slow_tests() -> bool { 375pub fn skip_slow_tests() -> bool {
360 let should_skip = std::env::var("CI").is_err() && std::env::var("RUN_SLOW_TESTS").is_err(); 376 let should_skip = std::env::var("CI").is_err() && std::env::var("RUN_SLOW_TESTS").is_err();
361 if should_skip { 377 if should_skip {
@@ -367,8 +383,9 @@ pub fn skip_slow_tests() -> bool {
367 should_skip 383 should_skip
368} 384}
369 385
370const REWRITE: bool = false; 386/// Asserts that `expected` and `actual` strings are equal. If they differ only
371 387/// in trailing or leading whitespace the test won't fail and
388/// the contents of `actual` will be written to the file located at `path`.
372fn assert_equal_text(expected: &str, actual: &str, path: &Path) { 389fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
373 if expected == actual { 390 if expected == actual {
374 return; 391 return;
@@ -381,6 +398,7 @@ fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
381 fs::write(path, actual).unwrap(); 398 fs::write(path, actual).unwrap();
382 return; 399 return;
383 } 400 }
401 const REWRITE: bool = false;
384 if REWRITE { 402 if REWRITE {
385 println!("rewriting {}", pretty_path.display()); 403 println!("rewriting {}", pretty_path.display());
386 fs::write(path, actual).unwrap(); 404 fs::write(path, actual).unwrap();
diff --git a/crates/test_utils/src/marks.rs b/crates/test_utils/src/marks.rs
index fe1813947..f8fabfaff 100644
--- a/crates/test_utils/src/marks.rs
+++ b/crates/test_utils/src/marks.rs
@@ -1,4 +1,4 @@
1//! This module implements manually tracked test coverage, which useful for 1//! This module implements manually tracked test coverage, which is useful for
2//! quickly finding a test responsible for testing a particular bit of code. 2//! quickly finding a test responsible for testing a particular bit of code.
3//! 3//!
4//! See <https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html> 4//! See <https://matklad.github.io/2018/06/18/a-trick-for-test-maintenance.html>