diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_assists/src/assists/auto_import.rs | 46 | ||||
-rw-r--r-- | crates/ra_cargo_watch/Cargo.toml | 1 | ||||
-rw-r--r-- | crates/ra_cargo_watch/src/lib.rs | 44 | ||||
-rw-r--r-- | crates/ra_hir/src/db.rs | 22 | ||||
-rw-r--r-- | crates/ra_hir_def/src/find_path.rs | 99 | ||||
-rw-r--r-- | crates/ra_hir_def/src/marks.rs | 1 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres.rs | 40 | ||||
-rw-r--r-- | crates/ra_hir_def/src/nameres/tests.rs | 43 | ||||
-rw-r--r-- | crates/ra_hir_expand/src/name.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/coercion.rs | 96 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/regression.rs | 8 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/simple.rs | 70 | ||||
-rw-r--r-- | crates/ra_hir_ty/src/tests/traits.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/change.rs | 51 | ||||
-rw-r--r-- | crates/ra_lsp_server/src/main_loop.rs | 92 | ||||
-rw-r--r-- | crates/ra_prof/src/lib.rs | 117 | ||||
-rw-r--r-- | crates/test_utils/src/lib.rs | 82 | ||||
-rw-r--r-- | crates/test_utils/src/marks.rs | 2 |
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 @@ | |||
1 | use hir::db::HirDatabase; | 1 | use hir::db::HirDatabase; |
2 | use ra_syntax::{ | 2 | use 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 | ||
72 | fn 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 | |||
87 | fn import_to_action(import: String, position: &SyntaxNode, anchor: &SyntaxNode) -> ActionBuilder { | 75 | fn 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" | |||
11 | cargo_metadata = "0.9.1" | 11 | cargo_metadata = "0.9.1" |
12 | jod-thread = "0.1.0" | 12 | jod-thread = "0.1.0" |
13 | parking_lot = "0.10.0" | 13 | parking_lot = "0.10.0" |
14 | serde_json = "1.0.45" | ||
14 | 15 | ||
15 | [dev-dependencies] | 16 | [dev-dependencies] |
16 | insta = "0.13.0" | 17 | insta = "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 | }; |
10 | use std::{ | 10 | use 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 | ||
3 | pub use hir_def::db::{ | 3 | pub 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 | }; |
10 | pub use hir_expand::db::{ | 12 | pub 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 | }; |
14 | pub use hir_ty::db::{ | 16 | pub 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 | }; |
10 | use hir_expand::name::Name; | 10 | use hir_expand::name::{known, Name}; |
11 | use test_utils::tested_by; | ||
11 | 12 | ||
12 | const MAX_PATH_LEN: usize = 15; | 13 | const MAX_PATH_LEN: usize = 15; |
13 | 14 | ||
15 | impl 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 | ||
123 | fn path_len(path: &ModPath) -> usize { | 153 | fn 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 | ||
134 | fn find_importable_locations( | 167 | fn 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 | ||
234 | impl ModuleData { | 274 | impl 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; | |||
10 | use ra_db::{fixture::WithFixture, SourceDatabase}; | 10 | use ra_db::{fixture::WithFixture, SourceDatabase}; |
11 | use test_utils::covers; | 11 | use test_utils::covers; |
12 | 12 | ||
13 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB, LocalModuleId}; | 13 | use crate::{db::DefDatabase, nameres::*, test_db::TestDB}; |
14 | 14 | ||
15 | fn def_map(fixture: &str) -> String { | 15 | fn 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 | ||
20 | fn compute_crate_def_map(fixture: &str) -> Arc<CrateDefMap> { | 19 | fn 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 | ||
26 | fn 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] |
65 | fn crate_def_map_smoke_test() { | 26 | fn 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; | |||
5 | mod subscriptions; | 5 | mod subscriptions; |
6 | pub(crate) mod pending_requests; | 6 | pub(crate) mod pending_requests; |
7 | 7 | ||
8 | use std::{error::Error, fmt, panic, path::PathBuf, sync::Arc, time::Instant}; | 8 | use std::{ |
9 | env, | ||
10 | error::Error, | ||
11 | fmt, panic, | ||
12 | path::PathBuf, | ||
13 | sync::Arc, | ||
14 | time::{Duration, Instant}, | ||
15 | }; | ||
9 | 16 | ||
10 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; | 17 | use crossbeam_channel::{select, unbounded, RecvError, Sender}; |
11 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 18 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
12 | use lsp_types::{ClientCapabilities, NumberOrString, Url}; | 19 | use lsp_types::{ClientCapabilities, NumberOrString}; |
13 | use ra_cargo_watch::{CheckOptions, CheckTask}; | 20 | use ra_cargo_watch::{CheckOptions, CheckTask}; |
14 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; | 21 | use ra_ide::{Canceled, FeatureFlags, FileId, LibraryData, SourceRootId}; |
15 | use ra_prof::profile; | 22 | use 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 | ||
604 | fn on_check_task( | 624 | fn 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 | ||
644 | fn 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 | ||
7 | use std::{ | 7 | use 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 | ||
20 | use itertools::Itertools; | ||
21 | use once_cell::sync::Lazy; | 20 | use once_cell::sync::Lazy; |
22 | 21 | ||
23 | pub use crate::memory_usage::{Bytes, MemoryUsage}; | 22 | pub 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 | ||
230 | fn print( | 229 | fn 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 | |
238 | fn 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"); | 288 | fn 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 | ||
22 | pub const CURSOR_MARKER: &str = "<|>"; | 22 | pub 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] |
25 | macro_rules! assert_eq_text { | 31 | macro_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()`. | ||
45 | pub fn extract_offset(text: &str) -> (TextUnit, String) { | 52 | pub 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. | ||
52 | fn try_extract_offset(text: &str) -> Option<(TextUnit, String)> { | 61 | fn 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()`. | ||
61 | pub fn extract_range(text: &str) -> (TextRange, String) { | 71 | pub 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. | ||
68 | fn try_extract_range(text: &str) -> Option<(TextRange, String)> { | 80 | fn 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`. | ||
88 | pub fn extract_range_or_offset(text: &str) -> (RangeOrOffset, String) { | 105 | pub 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` |
97 | pub fn extract_ranges(mut text: &str, tag: &str) -> (Vec<TextRange>, String) { | 114 | pub 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`. | ||
130 | pub fn add_cursor(text: &str, offset: TextUnit) -> String { | 148 | pub 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 | /// ``` |
154 | pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> { | 171 | pub 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. | ||
244 | pub fn find_mismatch<'a>(expected: &'a Value, actual: &'a Value) -> Option<(&'a Value, &'a Value)> { | 246 | pub 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. | ||
289 | pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], f: F) | 299 | pub fn dir_tests<F>(test_data_dir: &Path, paths: &[&str], f: F) |
290 | where | 300 | where |
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`. | ||
310 | pub fn collect_tests(test_data_dir: &Path, paths: &[&str]) -> Vec<(PathBuf, String)> { | 321 | pub 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>`. | ||
324 | fn test_from_dir(dir: &Path) -> Vec<PathBuf> { | 336 | fn 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. | ||
337 | pub fn project_dir() -> PathBuf { | 350 | pub 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. | ||
359 | pub fn skip_slow_tests() -> bool { | 375 | pub 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 | ||
370 | const 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`. | ||
372 | fn assert_equal_text(expected: &str, actual: &str, path: &Path) { | 389 | fn 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> |