aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/paths/Cargo.toml8
-rw-r--r--crates/paths/src/lib.rs123
-rw-r--r--crates/ra_assists/src/assist_context.rs2
-rw-r--r--crates/ra_assists/src/ast_transform.rs2
-rw-r--r--crates/ra_assists/src/handlers/auto_import.rs103
-rw-r--r--crates/ra_assists/src/handlers/fill_match_arms.rs50
-rw-r--r--crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs280
-rw-r--r--crates/ra_assists/src/utils.rs2
-rw-r--r--crates/ra_assists/src/utils/insert_use.rs24
-rw-r--r--crates/ra_db/src/input.rs8
-rw-r--r--crates/ra_db/src/lib.rs20
-rw-r--r--crates/ra_flycheck/src/lib.rs24
-rw-r--r--crates/ra_hir/src/code_model.rs18
-rw-r--r--crates/ra_hir/src/semantics.rs3
-rw-r--r--crates/ra_hir/src/source_analyzer.rs3
-rw-r--r--crates/ra_hir_def/Cargo.toml3
-rw-r--r--crates/ra_hir_def/src/body.rs2
-rw-r--r--crates/ra_hir_def/src/data.rs2
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs3
-rw-r--r--crates/ra_hir_def/src/find_path.rs59
-rw-r--r--crates/ra_hir_def/src/import_map.rs378
-rw-r--r--crates/ra_hir_def/src/lib.rs11
-rw-r--r--crates/ra_hir_def/src/nameres.rs3
-rw-r--r--crates/ra_hir_def/src/nameres/collector.rs39
-rw-r--r--crates/ra_hir_def/src/nameres/mod_resolution.rs8
-rw-r--r--crates/ra_hir_def/src/path.rs22
-rw-r--r--crates/ra_hir_def/src/test_db.rs3
-rw-r--r--crates/ra_hir_expand/Cargo.toml1
-rw-r--r--crates/ra_hir_expand/src/builtin_derive.rs31
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs18
-rw-r--r--crates/ra_hir_expand/src/eager.rs32
-rw-r--r--crates/ra_hir_expand/src/lib.rs11
-rw-r--r--crates/ra_hir_expand/src/test_db.rs3
-rw-r--r--crates/ra_hir_ty/src/_match.rs1045
-rw-r--r--crates/ra_hir_ty/src/expr.rs10
-rw-r--r--crates/ra_hir_ty/src/infer.rs16
-rw-r--r--crates/ra_hir_ty/src/test_db.rs3
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs4
-rw-r--r--crates/ra_hir_ty/src/tests/traits.rs30
-rw-r--r--crates/ra_ide/src/completion.rs1
-rw-r--r--crates/ra_ide/src/completion/complete_keyword.rs940
-rw-r--r--crates/ra_ide/src/completion/complete_postfix.rs8
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs52
-rw-r--r--crates/ra_ide/src/completion/completion_item.rs26
-rw-r--r--crates/ra_ide/src/completion/patterns.rs194
-rw-r--r--crates/ra_ide/src/completion/test_utils.rs46
-rw-r--r--crates/ra_ide/src/diagnostics.rs91
-rw-r--r--crates/ra_ide/src/lib.rs8
-rw-r--r--crates/ra_ide/src/prime_caches.rs2
-rw-r--r--crates/ra_ide/src/references/rename.rs91
-rw-r--r--crates/ra_ide/src/snapshots/highlight_doctest.html28
-rw-r--r--crates/ra_ide/src/snapshots/highlight_injection.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlight_strings.html8
-rw-r--r--crates/ra_ide/src/snapshots/highlight_unsafe.html2
-rw-r--r--crates/ra_ide/src/snapshots/highlighting.html14
-rw-r--r--crates/ra_ide/src/snapshots/rainbow_highlighting.html2
-rw-r--r--crates/ra_ide/src/ssr.rs74
-rw-r--r--crates/ra_ide/src/status.rs15
-rw-r--r--crates/ra_ide/src/syntax_highlighting.rs122
-rw-r--r--crates/ra_ide/src/syntax_highlighting/html.rs4
-rw-r--r--crates/ra_ide/src/syntax_highlighting/injection.rs16
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tags.rs2
-rw-r--r--crates/ra_ide/src/syntax_highlighting/tests.rs58
-rw-r--r--crates/ra_ide/src/typing.rs38
-rw-r--r--crates/ra_ide_db/src/change.rs86
-rw-r--r--crates/ra_ide_db/src/defs.rs9
-rw-r--r--crates/ra_ide_db/src/imports_locator.rs46
-rw-r--r--crates/ra_ide_db/src/lib.rs20
-rw-r--r--crates/ra_ide_db/src/source_change.rs6
-rw-r--r--crates/ra_ide_db/src/symbol_index.rs127
-rw-r--r--crates/ra_parser/src/grammar/expressions.rs6
-rw-r--r--crates/ra_parser/src/grammar/type_params.rs6
-rw-r--r--crates/ra_parser/src/grammar/types.rs16
-rw-r--r--crates/ra_project_model/src/lib.rs54
-rw-r--r--crates/ra_syntax/src/algo.rs8
-rw-r--r--crates/ra_syntax/src/ast.rs6
-rw-r--r--crates/ra_syntax/src/ast/generated/nodes.rs276
-rw-r--r--crates/ra_syntax/src/ast/generated/tokens.rs8
-rw-r--r--crates/ra_syntax/src/ast/tokens.rs10
-rw-r--r--crates/ra_syntax/src/lib.rs3
-rw-r--r--crates/ra_syntax/src/parsing/text_token_source.rs89
-rw-r--r--crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast15
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast240
-rw-r--r--crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs9
-rw-r--r--crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast1
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast117
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs2
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast292
-rw-r--r--crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs5
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast392
-rw-r--r--crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs30
-rw-r--r--crates/rust-analyzer/src/bin/main.rs8
-rw-r--r--crates/rust-analyzer/src/cargo_target_spec.rs32
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs42
-rw-r--r--crates/rust-analyzer/src/config.rs17
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs6
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap86
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap86
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs285
-rw-r--r--crates/rust-analyzer/src/from_proto.rs2
-rw-r--r--crates/rust-analyzer/src/global_state.rs145
-rw-r--r--crates/rust-analyzer/src/main_loop.rs103
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs4
-rw-r--r--crates/rust-analyzer/src/semantic_tokens.rs1
-rw-r--r--crates/rust-analyzer/src/to_proto.rs175
-rw-r--r--crates/test_utils/src/lib.rs12
-rw-r--r--crates/vfs/Cargo.toml14
-rw-r--r--crates/vfs/src/file_set.rs99
-rw-r--r--crates/vfs/src/lib.rs138
-rw-r--r--crates/vfs/src/loader.rs69
-rw-r--r--crates/vfs/src/path_interner.rs31
-rw-r--r--crates/vfs/src/vfs_path.rs49
-rw-r--r--crates/vfs/src/walkdir_loader.rs108
114 files changed, 5010 insertions, 2634 deletions
diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml
new file mode 100644
index 000000000..646ee7fd5
--- /dev/null
+++ b/crates/paths/Cargo.toml
@@ -0,0 +1,8 @@
1[package]
2name = "paths"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6
7[lib]
8doctest = false
diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs
new file mode 100644
index 000000000..c7ce0c42f
--- /dev/null
+++ b/crates/paths/src/lib.rs
@@ -0,0 +1,123 @@
1//! Thin wrappers around `std::path`, distinguishing between absolute and
2//! relative paths.
3use std::{
4 convert::{TryFrom, TryInto},
5 ops,
6 path::{Component, Path, PathBuf},
7};
8
9#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
10pub struct AbsPathBuf(PathBuf);
11
12impl From<AbsPathBuf> for PathBuf {
13 fn from(AbsPathBuf(path_buf): AbsPathBuf) -> PathBuf {
14 path_buf
15 }
16}
17
18impl ops::Deref for AbsPathBuf {
19 type Target = AbsPath;
20 fn deref(&self) -> &AbsPath {
21 self.as_path()
22 }
23}
24
25impl AsRef<Path> for AbsPathBuf {
26 fn as_ref(&self) -> &Path {
27 self.0.as_path()
28 }
29}
30
31impl TryFrom<PathBuf> for AbsPathBuf {
32 type Error = PathBuf;
33 fn try_from(path_buf: PathBuf) -> Result<AbsPathBuf, PathBuf> {
34 if !path_buf.is_absolute() {
35 return Err(path_buf);
36 }
37 Ok(AbsPathBuf(path_buf))
38 }
39}
40
41impl TryFrom<&str> for AbsPathBuf {
42 type Error = PathBuf;
43 fn try_from(path: &str) -> Result<AbsPathBuf, PathBuf> {
44 AbsPathBuf::try_from(PathBuf::from(path))
45 }
46}
47
48impl AbsPathBuf {
49 pub fn as_path(&self) -> &AbsPath {
50 AbsPath::new_unchecked(self.0.as_path())
51 }
52 pub fn pop(&mut self) -> bool {
53 self.0.pop()
54 }
55}
56
57#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
58#[repr(transparent)]
59pub struct AbsPath(Path);
60
61impl ops::Deref for AbsPath {
62 type Target = Path;
63 fn deref(&self) -> &Path {
64 &self.0
65 }
66}
67
68impl AsRef<Path> for AbsPath {
69 fn as_ref(&self) -> &Path {
70 &self.0
71 }
72}
73
74impl<'a> TryFrom<&'a Path> for &'a AbsPath {
75 type Error = &'a Path;
76 fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> {
77 if !path.is_absolute() {
78 return Err(path);
79 }
80 Ok(AbsPath::new_unchecked(path))
81 }
82}
83
84impl AbsPath {
85 fn new_unchecked(path: &Path) -> &AbsPath {
86 unsafe { &*(path as *const Path as *const AbsPath) }
87 }
88
89 pub fn join(&self, path: impl AsRef<Path>) -> AbsPathBuf {
90 self.as_ref().join(path).try_into().unwrap()
91 }
92 pub fn normalize(&self) -> AbsPathBuf {
93 AbsPathBuf(normalize_path(&self.0))
94 }
95}
96
97// https://github.com/rust-lang/cargo/blob/79c769c3d7b4c2cf6a93781575b7f592ef974255/src/cargo/util/paths.rs#L60-L85
98fn normalize_path(path: &Path) -> PathBuf {
99 let mut components = path.components().peekable();
100 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
101 components.next();
102 PathBuf::from(c.as_os_str())
103 } else {
104 PathBuf::new()
105 };
106
107 for component in components {
108 match component {
109 Component::Prefix(..) => unreachable!(),
110 Component::RootDir => {
111 ret.push(component.as_os_str());
112 }
113 Component::CurDir => {}
114 Component::ParentDir => {
115 ret.pop();
116 }
117 Component::Normal(c) => {
118 ret.push(c);
119 }
120 }
121 }
122 ret
123}
diff --git a/crates/ra_assists/src/assist_context.rs b/crates/ra_assists/src/assist_context.rs
index edd8255f4..ee614de72 100644
--- a/crates/ra_assists/src/assist_context.rs
+++ b/crates/ra_assists/src/assist_context.rs
@@ -252,7 +252,7 @@ impl AssistBuilder {
252 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) { 252 pub(crate) fn rewrite(&mut self, rewriter: SyntaxRewriter) {
253 let node = rewriter.rewrite_root().unwrap(); 253 let node = rewriter.rewrite_root().unwrap();
254 let new = rewriter.rewrite(&node); 254 let new = rewriter.rewrite(&node);
255 algo::diff(&node, &new).into_text_edit(&mut self.edit) 255 algo::diff(&node, &new).into_text_edit(&mut self.edit);
256 } 256 }
257 257
258 // FIXME: kill this API 258 // FIXME: kill this API
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 3079a02a2..00fa95b6c 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -106,6 +106,7 @@ impl<'a> SubstituteTypeParams<'a> {
106 _ => return None, 106 _ => return None,
107 }; 107 };
108 // FIXME: use `hir::Path::from_src` instead. 108 // FIXME: use `hir::Path::from_src` instead.
109 #[allow(deprecated)]
109 let path = hir::Path::from_ast(path)?; 110 let path = hir::Path::from_ast(path)?;
110 let resolution = self.source_scope.resolve_hir_path(&path)?; 111 let resolution = self.source_scope.resolve_hir_path(&path)?;
111 match resolution { 112 match resolution {
@@ -150,6 +151,7 @@ impl<'a> QualifyPaths<'a> {
150 return None; 151 return None;
151 } 152 }
152 // FIXME: use `hir::Path::from_src` instead. 153 // FIXME: use `hir::Path::from_src` instead.
154 #[allow(deprecated)]
153 let hir_path = hir::Path::from_ast(p.clone()); 155 let hir_path = hir::Path::from_ast(p.clone());
154 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?; 156 let resolution = self.source_scope.resolve_hir_path(&hir_path?)?;
155 match resolution { 157 match resolution {
diff --git a/crates/ra_assists/src/handlers/auto_import.rs b/crates/ra_assists/src/handlers/auto_import.rs
index edf96d50e..5092bf336 100644
--- a/crates/ra_assists/src/handlers/auto_import.rs
+++ b/crates/ra_assists/src/handlers/auto_import.rs
@@ -130,7 +130,7 @@ impl AutoImportAssets {
130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> { 130 fn search_for_imports(&self, db: &RootDatabase) -> BTreeSet<ModPath> {
131 let _p = profile("auto_import::search_for_imports"); 131 let _p = profile("auto_import::search_for_imports");
132 let current_crate = self.module_with_name_to_import.krate(); 132 let current_crate = self.module_with_name_to_import.krate();
133 ImportsLocator::new(db) 133 ImportsLocator::new(db, current_crate)
134 .find_imports(&self.get_search_query()) 134 .find_imports(&self.get_search_query())
135 .into_iter() 135 .into_iter()
136 .filter_map(|candidate| match &self.import_candidate { 136 .filter_map(|candidate| match &self.import_candidate {
@@ -841,4 +841,105 @@ fn main() {
841 ", 841 ",
842 ) 842 )
843 } 843 }
844
845 #[test]
846 fn dep_import() {
847 check_assist(
848 auto_import,
849 r"
850 //- /lib.rs crate:dep
851 pub struct Struct;
852
853 //- /main.rs crate:main deps:dep
854 fn main() {
855 Struct<|>
856 }",
857 r"use dep::Struct;
858
859fn main() {
860 Struct
861}
862",
863 );
864 }
865
866 #[test]
867 fn whole_segment() {
868 // Tests that only imports whose last segment matches the identifier get suggested.
869 check_assist(
870 auto_import,
871 r"
872 //- /lib.rs crate:dep
873 pub mod fmt {
874 pub trait Display {}
875 }
876
877 pub fn panic_fmt() {}
878
879 //- /main.rs crate:main deps:dep
880 struct S;
881
882 impl f<|>mt::Display for S {}",
883 r"use dep::fmt;
884
885struct S;
886impl fmt::Display for S {}
887",
888 );
889 }
890
891 #[test]
892 fn macro_generated() {
893 // Tests that macro-generated items are suggested from external crates.
894 check_assist(
895 auto_import,
896 r"
897 //- /lib.rs crate:dep
898
899 macro_rules! mac {
900 () => {
901 pub struct Cheese;
902 };
903 }
904
905 mac!();
906
907 //- /main.rs crate:main deps:dep
908
909 fn main() {
910 Cheese<|>;
911 }",
912 r"use dep::Cheese;
913
914fn main() {
915 Cheese;
916}
917",
918 );
919 }
920
921 #[test]
922 fn casing() {
923 // Tests that differently cased names don't interfere and we only suggest the matching one.
924 check_assist(
925 auto_import,
926 r"
927 //- /lib.rs crate:dep
928
929 pub struct FMT;
930 pub struct fmt;
931
932 //- /main.rs crate:main deps:dep
933
934 fn main() {
935 FMT<|>;
936 }",
937 r"use dep::FMT;
938
939fn main() {
940 FMT;
941}
942",
943 );
944 }
844} 945}
diff --git a/crates/ra_assists/src/handlers/fill_match_arms.rs b/crates/ra_assists/src/handlers/fill_match_arms.rs
index cc303285b..569efb768 100644
--- a/crates/ra_assists/src/handlers/fill_match_arms.rs
+++ b/crates/ra_assists/src/handlers/fill_match_arms.rs
@@ -136,8 +136,20 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
136} 136}
137 137
138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool { 138fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
139 let pat_head = pat.syntax().first_child().map(|node| node.text()); 139 let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
140 let var_head = var.syntax().first_child().map(|node| node.text()); 140
141 let pat_head = match pat {
142 Pat::BindPat(bind_pat) => {
143 if let Some(p) = bind_pat.pat() {
144 first_node_text(&p)
145 } else {
146 return false;
147 }
148 }
149 pat => first_node_text(pat),
150 };
151
152 let var_head = first_node_text(var);
141 153
142 pat_head == var_head 154 pat_head == var_head
143} 155}
@@ -351,6 +363,40 @@ mod tests {
351 } 363 }
352 364
353 #[test] 365 #[test]
366 fn partial_fill_bind_pat() {
367 check_assist(
368 fill_match_arms,
369 r#"
370 enum A {
371 As,
372 Bs,
373 Cs(Option<i32>),
374 }
375 fn main() {
376 match A::As<|> {
377 A::As(_) => {}
378 a @ A::Bs(_) => {}
379 }
380 }
381 "#,
382 r#"
383 enum A {
384 As,
385 Bs,
386 Cs(Option<i32>),
387 }
388 fn main() {
389 match A::As {
390 A::As(_) => {}
391 a @ A::Bs(_) => {}
392 $0A::Cs(_) => {}
393 }
394 }
395 "#,
396 );
397 }
398
399 #[test]
354 fn fill_match_arms_empty_body() { 400 fn fill_match_arms_empty_body() {
355 check_assist( 401 check_assist(
356 fill_match_arms, 402 fill_match_arms,
diff --git a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
index 0197a8cf0..b4784c333 100644
--- a/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
+++ b/crates/ra_assists/src/handlers/replace_qualified_name_with_use.rs
@@ -1,7 +1,10 @@
1use hir; 1use hir;
2use ra_syntax::{ast, AstNode, SmolStr, TextRange}; 2use ra_syntax::{algo::SyntaxRewriter, ast, match_ast, AstNode, SmolStr, SyntaxNode};
3 3
4use crate::{utils::insert_use_statement, AssistContext, AssistId, Assists}; 4use crate::{
5 utils::{find_insert_use_container, insert_use_statement},
6 AssistContext, AssistId, Assists,
7};
5 8
6// Assist: replace_qualified_name_with_use 9// Assist: replace_qualified_name_with_use
7// 10//
@@ -39,16 +42,18 @@ pub(crate) fn replace_qualified_name_with_use(
39 target, 42 target,
40 |builder| { 43 |builder| {
41 let path_to_import = hir_path.mod_path().clone(); 44 let path_to_import = hir_path.mod_path().clone();
45 let container = match find_insert_use_container(path.syntax(), ctx) {
46 Some(c) => c,
47 None => return,
48 };
42 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder()); 49 insert_use_statement(path.syntax(), &path_to_import, ctx, builder.text_edit_builder());
43 50
44 if let Some(last) = path.segment() { 51 // Now that we've brought the name into scope, re-qualify all paths that could be
45 // Here we are assuming the assist will provide a correct use statement 52 // affected (that is, all paths inside the node we added the `use` to).
46 // so we can delete the path qualifier 53 let mut rewriter = SyntaxRewriter::default();
47 builder.delete(TextRange::new( 54 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
48 path.syntax().text_range().start(), 55 shorten_paths(&mut rewriter, syntax, path);
49 last.syntax().text_range().start(), 56 builder.rewrite(rewriter);
50 ));
51 }
52 }, 57 },
53 ) 58 )
54} 59}
@@ -73,6 +78,69 @@ fn collect_hir_path_segments(path: &hir::Path) -> Option<Vec<SmolStr>> {
73 Some(ps) 78 Some(ps)
74} 79}
75 80
81/// Adds replacements to `re` that shorten `path` in all descendants of `node`.
82fn shorten_paths(rewriter: &mut SyntaxRewriter<'static>, node: SyntaxNode, path: ast::Path) {
83 for child in node.children() {
84 match_ast! {
85 match child {
86 // Don't modify `use` items, as this can break the `use` item when injecting a new
87 // import into the use tree.
88 ast::UseItem(_it) => continue,
89 // Don't descend into submodules, they don't have the same `use` items in scope.
90 ast::Module(_it) => continue,
91
92 ast::Path(p) => {
93 match maybe_replace_path(rewriter, p.clone(), path.clone()) {
94 Some(()) => {},
95 None => shorten_paths(rewriter, p.syntax().clone(), path.clone()),
96 }
97 },
98 _ => shorten_paths(rewriter, child, path.clone()),
99 }
100 }
101 }
102}
103
104fn maybe_replace_path(
105 rewriter: &mut SyntaxRewriter<'static>,
106 path: ast::Path,
107 target: ast::Path,
108) -> Option<()> {
109 if !path_eq(path.clone(), target.clone()) {
110 return None;
111 }
112
113 // Shorten `path`, leaving only its last segment.
114 if let Some(parent) = path.qualifier() {
115 rewriter.delete(parent.syntax());
116 }
117 if let Some(double_colon) = path.coloncolon_token() {
118 rewriter.delete(&double_colon);
119 }
120
121 Some(())
122}
123
124fn path_eq(lhs: ast::Path, rhs: ast::Path) -> bool {
125 let mut lhs_curr = lhs;
126 let mut rhs_curr = rhs;
127 loop {
128 match (lhs_curr.segment(), rhs_curr.segment()) {
129 (Some(lhs), Some(rhs)) if lhs.syntax().text() == rhs.syntax().text() => (),
130 _ => return false,
131 }
132
133 match (lhs_curr.qualifier(), rhs_curr.qualifier()) {
134 (Some(lhs), Some(rhs)) => {
135 lhs_curr = lhs;
136 rhs_curr = rhs;
137 }
138 (None, None) => return true,
139 _ => return false,
140 }
141 }
142}
143
76#[cfg(test)] 144#[cfg(test)]
77mod tests { 145mod tests {
78 use crate::tests::{check_assist, check_assist_not_applicable}; 146 use crate::tests::{check_assist, check_assist_not_applicable};
@@ -83,10 +151,10 @@ mod tests {
83 fn test_replace_add_use_no_anchor() { 151 fn test_replace_add_use_no_anchor() {
84 check_assist( 152 check_assist(
85 replace_qualified_name_with_use, 153 replace_qualified_name_with_use,
86 " 154 r"
87std::fmt::Debug<|> 155std::fmt::Debug<|>
88 ", 156 ",
89 " 157 r"
90use std::fmt::Debug; 158use std::fmt::Debug;
91 159
92Debug 160Debug
@@ -97,13 +165,13 @@ Debug
97 fn test_replace_add_use_no_anchor_with_item_below() { 165 fn test_replace_add_use_no_anchor_with_item_below() {
98 check_assist( 166 check_assist(
99 replace_qualified_name_with_use, 167 replace_qualified_name_with_use,
100 " 168 r"
101std::fmt::Debug<|> 169std::fmt::Debug<|>
102 170
103fn main() { 171fn main() {
104} 172}
105 ", 173 ",
106 " 174 r"
107use std::fmt::Debug; 175use std::fmt::Debug;
108 176
109Debug 177Debug
@@ -118,13 +186,13 @@ fn main() {
118 fn test_replace_add_use_no_anchor_with_item_above() { 186 fn test_replace_add_use_no_anchor_with_item_above() {
119 check_assist( 187 check_assist(
120 replace_qualified_name_with_use, 188 replace_qualified_name_with_use,
121 " 189 r"
122fn main() { 190fn main() {
123} 191}
124 192
125std::fmt::Debug<|> 193std::fmt::Debug<|>
126 ", 194 ",
127 " 195 r"
128use std::fmt::Debug; 196use std::fmt::Debug;
129 197
130fn main() { 198fn main() {
@@ -139,10 +207,10 @@ Debug
139 fn test_replace_add_use_no_anchor_2seg() { 207 fn test_replace_add_use_no_anchor_2seg() {
140 check_assist( 208 check_assist(
141 replace_qualified_name_with_use, 209 replace_qualified_name_with_use,
142 " 210 r"
143std::fmt<|>::Debug 211std::fmt<|>::Debug
144 ", 212 ",
145 " 213 r"
146use std::fmt; 214use std::fmt;
147 215
148fmt::Debug 216fmt::Debug
@@ -154,13 +222,13 @@ fmt::Debug
154 fn test_replace_add_use() { 222 fn test_replace_add_use() {
155 check_assist( 223 check_assist(
156 replace_qualified_name_with_use, 224 replace_qualified_name_with_use,
157 " 225 r"
158use stdx; 226use stdx;
159 227
160impl std::fmt::Debug<|> for Foo { 228impl std::fmt::Debug<|> for Foo {
161} 229}
162 ", 230 ",
163 " 231 r"
164use stdx; 232use stdx;
165use std::fmt::Debug; 233use std::fmt::Debug;
166 234
@@ -174,11 +242,11 @@ impl Debug for Foo {
174 fn test_replace_file_use_other_anchor() { 242 fn test_replace_file_use_other_anchor() {
175 check_assist( 243 check_assist(
176 replace_qualified_name_with_use, 244 replace_qualified_name_with_use,
177 " 245 r"
178impl std::fmt::Debug<|> for Foo { 246impl std::fmt::Debug<|> for Foo {
179} 247}
180 ", 248 ",
181 " 249 r"
182use std::fmt::Debug; 250use std::fmt::Debug;
183 251
184impl Debug for Foo { 252impl Debug for Foo {
@@ -191,11 +259,11 @@ impl Debug for Foo {
191 fn test_replace_add_use_other_anchor_indent() { 259 fn test_replace_add_use_other_anchor_indent() {
192 check_assist( 260 check_assist(
193 replace_qualified_name_with_use, 261 replace_qualified_name_with_use,
194 " 262 r"
195 impl std::fmt::Debug<|> for Foo { 263 impl std::fmt::Debug<|> for Foo {
196 } 264 }
197 ", 265 ",
198 " 266 r"
199 use std::fmt::Debug; 267 use std::fmt::Debug;
200 268
201 impl Debug for Foo { 269 impl Debug for Foo {
@@ -208,13 +276,13 @@ impl Debug for Foo {
208 fn test_replace_split_different() { 276 fn test_replace_split_different() {
209 check_assist( 277 check_assist(
210 replace_qualified_name_with_use, 278 replace_qualified_name_with_use,
211 " 279 r"
212use std::fmt; 280use std::fmt;
213 281
214impl std::io<|> for Foo { 282impl std::io<|> for Foo {
215} 283}
216 ", 284 ",
217 " 285 r"
218use std::{io, fmt}; 286use std::{io, fmt};
219 287
220impl io for Foo { 288impl io for Foo {
@@ -227,13 +295,13 @@ impl io for Foo {
227 fn test_replace_split_self_for_use() { 295 fn test_replace_split_self_for_use() {
228 check_assist( 296 check_assist(
229 replace_qualified_name_with_use, 297 replace_qualified_name_with_use,
230 " 298 r"
231use std::fmt; 299use std::fmt;
232 300
233impl std::fmt::Debug<|> for Foo { 301impl std::fmt::Debug<|> for Foo {
234} 302}
235 ", 303 ",
236 " 304 r"
237use std::fmt::{self, Debug, }; 305use std::fmt::{self, Debug, };
238 306
239impl Debug for Foo { 307impl Debug for Foo {
@@ -246,13 +314,13 @@ impl Debug for Foo {
246 fn test_replace_split_self_for_target() { 314 fn test_replace_split_self_for_target() {
247 check_assist( 315 check_assist(
248 replace_qualified_name_with_use, 316 replace_qualified_name_with_use,
249 " 317 r"
250use std::fmt::Debug; 318use std::fmt::Debug;
251 319
252impl std::fmt<|> for Foo { 320impl std::fmt<|> for Foo {
253} 321}
254 ", 322 ",
255 " 323 r"
256use std::fmt::{self, Debug}; 324use std::fmt::{self, Debug};
257 325
258impl fmt for Foo { 326impl fmt for Foo {
@@ -265,13 +333,13 @@ impl fmt for Foo {
265 fn test_replace_add_to_nested_self_nested() { 333 fn test_replace_add_to_nested_self_nested() {
266 check_assist( 334 check_assist(
267 replace_qualified_name_with_use, 335 replace_qualified_name_with_use,
268 " 336 r"
269use std::fmt::{Debug, nested::{Display}}; 337use std::fmt::{Debug, nested::{Display}};
270 338
271impl std::fmt::nested<|> for Foo { 339impl std::fmt::nested<|> for Foo {
272} 340}
273", 341",
274 " 342 r"
275use std::fmt::{Debug, nested::{Display, self}}; 343use std::fmt::{Debug, nested::{Display, self}};
276 344
277impl nested for Foo { 345impl nested for Foo {
@@ -284,13 +352,13 @@ impl nested for Foo {
284 fn test_replace_add_to_nested_self_already_included() { 352 fn test_replace_add_to_nested_self_already_included() {
285 check_assist( 353 check_assist(
286 replace_qualified_name_with_use, 354 replace_qualified_name_with_use,
287 " 355 r"
288use std::fmt::{Debug, nested::{self, Display}}; 356use std::fmt::{Debug, nested::{self, Display}};
289 357
290impl std::fmt::nested<|> for Foo { 358impl std::fmt::nested<|> for Foo {
291} 359}
292", 360",
293 " 361 r"
294use std::fmt::{Debug, nested::{self, Display}}; 362use std::fmt::{Debug, nested::{self, Display}};
295 363
296impl nested for Foo { 364impl nested for Foo {
@@ -303,13 +371,13 @@ impl nested for Foo {
303 fn test_replace_add_to_nested_nested() { 371 fn test_replace_add_to_nested_nested() {
304 check_assist( 372 check_assist(
305 replace_qualified_name_with_use, 373 replace_qualified_name_with_use,
306 " 374 r"
307use std::fmt::{Debug, nested::{Display}}; 375use std::fmt::{Debug, nested::{Display}};
308 376
309impl std::fmt::nested::Debug<|> for Foo { 377impl std::fmt::nested::Debug<|> for Foo {
310} 378}
311", 379",
312 " 380 r"
313use std::fmt::{Debug, nested::{Display, Debug}}; 381use std::fmt::{Debug, nested::{Display, Debug}};
314 382
315impl Debug for Foo { 383impl Debug for Foo {
@@ -322,13 +390,13 @@ impl Debug for Foo {
322 fn test_replace_split_common_target_longer() { 390 fn test_replace_split_common_target_longer() {
323 check_assist( 391 check_assist(
324 replace_qualified_name_with_use, 392 replace_qualified_name_with_use,
325 " 393 r"
326use std::fmt::Debug; 394use std::fmt::Debug;
327 395
328impl std::fmt::nested::Display<|> for Foo { 396impl std::fmt::nested::Display<|> for Foo {
329} 397}
330", 398",
331 " 399 r"
332use std::fmt::{nested::Display, Debug}; 400use std::fmt::{nested::Display, Debug};
333 401
334impl Display for Foo { 402impl Display for Foo {
@@ -341,13 +409,13 @@ impl Display for Foo {
341 fn test_replace_split_common_use_longer() { 409 fn test_replace_split_common_use_longer() {
342 check_assist( 410 check_assist(
343 replace_qualified_name_with_use, 411 replace_qualified_name_with_use,
344 " 412 r"
345use std::fmt::nested::Debug; 413use std::fmt::nested::Debug;
346 414
347impl std::fmt::Display<|> for Foo { 415impl std::fmt::Display<|> for Foo {
348} 416}
349", 417",
350 " 418 r"
351use std::fmt::{Display, nested::Debug}; 419use std::fmt::{Display, nested::Debug};
352 420
353impl Display for Foo { 421impl Display for Foo {
@@ -360,7 +428,7 @@ impl Display for Foo {
360 fn test_replace_use_nested_import() { 428 fn test_replace_use_nested_import() {
361 check_assist( 429 check_assist(
362 replace_qualified_name_with_use, 430 replace_qualified_name_with_use,
363 " 431 r"
364use crate::{ 432use crate::{
365 ty::{Substs, Ty}, 433 ty::{Substs, Ty},
366 AssocItem, 434 AssocItem,
@@ -368,7 +436,7 @@ use crate::{
368 436
369fn foo() { crate::ty::lower<|>::trait_env() } 437fn foo() { crate::ty::lower<|>::trait_env() }
370", 438",
371 " 439 r"
372use crate::{ 440use crate::{
373 ty::{Substs, Ty, lower}, 441 ty::{Substs, Ty, lower},
374 AssocItem, 442 AssocItem,
@@ -383,13 +451,13 @@ fn foo() { lower::trait_env() }
383 fn test_replace_alias() { 451 fn test_replace_alias() {
384 check_assist( 452 check_assist(
385 replace_qualified_name_with_use, 453 replace_qualified_name_with_use,
386 " 454 r"
387use std::fmt as foo; 455use std::fmt as foo;
388 456
389impl foo::Debug<|> for Foo { 457impl foo::Debug<|> for Foo {
390} 458}
391", 459",
392 " 460 r"
393use std::fmt as foo; 461use std::fmt as foo;
394 462
395impl Debug for Foo { 463impl Debug for Foo {
@@ -402,7 +470,7 @@ impl Debug for Foo {
402 fn test_replace_not_applicable_one_segment() { 470 fn test_replace_not_applicable_one_segment() {
403 check_assist_not_applicable( 471 check_assist_not_applicable(
404 replace_qualified_name_with_use, 472 replace_qualified_name_with_use,
405 " 473 r"
406impl foo<|> for Foo { 474impl foo<|> for Foo {
407} 475}
408", 476",
@@ -413,7 +481,7 @@ impl foo<|> for Foo {
413 fn test_replace_not_applicable_in_use() { 481 fn test_replace_not_applicable_in_use() {
414 check_assist_not_applicable( 482 check_assist_not_applicable(
415 replace_qualified_name_with_use, 483 replace_qualified_name_with_use,
416 " 484 r"
417use std::fmt<|>; 485use std::fmt<|>;
418", 486",
419 ); 487 );
@@ -423,14 +491,14 @@ use std::fmt<|>;
423 fn test_replace_add_use_no_anchor_in_mod_mod() { 491 fn test_replace_add_use_no_anchor_in_mod_mod() {
424 check_assist( 492 check_assist(
425 replace_qualified_name_with_use, 493 replace_qualified_name_with_use,
426 " 494 r"
427mod foo { 495mod foo {
428 mod bar { 496 mod bar {
429 std::fmt::Debug<|> 497 std::fmt::Debug<|>
430 } 498 }
431} 499}
432 ", 500 ",
433 " 501 r"
434mod foo { 502mod foo {
435 mod bar { 503 mod bar {
436 use std::fmt::Debug; 504 use std::fmt::Debug;
@@ -446,14 +514,14 @@ mod foo {
446 fn inserts_imports_after_inner_attributes() { 514 fn inserts_imports_after_inner_attributes() {
447 check_assist( 515 check_assist(
448 replace_qualified_name_with_use, 516 replace_qualified_name_with_use,
449 " 517 r"
450#![allow(dead_code)] 518#![allow(dead_code)]
451 519
452fn main() { 520fn main() {
453 std::fmt::Debug<|> 521 std::fmt::Debug<|>
454} 522}
455 ", 523 ",
456 " 524 r"
457#![allow(dead_code)] 525#![allow(dead_code)]
458use std::fmt::Debug; 526use std::fmt::Debug;
459 527
@@ -463,4 +531,116 @@ fn main() {
463 ", 531 ",
464 ); 532 );
465 } 533 }
534
535 #[test]
536 fn replaces_all_affected_paths() {
537 check_assist(
538 replace_qualified_name_with_use,
539 r"
540fn main() {
541 std::fmt::Debug<|>;
542 let x: std::fmt::Debug = std::fmt::Debug;
543}
544 ",
545 r"
546use std::fmt::Debug;
547
548fn main() {
549 Debug;
550 let x: Debug = Debug;
551}
552 ",
553 );
554 }
555
556 #[test]
557 fn replaces_all_affected_paths_mod() {
558 check_assist(
559 replace_qualified_name_with_use,
560 r"
561mod m {
562 fn f() {
563 std::fmt::Debug<|>;
564 let x: std::fmt::Debug = std::fmt::Debug;
565 }
566 fn g() {
567 std::fmt::Debug;
568 }
569}
570
571fn f() {
572 std::fmt::Debug;
573}
574 ",
575 r"
576mod m {
577 use std::fmt::Debug;
578
579 fn f() {
580 Debug;
581 let x: Debug = Debug;
582 }
583 fn g() {
584 Debug;
585 }
586}
587
588fn f() {
589 std::fmt::Debug;
590}
591 ",
592 );
593 }
594
595 #[test]
596 fn does_not_replace_in_submodules() {
597 check_assist(
598 replace_qualified_name_with_use,
599 r"
600fn main() {
601 std::fmt::Debug<|>;
602}
603
604mod sub {
605 fn f() {
606 std::fmt::Debug;
607 }
608}
609 ",
610 r"
611use std::fmt::Debug;
612
613fn main() {
614 Debug;
615}
616
617mod sub {
618 fn f() {
619 std::fmt::Debug;
620 }
621}
622 ",
623 );
624 }
625
626 #[test]
627 fn does_not_replace_in_use() {
628 check_assist(
629 replace_qualified_name_with_use,
630 r"
631use std::fmt::Display;
632
633fn main() {
634 std::fmt<|>;
635}
636 ",
637 r"
638use std::fmt::{self, Display};
639
640fn main() {
641 fmt;
642}
643 ",
644 );
645 }
466} 646}
diff --git a/crates/ra_assists/src/utils.rs b/crates/ra_assists/src/utils.rs
index 0038a9764..c1ff0de7b 100644
--- a/crates/ra_assists/src/utils.rs
+++ b/crates/ra_assists/src/utils.rs
@@ -13,7 +13,7 @@ use rustc_hash::FxHashSet;
13 13
14use crate::assist_config::SnippetCap; 14use crate::assist_config::SnippetCap;
15 15
16pub(crate) use insert_use::insert_use_statement; 16pub(crate) use insert_use::{find_insert_use_container, insert_use_statement};
17 17
18#[derive(Clone, Copy, Debug)] 18#[derive(Clone, Copy, Debug)]
19pub(crate) enum Cursor<'a> { 19pub(crate) enum Cursor<'a> {
diff --git a/crates/ra_assists/src/utils/insert_use.rs b/crates/ra_assists/src/utils/insert_use.rs
index 0ee43482f..8c4f33e59 100644
--- a/crates/ra_assists/src/utils/insert_use.rs
+++ b/crates/ra_assists/src/utils/insert_use.rs
@@ -12,6 +12,20 @@ use ra_syntax::{
12use ra_text_edit::TextEditBuilder; 12use ra_text_edit::TextEditBuilder;
13 13
14use crate::assist_context::AssistContext; 14use crate::assist_context::AssistContext;
15use either::Either;
16
17/// Determines the containing syntax node in which to insert a `use` statement affecting `position`.
18pub(crate) fn find_insert_use_container(
19 position: &SyntaxNode,
20 ctx: &AssistContext,
21) -> Option<Either<ast::ItemList, ast::SourceFile>> {
22 ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| {
23 if let Some(module) = ast::Module::cast(n.clone()) {
24 return module.item_list().map(|it| Either::Left(it));
25 }
26 Some(Either::Right(ast::SourceFile::cast(n)?))
27 })
28}
15 29
16/// Creates and inserts a use statement for the given path to import. 30/// Creates and inserts a use statement for the given path to import.
17/// The use statement is inserted in the scope most appropriate to the 31/// The use statement is inserted in the scope most appropriate to the
@@ -24,15 +38,11 @@ pub(crate) fn insert_use_statement(
24 builder: &mut TextEditBuilder, 38 builder: &mut TextEditBuilder,
25) { 39) {
26 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>(); 40 let target = path_to_import.to_string().split("::").map(SmolStr::new).collect::<Vec<_>>();
27 let container = ctx.sema.ancestors_with_macros(position.clone()).find_map(|n| { 41 let container = find_insert_use_container(position, ctx);
28 if let Some(module) = ast::Module::cast(n.clone()) {
29 return module.item_list().map(|it| it.syntax().clone());
30 }
31 ast::SourceFile::cast(n).map(|it| it.syntax().clone())
32 });
33 42
34 if let Some(container) = container { 43 if let Some(container) = container {
35 let action = best_action_for_target(container, position.clone(), &target); 44 let syntax = container.either(|l| l.syntax().clone(), |r| r.syntax().clone());
45 let action = best_action_for_target(syntax, position.clone(), &target);
36 make_assist(&action, &target, builder); 46 make_assist(&action, &target, builder);
37 } 47 }
38} 48}
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs
index a8d6466ea..bf26048f2 100644
--- a/crates/ra_db/src/input.rs
+++ b/crates/ra_db/src/input.rs
@@ -15,12 +15,10 @@ use std::{
15 15
16use ra_cfg::CfgOptions; 16use ra_cfg::CfgOptions;
17use ra_syntax::SmolStr; 17use ra_syntax::SmolStr;
18use rustc_hash::FxHashMap; 18use ra_tt::TokenExpander;
19use rustc_hash::FxHashSet; 19use rustc_hash::{FxHashMap, FxHashSet};
20 20
21use crate::{RelativePath, RelativePathBuf}; 21use crate::{RelativePath, RelativePathBuf};
22use fmt::Display;
23use ra_tt::TokenExpander;
24 22
25/// `FileId` is an integer which uniquely identifies a file. File paths are 23/// `FileId` is an integer which uniquely identifies a file. File paths are
26/// messy and system-dependent, so most of the code should work directly with 24/// messy and system-dependent, so most of the code should work directly with
@@ -111,7 +109,7 @@ impl CrateName {
111 } 109 }
112} 110}
113 111
114impl Display for CrateName { 112impl fmt::Display for CrateName {
115 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 113 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116 write!(f, "{}", self.0) 114 write!(f, "{}", self.0)
117 } 115 }
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index 2ab314884..80ddb6058 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -7,6 +7,7 @@ use std::{panic, sync::Arc};
7 7
8use ra_prof::profile; 8use ra_prof::profile;
9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize}; 9use ra_syntax::{ast, Parse, SourceFile, TextRange, TextSize};
10use rustc_hash::FxHashSet;
10 11
11pub use crate::{ 12pub use crate::{
12 cancellation::Canceled, 13 cancellation::Canceled,
@@ -95,7 +96,7 @@ pub trait FileLoader {
95 /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we 96 /// `struct StrPath(str)` for clarity some day, but it's a bit messy, so we
96 /// get by with a `&str` for the time being. 97 /// get by with a `&str` for the time being.
97 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>; 98 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId>;
98 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>>; 99 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>>;
99} 100}
100 101
101/// Database which stores all significant input facts: source code and project 102/// Database which stores all significant input facts: source code and project
@@ -133,16 +134,21 @@ pub trait SourceDatabaseExt: SourceDatabase {
133 #[salsa::input] 134 #[salsa::input]
134 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>; 135 fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
135 136
136 fn source_root_crates(&self, id: SourceRootId) -> Arc<Vec<CrateId>>; 137 fn source_root_crates(&self, id: SourceRootId) -> Arc<FxHashSet<CrateId>>;
137} 138}
138 139
139fn source_root_crates( 140fn source_root_crates(
140 db: &(impl SourceDatabaseExt + SourceDatabase), 141 db: &(impl SourceDatabaseExt + SourceDatabase),
141 id: SourceRootId, 142 id: SourceRootId,
142) -> Arc<Vec<CrateId>> { 143) -> Arc<FxHashSet<CrateId>> {
143 let root = db.source_root(id);
144 let graph = db.crate_graph(); 144 let graph = db.crate_graph();
145 let res = root.walk().filter_map(|it| graph.crate_id_for_crate_root(it)).collect::<Vec<_>>(); 145 let res = graph
146 .iter()
147 .filter(|&krate| {
148 let root_file = graph[krate].root_file_id;
149 db.file_source_root(root_file) == id
150 })
151 .collect::<FxHashSet<_>>();
146 Arc::new(res) 152 Arc::new(res)
147} 153}
148 154
@@ -156,7 +162,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
156 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 162 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
157 // FIXME: this *somehow* should be platform agnostic... 163 // FIXME: this *somehow* should be platform agnostic...
158 if std::path::Path::new(path).is_absolute() { 164 if std::path::Path::new(path).is_absolute() {
159 let krate = *self.relevant_crates(anchor).get(0)?; 165 let krate = *self.relevant_crates(anchor).iter().next()?;
160 let (extern_source_id, relative_file) = 166 let (extern_source_id, relative_file) =
161 self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?; 167 self.0.crate_graph()[krate].extern_source.extern_path(path.as_ref())?;
162 168
@@ -175,7 +181,7 @@ impl<T: SourceDatabaseExt> FileLoader for FileLoaderDelegate<&'_ T> {
175 } 181 }
176 } 182 }
177 183
178 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 184 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
179 let source_root = self.0.file_source_root(file_id); 185 let source_root = self.0.file_source_root(file_id);
180 self.0.source_root_crates(source_root) 186 self.0.source_root_crates(source_root)
181 } 187 }
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index 041e38a9f..6c4170529 100644
--- a/crates/ra_flycheck/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -18,8 +18,17 @@ pub use cargo_metadata::diagnostic::{
18 18
19#[derive(Clone, Debug, PartialEq, Eq)] 19#[derive(Clone, Debug, PartialEq, Eq)]
20pub enum FlycheckConfig { 20pub enum FlycheckConfig {
21 CargoCommand { command: String, all_targets: bool, all_features: bool, extra_args: Vec<String> }, 21 CargoCommand {
22 CustomCommand { command: String, args: Vec<String> }, 22 command: String,
23 all_targets: bool,
24 all_features: bool,
25 features: Vec<String>,
26 extra_args: Vec<String>,
27 },
28 CustomCommand {
29 command: String,
30 args: Vec<String>,
31 },
23} 32}
24 33
25/// Flycheck wraps the shared state and communication machinery used for 34/// Flycheck wraps the shared state and communication machinery used for
@@ -188,7 +197,13 @@ impl FlycheckThread {
188 self.check_process = None; 197 self.check_process = None;
189 198
190 let mut cmd = match &self.config { 199 let mut cmd = match &self.config {
191 FlycheckConfig::CargoCommand { command, all_targets, all_features, extra_args } => { 200 FlycheckConfig::CargoCommand {
201 command,
202 all_targets,
203 all_features,
204 extra_args,
205 features,
206 } => {
192 let mut cmd = Command::new(ra_toolchain::cargo()); 207 let mut cmd = Command::new(ra_toolchain::cargo());
193 cmd.arg(command); 208 cmd.arg(command);
194 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) 209 cmd.args(&["--workspace", "--message-format=json", "--manifest-path"])
@@ -198,6 +213,9 @@ impl FlycheckThread {
198 } 213 }
199 if *all_features { 214 if *all_features {
200 cmd.arg("--all-features"); 215 cmd.arg("--all-features");
216 } else if !features.is_empty() {
217 cmd.arg("--features");
218 cmd.arg(features.join(" "));
201 } 219 }
202 cmd.args(extra_args); 220 cmd.args(extra_args);
203 cmd 221 cmd
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index 4a06f3bcd..1a9f6cc76 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -9,6 +9,7 @@ use hir_def::{
9 builtin_type::BuiltinType, 9 builtin_type::BuiltinType,
10 docs::Documentation, 10 docs::Documentation,
11 expr::{BindingAnnotation, Pat, PatId}, 11 expr::{BindingAnnotation, Pat, PatId},
12 import_map,
12 per_ns::PerNs, 13 per_ns::PerNs,
13 resolver::{HasResolver, Resolver}, 14 resolver::{HasResolver, Resolver},
14 type_ref::{Mutability, TypeRef}, 15 type_ref::{Mutability, TypeRef},
@@ -98,6 +99,23 @@ impl Crate {
98 db.crate_graph()[self.id].display_name.as_ref().cloned() 99 db.crate_graph()[self.id].display_name.as_ref().cloned()
99 } 100 }
100 101
102 pub fn query_external_importables(
103 self,
104 db: &dyn DefDatabase,
105 query: &str,
106 ) -> impl Iterator<Item = Either<ModuleDef, MacroDef>> {
107 import_map::search_dependencies(
108 db,
109 self.into(),
110 import_map::Query::new(query).anchor_end().case_sensitive().limit(40),
111 )
112 .into_iter()
113 .map(|item| match item {
114 ItemInNs::Types(mod_id) | ItemInNs::Values(mod_id) => Either::Left(mod_id.into()),
115 ItemInNs::Macros(mac_id) => Either::Right(mac_id.into()),
116 })
117 }
118
101 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> { 119 pub fn all(db: &dyn HirDatabase) -> Vec<Crate> {
102 db.crate_graph().iter().map(|id| Crate { id }).collect() 120 db.crate_graph().iter().map(|id| Crate { id }).collect()
103 } 121 }
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs
index 7c1f79f27..a232a5856 100644
--- a/crates/ra_hir/src/semantics.rs
+++ b/crates/ra_hir/src/semantics.rs
@@ -122,8 +122,9 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
122 let macro_call = 122 let macro_call =
123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call); 123 self.find_file(actual_macro_call.syntax().clone()).with_value(actual_macro_call);
124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None); 124 let sa = self.analyze2(macro_call.map(|it| it.syntax()), None);
125 let krate = sa.resolver.krate()?;
125 let macro_call_id = macro_call 126 let macro_call_id = macro_call
126 .as_call_id(self.db, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?; 127 .as_call_id(self.db, krate, |path| sa.resolver.resolve_path_as_macro(self.db, &path))?;
127 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map) 128 hir_expand::db::expand_hypothetical(self.db, macro_call_id, hypothetical_args, token_to_map)
128 } 129 }
129 130
diff --git a/crates/ra_hir/src/source_analyzer.rs b/crates/ra_hir/src/source_analyzer.rs
index 4b509f07c..7c6bbea13 100644
--- a/crates/ra_hir/src/source_analyzer.rs
+++ b/crates/ra_hir/src/source_analyzer.rs
@@ -307,7 +307,8 @@ impl SourceAnalyzer {
307 db: &dyn HirDatabase, 307 db: &dyn HirDatabase,
308 macro_call: InFile<&ast::MacroCall>, 308 macro_call: InFile<&ast::MacroCall>,
309 ) -> Option<HirFileId> { 309 ) -> Option<HirFileId> {
310 let macro_call_id = macro_call.as_call_id(db.upcast(), |path| { 310 let krate = self.resolver.krate()?;
311 let macro_call_id = macro_call.as_call_id(db.upcast(), krate, |path| {
311 self.resolver.resolve_path_as_macro(db.upcast(), &path) 312 self.resolver.resolve_path_as_macro(db.upcast(), &path)
312 })?; 313 })?;
313 Some(macro_call_id.as_file()) 314 Some(macro_call_id.as_file())
diff --git a/crates/ra_hir_def/Cargo.toml b/crates/ra_hir_def/Cargo.toml
index b85358308..ef1f65ee0 100644
--- a/crates/ra_hir_def/Cargo.toml
+++ b/crates/ra_hir_def/Cargo.toml
@@ -14,6 +14,9 @@ rustc-hash = "1.1.0"
14either = "1.5.3" 14either = "1.5.3"
15anymap = "0.12.1" 15anymap = "0.12.1"
16drop_bomb = "0.1.4" 16drop_bomb = "0.1.4"
17fst = { version = "0.4", default-features = false }
18itertools = "0.9.0"
19indexmap = "1.4.0"
17 20
18stdx = { path = "../stdx" } 21stdx = { path = "../stdx" }
19 22
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 273036cee..4f2350915 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -97,7 +97,7 @@ impl Expander {
97 97
98 let macro_call = InFile::new(self.current_file_id, &macro_call); 98 let macro_call = InFile::new(self.current_file_id, &macro_call);
99 99
100 if let Some(call_id) = macro_call.as_call_id(db, |path| { 100 if let Some(call_id) = macro_call.as_call_id(db, self.crate_def_map.krate, |path| {
101 if let Some(local_scope) = local_scope { 101 if let Some(local_scope) = local_scope {
102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) { 102 if let Some(def) = path.as_ident().and_then(|n| local_scope.get_legacy_macro(n)) {
103 return Some(def); 103 return Some(def);
diff --git a/crates/ra_hir_def/src/data.rs b/crates/ra_hir_def/src/data.rs
index 807195d25..53599e74a 100644
--- a/crates/ra_hir_def/src/data.rs
+++ b/crates/ra_hir_def/src/data.rs
@@ -99,7 +99,7 @@ impl FunctionData {
99} 99}
100 100
101fn desugar_future_path(orig: TypeRef) -> Path { 101fn desugar_future_path(orig: TypeRef) -> Path {
102 let path = path![std::future::Future]; 102 let path = path![core::future::Future];
103 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect(); 103 let mut generic_args: Vec<_> = std::iter::repeat(None).take(path.segments.len() - 1).collect();
104 let mut last = GenericArgs::empty(); 104 let mut last = GenericArgs::empty();
105 last.bindings.push(AssociatedTypeBinding { 105 last.bindings.push(AssociatedTypeBinding {
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 510c5e064..30db48f86 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -3,7 +3,6 @@
3use std::any::Any; 3use std::any::Any;
4 4
5use hir_expand::diagnostics::Diagnostic; 5use hir_expand::diagnostics::Diagnostic;
6use ra_db::RelativePathBuf;
7use ra_syntax::{ast, AstPtr, SyntaxNodePtr}; 6use ra_syntax::{ast, AstPtr, SyntaxNodePtr};
8 7
9use hir_expand::{HirFileId, InFile}; 8use hir_expand::{HirFileId, InFile};
@@ -12,7 +11,7 @@ use hir_expand::{HirFileId, InFile};
12pub struct UnresolvedModule { 11pub struct UnresolvedModule {
13 pub file: HirFileId, 12 pub file: HirFileId,
14 pub decl: AstPtr<ast::Module>, 13 pub decl: AstPtr<ast::Module>,
15 pub candidate: RelativePathBuf, 14 pub candidate: String,
16} 15}
17 16
18impl Diagnostic for UnresolvedModule { 17impl Diagnostic for UnresolvedModule {
diff --git a/crates/ra_hir_def/src/find_path.rs b/crates/ra_hir_def/src/find_path.rs
index a7f59e028..06701a830 100644
--- a/crates/ra_hir_def/src/find_path.rs
+++ b/crates/ra_hir_def/src/find_path.rs
@@ -159,10 +159,16 @@ fn find_path_inner(
159 let crate_graph = db.crate_graph(); 159 let crate_graph = db.crate_graph();
160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| { 160 let extern_paths = crate_graph[from.krate].dependencies.iter().filter_map(|dep| {
161 let import_map = db.import_map(dep.crate_id); 161 let import_map = db.import_map(dep.crate_id);
162 import_map.path_of(item).map(|modpath| { 162 import_map.import_info_for(item).and_then(|info| {
163 let mut modpath = modpath.clone(); 163 // Determine best path for containing module and append last segment from `info`.
164 modpath.segments.insert(0, dep.as_name()); 164 let mut path = find_path_inner(
165 modpath 165 db,
166 ItemInNs::Types(ModuleDefId::ModuleId(info.container)),
167 from,
168 best_path_len - 1,
169 )?;
170 path.segments.push(info.path.segments.last().unwrap().clone());
171 Some(path)
166 }) 172 })
167 }); 173 });
168 174
@@ -299,8 +305,8 @@ mod tests {
299 /// `code` needs to contain a cursor marker; checks that `find_path` for the 305 /// `code` needs to contain a cursor marker; checks that `find_path` for the
300 /// item the `path` refers to returns that same path when called from the 306 /// item the `path` refers to returns that same path when called from the
301 /// module the cursor is in. 307 /// module the cursor is in.
302 fn check_found_path(code: &str, path: &str) { 308 fn check_found_path(ra_fixture: &str, path: &str) {
303 let (db, pos) = TestDB::with_position(code); 309 let (db, pos) = TestDB::with_position(ra_fixture);
304 let module = db.module_for_file(pos.file_id); 310 let module = db.module_for_file(pos.file_id);
305 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path)); 311 let parsed_path_file = ra_syntax::SourceFile::parse(&format!("use {};", path));
306 let ast_path = parsed_path_file 312 let ast_path = parsed_path_file
@@ -420,7 +426,6 @@ mod tests {
420 426
421 #[test] 427 #[test]
422 fn different_crate_renamed() { 428 fn different_crate_renamed() {
423 // Even if a local path exists, if the item is defined externally, prefer an external path.
424 let code = r#" 429 let code = r#"
425 //- /main.rs crate:main deps:std 430 //- /main.rs crate:main deps:std
426 extern crate std as std_renamed; 431 extern crate std as std_renamed;
@@ -428,7 +433,45 @@ mod tests {
428 //- /std.rs crate:std 433 //- /std.rs crate:std
429 pub struct S; 434 pub struct S;
430 "#; 435 "#;
431 check_found_path(code, "std::S"); 436 check_found_path(code, "std_renamed::S");
437 }
438
439 #[test]
440 fn partially_imported() {
441 // Tests that short paths are used even for external items, when parts of the path are
442 // already in scope.
443 check_found_path(
444 r#"
445 //- /main.rs crate:main deps:ra_syntax
446
447 use ra_syntax::ast;
448 <|>
449
450 //- /lib.rs crate:ra_syntax
451 pub mod ast {
452 pub enum ModuleItem {
453 A, B, C,
454 }
455 }
456 "#,
457 "ast::ModuleItem",
458 );
459
460 check_found_path(
461 r#"
462 //- /main.rs crate:main deps:ra_syntax
463
464 <|>
465
466 //- /lib.rs crate:ra_syntax
467 pub mod ast {
468 pub enum ModuleItem {
469 A, B, C,
470 }
471 }
472 "#,
473 "ra_syntax::ast::ModuleItem",
474 );
432 } 475 }
433 476
434 #[test] 477 #[test]
diff --git a/crates/ra_hir_def/src/import_map.rs b/crates/ra_hir_def/src/import_map.rs
index 4284a0a91..68e20d06b 100644
--- a/crates/ra_hir_def/src/import_map.rs
+++ b/crates/ra_hir_def/src/import_map.rs
@@ -1,9 +1,11 @@
1//! A map of all publicly exported items in a crate. 1//! A map of all publicly exported items in a crate.
2 2
3use std::{collections::hash_map::Entry, fmt, sync::Arc}; 3use std::{cmp::Ordering, fmt, hash::BuildHasherDefault, sync::Arc};
4 4
5use fst::{self, Streamer};
6use indexmap::{map::Entry, IndexMap};
5use ra_db::CrateId; 7use ra_db::CrateId;
6use rustc_hash::FxHashMap; 8use rustc_hash::FxHasher;
7 9
8use crate::{ 10use crate::{
9 db::DefDatabase, 11 db::DefDatabase,
@@ -13,6 +15,17 @@ use crate::{
13 ModuleDefId, ModuleId, 15 ModuleDefId, ModuleId,
14}; 16};
15 17
18type FxIndexMap<K, V> = IndexMap<K, V, BuildHasherDefault<FxHasher>>;
19
20/// Item import details stored in the `ImportMap`.
21#[derive(Debug, Clone, Eq, PartialEq)]
22pub struct ImportInfo {
23 /// A path that can be used to import the item, relative to the crate's root.
24 pub path: ModPath,
25 /// The module containing this item.
26 pub container: ModuleId,
27}
28
16/// A map from publicly exported items to the path needed to import/name them from a downstream 29/// A map from publicly exported items to the path needed to import/name them from a downstream
17/// crate. 30/// crate.
18/// 31///
@@ -21,16 +34,24 @@ use crate::{
21/// 34///
22/// Note that all paths are relative to the containing crate's root, so the crate name still needs 35/// Note that all paths are relative to the containing crate's root, so the crate name still needs
23/// to be prepended to the `ModPath` before the path is valid. 36/// to be prepended to the `ModPath` before the path is valid.
24#[derive(Eq, PartialEq)]
25pub struct ImportMap { 37pub struct ImportMap {
26 map: FxHashMap<ItemInNs, ModPath>, 38 map: FxIndexMap<ItemInNs, ImportInfo>,
39
40 /// List of keys stored in `map`, sorted lexicographically by their `ModPath`. Indexed by the
41 /// values returned by running `fst`.
42 ///
43 /// Since a path can refer to multiple items due to namespacing, we store all items with the
44 /// same path right after each other. This allows us to find all items after the FST gives us
45 /// the index of the first one.
46 importables: Vec<ItemInNs>,
47 fst: fst::Map<Vec<u8>>,
27} 48}
28 49
29impl ImportMap { 50impl ImportMap {
30 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> { 51 pub fn import_map_query(db: &dyn DefDatabase, krate: CrateId) -> Arc<Self> {
31 let _p = ra_prof::profile("import_map_query"); 52 let _p = ra_prof::profile("import_map_query");
32 let def_map = db.crate_def_map(krate); 53 let def_map = db.crate_def_map(krate);
33 let mut import_map = FxHashMap::with_capacity_and_hasher(64, Default::default()); 54 let mut import_map = FxIndexMap::with_capacity_and_hasher(64, Default::default());
34 55
35 // We look only into modules that are public(ly reexported), starting with the crate root. 56 // We look only into modules that are public(ly reexported), starting with the crate root.
36 let empty = ModPath { kind: PathKind::Plain, segments: vec![] }; 57 let empty = ModPath { kind: PathKind::Plain, segments: vec![] };
@@ -66,12 +87,12 @@ impl ImportMap {
66 let path = mk_path(); 87 let path = mk_path();
67 match import_map.entry(item) { 88 match import_map.entry(item) {
68 Entry::Vacant(entry) => { 89 Entry::Vacant(entry) => {
69 entry.insert(path); 90 entry.insert(ImportInfo { path, container: module });
70 } 91 }
71 Entry::Occupied(mut entry) => { 92 Entry::Occupied(mut entry) => {
72 // If the new path is shorter, prefer that one. 93 // If the new path is shorter, prefer that one.
73 if path.len() < entry.get().len() { 94 if path.len() < entry.get().path.len() {
74 *entry.get_mut() = path; 95 *entry.get_mut() = ImportInfo { path, container: module };
75 } else { 96 } else {
76 continue; 97 continue;
77 } 98 }
@@ -88,27 +109,67 @@ impl ImportMap {
88 } 109 }
89 } 110 }
90 111
91 Arc::new(Self { map: import_map }) 112 let mut importables = import_map.iter().collect::<Vec<_>>();
113
114 importables.sort_by(cmp);
115
116 // Build the FST, taking care not to insert duplicate values.
117
118 let mut builder = fst::MapBuilder::memory();
119 let mut last_batch_start = 0;
120
121 for idx in 0..importables.len() {
122 if let Some(next_item) = importables.get(idx + 1) {
123 if cmp(&importables[last_batch_start], next_item) == Ordering::Equal {
124 continue;
125 }
126 }
127
128 let start = last_batch_start;
129 last_batch_start = idx + 1;
130
131 let key = fst_path(&importables[start].1.path);
132
133 builder.insert(key, start as u64).unwrap();
134 }
135
136 let fst = fst::Map::new(builder.into_inner().unwrap()).unwrap();
137 let importables = importables.iter().map(|(item, _)| **item).collect();
138
139 Arc::new(Self { map: import_map, fst, importables })
92 } 140 }
93 141
94 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root. 142 /// Returns the `ModPath` needed to import/mention `item`, relative to this crate's root.
95 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> { 143 pub fn path_of(&self, item: ItemInNs) -> Option<&ModPath> {
144 Some(&self.map.get(&item)?.path)
145 }
146
147 pub fn import_info_for(&self, item: ItemInNs) -> Option<&ImportInfo> {
96 self.map.get(&item) 148 self.map.get(&item)
97 } 149 }
98} 150}
99 151
152impl PartialEq for ImportMap {
153 fn eq(&self, other: &Self) -> bool {
154 // `fst` and `importables` are built from `map`, so we don't need to compare them.
155 self.map == other.map
156 }
157}
158
159impl Eq for ImportMap {}
160
100impl fmt::Debug for ImportMap { 161impl fmt::Debug for ImportMap {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 162 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 let mut importable_paths: Vec<_> = self 163 let mut importable_paths: Vec<_> = self
103 .map 164 .map
104 .iter() 165 .iter()
105 .map(|(item, modpath)| { 166 .map(|(item, info)| {
106 let ns = match item { 167 let ns = match item {
107 ItemInNs::Types(_) => "t", 168 ItemInNs::Types(_) => "t",
108 ItemInNs::Values(_) => "v", 169 ItemInNs::Values(_) => "v",
109 ItemInNs::Macros(_) => "m", 170 ItemInNs::Macros(_) => "m",
110 }; 171 };
111 format!("- {} ({})", modpath, ns) 172 format!("- {} ({})", info.path, ns)
112 }) 173 })
113 .collect(); 174 .collect();
114 175
@@ -117,19 +178,135 @@ impl fmt::Debug for ImportMap {
117 } 178 }
118} 179}
119 180
181fn fst_path(path: &ModPath) -> String {
182 let mut s = path.to_string();
183 s.make_ascii_lowercase();
184 s
185}
186
187fn cmp((_, lhs): &(&ItemInNs, &ImportInfo), (_, rhs): &(&ItemInNs, &ImportInfo)) -> Ordering {
188 let lhs_str = fst_path(&lhs.path);
189 let rhs_str = fst_path(&rhs.path);
190 lhs_str.cmp(&rhs_str)
191}
192
193#[derive(Debug)]
194pub struct Query {
195 query: String,
196 lowercased: String,
197 anchor_end: bool,
198 case_sensitive: bool,
199 limit: usize,
200}
201
202impl Query {
203 pub fn new(query: &str) -> Self {
204 Self {
205 lowercased: query.to_lowercase(),
206 query: query.to_string(),
207 anchor_end: false,
208 case_sensitive: false,
209 limit: usize::max_value(),
210 }
211 }
212
213 /// Only returns items whose paths end with the (case-insensitive) query string as their last
214 /// segment.
215 pub fn anchor_end(self) -> Self {
216 Self { anchor_end: true, ..self }
217 }
218
219 /// Limits the returned number of items to `limit`.
220 pub fn limit(self, limit: usize) -> Self {
221 Self { limit, ..self }
222 }
223
224 /// Respect casing of the query string when matching.
225 pub fn case_sensitive(self) -> Self {
226 Self { case_sensitive: true, ..self }
227 }
228}
229
230/// Searches dependencies of `krate` for an importable path matching `query`.
231///
232/// This returns a list of items that could be imported from dependencies of `krate`.
233pub fn search_dependencies<'a>(
234 db: &'a dyn DefDatabase,
235 krate: CrateId,
236 query: Query,
237) -> Vec<ItemInNs> {
238 let _p = ra_prof::profile("search_dependencies").detail(|| format!("{:?}", query));
239
240 let graph = db.crate_graph();
241 let import_maps: Vec<_> =
242 graph[krate].dependencies.iter().map(|dep| db.import_map(dep.crate_id)).collect();
243
244 let automaton = fst::automaton::Subsequence::new(&query.lowercased);
245
246 let mut op = fst::map::OpBuilder::new();
247 for map in &import_maps {
248 op = op.add(map.fst.search(&automaton));
249 }
250
251 let mut stream = op.union();
252 let mut res = Vec::new();
253 while let Some((_, indexed_values)) = stream.next() {
254 for indexed_value in indexed_values {
255 let import_map = &import_maps[indexed_value.index];
256 let importables = &import_map.importables[indexed_value.value as usize..];
257
258 // Path shared by the importable items in this group.
259 let path = &import_map.map[&importables[0]].path;
260
261 if query.anchor_end {
262 // Last segment must match query.
263 let last = path.segments.last().unwrap().to_string();
264 if last.to_lowercase() != query.lowercased {
265 continue;
266 }
267 }
268
269 // Add the items from this `ModPath` group. Those are all subsequent items in
270 // `importables` whose paths match `path`.
271 let iter = importables.iter().copied().take_while(|item| {
272 let item_path = &import_map.map[item].path;
273 fst_path(item_path) == fst_path(path)
274 });
275
276 if query.case_sensitive {
277 // FIXME: This does not do a subsequence match.
278 res.extend(iter.filter(|item| {
279 let item_path = &import_map.map[item].path;
280 item_path.to_string().contains(&query.query)
281 }));
282 } else {
283 res.extend(iter);
284 }
285
286 if res.len() >= query.limit {
287 res.truncate(query.limit);
288 return res;
289 }
290 }
291 }
292
293 res
294}
295
120#[cfg(test)] 296#[cfg(test)]
121mod tests { 297mod tests {
122 use super::*; 298 use super::*;
123 use crate::test_db::TestDB; 299 use crate::test_db::TestDB;
124 use insta::assert_snapshot; 300 use insta::assert_snapshot;
301 use itertools::Itertools;
125 use ra_db::fixture::WithFixture; 302 use ra_db::fixture::WithFixture;
126 use ra_db::SourceDatabase; 303 use ra_db::{SourceDatabase, Upcast};
127 304
128 fn import_map(ra_fixture: &str) -> String { 305 fn import_map(ra_fixture: &str) -> String {
129 let db = TestDB::with_files(ra_fixture); 306 let db = TestDB::with_files(ra_fixture);
130 let crate_graph = db.crate_graph(); 307 let crate_graph = db.crate_graph();
131 308
132 let import_maps: Vec<_> = crate_graph 309 let s = crate_graph
133 .iter() 310 .iter()
134 .filter_map(|krate| { 311 .filter_map(|krate| {
135 let cdata = &crate_graph[krate]; 312 let cdata = &crate_graph[krate];
@@ -139,9 +316,41 @@ mod tests {
139 316
140 Some(format!("{}:\n{:?}", name, map)) 317 Some(format!("{}:\n{:?}", name, map))
141 }) 318 })
142 .collect(); 319 .join("\n");
320 s
321 }
143 322
144 import_maps.join("\n") 323 fn search_dependencies_of(ra_fixture: &str, krate_name: &str, query: Query) -> String {
324 let db = TestDB::with_files(ra_fixture);
325 let crate_graph = db.crate_graph();
326 let krate = crate_graph
327 .iter()
328 .find(|krate| {
329 crate_graph[*krate].display_name.as_ref().map(|n| n.to_string())
330 == Some(krate_name.to_string())
331 })
332 .unwrap();
333
334 search_dependencies(db.upcast(), krate, query)
335 .into_iter()
336 .filter_map(|item| {
337 let mark = match item {
338 ItemInNs::Types(_) => "t",
339 ItemInNs::Values(_) => "v",
340 ItemInNs::Macros(_) => "m",
341 };
342 item.krate(db.upcast()).map(|krate| {
343 let map = db.import_map(krate);
344 let path = map.path_of(item).unwrap();
345 format!(
346 "{}::{} ({})",
347 crate_graph[krate].display_name.as_ref().unwrap(),
348 path,
349 mark
350 )
351 })
352 })
353 .join("\n")
145 } 354 }
146 355
147 #[test] 356 #[test]
@@ -328,4 +537,143 @@ mod tests {
328 lib: 537 lib:
329 "###); 538 "###);
330 } 539 }
540
541 #[test]
542 fn namespacing() {
543 let map = import_map(
544 r"
545 //- /lib.rs crate:lib
546 pub struct Thing; // t + v
547 #[macro_export]
548 macro_rules! Thing { // m
549 () => {};
550 }
551 ",
552 );
553
554 assert_snapshot!(map, @r###"
555 lib:
556 - Thing (m)
557 - Thing (t)
558 - Thing (v)
559 "###);
560
561 let map = import_map(
562 r"
563 //- /lib.rs crate:lib
564 pub mod Thing {} // t
565 #[macro_export]
566 macro_rules! Thing { // m
567 () => {};
568 }
569 ",
570 );
571
572 assert_snapshot!(map, @r###"
573 lib:
574 - Thing (m)
575 - Thing (t)
576 "###);
577 }
578
579 #[test]
580 fn search() {
581 let ra_fixture = r#"
582 //- /main.rs crate:main deps:dep
583 //- /dep.rs crate:dep deps:tdep
584 use tdep::fmt as fmt_dep;
585 pub mod fmt {
586 pub trait Display {
587 fn fmt();
588 }
589 }
590 #[macro_export]
591 macro_rules! Fmt {
592 () => {};
593 }
594 pub struct Fmt;
595
596 pub fn format() {}
597 pub fn no() {}
598
599 //- /tdep.rs crate:tdep
600 pub mod fmt {
601 pub struct NotImportableFromMain;
602 }
603 "#;
604
605 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt"));
606 assert_snapshot!(res, @r###"
607 dep::fmt (t)
608 dep::Fmt (t)
609 dep::Fmt (v)
610 dep::Fmt (m)
611 dep::fmt::Display (t)
612 dep::format (v)
613 "###);
614
615 let res = search_dependencies_of(ra_fixture, "main", Query::new("fmt").anchor_end());
616 assert_snapshot!(res, @r###"
617 dep::fmt (t)
618 dep::Fmt (t)
619 dep::Fmt (v)
620 dep::Fmt (m)
621 "###);
622 }
623
624 #[test]
625 fn search_casing() {
626 let ra_fixture = r#"
627 //- /main.rs crate:main deps:dep
628 //- /dep.rs crate:dep
629
630 pub struct fmt;
631 pub struct FMT;
632 "#;
633
634 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT"));
635
636 assert_snapshot!(res, @r###"
637 dep::fmt (t)
638 dep::fmt (v)
639 dep::FMT (t)
640 dep::FMT (v)
641 "###);
642
643 let res = search_dependencies_of(ra_fixture, "main", Query::new("FMT").case_sensitive());
644
645 assert_snapshot!(res, @r###"
646 dep::FMT (t)
647 dep::FMT (v)
648 "###);
649 }
650
651 #[test]
652 fn search_limit() {
653 let res = search_dependencies_of(
654 r#"
655 //- /main.rs crate:main deps:dep
656 //- /dep.rs crate:dep
657 pub mod fmt {
658 pub trait Display {
659 fn fmt();
660 }
661 }
662 #[macro_export]
663 macro_rules! Fmt {
664 () => {};
665 }
666 pub struct Fmt;
667
668 pub fn format() {}
669 pub fn no() {}
670 "#,
671 "main",
672 Query::new("").limit(2),
673 );
674 assert_snapshot!(res, @r###"
675 dep::fmt (t)
676 dep::Fmt (t)
677 "###);
678 }
331} 679}
diff --git a/crates/ra_hir_def/src/lib.rs b/crates/ra_hir_def/src/lib.rs
index de490fcc5..edc59e5a8 100644
--- a/crates/ra_hir_def/src/lib.rs
+++ b/crates/ra_hir_def/src/lib.rs
@@ -417,6 +417,7 @@ pub trait AsMacroCall {
417 fn as_call_id( 417 fn as_call_id(
418 &self, 418 &self,
419 db: &dyn db::DefDatabase, 419 db: &dyn db::DefDatabase,
420 krate: CrateId,
420 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 421 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
421 ) -> Option<MacroCallId>; 422 ) -> Option<MacroCallId>;
422} 423}
@@ -425,13 +426,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
425 fn as_call_id( 426 fn as_call_id(
426 &self, 427 &self,
427 db: &dyn db::DefDatabase, 428 db: &dyn db::DefDatabase,
429 krate: CrateId,
428 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 430 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
429 ) -> Option<MacroCallId> { 431 ) -> Option<MacroCallId> {
430 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value)); 432 let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
431 let h = Hygiene::new(db.upcast(), self.file_id); 433 let h = Hygiene::new(db.upcast(), self.file_id);
432 let path = path::ModPath::from_src(self.value.path()?, &h)?; 434 let path = path::ModPath::from_src(self.value.path()?, &h)?;
433 435
434 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, resolver) 436 AstIdWithPath::new(ast_id.file_id, ast_id.value, path).as_call_id(db, krate, resolver)
435 } 437 }
436} 438}
437 439
@@ -452,6 +454,7 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
452 fn as_call_id( 454 fn as_call_id(
453 &self, 455 &self,
454 db: &dyn db::DefDatabase, 456 db: &dyn db::DefDatabase,
457 krate: CrateId,
455 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 458 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
456 ) -> Option<MacroCallId> { 459 ) -> Option<MacroCallId> {
457 let def: MacroDefId = resolver(self.path.clone())?; 460 let def: MacroDefId = resolver(self.path.clone())?;
@@ -461,13 +464,13 @@ impl AsMacroCall for AstIdWithPath<ast::MacroCall> {
461 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id); 464 let hygiene = Hygiene::new(db.upcast(), self.ast_id.file_id);
462 465
463 Some( 466 Some(
464 expand_eager_macro(db.upcast(), macro_call, def, &|path: ast::Path| { 467 expand_eager_macro(db.upcast(), krate, macro_call, def, &|path: ast::Path| {
465 resolver(path::ModPath::from_src(path, &hygiene)?) 468 resolver(path::ModPath::from_src(path, &hygiene)?)
466 })? 469 })?
467 .into(), 470 .into(),
468 ) 471 )
469 } else { 472 } else {
470 Some(def.as_lazy_macro(db.upcast(), MacroCallKind::FnLike(self.ast_id)).into()) 473 Some(def.as_lazy_macro(db.upcast(), krate, MacroCallKind::FnLike(self.ast_id)).into())
471 } 474 }
472 } 475 }
473} 476}
@@ -476,12 +479,14 @@ impl AsMacroCall for AstIdWithPath<ast::ModuleItem> {
476 fn as_call_id( 479 fn as_call_id(
477 &self, 480 &self,
478 db: &dyn db::DefDatabase, 481 db: &dyn db::DefDatabase,
482 krate: CrateId,
479 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, 483 resolver: impl Fn(path::ModPath) -> Option<MacroDefId>,
480 ) -> Option<MacroCallId> { 484 ) -> Option<MacroCallId> {
481 let def = resolver(self.path.clone())?; 485 let def = resolver(self.path.clone())?;
482 Some( 486 Some(
483 def.as_lazy_macro( 487 def.as_lazy_macro(
484 db.upcast(), 488 db.upcast(),
489 krate,
485 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()), 490 MacroCallKind::Attr(self.ast_id, self.path.segments.last()?.to_string()),
486 ) 491 )
487 .into(), 492 .into(),
diff --git a/crates/ra_hir_def/src/nameres.rs b/crates/ra_hir_def/src/nameres.rs
index f279c2ad4..b3e5f491a 100644
--- a/crates/ra_hir_def/src/nameres.rs
+++ b/crates/ra_hir_def/src/nameres.rs
@@ -296,7 +296,6 @@ pub enum ModuleSource {
296 296
297mod diagnostics { 297mod diagnostics {
298 use hir_expand::diagnostics::DiagnosticSink; 298 use hir_expand::diagnostics::DiagnosticSink;
299 use ra_db::RelativePathBuf;
300 use ra_syntax::{ast, AstPtr}; 299 use ra_syntax::{ast, AstPtr};
301 300
302 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId}; 301 use crate::{db::DefDatabase, diagnostics::UnresolvedModule, nameres::LocalModuleId, AstId};
@@ -306,7 +305,7 @@ mod diagnostics {
306 UnresolvedModule { 305 UnresolvedModule {
307 module: LocalModuleId, 306 module: LocalModuleId,
308 declaration: AstId<ast::Module>, 307 declaration: AstId<ast::Module>,
309 candidate: RelativePathBuf, 308 candidate: String,
310 }, 309 },
311 } 310 }
312 311
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs
index 353a31ad4..976e5e585 100644
--- a/crates/ra_hir_def/src/nameres/collector.rs
+++ b/crates/ra_hir_def/src/nameres/collector.rs
@@ -571,16 +571,18 @@ impl DefCollector<'_> {
571 return false; 571 return false;
572 } 572 }
573 573
574 if let Some(call_id) = directive.ast_id.as_call_id(self.db, |path| { 574 if let Some(call_id) =
575 let resolved_res = self.def_map.resolve_path_fp_with_macro( 575 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
576 self.db, 576 let resolved_res = self.def_map.resolve_path_fp_with_macro(
577 ResolveMode::Other, 577 self.db,
578 directive.module_id, 578 ResolveMode::Other,
579 &path, 579 directive.module_id,
580 BuiltinShadowMode::Module, 580 &path,
581 ); 581 BuiltinShadowMode::Module,
582 resolved_res.resolved_def.take_macros() 582 );
583 }) { 583 resolved_res.resolved_def.take_macros()
584 })
585 {
584 resolved.push((directive.module_id, call_id, directive.depth)); 586 resolved.push((directive.module_id, call_id, directive.depth));
585 res = ReachedFixedPoint::No; 587 res = ReachedFixedPoint::No;
586 return false; 588 return false;
@@ -589,9 +591,10 @@ impl DefCollector<'_> {
589 true 591 true
590 }); 592 });
591 attribute_macros.retain(|directive| { 593 attribute_macros.retain(|directive| {
592 if let Some(call_id) = directive 594 if let Some(call_id) =
593 .ast_id 595 directive.ast_id.as_call_id(self.db, self.def_map.krate, |path| {
594 .as_call_id(self.db, |path| self.resolve_attribute_macro(&directive, &path)) 596 self.resolve_attribute_macro(&directive, &path)
597 })
595 { 598 {
596 resolved.push((directive.module_id, call_id, 0)); 599 resolved.push((directive.module_id, call_id, 0));
597 res = ReachedFixedPoint::No; 600 res = ReachedFixedPoint::No;
@@ -957,11 +960,13 @@ impl ModCollector<'_, '_> {
957 } 960 }
958 961
959 // Case 2: try to resolve in legacy scope and expand macro_rules 962 // Case 2: try to resolve in legacy scope and expand macro_rules
960 if let Some(macro_call_id) = ast_id.as_call_id(self.def_collector.db, |path| { 963 if let Some(macro_call_id) =
961 path.as_ident().and_then(|name| { 964 ast_id.as_call_id(self.def_collector.db, self.def_collector.def_map.krate, |path| {
962 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name) 965 path.as_ident().and_then(|name| {
966 self.def_collector.def_map[self.module_id].scope.get_legacy_macro(&name)
967 })
963 }) 968 })
964 }) { 969 {
965 self.def_collector.unexpanded_macros.push(MacroDirective { 970 self.def_collector.unexpanded_macros.push(MacroDirective {
966 module_id: self.module_id, 971 module_id: self.module_id,
967 ast_id, 972 ast_id,
diff --git a/crates/ra_hir_def/src/nameres/mod_resolution.rs b/crates/ra_hir_def/src/nameres/mod_resolution.rs
index cede4a6fc..19fe0615a 100644
--- a/crates/ra_hir_def/src/nameres/mod_resolution.rs
+++ b/crates/ra_hir_def/src/nameres/mod_resolution.rs
@@ -44,7 +44,7 @@ impl ModDir {
44 file_id: HirFileId, 44 file_id: HirFileId,
45 name: &Name, 45 name: &Name,
46 attr_path: Option<&SmolStr>, 46 attr_path: Option<&SmolStr>,
47 ) -> Result<(FileId, ModDir), RelativePathBuf> { 47 ) -> Result<(FileId, ModDir), String> {
48 let file_id = file_id.original_file(db.upcast()); 48 let file_id = file_id.original_file(db.upcast());
49 49
50 let mut candidate_files = Vec::new(); 50 let mut candidate_files = Vec::new();
@@ -52,11 +52,11 @@ impl ModDir {
52 Some(attr_path) => { 52 Some(attr_path) => {
53 let base = 53 let base =
54 if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path }; 54 if self.root_non_dir_owner { self.path.parent().unwrap() } else { &self.path };
55 candidate_files.push(base.join(attr_path)) 55 candidate_files.push(base.join(attr_path).to_string())
56 } 56 }
57 None => { 57 None => {
58 candidate_files.push(self.path.join(&format!("{}.rs", name))); 58 candidate_files.push(self.path.join(&format!("{}.rs", name)).to_string());
59 candidate_files.push(self.path.join(&format!("{}/mod.rs", name))); 59 candidate_files.push(self.path.join(&format!("{}/mod.rs", name)).to_string());
60 } 60 }
61 }; 61 };
62 62
diff --git a/crates/ra_hir_def/src/path.rs b/crates/ra_hir_def/src/path.rs
index bfa921de2..190d6d98d 100644
--- a/crates/ra_hir_def/src/path.rs
+++ b/crates/ra_hir_def/src/path.rs
@@ -154,7 +154,7 @@ pub enum GenericArg {
154 154
155impl Path { 155impl Path {
156 /// Converts an `ast::Path` to `Path`. Works with use trees. 156 /// Converts an `ast::Path` to `Path`. Works with use trees.
157 /// DEPRECATED: It does not handle `$crate` from macro call. 157 #[deprecated = "Doesn't handle hygiene, don't add new calls, remove old ones"]
158 pub fn from_ast(path: ast::Path) -> Option<Path> { 158 pub fn from_ast(path: ast::Path) -> Option<Path> {
159 lower::lower_path(path, &Hygiene::new_unhygienic()) 159 lower::lower_path(path, &Hygiene::new_unhygienic())
160 } 160 }
@@ -323,16 +323,16 @@ pub use hir_expand::name as __name;
323 323
324#[macro_export] 324#[macro_export]
325macro_rules! __known_path { 325macro_rules! __known_path {
326 (std::iter::IntoIterator) => {}; 326 (core::iter::IntoIterator) => {};
327 (std::result::Result) => {}; 327 (core::result::Result) => {};
328 (std::ops::Range) => {}; 328 (core::ops::Range) => {};
329 (std::ops::RangeFrom) => {}; 329 (core::ops::RangeFrom) => {};
330 (std::ops::RangeFull) => {}; 330 (core::ops::RangeFull) => {};
331 (std::ops::RangeTo) => {}; 331 (core::ops::RangeTo) => {};
332 (std::ops::RangeToInclusive) => {}; 332 (core::ops::RangeToInclusive) => {};
333 (std::ops::RangeInclusive) => {}; 333 (core::ops::RangeInclusive) => {};
334 (std::future::Future) => {}; 334 (core::future::Future) => {};
335 (std::ops::Try) => {}; 335 (core::ops::Try) => {};
336 ($path:path) => { 336 ($path:path) => {
337 compile_error!("Please register your known path in the path module") 337 compile_error!("Please register your known path in the path module")
338 }; 338 };
diff --git a/crates/ra_hir_def/src/test_db.rs b/crates/ra_hir_def/src/test_db.rs
index bcfa66ac9..4581d8745 100644
--- a/crates/ra_hir_def/src/test_db.rs
+++ b/crates/ra_hir_def/src/test_db.rs
@@ -7,6 +7,7 @@ use std::{
7 7
8use hir_expand::db::AstDatabase; 8use hir_expand::db::AstDatabase;
9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast}; 9use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, Upcast};
10use rustc_hash::FxHashSet;
10 11
11use crate::db::DefDatabase; 12use crate::db::DefDatabase;
12 13
@@ -59,7 +60,7 @@ impl FileLoader for TestDB {
59 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
60 FileLoaderDelegate(self).resolve_path(anchor, path) 61 FileLoaderDelegate(self).resolve_path(anchor, path)
61 } 62 }
62 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 63 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
63 FileLoaderDelegate(self).relevant_crates(file_id) 64 FileLoaderDelegate(self).relevant_crates(file_id)
64 } 65 }
65} 66}
diff --git a/crates/ra_hir_expand/Cargo.toml b/crates/ra_hir_expand/Cargo.toml
index 2cd522766..e5c9f3e99 100644
--- a/crates/ra_hir_expand/Cargo.toml
+++ b/crates/ra_hir_expand/Cargo.toml
@@ -10,6 +10,7 @@ doctest = false
10[dependencies] 10[dependencies]
11log = "0.4.8" 11log = "0.4.8"
12either = "1.5.3" 12either = "1.5.3"
13rustc-hash = "1.0.0"
13 14
14ra_arena = { path = "../ra_arena" } 15ra_arena = { path = "../ra_arena" }
15ra_db = { path = "../ra_db" } 16ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir_expand/src/builtin_derive.rs b/crates/ra_hir_expand/src/builtin_derive.rs
index 1dc9cac66..26b667b55 100644
--- a/crates/ra_hir_expand/src/builtin_derive.rs
+++ b/crates/ra_hir_expand/src/builtin_derive.rs
@@ -8,8 +8,7 @@ use ra_syntax::{
8 match_ast, 8 match_ast,
9}; 9};
10 10
11use crate::db::AstDatabase; 11use crate::{db::AstDatabase, name, quote, LazyMacroId, MacroDefId, MacroDefKind};
12use crate::{name, quote, LazyMacroId, MacroCallId, MacroDefId, MacroDefKind};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( $($trait:ident => $expand:ident),* ) => { 14 ( $($trait:ident => $expand:ident),* ) => {
@@ -156,23 +155,13 @@ fn expand_simple_derive(
156fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree { 155fn find_builtin_crate(db: &dyn AstDatabase, id: LazyMacroId) -> tt::TokenTree {
157 // FIXME: make hygiene works for builtin derive macro 156 // FIXME: make hygiene works for builtin derive macro
158 // such that $crate can be used here. 157 // such that $crate can be used here.
159
160 let m: MacroCallId = id.into();
161 let file_id = m.as_file().original_file(db);
162 let cg = db.crate_graph(); 158 let cg = db.crate_graph();
163 let krates = db.relevant_crates(file_id); 159 let krate = db.lookup_intern_macro(id).krate;
164 let krate = match krates.get(0) {
165 Some(krate) => krate,
166 None => {
167 let tt = quote! { core };
168 return tt.token_trees[0].clone();
169 }
170 };
171 160
172 // XXX 161 // XXX
173 // All crates except core itself should have a dependency on core, 162 // All crates except core itself should have a dependency on core,
174 // We detect `core` by seeing whether it doesn't have such a dependency. 163 // We detect `core` by seeing whether it doesn't have such a dependency.
175 let tt = if cg[*krate].dependencies.iter().any(|dep| dep.name == "core") { 164 let tt = if cg[krate].dependencies.iter().any(|dep| dep.name == "core") {
176 quote! { core } 165 quote! { core }
177 } else { 166 } else {
178 quote! { crate } 167 quote! { crate }
@@ -264,10 +253,12 @@ fn partial_ord_expand(
264 253
265#[cfg(test)] 254#[cfg(test)]
266mod tests { 255mod tests {
267 use super::*;
268 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
269 use name::{known, Name}; 256 use name::{known, Name};
270 use ra_db::{fixture::WithFixture, SourceDatabase}; 257 use ra_db::{fixture::WithFixture, CrateId, SourceDatabase};
258
259 use crate::{test_db::TestDB, AstId, MacroCallId, MacroCallKind, MacroCallLoc};
260
261 use super::*;
271 262
272 fn expand_builtin_derive(s: &str, name: Name) -> String { 263 fn expand_builtin_derive(s: &str, name: Name) -> String {
273 let def = find_builtin_derive(&name).unwrap(); 264 let def = find_builtin_derive(&name).unwrap();
@@ -291,7 +282,11 @@ mod tests {
291 282
292 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0])); 283 let attr_id = AstId::new(file_id.into(), ast_id_map.ast_id(&items[0]));
293 284
294 let loc = MacroCallLoc { def, kind: MacroCallKind::Attr(attr_id, name.to_string()) }; 285 let loc = MacroCallLoc {
286 def,
287 krate: CrateId(0),
288 kind: MacroCallKind::Attr(attr_id, name.to_string()),
289 };
295 290
296 let id: MacroCallId = db.intern_macro(loc).into(); 291 let id: MacroCallId = db.intern_macro(loc).into();
297 let parsed = db.parse_or_expand(id.as_file()).unwrap(); 292 let parsed = db.parse_or_expand(id.as_file()).unwrap();
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 7579546d2..b50eb347c 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -1,15 +1,14 @@
1//! Builtin macro 1//! Builtin macro
2use crate::db::AstDatabase;
3use crate::{ 2use crate::{
4 ast::{self, AstToken, HasStringValue}, 3 db::AstDatabase, name, quote, AstId, CrateId, EagerMacroId, LazyMacroId, MacroCallId,
5 name, AstId, CrateId, MacroDefId, MacroDefKind, TextSize, 4 MacroDefId, MacroDefKind, TextSize,
6}; 5};
7 6
8use crate::{quote, EagerMacroId, LazyMacroId, MacroCallId};
9use either::Either; 7use either::Either;
10use mbe::parse_to_token_tree; 8use mbe::parse_to_token_tree;
11use ra_db::FileId; 9use ra_db::FileId;
12use ra_parser::FragmentKind; 10use ra_parser::FragmentKind;
11use ra_syntax::ast::{self, AstToken, HasStringValue};
13 12
14macro_rules! register_builtin { 13macro_rules! register_builtin {
15 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => { 14 ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
@@ -333,10 +332,7 @@ fn include_expand(
333} 332}
334 333
335fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> { 334fn get_env_inner(db: &dyn AstDatabase, arg_id: EagerMacroId, key: &str) -> Option<String> {
336 let call_id: MacroCallId = arg_id.into(); 335 let krate = db.lookup_intern_eager_expansion(arg_id).krate;
337 let original_file = call_id.as_file().original_file(db);
338
339 let krate = *db.relevant_crates(original_file).get(0)?;
340 db.crate_graph()[krate].env.get(key) 336 db.crate_graph()[krate].env.get(key)
341} 337}
342 338
@@ -395,6 +391,7 @@ mod tests {
395 391
396 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap(); 392 let expander = find_by_name(&macro_calls[0].name().unwrap().as_name()).unwrap();
397 393
394 let krate = CrateId(0);
398 let file_id = match expander { 395 let file_id = match expander {
399 Either::Left(expander) => { 396 Either::Left(expander) => {
400 // the first one should be a macro_rules 397 // the first one should be a macro_rules
@@ -407,6 +404,7 @@ mod tests {
407 404
408 let loc = MacroCallLoc { 405 let loc = MacroCallLoc {
409 def, 406 def,
407 krate,
410 kind: MacroCallKind::FnLike(AstId::new( 408 kind: MacroCallKind::FnLike(AstId::new(
411 file_id.into(), 409 file_id.into(),
412 ast_id_map.ast_id(&macro_calls[1]), 410 ast_id_map.ast_id(&macro_calls[1]),
@@ -419,7 +417,7 @@ mod tests {
419 Either::Right(expander) => { 417 Either::Right(expander) => {
420 // the first one should be a macro_rules 418 // the first one should be a macro_rules
421 let def = MacroDefId { 419 let def = MacroDefId {
422 krate: Some(CrateId(0)), 420 krate: Some(krate),
423 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))), 421 ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
424 kind: MacroDefKind::BuiltInEager(expander), 422 kind: MacroDefKind::BuiltInEager(expander),
425 local_inner: false, 423 local_inner: false,
@@ -433,6 +431,7 @@ mod tests {
433 def, 431 def,
434 fragment: FragmentKind::Expr, 432 fragment: FragmentKind::Expr,
435 subtree: Arc::new(parsed_args.clone()), 433 subtree: Arc::new(parsed_args.clone()),
434 krate,
436 file_id: file_id.into(), 435 file_id: file_id.into(),
437 } 436 }
438 }); 437 });
@@ -442,6 +441,7 @@ mod tests {
442 def, 441 def,
443 fragment, 442 fragment,
444 subtree: Arc::new(subtree), 443 subtree: Arc::new(subtree),
444 krate,
445 file_id: file_id.into(), 445 file_id: file_id.into(),
446 }; 446 };
447 447
diff --git a/crates/ra_hir_expand/src/eager.rs b/crates/ra_hir_expand/src/eager.rs
index 932f47c30..302d2b3e0 100644
--- a/crates/ra_hir_expand/src/eager.rs
+++ b/crates/ra_hir_expand/src/eager.rs
@@ -25,12 +25,14 @@ use crate::{
25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind, 25 EagerCallLoc, EagerMacroId, InFile, MacroCallId, MacroCallKind, MacroDefId, MacroDefKind,
26}; 26};
27 27
28use ra_db::CrateId;
28use ra_parser::FragmentKind; 29use ra_parser::FragmentKind;
29use ra_syntax::{algo::SyntaxRewriter, SyntaxNode}; 30use ra_syntax::{algo::SyntaxRewriter, SyntaxNode};
30use std::sync::Arc; 31use std::sync::Arc;
31 32
32pub fn expand_eager_macro( 33pub fn expand_eager_macro(
33 db: &dyn AstDatabase, 34 db: &dyn AstDatabase,
35 krate: CrateId,
34 macro_call: InFile<ast::MacroCall>, 36 macro_call: InFile<ast::MacroCall>,
35 def: MacroDefId, 37 def: MacroDefId,
36 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 38 resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
@@ -47,6 +49,7 @@ pub fn expand_eager_macro(
47 def, 49 def,
48 fragment: FragmentKind::Expr, 50 fragment: FragmentKind::Expr,
49 subtree: Arc::new(parsed_args.clone()), 51 subtree: Arc::new(parsed_args.clone()),
52 krate,
50 file_id: macro_call.file_id, 53 file_id: macro_call.file_id,
51 } 54 }
52 }); 55 });
@@ -56,14 +59,20 @@ pub fn expand_eager_macro(
56 let result = eager_macro_recur( 59 let result = eager_macro_recur(
57 db, 60 db,
58 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()), 61 InFile::new(arg_file_id.as_file(), parsed_args.syntax_node()),
62 krate,
59 resolver, 63 resolver,
60 )?; 64 )?;
61 let subtree = to_subtree(&result)?; 65 let subtree = to_subtree(&result)?;
62 66
63 if let MacroDefKind::BuiltInEager(eager) = def.kind { 67 if let MacroDefKind::BuiltInEager(eager) = def.kind {
64 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?; 68 let (subtree, fragment) = eager.expand(db, arg_id, &subtree).ok()?;
65 let eager = 69 let eager = EagerCallLoc {
66 EagerCallLoc { def, fragment, subtree: Arc::new(subtree), file_id: macro_call.file_id }; 70 def,
71 fragment,
72 subtree: Arc::new(subtree),
73 krate,
74 file_id: macro_call.file_id,
75 };
67 76
68 Some(db.intern_eager_expansion(eager)) 77 Some(db.intern_eager_expansion(eager))
69 } else { 78 } else {
@@ -81,11 +90,12 @@ fn lazy_expand(
81 db: &dyn AstDatabase, 90 db: &dyn AstDatabase,
82 def: &MacroDefId, 91 def: &MacroDefId,
83 macro_call: InFile<ast::MacroCall>, 92 macro_call: InFile<ast::MacroCall>,
93 krate: CrateId,
84) -> Option<InFile<SyntaxNode>> { 94) -> Option<InFile<SyntaxNode>> {
85 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value); 95 let ast_id = db.ast_id_map(macro_call.file_id).ast_id(&macro_call.value);
86 96
87 let id: MacroCallId = 97 let id: MacroCallId =
88 def.as_lazy_macro(db, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into(); 98 def.as_lazy_macro(db, krate, MacroCallKind::FnLike(macro_call.with_value(ast_id))).into();
89 99
90 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node)) 100 db.parse_or_expand(id.as_file()).map(|node| InFile::new(id.as_file(), node))
91} 101}
@@ -93,6 +103,7 @@ fn lazy_expand(
93fn eager_macro_recur( 103fn eager_macro_recur(
94 db: &dyn AstDatabase, 104 db: &dyn AstDatabase,
95 curr: InFile<SyntaxNode>, 105 curr: InFile<SyntaxNode>,
106 krate: CrateId,
96 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>, 107 macro_resolver: &dyn Fn(ast::Path) -> Option<MacroDefId>,
97) -> Option<SyntaxNode> { 108) -> Option<SyntaxNode> {
98 let original = curr.value.clone(); 109 let original = curr.value.clone();
@@ -105,18 +116,23 @@ fn eager_macro_recur(
105 let def: MacroDefId = macro_resolver(child.path()?)?; 116 let def: MacroDefId = macro_resolver(child.path()?)?;
106 let insert = match def.kind { 117 let insert = match def.kind {
107 MacroDefKind::BuiltInEager(_) => { 118 MacroDefKind::BuiltInEager(_) => {
108 let id: MacroCallId = 119 let id: MacroCallId = expand_eager_macro(
109 expand_eager_macro(db, curr.with_value(child.clone()), def, macro_resolver)? 120 db,
110 .into(); 121 krate,
122 curr.with_value(child.clone()),
123 def,
124 macro_resolver,
125 )?
126 .into();
111 db.parse_or_expand(id.as_file())? 127 db.parse_or_expand(id.as_file())?
112 } 128 }
113 MacroDefKind::Declarative 129 MacroDefKind::Declarative
114 | MacroDefKind::BuiltIn(_) 130 | MacroDefKind::BuiltIn(_)
115 | MacroDefKind::BuiltInDerive(_) 131 | MacroDefKind::BuiltInDerive(_)
116 | MacroDefKind::CustomDerive(_) => { 132 | MacroDefKind::CustomDerive(_) => {
117 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()))?; 133 let expanded = lazy_expand(db, &def, curr.with_value(child.clone()), krate)?;
118 // replace macro inside 134 // replace macro inside
119 eager_macro_recur(db, expanded, macro_resolver)? 135 eager_macro_recur(db, expanded, krate, macro_resolver)?
120 } 136 }
121 }; 137 };
122 138
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index f440c073b..5eac2605b 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -209,8 +209,13 @@ pub struct MacroDefId {
209} 209}
210 210
211impl MacroDefId { 211impl MacroDefId {
212 pub fn as_lazy_macro(self, db: &dyn db::AstDatabase, kind: MacroCallKind) -> LazyMacroId { 212 pub fn as_lazy_macro(
213 db.intern_macro(MacroCallLoc { def: self, kind }) 213 self,
214 db: &dyn db::AstDatabase,
215 krate: CrateId,
216 kind: MacroCallKind,
217 ) -> LazyMacroId {
218 db.intern_macro(MacroCallLoc { def: self, krate, kind })
214 } 219 }
215} 220}
216 221
@@ -227,6 +232,7 @@ pub enum MacroDefKind {
227#[derive(Debug, Clone, PartialEq, Eq, Hash)] 232#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct MacroCallLoc { 233pub struct MacroCallLoc {
229 pub(crate) def: MacroDefId, 234 pub(crate) def: MacroDefId,
235 pub(crate) krate: CrateId,
230 pub(crate) kind: MacroCallKind, 236 pub(crate) kind: MacroCallKind,
231} 237}
232 238
@@ -274,6 +280,7 @@ pub struct EagerCallLoc {
274 pub(crate) def: MacroDefId, 280 pub(crate) def: MacroDefId,
275 pub(crate) fragment: FragmentKind, 281 pub(crate) fragment: FragmentKind,
276 pub(crate) subtree: Arc<tt::Subtree>, 282 pub(crate) subtree: Arc<tt::Subtree>,
283 pub(crate) krate: CrateId,
277 pub(crate) file_id: HirFileId, 284 pub(crate) file_id: HirFileId,
278} 285}
279 286
diff --git a/crates/ra_hir_expand/src/test_db.rs b/crates/ra_hir_expand/src/test_db.rs
index fdf225f55..09fc18c36 100644
--- a/crates/ra_hir_expand/src/test_db.rs
+++ b/crates/ra_hir_expand/src/test_db.rs
@@ -6,6 +6,7 @@ use std::{
6}; 6};
7 7
8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate}; 8use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate};
9use rustc_hash::FxHashSet;
9 10
10#[salsa::database( 11#[salsa::database(
11 ra_db::SourceDatabaseExtStorage, 12 ra_db::SourceDatabaseExtStorage,
@@ -44,7 +45,7 @@ impl FileLoader for TestDB {
44 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 45 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
45 FileLoaderDelegate(self).resolve_path(anchor, path) 46 FileLoaderDelegate(self).resolve_path(anchor, path)
46 } 47 }
47 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 48 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
48 FileLoaderDelegate(self).relevant_crates(file_id) 49 FileLoaderDelegate(self).relevant_crates(file_id)
49 } 50 }
50} 51}
diff --git a/crates/ra_hir_ty/src/_match.rs b/crates/ra_hir_ty/src/_match.rs
index 3e6e1e333..02a7a61f1 100644
--- a/crates/ra_hir_ty/src/_match.rs
+++ b/crates/ra_hir_ty/src/_match.rs
@@ -8,11 +8,11 @@
8//! This file includes the logic for exhaustiveness and usefulness checking for 8//! This file includes the logic for exhaustiveness and usefulness checking for
9//! pattern-matching. Specifically, given a list of patterns for a type, we can 9//! pattern-matching. Specifically, given a list of patterns for a type, we can
10//! tell whether: 10//! tell whether:
11//! (a) the patterns cover every possible constructor for the type [exhaustiveness] 11//! - (a) the patterns cover every possible constructor for the type (exhaustiveness).
12//! (b) each pattern is necessary [usefulness] 12//! - (b) each pattern is necessary (usefulness).
13//! 13//!
14//! The algorithm implemented here is a modified version of the one described in: 14//! The algorithm implemented here is a modified version of the one described in
15//! http://moscova.inria.fr/~maranget/papers/warn/index.html 15//! <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
16//! However, to save future implementors from reading the original paper, we 16//! However, to save future implementors from reading the original paper, we
17//! summarise the algorithm here to hopefully save time and be a little clearer 17//! summarise the algorithm here to hopefully save time and be a little clearer
18//! (without being so rigorous). 18//! (without being so rigorous).
@@ -37,20 +37,26 @@
37//! new pattern `p`. 37//! new pattern `p`.
38//! 38//!
39//! For example, say we have the following: 39//! For example, say we have the following:
40//!
41//! ```ignore
42//! // x: (Option<bool>, Result<()>)
43//! match x {
44//! (Some(true), _) => {}
45//! (None, Err(())) => {}
46//! (None, Err(_)) => {}
47//! }
40//! ``` 48//! ```
41//! // x: (Option<bool>, Result<()>) 49//!
42//! match x {
43//! (Some(true), _) => {}
44//! (None, Err(())) => {}
45//! (None, Err(_)) => {}
46//! }
47//! ```
48//! Here, the matrix `P` starts as: 50//! Here, the matrix `P` starts as:
51//!
52//! ```text
49//! [ 53//! [
50//! [(Some(true), _)], 54//! [(Some(true), _)],
51//! [(None, Err(()))], 55//! [(None, Err(()))],
52//! [(None, Err(_))], 56//! [(None, Err(_))],
53//! ] 57//! ]
58//! ```
59//!
54//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering 60//! We can tell it's not exhaustive, because `U(P, _)` is true (we're not covering
55//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because 61//! `[(Some(false), _)]`, for instance). In addition, row 3 is not useful, because
56//! all the values it covers are already covered by row 2. 62//! all the values it covers are already covered by row 2.
@@ -60,53 +66,61 @@
60//! To match the paper, the top of the stack is at the beginning / on the left. 66//! To match the paper, the top of the stack is at the beginning / on the left.
61//! 67//!
62//! There are two important operations on pattern-stacks necessary to understand the algorithm: 68//! There are two important operations on pattern-stacks necessary to understand the algorithm:
63//! 1. We can pop a given constructor off the top of a stack. This operation is called
64//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
65//! `None`) and `p` a pattern-stack.
66//! If the pattern on top of the stack can cover `c`, this removes the constructor and
67//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
68//! Otherwise the pattern-stack is discarded.
69//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
70//! discards the others.
71//! 69//!
72//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we 70//! 1. We can pop a given constructor off the top of a stack. This operation is called
73//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the 71//! `specialize`, and is denoted `S(c, p)` where `c` is a constructor (like `Some` or
74//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get 72//! `None`) and `p` a pattern-stack.
75//! nothing back. 73//! If the pattern on top of the stack can cover `c`, this removes the constructor and
74//! pushes its arguments onto the stack. It also expands OR-patterns into distinct patterns.
75//! Otherwise the pattern-stack is discarded.
76//! This essentially filters those pattern-stacks whose top covers the constructor `c` and
77//! discards the others.
78//!
79//! For example, the first pattern above initially gives a stack `[(Some(true), _)]`. If we
80//! pop the tuple constructor, we are left with `[Some(true), _]`, and if we then pop the
81//! `Some` constructor we get `[true, _]`. If we had popped `None` instead, we would get
82//! nothing back.
83//!
84//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1`
85//! on top of the stack, and we have four cases:
86//!
87//! * 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We push onto
88//! the stack the arguments of this constructor, and return the result:
76//! 89//!
77//! This returns zero or more new pattern-stacks, as follows. We look at the pattern `p_1` 90//! r_1, .., r_a, p_2, .., p_n
78//! on top of the stack, and we have four cases:
79//! 1.1. `p_1 = c(r_1, .., r_a)`, i.e. the top of the stack has constructor `c`. We
80//! push onto the stack the arguments of this constructor, and return the result:
81//! r_1, .., r_a, p_2, .., p_n
82//! 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and
83//! return nothing.
84//! 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
85//! arguments (its arity), and return the resulting stack:
86//! _, .., _, p_2, .., p_n
87//! 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
88//! stack:
89//! S(c, (r_1, p_2, .., p_n))
90//! S(c, (r_2, p_2, .., p_n))
91//! 91//!
92//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is 92//! * 1.2. `p_1 = c'(r_1, .., r_a')` where `c ≠ c'`. We discard the current stack and return
93//! a pattern-stack. 93//! nothing.
94//! This is used when we know there are missing constructor cases, but there might be 94//! * 1.3. `p_1 = _`. We push onto the stack as many wildcards as the constructor `c` has
95//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check 95//! arguments (its arity), and return the resulting stack:
96//! all its *other* components.
97//! 96//!
98//! It is computed as follows. We look at the pattern `p_1` on top of the stack, 97//! _, .., _, p_2, .., p_n
99//! and we have three cases:
100//! 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
101//! 1.2. `p_1 = _`. We return the rest of the stack:
102//! p_2, .., p_n
103//! 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting
104//! stack.
105//! D((r_1, p_2, .., p_n))
106//! D((r_2, p_2, .., p_n))
107//! 98//!
108//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the 99//! * 1.4. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
109//! exhaustive integer matching rules, so they're written here for posterity. 100//!
101//! S(c, (r_1, p_2, .., p_n))
102//! S(c, (r_2, p_2, .., p_n))
103//!
104//! 2. We can pop a wildcard off the top of the stack. This is called `D(p)`, where `p` is
105//! a pattern-stack.
106//! This is used when we know there are missing constructor cases, but there might be
107//! existing wildcard patterns, so to check the usefulness of the matrix, we have to check
108//! all its *other* components.
109//!
110//! It is computed as follows. We look at the pattern `p_1` on top of the stack,
111//! and we have three cases:
112//! * 1.1. `p_1 = c(r_1, .., r_a)`. We discard the current stack and return nothing.
113//! * 1.2. `p_1 = _`. We return the rest of the stack:
114//!
115//! p_2, .., p_n
116//!
117//! * 1.3. `p_1 = r_1 | r_2`. We expand the OR-pattern and then recurse on each resulting stack:
118//!
119//! D((r_1, p_2, .., p_n))
120//! D((r_2, p_2, .., p_n))
121//!
122//! Note that the OR-patterns are not always used directly in Rust, but are used to derive the
123//! exhaustive integer matching rules, so they're written here for posterity.
110//! 124//!
111//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by 125//! Both those operations extend straightforwardly to a list or pattern-stacks, i.e. a matrix, by
112//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with 126//! working row-by-row. Popping a constructor ends up keeping only the matrix rows that start with
@@ -120,73 +134,88 @@
120//! operates principally on the first component of the matrix and new pattern-stack `p`. 134//! operates principally on the first component of the matrix and new pattern-stack `p`.
121//! This algorithm is realised in the `is_useful` function. 135//! This algorithm is realised in the `is_useful` function.
122//! 136//!
123//! Base case. (`n = 0`, i.e., an empty tuple pattern) 137//! Base case (`n = 0`, i.e., an empty tuple pattern):
124//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), 138//! - If `P` already contains an empty pattern (i.e., if the number of patterns `m > 0`), then
125//! then `U(P, p)` is false. 139//! `U(P, p)` is false.
126//! - Otherwise, `P` must be empty, so `U(P, p)` is true. 140//! - Otherwise, `P` must be empty, so `U(P, p)` is true.
141//!
142//! Inductive step (`n > 0`, i.e., whether there's at least one column [which may then be expanded
143//! into further columns later]). We're going to match on the top of the new pattern-stack, `p_1`:
144//!
145//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
146//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
147//! we ignore all the patterns in the first column of `P` that involve other constructors.
148//! This is where `S(c, P)` comes in:
127//! 149//!
128//! Inductive step. (`n > 0`, i.e., whether there's at least one column 150//! ```text
129//! [which may then be expanded into further columns later]) 151//! U(P, p) := U(S(c, P), S(c, p))
130//! We're going to match on the top of the new pattern-stack, `p_1`. 152//! ```
131//! - If `p_1 == c(r_1, .., r_a)`, i.e. we have a constructor pattern.
132//! Then, the usefulness of `p_1` can be reduced to whether it is useful when
133//! we ignore all the patterns in the first column of `P` that involve other constructors.
134//! This is where `S(c, P)` comes in:
135//! `U(P, p) := U(S(c, P), S(c, p))`
136//! This special case is handled in `is_useful_specialized`.
137//! 153//!
138//! For example, if `P` is: 154//! This special case is handled in `is_useful_specialized`.
139//! [
140//! [Some(true), _],
141//! [None, 0],
142//! ]
143//! and `p` is [Some(false), 0], then we don't care about row 2 since we know `p` only
144//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
145//! arguments of `Some` to know whether some new value is covered. So we compute
146//! `U([[true, _]], [false, 0])`.
147//! 155//!
148//! - If `p_1 == _`, then we look at the list of constructors that appear in the first 156//! For example, if `P` is:
149//! component of the rows of `P`:
150//! + If there are some constructors that aren't present, then we might think that the
151//! wildcard `_` is useful, since it covers those constructors that weren't covered
152//! before.
153//! That's almost correct, but only works if there were no wildcards in those first
154//! components. So we need to check that `p` is useful with respect to the rows that
155//! start with a wildcard, if there are any. This is where `D` comes in:
156//! `U(P, p) := U(D(P), D(p))`
157//! 157//!
158//! For example, if `P` is: 158//! ```text
159//! [ 159//! [
160//! [_, true, _], 160//! [Some(true), _],
161//! [None, false, 1], 161//! [None, 0],
162//! ] 162//! ]
163//! and `p` is [_, false, _], the `Some` constructor doesn't appear in `P`. So if we 163//! ```
164//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
165//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
166//! 164//!
167//! + Otherwise, all possible constructors (for the relevant type) are present. In this 165//! and `p` is `[Some(false), 0]`, then we don't care about row 2 since we know `p` only
168//! case we must check whether the wildcard pattern covers any unmatched value. For 166//! matches values that row 2 doesn't. For row 1 however, we need to dig into the
169//! that, we can think of the `_` pattern as a big OR-pattern that covers all 167//! arguments of `Some` to know whether some new value is covered. So we compute
170//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for 168//! `U([[true, _]], [false, 0])`.
171//! example. The wildcard pattern is useful in this case if it is useful when
172//! specialized to one of the possible constructors. So we compute:
173//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
174//! 169//!
175//! For example, if `P` is: 170//! - If `p_1 == _`, then we look at the list of constructors that appear in the first component of
176//! [ 171//! the rows of `P`:
177//! [Some(true), _], 172//! - If there are some constructors that aren't present, then we might think that the
178//! [None, false], 173//! wildcard `_` is useful, since it covers those constructors that weren't covered
179//! ] 174//! before.
180//! and `p` is [_, false], both `None` and `Some` constructors appear in the first 175//! That's almost correct, but only works if there were no wildcards in those first
181//! components of `P`. We will therefore try popping both constructors in turn: we 176//! components. So we need to check that `p` is useful with respect to the rows that
182//! compute U([[true, _]], [_, false]) for the `Some` constructor, and U([[false]], 177//! start with a wildcard, if there are any. This is where `D` comes in:
183//! [false]) for the `None` constructor. The first case returns true, so we know that 178//! `U(P, p) := U(D(P), D(p))`
184//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
185//! before.
186//! 179//!
187//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately: 180//! For example, if `P` is:
188//! `U(P, p) := U(P, (r_1, p_2, .., p_n)) 181//! ```text
189//! || U(P, (r_2, p_2, .., p_n))` 182//! [
183//! [_, true, _],
184//! [None, false, 1],
185//! ]
186//! ```
187//! and `p` is `[_, false, _]`, the `Some` constructor doesn't appear in `P`. So if we
188//! only had row 2, we'd know that `p` is useful. However row 1 starts with a
189//! wildcard, so we need to check whether `U([[true, _]], [false, 1])`.
190//!
191//! - Otherwise, all possible constructors (for the relevant type) are present. In this
192//! case we must check whether the wildcard pattern covers any unmatched value. For
193//! that, we can think of the `_` pattern as a big OR-pattern that covers all
194//! possible constructors. For `Option`, that would mean `_ = None | Some(_)` for
195//! example. The wildcard pattern is useful in this case if it is useful when
196//! specialized to one of the possible constructors. So we compute:
197//! `U(P, p) := ∃(k ϵ constructors) U(S(k, P), S(k, p))`
198//!
199//! For example, if `P` is:
200//! ```text
201//! [
202//! [Some(true), _],
203//! [None, false],
204//! ]
205//! ```
206//! and `p` is `[_, false]`, both `None` and `Some` constructors appear in the first
207//! components of `P`. We will therefore try popping both constructors in turn: we
208//! compute `U([[true, _]], [_, false])` for the `Some` constructor, and `U([[false]],
209//! [false])` for the `None` constructor. The first case returns true, so we know that
210//! `p` is useful for `P`. Indeed, it matches `[Some(false), _]` that wasn't matched
211//! before.
212//!
213//! - If `p_1 == r_1 | r_2`, then the usefulness depends on each `r_i` separately:
214//!
215//! ```text
216//! U(P, p) := U(P, (r_1, p_2, .., p_n))
217//! || U(P, (r_2, p_2, .., p_n))
218//! ```
190use std::sync::Arc; 219use std::sync::Arc;
191 220
192use smallvec::{smallvec, SmallVec}; 221use smallvec::{smallvec, SmallVec};
@@ -333,7 +362,12 @@ impl PatStack {
333 cx: &MatchCheckCtx, 362 cx: &MatchCheckCtx,
334 constructor: &Constructor, 363 constructor: &Constructor,
335 ) -> MatchCheckResult<Option<PatStack>> { 364 ) -> MatchCheckResult<Option<PatStack>> {
336 let result = match (self.head().as_pat(cx), constructor) { 365 if self.is_empty() {
366 return Ok(None);
367 }
368
369 let head_pat = self.head().as_pat(cx);
370 let result = match (head_pat, constructor) {
337 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => { 371 (Pat::Tuple { args: ref pat_ids, ellipsis }, Constructor::Tuple { arity: _ }) => {
338 if ellipsis.is_some() { 372 if ellipsis.is_some() {
339 // If there are ellipsis here, we should add the correct number of 373 // If there are ellipsis here, we should add the correct number of
@@ -502,7 +536,7 @@ impl Matrix {
502 } 536 }
503 537
504 fn heads(&self) -> Vec<PatIdOrWild> { 538 fn heads(&self) -> Vec<PatIdOrWild> {
505 self.0.iter().map(|p| p.head()).collect() 539 self.0.iter().flat_map(|p| p.get_head()).collect()
506 } 540 }
507 541
508 /// Computes `D(self)` for each contained PatStack. 542 /// Computes `D(self)` for each contained PatStack.
@@ -808,194 +842,193 @@ mod tests {
808 842
809 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB}; 843 pub(super) use crate::{diagnostics::MissingMatchArms, test_db::TestDB};
810 844
811 pub(super) fn check_diagnostic_message(content: &str) -> String { 845 pub(super) fn check_diagnostic_message(ra_fixture: &str) -> String {
812 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().0 846 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().0
813 } 847 }
814 848
815 pub(super) fn check_diagnostic(content: &str) { 849 pub(super) fn check_diagnostic(ra_fixture: &str) {
816 let diagnostic_count = 850 let diagnostic_count =
817 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; 851 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
818 852
819 assert_eq!(1, diagnostic_count, "no diagnostic reported"); 853 assert_eq!(1, diagnostic_count, "no diagnostic reported");
820 } 854 }
821 855
822 pub(super) fn check_no_diagnostic(content: &str) { 856 pub(super) fn check_no_diagnostic(ra_fixture: &str) {
823 let diagnostic_count = 857 let diagnostic_count =
824 TestDB::with_single_file(content).0.diagnostic::<MissingMatchArms>().1; 858 TestDB::with_single_file(ra_fixture).0.diagnostic::<MissingMatchArms>().1;
825 859
826 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one"); 860 assert_eq!(0, diagnostic_count, "expected no diagnostic, found one");
827 } 861 }
828 862
829 #[test] 863 #[test]
830 fn empty_tuple_no_arms_diagnostic_message() { 864 fn empty_tuple_no_arms_diagnostic_message() {
831 let content = r"
832 fn test_fn() {
833 match () {
834 }
835 }
836 ";
837
838 assert_snapshot!( 865 assert_snapshot!(
839 check_diagnostic_message(content), 866 check_diagnostic_message(r"
867 fn test_fn() {
868 match () {
869 }
870 }
871 "),
840 @"\"()\": Missing match arm\n" 872 @"\"()\": Missing match arm\n"
841 ); 873 );
842 } 874 }
843 875
844 #[test] 876 #[test]
845 fn empty_tuple_no_arms() { 877 fn empty_tuple_no_arms() {
846 let content = r" 878 check_diagnostic(
879 r"
847 fn test_fn() { 880 fn test_fn() {
848 match () { 881 match () {
849 } 882 }
850 } 883 }
851 "; 884 ",
852 885 );
853 check_diagnostic(content);
854 } 886 }
855 887
856 #[test] 888 #[test]
857 fn empty_tuple_wild() { 889 fn empty_tuple_wild() {
858 let content = r" 890 check_no_diagnostic(
891 r"
859 fn test_fn() { 892 fn test_fn() {
860 match () { 893 match () {
861 _ => {} 894 _ => {}
862 } 895 }
863 } 896 }
864 "; 897 ",
865 898 );
866 check_no_diagnostic(content);
867 } 899 }
868 900
869 #[test] 901 #[test]
870 fn empty_tuple_no_diagnostic() { 902 fn empty_tuple_no_diagnostic() {
871 let content = r" 903 check_no_diagnostic(
904 r"
872 fn test_fn() { 905 fn test_fn() {
873 match () { 906 match () {
874 () => {} 907 () => {}
875 } 908 }
876 } 909 }
877 "; 910 ",
878 911 );
879 check_no_diagnostic(content);
880 } 912 }
881 913
882 #[test] 914 #[test]
883 fn tuple_of_empty_tuple_no_arms() { 915 fn tuple_of_empty_tuple_no_arms() {
884 let content = r" 916 check_diagnostic(
917 r"
885 fn test_fn() { 918 fn test_fn() {
886 match (()) { 919 match (()) {
887 } 920 }
888 } 921 }
889 "; 922 ",
890 923 );
891 check_diagnostic(content);
892 } 924 }
893 925
894 #[test] 926 #[test]
895 fn tuple_of_empty_tuple_no_diagnostic() { 927 fn tuple_of_empty_tuple_no_diagnostic() {
896 let content = r" 928 check_no_diagnostic(
929 r"
897 fn test_fn() { 930 fn test_fn() {
898 match (()) { 931 match (()) {
899 (()) => {} 932 (()) => {}
900 } 933 }
901 } 934 }
902 "; 935 ",
903 936 );
904 check_no_diagnostic(content);
905 } 937 }
906 938
907 #[test] 939 #[test]
908 fn tuple_of_two_empty_tuple_no_arms() { 940 fn tuple_of_two_empty_tuple_no_arms() {
909 let content = r" 941 check_diagnostic(
942 r"
910 fn test_fn() { 943 fn test_fn() {
911 match ((), ()) { 944 match ((), ()) {
912 } 945 }
913 } 946 }
914 "; 947 ",
915 948 );
916 check_diagnostic(content);
917 } 949 }
918 950
919 #[test] 951 #[test]
920 fn tuple_of_two_empty_tuple_no_diagnostic() { 952 fn tuple_of_two_empty_tuple_no_diagnostic() {
921 let content = r" 953 check_no_diagnostic(
954 r"
922 fn test_fn() { 955 fn test_fn() {
923 match ((), ()) { 956 match ((), ()) {
924 ((), ()) => {} 957 ((), ()) => {}
925 } 958 }
926 } 959 }
927 "; 960 ",
928 961 );
929 check_no_diagnostic(content);
930 } 962 }
931 963
932 #[test] 964 #[test]
933 fn bool_no_arms() { 965 fn bool_no_arms() {
934 let content = r" 966 check_diagnostic(
967 r"
935 fn test_fn() { 968 fn test_fn() {
936 match false { 969 match false {
937 } 970 }
938 } 971 }
939 "; 972 ",
940 973 );
941 check_diagnostic(content);
942 } 974 }
943 975
944 #[test] 976 #[test]
945 fn bool_missing_arm() { 977 fn bool_missing_arm() {
946 let content = r" 978 check_diagnostic(
979 r"
947 fn test_fn() { 980 fn test_fn() {
948 match false { 981 match false {
949 true => {} 982 true => {}
950 } 983 }
951 } 984 }
952 "; 985 ",
953 986 );
954 check_diagnostic(content);
955 } 987 }
956 988
957 #[test] 989 #[test]
958 fn bool_no_diagnostic() { 990 fn bool_no_diagnostic() {
959 let content = r" 991 check_no_diagnostic(
992 r"
960 fn test_fn() { 993 fn test_fn() {
961 match false { 994 match false {
962 true => {} 995 true => {}
963 false => {} 996 false => {}
964 } 997 }
965 } 998 }
966 "; 999 ",
967 1000 );
968 check_no_diagnostic(content);
969 } 1001 }
970 1002
971 #[test] 1003 #[test]
972 fn tuple_of_bools_no_arms() { 1004 fn tuple_of_bools_no_arms() {
973 let content = r" 1005 check_diagnostic(
1006 r"
974 fn test_fn() { 1007 fn test_fn() {
975 match (false, true) { 1008 match (false, true) {
976 } 1009 }
977 } 1010 }
978 "; 1011 ",
979 1012 );
980 check_diagnostic(content);
981 } 1013 }
982 1014
983 #[test] 1015 #[test]
984 fn tuple_of_bools_missing_arms() { 1016 fn tuple_of_bools_missing_arms() {
985 let content = r" 1017 check_diagnostic(
1018 r"
986 fn test_fn() { 1019 fn test_fn() {
987 match (false, true) { 1020 match (false, true) {
988 (true, true) => {}, 1021 (true, true) => {},
989 } 1022 }
990 } 1023 }
991 "; 1024 ",
992 1025 );
993 check_diagnostic(content);
994 } 1026 }
995 1027
996 #[test] 1028 #[test]
997 fn tuple_of_bools_missing_arm() { 1029 fn tuple_of_bools_missing_arm() {
998 let content = r" 1030 check_diagnostic(
1031 r"
999 fn test_fn() { 1032 fn test_fn() {
1000 match (false, true) { 1033 match (false, true) {
1001 (false, true) => {}, 1034 (false, true) => {},
@@ -1003,14 +1036,14 @@ mod tests {
1003 (true, false) => {}, 1036 (true, false) => {},
1004 } 1037 }
1005 } 1038 }
1006 "; 1039 ",
1007 1040 );
1008 check_diagnostic(content);
1009 } 1041 }
1010 1042
1011 #[test] 1043 #[test]
1012 fn tuple_of_bools_with_wilds() { 1044 fn tuple_of_bools_with_wilds() {
1013 let content = r" 1045 check_no_diagnostic(
1046 r"
1014 fn test_fn() { 1047 fn test_fn() {
1015 match (false, true) { 1048 match (false, true) {
1016 (false, _) => {}, 1049 (false, _) => {},
@@ -1018,14 +1051,14 @@ mod tests {
1018 (_, true) => {}, 1051 (_, true) => {},
1019 } 1052 }
1020 } 1053 }
1021 "; 1054 ",
1022 1055 );
1023 check_no_diagnostic(content);
1024 } 1056 }
1025 1057
1026 #[test] 1058 #[test]
1027 fn tuple_of_bools_no_diagnostic() { 1059 fn tuple_of_bools_no_diagnostic() {
1028 let content = r" 1060 check_no_diagnostic(
1061 r"
1029 fn test_fn() { 1062 fn test_fn() {
1030 match (false, true) { 1063 match (false, true) {
1031 (true, true) => {}, 1064 (true, true) => {},
@@ -1034,27 +1067,27 @@ mod tests {
1034 (false, false) => {}, 1067 (false, false) => {},
1035 } 1068 }
1036 } 1069 }
1037 "; 1070 ",
1038 1071 );
1039 check_no_diagnostic(content);
1040 } 1072 }
1041 1073
1042 #[test] 1074 #[test]
1043 fn tuple_of_bools_binding_missing_arms() { 1075 fn tuple_of_bools_binding_missing_arms() {
1044 let content = r" 1076 check_diagnostic(
1077 r"
1045 fn test_fn() { 1078 fn test_fn() {
1046 match (false, true) { 1079 match (false, true) {
1047 (true, _x) => {}, 1080 (true, _x) => {},
1048 } 1081 }
1049 } 1082 }
1050 "; 1083 ",
1051 1084 );
1052 check_diagnostic(content);
1053 } 1085 }
1054 1086
1055 #[test] 1087 #[test]
1056 fn tuple_of_bools_binding_no_diagnostic() { 1088 fn tuple_of_bools_binding_no_diagnostic() {
1057 let content = r" 1089 check_no_diagnostic(
1090 r"
1058 fn test_fn() { 1091 fn test_fn() {
1059 match (false, true) { 1092 match (false, true) {
1060 (true, _x) => {}, 1093 (true, _x) => {},
@@ -1062,80 +1095,80 @@ mod tests {
1062 (false, false) => {}, 1095 (false, false) => {},
1063 } 1096 }
1064 } 1097 }
1065 "; 1098 ",
1066 1099 );
1067 check_no_diagnostic(content);
1068 } 1100 }
1069 1101
1070 #[test] 1102 #[test]
1071 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() { 1103 fn tuple_of_bools_with_ellipsis_at_end_no_diagnostic() {
1072 let content = r" 1104 check_no_diagnostic(
1105 r"
1073 fn test_fn() { 1106 fn test_fn() {
1074 match (false, true, false) { 1107 match (false, true, false) {
1075 (false, ..) => {}, 1108 (false, ..) => {},
1076 (true, ..) => {}, 1109 (true, ..) => {},
1077 } 1110 }
1078 } 1111 }
1079 "; 1112 ",
1080 1113 );
1081 check_no_diagnostic(content);
1082 } 1114 }
1083 1115
1084 #[test] 1116 #[test]
1085 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() { 1117 fn tuple_of_bools_with_ellipsis_at_beginning_no_diagnostic() {
1086 let content = r" 1118 check_no_diagnostic(
1119 r"
1087 fn test_fn() { 1120 fn test_fn() {
1088 match (false, true, false) { 1121 match (false, true, false) {
1089 (.., false) => {}, 1122 (.., false) => {},
1090 (.., true) => {}, 1123 (.., true) => {},
1091 } 1124 }
1092 } 1125 }
1093 "; 1126 ",
1094 1127 );
1095 check_no_diagnostic(content);
1096 } 1128 }
1097 1129
1098 #[test] 1130 #[test]
1099 fn tuple_of_bools_with_ellipsis_no_diagnostic() { 1131 fn tuple_of_bools_with_ellipsis_no_diagnostic() {
1100 let content = r" 1132 check_no_diagnostic(
1133 r"
1101 fn test_fn() { 1134 fn test_fn() {
1102 match (false, true, false) { 1135 match (false, true, false) {
1103 (..) => {}, 1136 (..) => {},
1104 } 1137 }
1105 } 1138 }
1106 "; 1139 ",
1107 1140 );
1108 check_no_diagnostic(content);
1109 } 1141 }
1110 1142
1111 #[test] 1143 #[test]
1112 fn tuple_of_tuple_and_bools_no_arms() { 1144 fn tuple_of_tuple_and_bools_no_arms() {
1113 let content = r" 1145 check_diagnostic(
1146 r"
1114 fn test_fn() { 1147 fn test_fn() {
1115 match (false, ((), false)) { 1148 match (false, ((), false)) {
1116 } 1149 }
1117 } 1150 }
1118 "; 1151 ",
1119 1152 );
1120 check_diagnostic(content);
1121 } 1153 }
1122 1154
1123 #[test] 1155 #[test]
1124 fn tuple_of_tuple_and_bools_missing_arms() { 1156 fn tuple_of_tuple_and_bools_missing_arms() {
1125 let content = r" 1157 check_diagnostic(
1158 r"
1126 fn test_fn() { 1159 fn test_fn() {
1127 match (false, ((), false)) { 1160 match (false, ((), false)) {
1128 (true, ((), true)) => {}, 1161 (true, ((), true)) => {},
1129 } 1162 }
1130 } 1163 }
1131 "; 1164 ",
1132 1165 );
1133 check_diagnostic(content);
1134 } 1166 }
1135 1167
1136 #[test] 1168 #[test]
1137 fn tuple_of_tuple_and_bools_no_diagnostic() { 1169 fn tuple_of_tuple_and_bools_no_diagnostic() {
1138 let content = r" 1170 check_no_diagnostic(
1171 r"
1139 fn test_fn() { 1172 fn test_fn() {
1140 match (false, ((), false)) { 1173 match (false, ((), false)) {
1141 (true, ((), true)) => {}, 1174 (true, ((), true)) => {},
@@ -1144,27 +1177,27 @@ mod tests {
1144 (false, ((), false)) => {}, 1177 (false, ((), false)) => {},
1145 } 1178 }
1146 } 1179 }
1147 "; 1180 ",
1148 1181 );
1149 check_no_diagnostic(content);
1150 } 1182 }
1151 1183
1152 #[test] 1184 #[test]
1153 fn tuple_of_tuple_and_bools_wildcard_missing_arms() { 1185 fn tuple_of_tuple_and_bools_wildcard_missing_arms() {
1154 let content = r" 1186 check_diagnostic(
1187 r"
1155 fn test_fn() { 1188 fn test_fn() {
1156 match (false, ((), false)) { 1189 match (false, ((), false)) {
1157 (true, _) => {}, 1190 (true, _) => {},
1158 } 1191 }
1159 } 1192 }
1160 "; 1193 ",
1161 1194 );
1162 check_diagnostic(content);
1163 } 1195 }
1164 1196
1165 #[test] 1197 #[test]
1166 fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() { 1198 fn tuple_of_tuple_and_bools_wildcard_no_diagnostic() {
1167 let content = r" 1199 check_no_diagnostic(
1200 r"
1168 fn test_fn() { 1201 fn test_fn() {
1169 match (false, ((), false)) { 1202 match (false, ((), false)) {
1170 (true, ((), true)) => {}, 1203 (true, ((), true)) => {},
@@ -1172,14 +1205,14 @@ mod tests {
1172 (false, _) => {}, 1205 (false, _) => {},
1173 } 1206 }
1174 } 1207 }
1175 "; 1208 ",
1176 1209 );
1177 check_no_diagnostic(content);
1178 } 1210 }
1179 1211
1180 #[test] 1212 #[test]
1181 fn enum_no_arms() { 1213 fn enum_no_arms() {
1182 let content = r" 1214 check_diagnostic(
1215 r"
1183 enum Either { 1216 enum Either {
1184 A, 1217 A,
1185 B, 1218 B,
@@ -1188,14 +1221,14 @@ mod tests {
1188 match Either::A { 1221 match Either::A {
1189 } 1222 }
1190 } 1223 }
1191 "; 1224 ",
1192 1225 );
1193 check_diagnostic(content);
1194 } 1226 }
1195 1227
1196 #[test] 1228 #[test]
1197 fn enum_missing_arms() { 1229 fn enum_missing_arms() {
1198 let content = r" 1230 check_diagnostic(
1231 r"
1199 enum Either { 1232 enum Either {
1200 A, 1233 A,
1201 B, 1234 B,
@@ -1205,14 +1238,14 @@ mod tests {
1205 Either::A => {}, 1238 Either::A => {},
1206 } 1239 }
1207 } 1240 }
1208 "; 1241 ",
1209 1242 );
1210 check_diagnostic(content);
1211 } 1243 }
1212 1244
1213 #[test] 1245 #[test]
1214 fn enum_no_diagnostic() { 1246 fn enum_no_diagnostic() {
1215 let content = r" 1247 check_no_diagnostic(
1248 r"
1216 enum Either { 1249 enum Either {
1217 A, 1250 A,
1218 B, 1251 B,
@@ -1223,14 +1256,14 @@ mod tests {
1223 Either::B => {}, 1256 Either::B => {},
1224 } 1257 }
1225 } 1258 }
1226 "; 1259 ",
1227 1260 );
1228 check_no_diagnostic(content);
1229 } 1261 }
1230 1262
1231 #[test] 1263 #[test]
1232 fn enum_ref_missing_arms() { 1264 fn enum_ref_missing_arms() {
1233 let content = r" 1265 check_diagnostic(
1266 r"
1234 enum Either { 1267 enum Either {
1235 A, 1268 A,
1236 B, 1269 B,
@@ -1240,14 +1273,14 @@ mod tests {
1240 Either::A => {}, 1273 Either::A => {},
1241 } 1274 }
1242 } 1275 }
1243 "; 1276 ",
1244 1277 );
1245 check_diagnostic(content);
1246 } 1278 }
1247 1279
1248 #[test] 1280 #[test]
1249 fn enum_ref_no_diagnostic() { 1281 fn enum_ref_no_diagnostic() {
1250 let content = r" 1282 check_no_diagnostic(
1283 r"
1251 enum Either { 1284 enum Either {
1252 A, 1285 A,
1253 B, 1286 B,
@@ -1258,14 +1291,14 @@ mod tests {
1258 Either::B => {}, 1291 Either::B => {},
1259 } 1292 }
1260 } 1293 }
1261 "; 1294 ",
1262 1295 );
1263 check_no_diagnostic(content);
1264 } 1296 }
1265 1297
1266 #[test] 1298 #[test]
1267 fn enum_containing_bool_no_arms() { 1299 fn enum_containing_bool_no_arms() {
1268 let content = r" 1300 check_diagnostic(
1301 r"
1269 enum Either { 1302 enum Either {
1270 A(bool), 1303 A(bool),
1271 B, 1304 B,
@@ -1274,14 +1307,14 @@ mod tests {
1274 match Either::B { 1307 match Either::B {
1275 } 1308 }
1276 } 1309 }
1277 "; 1310 ",
1278 1311 );
1279 check_diagnostic(content);
1280 } 1312 }
1281 1313
1282 #[test] 1314 #[test]
1283 fn enum_containing_bool_missing_arms() { 1315 fn enum_containing_bool_missing_arms() {
1284 let content = r" 1316 check_diagnostic(
1317 r"
1285 enum Either { 1318 enum Either {
1286 A(bool), 1319 A(bool),
1287 B, 1320 B,
@@ -1292,14 +1325,14 @@ mod tests {
1292 Either::B => (), 1325 Either::B => (),
1293 } 1326 }
1294 } 1327 }
1295 "; 1328 ",
1296 1329 );
1297 check_diagnostic(content);
1298 } 1330 }
1299 1331
1300 #[test] 1332 #[test]
1301 fn enum_containing_bool_no_diagnostic() { 1333 fn enum_containing_bool_no_diagnostic() {
1302 let content = r" 1334 check_no_diagnostic(
1335 r"
1303 enum Either { 1336 enum Either {
1304 A(bool), 1337 A(bool),
1305 B, 1338 B,
@@ -1311,14 +1344,14 @@ mod tests {
1311 Either::B => (), 1344 Either::B => (),
1312 } 1345 }
1313 } 1346 }
1314 "; 1347 ",
1315 1348 );
1316 check_no_diagnostic(content);
1317 } 1349 }
1318 1350
1319 #[test] 1351 #[test]
1320 fn enum_containing_bool_with_wild_no_diagnostic() { 1352 fn enum_containing_bool_with_wild_no_diagnostic() {
1321 let content = r" 1353 check_no_diagnostic(
1354 r"
1322 enum Either { 1355 enum Either {
1323 A(bool), 1356 A(bool),
1324 B, 1357 B,
@@ -1329,14 +1362,14 @@ mod tests {
1329 _ => (), 1362 _ => (),
1330 } 1363 }
1331 } 1364 }
1332 "; 1365 ",
1333 1366 );
1334 check_no_diagnostic(content);
1335 } 1367 }
1336 1368
1337 #[test] 1369 #[test]
1338 fn enum_containing_bool_with_wild_2_no_diagnostic() { 1370 fn enum_containing_bool_with_wild_2_no_diagnostic() {
1339 let content = r" 1371 check_no_diagnostic(
1372 r"
1340 enum Either { 1373 enum Either {
1341 A(bool), 1374 A(bool),
1342 B, 1375 B,
@@ -1347,14 +1380,14 @@ mod tests {
1347 Either::B => (), 1380 Either::B => (),
1348 } 1381 }
1349 } 1382 }
1350 "; 1383 ",
1351 1384 );
1352 check_no_diagnostic(content);
1353 } 1385 }
1354 1386
1355 #[test] 1387 #[test]
1356 fn enum_different_sizes_missing_arms() { 1388 fn enum_different_sizes_missing_arms() {
1357 let content = r" 1389 check_diagnostic(
1390 r"
1358 enum Either { 1391 enum Either {
1359 A(bool), 1392 A(bool),
1360 B(bool, bool), 1393 B(bool, bool),
@@ -1365,14 +1398,14 @@ mod tests {
1365 Either::B(false, _) => (), 1398 Either::B(false, _) => (),
1366 } 1399 }
1367 } 1400 }
1368 "; 1401 ",
1369 1402 );
1370 check_diagnostic(content);
1371 } 1403 }
1372 1404
1373 #[test] 1405 #[test]
1374 fn enum_different_sizes_no_diagnostic() { 1406 fn enum_different_sizes_no_diagnostic() {
1375 let content = r" 1407 check_no_diagnostic(
1408 r"
1376 enum Either { 1409 enum Either {
1377 A(bool), 1410 A(bool),
1378 B(bool, bool), 1411 B(bool, bool),
@@ -1384,14 +1417,14 @@ mod tests {
1384 Either::B(false, _) => (), 1417 Either::B(false, _) => (),
1385 } 1418 }
1386 } 1419 }
1387 "; 1420 ",
1388 1421 );
1389 check_no_diagnostic(content);
1390 } 1422 }
1391 1423
1392 #[test] 1424 #[test]
1393 fn or_no_diagnostic() { 1425 fn or_no_diagnostic() {
1394 let content = r" 1426 check_no_diagnostic(
1427 r"
1395 enum Either { 1428 enum Either {
1396 A(bool), 1429 A(bool),
1397 B(bool, bool), 1430 B(bool, bool),
@@ -1403,14 +1436,14 @@ mod tests {
1403 Either::B(false, _) => (), 1436 Either::B(false, _) => (),
1404 } 1437 }
1405 } 1438 }
1406 "; 1439 ",
1407 1440 );
1408 check_no_diagnostic(content);
1409 } 1441 }
1410 1442
1411 #[test] 1443 #[test]
1412 fn tuple_of_enum_no_diagnostic() { 1444 fn tuple_of_enum_no_diagnostic() {
1413 let content = r" 1445 check_no_diagnostic(
1446 r"
1414 enum Either { 1447 enum Either {
1415 A(bool), 1448 A(bool),
1416 B(bool, bool), 1449 B(bool, bool),
@@ -1427,14 +1460,16 @@ mod tests {
1427 (Either::B(_, _), Either2::D) => (), 1460 (Either::B(_, _), Either2::D) => (),
1428 } 1461 }
1429 } 1462 }
1430 "; 1463 ",
1431 1464 );
1432 check_no_diagnostic(content);
1433 } 1465 }
1434 1466
1435 #[test] 1467 #[test]
1436 fn mismatched_types() { 1468 fn mismatched_types() {
1437 let content = r" 1469 // Match statements with arms that don't match the
1470 // expression pattern do not fire this diagnostic.
1471 check_no_diagnostic(
1472 r"
1438 enum Either { 1473 enum Either {
1439 A, 1474 A,
1440 B, 1475 B,
@@ -1449,47 +1484,47 @@ mod tests {
1449 Either2::D => (), 1484 Either2::D => (),
1450 } 1485 }
1451 } 1486 }
1452 "; 1487 ",
1453 1488 );
1454 // Match statements with arms that don't match the
1455 // expression pattern do not fire this diagnostic.
1456 check_no_diagnostic(content);
1457 } 1489 }
1458 1490
1459 #[test] 1491 #[test]
1460 fn mismatched_types_with_different_arity() { 1492 fn mismatched_types_with_different_arity() {
1461 let content = r" 1493 // Match statements with arms that don't match the
1494 // expression pattern do not fire this diagnostic.
1495 check_no_diagnostic(
1496 r"
1462 fn test_fn() { 1497 fn test_fn() {
1463 match (true, false) { 1498 match (true, false) {
1464 (true, false, true) => (), 1499 (true, false, true) => (),
1465 (true) => (), 1500 (true) => (),
1466 } 1501 }
1467 } 1502 }
1468 "; 1503 ",
1469 1504 );
1470 // Match statements with arms that don't match the
1471 // expression pattern do not fire this diagnostic.
1472 check_no_diagnostic(content);
1473 } 1505 }
1474 1506
1475 #[test] 1507 #[test]
1476 fn malformed_match_arm_tuple_missing_pattern() { 1508 fn malformed_match_arm_tuple_missing_pattern() {
1477 let content = r" 1509 // Match statements with arms that don't match the
1510 // expression pattern do not fire this diagnostic.
1511 check_no_diagnostic(
1512 r"
1478 fn test_fn() { 1513 fn test_fn() {
1479 match (0) { 1514 match (0) {
1480 () => (), 1515 () => (),
1481 } 1516 }
1482 } 1517 }
1483 "; 1518 ",
1484 1519 );
1485 // Match statements with arms that don't match the
1486 // expression pattern do not fire this diagnostic.
1487 check_no_diagnostic(content);
1488 } 1520 }
1489 1521
1490 #[test] 1522 #[test]
1491 fn malformed_match_arm_tuple_enum_missing_pattern() { 1523 fn malformed_match_arm_tuple_enum_missing_pattern() {
1492 let content = r" 1524 // We are testing to be sure we don't panic here when the match
1525 // arm `Either::B` is missing its pattern.
1526 check_no_diagnostic(
1527 r"
1493 enum Either { 1528 enum Either {
1494 A, 1529 A,
1495 B(u32), 1530 B(u32),
@@ -1500,32 +1535,30 @@ mod tests {
1500 Either::B() => (), 1535 Either::B() => (),
1501 } 1536 }
1502 } 1537 }
1503 "; 1538 ",
1504 1539 );
1505 // We are testing to be sure we don't panic here when the match
1506 // arm `Either::B` is missing its pattern.
1507 check_no_diagnostic(content);
1508 } 1540 }
1509 1541
1510 #[test] 1542 #[test]
1511 fn enum_not_in_scope() { 1543 fn enum_not_in_scope() {
1512 let content = r" 1544 // The enum is not in scope so we don't perform exhaustiveness
1545 // checking, but we want to be sure we don't panic here (and
1546 // we don't create a diagnostic).
1547 check_no_diagnostic(
1548 r"
1513 fn test_fn() { 1549 fn test_fn() {
1514 match Foo::Bar { 1550 match Foo::Bar {
1515 Foo::Baz => (), 1551 Foo::Baz => (),
1516 } 1552 }
1517 } 1553 }
1518 "; 1554 ",
1519 1555 );
1520 // The enum is not in scope so we don't perform exhaustiveness
1521 // checking, but we want to be sure we don't panic here (and
1522 // we don't create a diagnostic).
1523 check_no_diagnostic(content);
1524 } 1556 }
1525 1557
1526 #[test] 1558 #[test]
1527 fn expr_diverges() { 1559 fn expr_diverges() {
1528 let content = r" 1560 check_no_diagnostic(
1561 r"
1529 enum Either { 1562 enum Either {
1530 A, 1563 A,
1531 B, 1564 B,
@@ -1536,14 +1569,14 @@ mod tests {
1536 Either::B => (), 1569 Either::B => (),
1537 } 1570 }
1538 } 1571 }
1539 "; 1572 ",
1540 1573 );
1541 check_no_diagnostic(content);
1542 } 1574 }
1543 1575
1544 #[test] 1576 #[test]
1545 fn expr_loop_with_break() { 1577 fn expr_loop_with_break() {
1546 let content = r" 1578 check_no_diagnostic(
1579 r"
1547 enum Either { 1580 enum Either {
1548 A, 1581 A,
1549 B, 1582 B,
@@ -1554,14 +1587,14 @@ mod tests {
1554 Either::B => (), 1587 Either::B => (),
1555 } 1588 }
1556 } 1589 }
1557 "; 1590 ",
1558 1591 );
1559 check_no_diagnostic(content);
1560 } 1592 }
1561 1593
1562 #[test] 1594 #[test]
1563 fn expr_partially_diverges() { 1595 fn expr_partially_diverges() {
1564 let content = r" 1596 check_no_diagnostic(
1597 r"
1565 enum Either<T> { 1598 enum Either<T> {
1566 A(T), 1599 A(T),
1567 B, 1600 B,
@@ -1575,14 +1608,14 @@ mod tests {
1575 Either::B => 0, 1608 Either::B => 0,
1576 } 1609 }
1577 } 1610 }
1578 "; 1611 ",
1579 1612 );
1580 check_no_diagnostic(content);
1581 } 1613 }
1582 1614
1583 #[test] 1615 #[test]
1584 fn enum_record_no_arms() { 1616 fn enum_record_no_arms() {
1585 let content = r" 1617 check_diagnostic(
1618 r"
1586 enum Either { 1619 enum Either {
1587 A { foo: bool }, 1620 A { foo: bool },
1588 B, 1621 B,
@@ -1592,14 +1625,14 @@ mod tests {
1592 match a { 1625 match a {
1593 } 1626 }
1594 } 1627 }
1595 "; 1628 ",
1596 1629 );
1597 check_diagnostic(content);
1598 } 1630 }
1599 1631
1600 #[test] 1632 #[test]
1601 fn enum_record_missing_arms() { 1633 fn enum_record_missing_arms() {
1602 let content = r" 1634 check_diagnostic(
1635 r"
1603 enum Either { 1636 enum Either {
1604 A { foo: bool }, 1637 A { foo: bool },
1605 B, 1638 B,
@@ -1610,14 +1643,14 @@ mod tests {
1610 Either::A { foo: true } => (), 1643 Either::A { foo: true } => (),
1611 } 1644 }
1612 } 1645 }
1613 "; 1646 ",
1614 1647 );
1615 check_diagnostic(content);
1616 } 1648 }
1617 1649
1618 #[test] 1650 #[test]
1619 fn enum_record_no_diagnostic() { 1651 fn enum_record_no_diagnostic() {
1620 let content = r" 1652 check_no_diagnostic(
1653 r"
1621 enum Either { 1654 enum Either {
1622 A { foo: bool }, 1655 A { foo: bool },
1623 B, 1656 B,
@@ -1630,14 +1663,17 @@ mod tests {
1630 Either::B => (), 1663 Either::B => (),
1631 } 1664 }
1632 } 1665 }
1633 "; 1666 ",
1634 1667 );
1635 check_no_diagnostic(content);
1636 } 1668 }
1637 1669
1638 #[test] 1670 #[test]
1639 fn enum_record_missing_field_no_diagnostic() { 1671 fn enum_record_missing_field_no_diagnostic() {
1640 let content = r" 1672 // When `Either::A` is missing a struct member, we don't want
1673 // to fire the missing match arm diagnostic. This should fire
1674 // some other diagnostic.
1675 check_no_diagnostic(
1676 r"
1641 enum Either { 1677 enum Either {
1642 A { foo: bool }, 1678 A { foo: bool },
1643 B, 1679 B,
@@ -1649,17 +1685,16 @@ mod tests {
1649 Either::B => (), 1685 Either::B => (),
1650 } 1686 }
1651 } 1687 }
1652 "; 1688 ",
1653 1689 );
1654 // When `Either::A` is missing a struct member, we don't want
1655 // to fire the missing match arm diagnostic. This should fire
1656 // some other diagnostic.
1657 check_no_diagnostic(content);
1658 } 1690 }
1659 1691
1660 #[test] 1692 #[test]
1661 fn enum_record_missing_field_missing_match_arm() { 1693 fn enum_record_missing_field_missing_match_arm() {
1662 let content = r" 1694 // Even though `Either::A` is missing fields, we still want to fire
1695 // the missing arm diagnostic here, since we know `Either::B` is missing.
1696 check_diagnostic(
1697 r"
1663 enum Either { 1698 enum Either {
1664 A { foo: bool }, 1699 A { foo: bool },
1665 B, 1700 B,
@@ -1670,16 +1705,14 @@ mod tests {
1670 Either::A { } => (), 1705 Either::A { } => (),
1671 } 1706 }
1672 } 1707 }
1673 "; 1708 ",
1674 1709 );
1675 // Even though `Either::A` is missing fields, we still want to fire
1676 // the missing arm diagnostic here, since we know `Either::B` is missing.
1677 check_diagnostic(content);
1678 } 1710 }
1679 1711
1680 #[test] 1712 #[test]
1681 fn enum_record_no_diagnostic_wild() { 1713 fn enum_record_no_diagnostic_wild() {
1682 let content = r" 1714 check_no_diagnostic(
1715 r"
1683 enum Either { 1716 enum Either {
1684 A { foo: bool }, 1717 A { foo: bool },
1685 B, 1718 B,
@@ -1691,14 +1724,14 @@ mod tests {
1691 Either::B => (), 1724 Either::B => (),
1692 } 1725 }
1693 } 1726 }
1694 "; 1727 ",
1695 1728 );
1696 check_no_diagnostic(content);
1697 } 1729 }
1698 1730
1699 #[test] 1731 #[test]
1700 fn enum_record_fields_out_of_order_missing_arm() { 1732 fn enum_record_fields_out_of_order_missing_arm() {
1701 let content = r" 1733 check_diagnostic(
1734 r"
1702 enum Either { 1735 enum Either {
1703 A { foo: bool, bar: () }, 1736 A { foo: bool, bar: () },
1704 B, 1737 B,
@@ -1710,14 +1743,14 @@ mod tests {
1710 Either::A { foo: true, bar: () } => (), 1743 Either::A { foo: true, bar: () } => (),
1711 } 1744 }
1712 } 1745 }
1713 "; 1746 ",
1714 1747 );
1715 check_diagnostic(content);
1716 } 1748 }
1717 1749
1718 #[test] 1750 #[test]
1719 fn enum_record_fields_out_of_order_no_diagnostic() { 1751 fn enum_record_fields_out_of_order_no_diagnostic() {
1720 let content = r" 1752 check_no_diagnostic(
1753 r"
1721 enum Either { 1754 enum Either {
1722 A { foo: bool, bar: () }, 1755 A { foo: bool, bar: () },
1723 B, 1756 B,
@@ -1730,89 +1763,89 @@ mod tests {
1730 Either::B => (), 1763 Either::B => (),
1731 } 1764 }
1732 } 1765 }
1733 "; 1766 ",
1734 1767 );
1735 check_no_diagnostic(content);
1736 } 1768 }
1737 1769
1738 #[test] 1770 #[test]
1739 fn enum_record_ellipsis_missing_arm() { 1771 fn enum_record_ellipsis_missing_arm() {
1740 let content = r" 1772 check_diagnostic(
1741 enum Either { 1773 r"
1742 A { foo: bool, bar: bool }, 1774 enum Either {
1743 B, 1775 A { foo: bool, bar: bool },
1744 } 1776 B,
1745 fn test_fn() { 1777 }
1746 match Either::B { 1778 fn test_fn() {
1747 Either::A { foo: true, .. } => (), 1779 match Either::B {
1748 Either::B => (), 1780 Either::A { foo: true, .. } => (),
1749 } 1781 Either::B => (),
1750 } 1782 }
1751 "; 1783 }
1752 1784 ",
1753 check_diagnostic(content); 1785 );
1754 } 1786 }
1755 1787
1756 #[test] 1788 #[test]
1757 fn enum_record_ellipsis_no_diagnostic() { 1789 fn enum_record_ellipsis_no_diagnostic() {
1758 let content = r" 1790 check_no_diagnostic(
1759 enum Either { 1791 r"
1760 A { foo: bool, bar: bool }, 1792 enum Either {
1761 B, 1793 A { foo: bool, bar: bool },
1762 } 1794 B,
1763 fn test_fn() { 1795 }
1764 let a = Either::A { foo: true }; 1796 fn test_fn() {
1765 match a { 1797 let a = Either::A { foo: true };
1766 Either::A { foo: true, .. } => (), 1798 match a {
1767 Either::A { foo: false, .. } => (), 1799 Either::A { foo: true, .. } => (),
1768 Either::B => (), 1800 Either::A { foo: false, .. } => (),
1769 } 1801 Either::B => (),
1770 } 1802 }
1771 "; 1803 }
1772 1804 ",
1773 check_no_diagnostic(content); 1805 );
1774 } 1806 }
1775 1807
1776 #[test] 1808 #[test]
1777 fn enum_record_ellipsis_all_fields_missing_arm() { 1809 fn enum_record_ellipsis_all_fields_missing_arm() {
1778 let content = r" 1810 check_diagnostic(
1779 enum Either { 1811 r"
1780 A { foo: bool, bar: bool }, 1812 enum Either {
1781 B, 1813 A { foo: bool, bar: bool },
1782 } 1814 B,
1783 fn test_fn() { 1815 }
1784 let a = Either::B; 1816 fn test_fn() {
1785 match a { 1817 let a = Either::B;
1786 Either::A { .. } => (), 1818 match a {
1787 } 1819 Either::A { .. } => (),
1788 } 1820 }
1789 "; 1821 }
1790 1822 ",
1791 check_diagnostic(content); 1823 );
1792 } 1824 }
1793 1825
1794 #[test] 1826 #[test]
1795 fn enum_record_ellipsis_all_fields_no_diagnostic() { 1827 fn enum_record_ellipsis_all_fields_no_diagnostic() {
1796 let content = r" 1828 check_no_diagnostic(
1797 enum Either { 1829 r"
1798 A { foo: bool, bar: bool }, 1830 enum Either {
1799 B, 1831 A { foo: bool, bar: bool },
1800 } 1832 B,
1801 fn test_fn() { 1833 }
1802 let a = Either::B; 1834 fn test_fn() {
1803 match a { 1835 let a = Either::B;
1804 Either::A { .. } => (), 1836 match a {
1805 Either::B => (), 1837 Either::A { .. } => (),
1806 } 1838 Either::B => (),
1807 } 1839 }
1808 "; 1840 }
1809 1841 ",
1810 check_no_diagnostic(content); 1842 );
1811 } 1843 }
1812 1844
1813 #[test] 1845 #[test]
1814 fn enum_tuple_partial_ellipsis_no_diagnostic() { 1846 fn enum_tuple_partial_ellipsis_no_diagnostic() {
1815 let content = r" 1847 check_no_diagnostic(
1848 r"
1816 enum Either { 1849 enum Either {
1817 A(bool, bool, bool, bool), 1850 A(bool, bool, bool, bool),
1818 B, 1851 B,
@@ -1826,14 +1859,14 @@ mod tests {
1826 Either::B => {}, 1859 Either::B => {},
1827 } 1860 }
1828 } 1861 }
1829 "; 1862 ",
1830 1863 );
1831 check_no_diagnostic(content);
1832 } 1864 }
1833 1865
1834 #[test] 1866 #[test]
1835 fn enum_tuple_partial_ellipsis_2_no_diagnostic() { 1867 fn enum_tuple_partial_ellipsis_2_no_diagnostic() {
1836 let content = r" 1868 check_no_diagnostic(
1869 r"
1837 enum Either { 1870 enum Either {
1838 A(bool, bool, bool, bool), 1871 A(bool, bool, bool, bool),
1839 B, 1872 B,
@@ -1847,14 +1880,14 @@ mod tests {
1847 Either::B => {}, 1880 Either::B => {},
1848 } 1881 }
1849 } 1882 }
1850 "; 1883 ",
1851 1884 );
1852 check_no_diagnostic(content);
1853 } 1885 }
1854 1886
1855 #[test] 1887 #[test]
1856 fn enum_tuple_partial_ellipsis_missing_arm() { 1888 fn enum_tuple_partial_ellipsis_missing_arm() {
1857 let content = r" 1889 check_diagnostic(
1890 r"
1858 enum Either { 1891 enum Either {
1859 A(bool, bool, bool, bool), 1892 A(bool, bool, bool, bool),
1860 B, 1893 B,
@@ -1867,14 +1900,14 @@ mod tests {
1867 Either::B => {}, 1900 Either::B => {},
1868 } 1901 }
1869 } 1902 }
1870 "; 1903 ",
1871 1904 );
1872 check_diagnostic(content);
1873 } 1905 }
1874 1906
1875 #[test] 1907 #[test]
1876 fn enum_tuple_partial_ellipsis_2_missing_arm() { 1908 fn enum_tuple_partial_ellipsis_2_missing_arm() {
1877 let content = r" 1909 check_diagnostic(
1910 r"
1878 enum Either { 1911 enum Either {
1879 A(bool, bool, bool, bool), 1912 A(bool, bool, bool, bool),
1880 B, 1913 B,
@@ -1887,14 +1920,14 @@ mod tests {
1887 Either::B => {}, 1920 Either::B => {},
1888 } 1921 }
1889 } 1922 }
1890 "; 1923 ",
1891 1924 );
1892 check_diagnostic(content);
1893 } 1925 }
1894 1926
1895 #[test] 1927 #[test]
1896 fn enum_tuple_ellipsis_no_diagnostic() { 1928 fn enum_tuple_ellipsis_no_diagnostic() {
1897 let content = r" 1929 check_no_diagnostic(
1930 r"
1898 enum Either { 1931 enum Either {
1899 A(bool, bool, bool, bool), 1932 A(bool, bool, bool, bool),
1900 B, 1933 B,
@@ -1905,51 +1938,51 @@ mod tests {
1905 Either::B => {}, 1938 Either::B => {},
1906 } 1939 }
1907 } 1940 }
1908 "; 1941 ",
1909 1942 );
1910 check_no_diagnostic(content);
1911 } 1943 }
1912 1944
1913 #[test] 1945 #[test]
1914 fn enum_never() { 1946 fn enum_never() {
1915 let content = r" 1947 check_no_diagnostic(
1948 r"
1916 enum Never {} 1949 enum Never {}
1917 1950
1918 fn test_fn(never: Never) { 1951 fn test_fn(never: Never) {
1919 match never {} 1952 match never {}
1920 } 1953 }
1921 "; 1954 ",
1922 1955 );
1923 check_no_diagnostic(content);
1924 } 1956 }
1925 1957
1926 #[test] 1958 #[test]
1927 fn type_never() { 1959 fn type_never() {
1928 let content = r" 1960 check_no_diagnostic(
1961 r"
1929 fn test_fn(never: !) { 1962 fn test_fn(never: !) {
1930 match never {} 1963 match never {}
1931 } 1964 }
1932 "; 1965 ",
1933 1966 );
1934 check_no_diagnostic(content);
1935 } 1967 }
1936 1968
1937 #[test] 1969 #[test]
1938 fn enum_never_ref() { 1970 fn enum_never_ref() {
1939 let content = r" 1971 check_no_diagnostic(
1972 r"
1940 enum Never {} 1973 enum Never {}
1941 1974
1942 fn test_fn(never: &Never) { 1975 fn test_fn(never: &Never) {
1943 match never {} 1976 match never {}
1944 } 1977 }
1945 "; 1978 ",
1946 1979 );
1947 check_no_diagnostic(content);
1948 } 1980 }
1949 1981
1950 #[test] 1982 #[test]
1951 fn expr_diverges_missing_arm() { 1983 fn expr_diverges_missing_arm() {
1952 let content = r" 1984 check_no_diagnostic(
1985 r"
1953 enum Either { 1986 enum Either {
1954 A, 1987 A,
1955 B, 1988 B,
@@ -1959,9 +1992,27 @@ mod tests {
1959 Either::A => (), 1992 Either::A => (),
1960 } 1993 }
1961 } 1994 }
1962 "; 1995 ",
1996 );
1997 }
1963 1998
1964 check_no_diagnostic(content); 1999 #[test]
2000 fn or_pattern_panic() {
2001 check_no_diagnostic(
2002 r"
2003 pub enum Category {
2004 Infinity,
2005 Zero,
2006 }
2007
2008 fn panic(a: Category, b: Category) {
2009 match (a, b) {
2010 (Category::Zero | Category::Infinity, _) => {}
2011 (_, Category::Zero | Category::Infinity) => {}
2012 }
2013 }
2014 ",
2015 );
1965 } 2016 }
1966} 2017}
1967 2018
@@ -1981,23 +2032,26 @@ mod false_negatives {
1981 2032
1982 #[test] 2033 #[test]
1983 fn integers() { 2034 fn integers() {
1984 let content = r" 2035 // This is a false negative.
2036 // We don't currently check integer exhaustiveness.
2037 check_no_diagnostic(
2038 r"
1985 fn test_fn() { 2039 fn test_fn() {
1986 match 5 { 2040 match 5 {
1987 10 => (), 2041 10 => (),
1988 11..20 => (), 2042 11..20 => (),
1989 } 2043 }
1990 } 2044 }
1991 "; 2045 ",
1992 2046 );
1993 // This is a false negative.
1994 // We don't currently check integer exhaustiveness.
1995 check_no_diagnostic(content);
1996 } 2047 }
1997 2048
1998 #[test] 2049 #[test]
1999 fn internal_or() { 2050 fn internal_or() {
2000 let content = r" 2051 // This is a false negative.
2052 // We do not currently handle patterns with internal `or`s.
2053 check_no_diagnostic(
2054 r"
2001 fn test_fn() { 2055 fn test_fn() {
2002 enum Either { 2056 enum Either {
2003 A(bool), 2057 A(bool),
@@ -2007,16 +2061,18 @@ mod false_negatives {
2007 Either::A(true | false) => (), 2061 Either::A(true | false) => (),
2008 } 2062 }
2009 } 2063 }
2010 "; 2064 ",
2011 2065 );
2012 // This is a false negative.
2013 // We do not currently handle patterns with internal `or`s.
2014 check_no_diagnostic(content);
2015 } 2066 }
2016 2067
2017 #[test] 2068 #[test]
2018 fn expr_loop_missing_arm() { 2069 fn expr_loop_missing_arm() {
2019 let content = r" 2070 // This is a false negative.
2071 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2072 // causes us to skip the diagnostic since `Either::A` doesn't type check
2073 // with `!`.
2074 check_diagnostic(
2075 r"
2020 enum Either { 2076 enum Either {
2021 A, 2077 A,
2022 B, 2078 B,
@@ -2026,48 +2082,46 @@ mod false_negatives {
2026 Either::A => (), 2082 Either::A => (),
2027 } 2083 }
2028 } 2084 }
2029 "; 2085 ",
2030 2086 );
2031 // This is a false negative.
2032 // We currently infer the type of `loop { break Foo::A }` to `!`, which
2033 // causes us to skip the diagnostic since `Either::A` doesn't type check
2034 // with `!`.
2035 check_diagnostic(content);
2036 } 2087 }
2037 2088
2038 #[test] 2089 #[test]
2039 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() { 2090 fn tuple_of_bools_with_ellipsis_at_end_missing_arm() {
2040 let content = r" 2091 // This is a false negative.
2092 // We don't currently handle tuple patterns with ellipsis.
2093 check_no_diagnostic(
2094 r"
2041 fn test_fn() { 2095 fn test_fn() {
2042 match (false, true, false) { 2096 match (false, true, false) {
2043 (false, ..) => {}, 2097 (false, ..) => {},
2044 } 2098 }
2045 } 2099 }
2046 "; 2100 ",
2047 2101 );
2048 // This is a false negative.
2049 // We don't currently handle tuple patterns with ellipsis.
2050 check_no_diagnostic(content);
2051 } 2102 }
2052 2103
2053 #[test] 2104 #[test]
2054 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() { 2105 fn tuple_of_bools_with_ellipsis_at_beginning_missing_arm() {
2055 let content = r" 2106 // This is a false negative.
2107 // We don't currently handle tuple patterns with ellipsis.
2108 check_no_diagnostic(
2109 r"
2056 fn test_fn() { 2110 fn test_fn() {
2057 match (false, true, false) { 2111 match (false, true, false) {
2058 (.., false) => {}, 2112 (.., false) => {},
2059 } 2113 }
2060 } 2114 }
2061 "; 2115 ",
2062 2116 );
2063 // This is a false negative.
2064 // We don't currently handle tuple patterns with ellipsis.
2065 check_no_diagnostic(content);
2066 } 2117 }
2067 2118
2068 #[test] 2119 #[test]
2069 fn struct_missing_arm() { 2120 fn struct_missing_arm() {
2070 let content = r" 2121 // This is a false negative.
2122 // We don't currently handle structs.
2123 check_no_diagnostic(
2124 r"
2071 struct Foo { 2125 struct Foo {
2072 a: bool, 2126 a: bool,
2073 } 2127 }
@@ -2076,10 +2130,7 @@ mod false_negatives {
2076 Foo { a: true } => {}, 2130 Foo { a: true } => {},
2077 } 2131 }
2078 } 2132 }
2079 "; 2133 ",
2080 2134 );
2081 // This is a false negative.
2082 // We don't currently handle structs.
2083 check_no_diagnostic(content);
2084 } 2135 }
2085} 2136}
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index f04968e14..7db928dde 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -226,17 +226,19 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
226 None => return, 226 None => return,
227 }; 227 };
228 228
229 let std_result_path = path![std::result::Result]; 229 let core_result_path = path![core::result::Result];
230 230
231 let resolver = self.func.resolver(db.upcast()); 231 let resolver = self.func.resolver(db.upcast());
232 let std_result_enum = match resolver.resolve_known_enum(db.upcast(), &std_result_path) { 232 let core_result_enum = match resolver.resolve_known_enum(db.upcast(), &core_result_path) {
233 Some(it) => it, 233 Some(it) => it,
234 _ => return, 234 _ => return,
235 }; 235 };
236 236
237 let std_result_ctor = TypeCtor::Adt(AdtId::EnumId(std_result_enum)); 237 let core_result_ctor = TypeCtor::Adt(AdtId::EnumId(core_result_enum));
238 let params = match &mismatch.expected { 238 let params = match &mismatch.expected {
239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &std_result_ctor => parameters, 239 Ty::Apply(ApplicationTy { ctor, parameters }) if ctor == &core_result_ctor => {
240 parameters
241 }
240 _ => return, 242 _ => return,
241 }; 243 };
242 244
diff --git a/crates/ra_hir_ty/src/infer.rs b/crates/ra_hir_ty/src/infer.rs
index f965eb2b5..3719f76a6 100644
--- a/crates/ra_hir_ty/src/infer.rs
+++ b/crates/ra_hir_ty/src/infer.rs
@@ -555,13 +555,13 @@ impl<'a> InferenceContext<'a> {
555 } 555 }
556 556
557 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> { 557 fn resolve_into_iter_item(&self) -> Option<TypeAliasId> {
558 let path = path![std::iter::IntoIterator]; 558 let path = path![core::iter::IntoIterator];
559 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 559 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
560 self.db.trait_data(trait_).associated_type_by_name(&name![Item]) 560 self.db.trait_data(trait_).associated_type_by_name(&name![Item])
561 } 561 }
562 562
563 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> { 563 fn resolve_ops_try_ok(&self) -> Option<TypeAliasId> {
564 let path = path![std::ops::Try]; 564 let path = path![core::ops::Try];
565 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?; 565 let trait_ = self.resolver.resolve_known_trait(self.db.upcast(), &path)?;
566 self.db.trait_data(trait_).associated_type_by_name(&name![Ok]) 566 self.db.trait_data(trait_).associated_type_by_name(&name![Ok])
567 } 567 }
@@ -587,37 +587,37 @@ impl<'a> InferenceContext<'a> {
587 } 587 }
588 588
589 fn resolve_range_full(&self) -> Option<AdtId> { 589 fn resolve_range_full(&self) -> Option<AdtId> {
590 let path = path![std::ops::RangeFull]; 590 let path = path![core::ops::RangeFull];
591 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 591 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
592 Some(struct_.into()) 592 Some(struct_.into())
593 } 593 }
594 594
595 fn resolve_range(&self) -> Option<AdtId> { 595 fn resolve_range(&self) -> Option<AdtId> {
596 let path = path![std::ops::Range]; 596 let path = path![core::ops::Range];
597 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 597 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
598 Some(struct_.into()) 598 Some(struct_.into())
599 } 599 }
600 600
601 fn resolve_range_inclusive(&self) -> Option<AdtId> { 601 fn resolve_range_inclusive(&self) -> Option<AdtId> {
602 let path = path![std::ops::RangeInclusive]; 602 let path = path![core::ops::RangeInclusive];
603 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 603 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
604 Some(struct_.into()) 604 Some(struct_.into())
605 } 605 }
606 606
607 fn resolve_range_from(&self) -> Option<AdtId> { 607 fn resolve_range_from(&self) -> Option<AdtId> {
608 let path = path![std::ops::RangeFrom]; 608 let path = path![core::ops::RangeFrom];
609 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 609 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
610 Some(struct_.into()) 610 Some(struct_.into())
611 } 611 }
612 612
613 fn resolve_range_to(&self) -> Option<AdtId> { 613 fn resolve_range_to(&self) -> Option<AdtId> {
614 let path = path![std::ops::RangeTo]; 614 let path = path![core::ops::RangeTo];
615 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 615 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
616 Some(struct_.into()) 616 Some(struct_.into())
617 } 617 }
618 618
619 fn resolve_range_to_inclusive(&self) -> Option<AdtId> { 619 fn resolve_range_to_inclusive(&self) -> Option<AdtId> {
620 let path = path![std::ops::RangeToInclusive]; 620 let path = path![core::ops::RangeToInclusive];
621 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?; 621 let struct_ = self.resolver.resolve_known_struct(self.db.upcast(), &path)?;
622 Some(struct_.into()) 622 Some(struct_.into())
623 } 623 }
diff --git a/crates/ra_hir_ty/src/test_db.rs b/crates/ra_hir_ty/src/test_db.rs
index e484968a0..ad04e3e0f 100644
--- a/crates/ra_hir_ty/src/test_db.rs
+++ b/crates/ra_hir_ty/src/test_db.rs
@@ -8,6 +8,7 @@ use std::{
8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId}; 8use hir_def::{db::DefDatabase, AssocItemId, ModuleDefId, ModuleId};
9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink}; 9use hir_expand::{db::AstDatabase, diagnostics::DiagnosticSink};
10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast}; 10use ra_db::{salsa, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, Upcast};
11use rustc_hash::FxHashSet;
11use stdx::format_to; 12use stdx::format_to;
12 13
13use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator}; 14use crate::{db::HirDatabase, diagnostics::Diagnostic, expr::ExprValidator};
@@ -73,7 +74,7 @@ impl FileLoader for TestDB {
73 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 74 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
74 FileLoaderDelegate(self).resolve_path(anchor, path) 75 FileLoaderDelegate(self).resolve_path(anchor, path)
75 } 76 }
76 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 77 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
77 FileLoaderDelegate(self).relevant_crates(file_id) 78 FileLoaderDelegate(self).relevant_crates(file_id)
78 } 79 }
79} 80}
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index 8a5031756..37659cd02 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -95,7 +95,7 @@ fn foo() {
95fn infer_ranges() { 95fn infer_ranges() {
96 let (db, pos) = TestDB::with_position( 96 let (db, pos) = TestDB::with_position(
97 r#" 97 r#"
98//- /main.rs crate:main deps:std 98//- /main.rs crate:main deps:core
99fn test() { 99fn test() {
100 let a = ..; 100 let a = ..;
101 let b = 1..; 101 let b = 1..;
@@ -108,7 +108,7 @@ fn test() {
108 t<|>; 108 t<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112#[prelude_import] use prelude::*; 112#[prelude_import] use prelude::*;
113mod prelude {} 113mod prelude {}
114 114
diff --git a/crates/ra_hir_ty/src/tests/traits.rs b/crates/ra_hir_ty/src/tests/traits.rs
index 133fb5f39..e81193a3c 100644
--- a/crates/ra_hir_ty/src/tests/traits.rs
+++ b/crates/ra_hir_ty/src/tests/traits.rs
@@ -10,7 +10,7 @@ use super::{infer, infer_with_mismatches, type_at, type_at_pos};
10fn infer_await() { 10fn infer_await() {
11 let (db, pos) = TestDB::with_position( 11 let (db, pos) = TestDB::with_position(
12 r#" 12 r#"
13//- /main.rs crate:main deps:std 13//- /main.rs crate:main deps:core
14 14
15struct IntFuture; 15struct IntFuture;
16 16
@@ -24,7 +24,7 @@ fn test() {
24 v<|>; 24 v<|>;
25} 25}
26 26
27//- /std.rs crate:std 27//- /core.rs crate:core
28#[prelude_import] use future::*; 28#[prelude_import] use future::*;
29mod future { 29mod future {
30 #[lang = "future_trait"] 30 #[lang = "future_trait"]
@@ -42,7 +42,7 @@ mod future {
42fn infer_async() { 42fn infer_async() {
43 let (db, pos) = TestDB::with_position( 43 let (db, pos) = TestDB::with_position(
44 r#" 44 r#"
45//- /main.rs crate:main deps:std 45//- /main.rs crate:main deps:core
46 46
47async fn foo() -> u64 { 47async fn foo() -> u64 {
48 128 48 128
@@ -54,7 +54,7 @@ fn test() {
54 v<|>; 54 v<|>;
55} 55}
56 56
57//- /std.rs crate:std 57//- /core.rs crate:core
58#[prelude_import] use future::*; 58#[prelude_import] use future::*;
59mod future { 59mod future {
60 #[lang = "future_trait"] 60 #[lang = "future_trait"]
@@ -72,7 +72,7 @@ mod future {
72fn infer_desugar_async() { 72fn infer_desugar_async() {
73 let (db, pos) = TestDB::with_position( 73 let (db, pos) = TestDB::with_position(
74 r#" 74 r#"
75//- /main.rs crate:main deps:std 75//- /main.rs crate:main deps:core
76 76
77async fn foo() -> u64 { 77async fn foo() -> u64 {
78 128 78 128
@@ -83,7 +83,7 @@ fn test() {
83 r<|>; 83 r<|>;
84} 84}
85 85
86//- /std.rs crate:std 86//- /core.rs crate:core
87#[prelude_import] use future::*; 87#[prelude_import] use future::*;
88mod future { 88mod future {
89 trait Future { 89 trait Future {
@@ -100,7 +100,7 @@ mod future {
100fn infer_try() { 100fn infer_try() {
101 let (db, pos) = TestDB::with_position( 101 let (db, pos) = TestDB::with_position(
102 r#" 102 r#"
103//- /main.rs crate:main deps:std 103//- /main.rs crate:main deps:core
104 104
105fn test() { 105fn test() {
106 let r: Result<i32, u64> = Result::Ok(1); 106 let r: Result<i32, u64> = Result::Ok(1);
@@ -108,7 +108,7 @@ fn test() {
108 v<|>; 108 v<|>;
109} 109}
110 110
111//- /std.rs crate:std 111//- /core.rs crate:core
112 112
113#[prelude_import] use ops::*; 113#[prelude_import] use ops::*;
114mod ops { 114mod ops {
@@ -140,9 +140,9 @@ mod result {
140fn infer_for_loop() { 140fn infer_for_loop() {
141 let (db, pos) = TestDB::with_position( 141 let (db, pos) = TestDB::with_position(
142 r#" 142 r#"
143//- /main.rs crate:main deps:std 143//- /main.rs crate:main deps:core,alloc
144 144
145use std::collections::Vec; 145use alloc::collections::Vec;
146 146
147fn test() { 147fn test() {
148 let v = Vec::new(); 148 let v = Vec::new();
@@ -152,7 +152,7 @@ fn test() {
152 } 152 }
153} 153}
154 154
155//- /std.rs crate:std 155//- /core.rs crate:core
156 156
157#[prelude_import] use iter::*; 157#[prelude_import] use iter::*;
158mod iter { 158mod iter {
@@ -161,6 +161,8 @@ mod iter {
161 } 161 }
162} 162}
163 163
164//- /alloc.rs crate:alloc deps:core
165
164mod collections { 166mod collections {
165 struct Vec<T> {} 167 struct Vec<T> {}
166 impl<T> Vec<T> { 168 impl<T> Vec<T> {
@@ -168,7 +170,7 @@ mod collections {
168 fn push(&mut self, t: T) { } 170 fn push(&mut self, t: T) { }
169 } 171 }
170 172
171 impl<T> crate::iter::IntoIterator for Vec<T> { 173 impl<T> IntoIterator for Vec<T> {
172 type Item=T; 174 type Item=T;
173 } 175 }
174} 176}
@@ -2846,12 +2848,12 @@ fn test() {
2846fn integer_range_iterate() { 2848fn integer_range_iterate() {
2847 let t = type_at( 2849 let t = type_at(
2848 r#" 2850 r#"
2849//- /main.rs crate:main deps:std 2851//- /main.rs crate:main deps:core
2850fn test() { 2852fn test() {
2851 for x in 0..100 { x<|>; } 2853 for x in 0..100 { x<|>; }
2852} 2854}
2853 2855
2854//- /std.rs crate:std 2856//- /core.rs crate:core
2855pub mod ops { 2857pub mod ops {
2856 pub struct Range<Idx> { 2858 pub struct Range<Idx> {
2857 pub start: Idx, 2859 pub start: Idx,
diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs
index a721e23c6..e1fcf379d 100644
--- a/crates/ra_ide/src/completion.rs
+++ b/crates/ra_ide/src/completion.rs
@@ -15,6 +15,7 @@ mod complete_unqualified_path;
15mod complete_postfix; 15mod complete_postfix;
16mod complete_macro_in_item_position; 16mod complete_macro_in_item_position;
17mod complete_trait_impl; 17mod complete_trait_impl;
18mod patterns;
18#[cfg(test)] 19#[cfg(test)]
19mod test_utils; 20mod test_utils;
20 21
diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs
index fd95bc410..b2f621a11 100644
--- a/crates/ra_ide/src/completion/complete_keyword.rs
+++ b/crates/ra_ide/src/completion/complete_keyword.rs
@@ -1,11 +1,6 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use ra_syntax::{ 3use ra_syntax::ast;
4 ast::{self, LoopBodyOwner},
5 match_ast, AstNode,
6 SyntaxKind::*,
7 SyntaxToken,
8};
9 4
10use crate::completion::{ 5use crate::completion::{
11 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, 6 CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
@@ -41,68 +36,122 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC
41 } 36 }
42} 37}
43 38
44fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { 39pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
45 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) 40 let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
46 .kind(CompletionItemKind::Keyword); 41 if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
47 42 add_keyword(ctx, acc, "where", "where ");
48 match ctx.config.snippet_cap { 43 return;
49 Some(cap) => res.insert_snippet(cap, snippet),
50 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
51 } 44 }
52 .build() 45 if ctx.unsafe_is_prev {
53} 46 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
47 add_keyword(ctx, acc, "fn", "fn $0() {}")
48 }
49
50 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
51 || ctx.block_expr_parent
52 {
53 add_keyword(ctx, acc, "trait", "trait $0 {}");
54 add_keyword(ctx, acc, "impl", "impl $0 {}");
55 }
54 56
55pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
56 if !ctx.is_trivial_path {
57 return; 57 return;
58 } 58 }
59 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
60 add_keyword(ctx, acc, "fn", "fn $0() {}");
61 }
62 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
63 || ctx.block_expr_parent
64 {
65 add_keyword(ctx, acc, "use", "use ");
66 add_keyword(ctx, acc, "impl", "impl $0 {}");
67 add_keyword(ctx, acc, "trait", "trait $0 {}");
68 }
59 69
60 let fn_def = match &ctx.function_syntax { 70 if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent {
61 Some(it) => it, 71 add_keyword(ctx, acc, "enum", "enum $0 {}");
62 None => return, 72 add_keyword(ctx, acc, "struct", "struct $0 {}");
63 }; 73 add_keyword(ctx, acc, "union", "union $0 {}");
64 acc.add(keyword(ctx, "if", "if $0 {}")); 74 }
65 acc.add(keyword(ctx, "match", "match $0 {}"));
66 acc.add(keyword(ctx, "while", "while $0 {}"));
67 acc.add(keyword(ctx, "loop", "loop {$0}"));
68 75
76 if ctx.block_expr_parent || ctx.is_match_arm {
77 add_keyword(ctx, acc, "match", "match $0 {}");
78 add_keyword(ctx, acc, "loop", "loop {$0}");
79 }
80 if ctx.block_expr_parent {
81 add_keyword(ctx, acc, "while", "while $0 {}");
82 }
83 if ctx.if_is_prev || ctx.block_expr_parent {
84 add_keyword(ctx, acc, "let", "let ");
85 }
86 if ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm {
87 add_keyword(ctx, acc, "if", "if ");
88 add_keyword(ctx, acc, "if let", "if let ");
89 }
69 if ctx.after_if { 90 if ctx.after_if {
70 acc.add(keyword(ctx, "else", "else {$0}")); 91 add_keyword(ctx, acc, "else", "else {$0}");
71 acc.add(keyword(ctx, "else if", "else if $0 {}")); 92 add_keyword(ctx, acc, "else if", "else if $0 {}");
93 }
94 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
95 || ctx.block_expr_parent
96 {
97 add_keyword(ctx, acc, "mod", "mod $0 {}");
98 }
99 if ctx.bind_pat_parent || ctx.ref_pat_parent {
100 add_keyword(ctx, acc, "mut", "mut ");
72 } 101 }
73 if is_in_loop_body(&ctx.token) { 102 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
103 add_keyword(ctx, acc, "const", "const ");
104 add_keyword(ctx, acc, "type", "type ");
105 }
106 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
107 || ctx.block_expr_parent
108 {
109 add_keyword(ctx, acc, "static", "static ");
110 };
111 if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
112 || ctx.block_expr_parent
113 {
114 add_keyword(ctx, acc, "extern", "extern ");
115 }
116 if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm {
117 add_keyword(ctx, acc, "unsafe", "unsafe ");
118 }
119 if ctx.in_loop_body {
74 if ctx.can_be_stmt { 120 if ctx.can_be_stmt {
75 acc.add(keyword(ctx, "continue", "continue;")); 121 add_keyword(ctx, acc, "continue", "continue;");
76 acc.add(keyword(ctx, "break", "break;")); 122 add_keyword(ctx, acc, "break", "break;");
77 } else { 123 } else {
78 acc.add(keyword(ctx, "continue", "continue")); 124 add_keyword(ctx, acc, "continue", "continue");
79 acc.add(keyword(ctx, "break", "break")); 125 add_keyword(ctx, acc, "break", "break");
80 } 126 }
81 } 127 }
128 if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent {
129 add_keyword(ctx, acc, "pub", "pub ")
130 }
131
132 if !ctx.is_trivial_path {
133 return;
134 }
135 let fn_def = match &ctx.function_syntax {
136 Some(it) => it,
137 None => return,
138 };
82 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); 139 acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
83} 140}
84 141
85fn is_in_loop_body(leaf: &SyntaxToken) -> bool { 142fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
86 // FIXME move this to CompletionContext and make it handle macros 143 let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
87 for node in leaf.parent().ancestors() { 144 .kind(CompletionItemKind::Keyword);
88 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { 145
89 break; 146 match ctx.config.snippet_cap {
90 } 147 Some(cap) => res.insert_snippet(cap, snippet),
91 let loop_body = match_ast! { 148 _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
92 match node {
93 ast::ForExpr(it) => it.loop_body(),
94 ast::WhileExpr(it) => it.loop_body(),
95 ast::LoopExpr(it) => it.loop_body(),
96 _ => None,
97 }
98 };
99 if let Some(body) = loop_body {
100 if body.syntax().text_range().contains_range(leaf.text_range()) {
101 return true;
102 }
103 }
104 } 149 }
105 false 150 .build()
151}
152
153fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
154 acc.add(keyword(ctx, kw, snippet));
106} 155}
107 156
108fn complete_return( 157fn complete_return(
@@ -121,327 +170,156 @@ fn complete_return(
121 170
122#[cfg(test)] 171#[cfg(test)]
123mod tests { 172mod tests {
124 use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; 173 use crate::completion::{test_utils::completion_list, CompletionKind};
125 use insta::assert_debug_snapshot; 174 use insta::assert_snapshot;
126 175
127 fn do_keyword_completion(code: &str) -> Vec<CompletionItem> { 176 fn get_keyword_completions(code: &str) -> String {
128 do_completion(code, CompletionKind::Keyword) 177 completion_list(code, CompletionKind::Keyword)
129 } 178 }
130 179
131 #[test] 180 #[test]
132 fn completes_keywords_in_use_stmt() { 181 fn test_keywords_in_use_stmt() {
133 assert_debug_snapshot!( 182 assert_snapshot!(
134 do_keyword_completion( 183 get_keyword_completions(r"use <|>"),
135 r"
136 use <|>
137 ",
138 ),
139 @r###" 184 @r###"
140 [ 185 kw crate
141 CompletionItem { 186 kw self
142 label: "crate", 187 kw super
143 source_range: 21..21,
144 delete: 21..21,
145 insert: "crate::",
146 kind: Keyword,
147 },
148 CompletionItem {
149 label: "self",
150 source_range: 21..21,
151 delete: 21..21,
152 insert: "self",
153 kind: Keyword,
154 },
155 CompletionItem {
156 label: "super",
157 source_range: 21..21,
158 delete: 21..21,
159 insert: "super::",
160 kind: Keyword,
161 },
162 ]
163 "### 188 "###
164 ); 189 );
165 190
166 assert_debug_snapshot!( 191 assert_snapshot!(
167 do_keyword_completion( 192 get_keyword_completions(r"use a::<|>"),
168 r"
169 use a::<|>
170 ",
171 ),
172 @r###" 193 @r###"
173 [ 194 kw self
174 CompletionItem { 195 kw super
175 label: "self",
176 source_range: 24..24,
177 delete: 24..24,
178 insert: "self",
179 kind: Keyword,
180 },
181 CompletionItem {
182 label: "super",
183 source_range: 24..24,
184 delete: 24..24,
185 insert: "super::",
186 kind: Keyword,
187 },
188 ]
189 "### 196 "###
190 ); 197 );
191 198
192 assert_debug_snapshot!( 199 assert_snapshot!(
193 do_keyword_completion( 200 get_keyword_completions(r"use a::{b, <|>}"),
194 r"
195 use a::{b, <|>}
196 ",
197 ),
198 @r###" 201 @r###"
199 [ 202 kw self
200 CompletionItem { 203 kw super
201 label: "self",
202 source_range: 28..28,
203 delete: 28..28,
204 insert: "self",
205 kind: Keyword,
206 },
207 CompletionItem {
208 label: "super",
209 source_range: 28..28,
210 delete: 28..28,
211 insert: "super::",
212 kind: Keyword,
213 },
214 ]
215 "### 204 "###
216 ); 205 );
217 } 206 }
218 207
219 #[test] 208 #[test]
220 fn completes_various_keywords_in_function() { 209 fn test_keywords_at_source_file_level() {
221 assert_debug_snapshot!( 210 assert_snapshot!(
222 do_keyword_completion( 211 get_keyword_completions(r"m<|>"),
223 r"
224 fn quux() {
225 <|>
226 }
227 ",
228 ),
229 @r###" 212 @r###"
230 [ 213 kw const
231 CompletionItem { 214 kw enum
232 label: "if", 215 kw extern
233 source_range: 49..49, 216 kw fn
234 delete: 49..49, 217 kw impl
235 insert: "if $0 {}", 218 kw mod
236 kind: Keyword, 219 kw pub
237 }, 220 kw static
238 CompletionItem { 221 kw struct
239 label: "loop", 222 kw trait
240 source_range: 49..49, 223 kw type
241 delete: 49..49, 224 kw union
242 insert: "loop {$0}", 225 kw unsafe
243 kind: Keyword, 226 kw use
244 },
245 CompletionItem {
246 label: "match",
247 source_range: 49..49,
248 delete: 49..49,
249 insert: "match $0 {}",
250 kind: Keyword,
251 },
252 CompletionItem {
253 label: "return",
254 source_range: 49..49,
255 delete: 49..49,
256 insert: "return;",
257 kind: Keyword,
258 },
259 CompletionItem {
260 label: "while",
261 source_range: 49..49,
262 delete: 49..49,
263 insert: "while $0 {}",
264 kind: Keyword,
265 },
266 ]
267 "### 227 "###
268 ); 228 );
269 } 229 }
270 230
271 #[test] 231 #[test]
272 fn completes_else_after_if() { 232 fn test_keywords_in_function() {
273 assert_debug_snapshot!( 233 assert_snapshot!(
274 do_keyword_completion( 234 get_keyword_completions(r"fn quux() { <|> }"),
275 r"
276 fn quux() {
277 if true {
278 ()
279 } <|>
280 }
281 ",
282 ),
283 @r###" 235 @r###"
284 [ 236 kw const
285 CompletionItem { 237 kw extern
286 label: "else", 238 kw fn
287 source_range: 108..108, 239 kw if
288 delete: 108..108, 240 kw if let
289 insert: "else {$0}", 241 kw impl
290 kind: Keyword, 242 kw let
291 }, 243 kw loop
292 CompletionItem { 244 kw match
293 label: "else if", 245 kw mod
294 source_range: 108..108, 246 kw return
295 delete: 108..108, 247 kw static
296 insert: "else if $0 {}", 248 kw trait
297 kind: Keyword, 249 kw type
298 }, 250 kw unsafe
299 CompletionItem { 251 kw use
300 label: "if", 252 kw while
301 source_range: 108..108,
302 delete: 108..108,
303 insert: "if $0 {}",
304 kind: Keyword,
305 },
306 CompletionItem {
307 label: "loop",
308 source_range: 108..108,
309 delete: 108..108,
310 insert: "loop {$0}",
311 kind: Keyword,
312 },
313 CompletionItem {
314 label: "match",
315 source_range: 108..108,
316 delete: 108..108,
317 insert: "match $0 {}",
318 kind: Keyword,
319 },
320 CompletionItem {
321 label: "return",
322 source_range: 108..108,
323 delete: 108..108,
324 insert: "return;",
325 kind: Keyword,
326 },
327 CompletionItem {
328 label: "while",
329 source_range: 108..108,
330 delete: 108..108,
331 insert: "while $0 {}",
332 kind: Keyword,
333 },
334 ]
335 "### 253 "###
336 ); 254 );
337 } 255 }
338 256
339 #[test] 257 #[test]
340 fn test_completion_return_value() { 258 fn test_keywords_inside_block() {
341 assert_debug_snapshot!( 259 assert_snapshot!(
342 do_keyword_completion( 260 get_keyword_completions(r"fn quux() { if true { <|> } }"),
343 r"
344 fn quux() -> i32 {
345 <|>
346 92
347 }
348 ",
349 ),
350 @r###" 261 @r###"
351 [ 262 kw const
352 CompletionItem { 263 kw extern
353 label: "if", 264 kw fn
354 source_range: 56..56, 265 kw if
355 delete: 56..56, 266 kw if let
356 insert: "if $0 {}", 267 kw impl
357 kind: Keyword, 268 kw let
358 }, 269 kw loop
359 CompletionItem { 270 kw match
360 label: "loop", 271 kw mod
361 source_range: 56..56, 272 kw return
362 delete: 56..56, 273 kw static
363 insert: "loop {$0}", 274 kw trait
364 kind: Keyword, 275 kw type
365 }, 276 kw unsafe
366 CompletionItem { 277 kw use
367 label: "match", 278 kw while
368 source_range: 56..56,
369 delete: 56..56,
370 insert: "match $0 {}",
371 kind: Keyword,
372 },
373 CompletionItem {
374 label: "return",
375 source_range: 56..56,
376 delete: 56..56,
377 insert: "return $0;",
378 kind: Keyword,
379 },
380 CompletionItem {
381 label: "while",
382 source_range: 56..56,
383 delete: 56..56,
384 insert: "while $0 {}",
385 kind: Keyword,
386 },
387 ]
388 "### 279 "###
389 ); 280 );
390 assert_debug_snapshot!( 281 }
391 do_keyword_completion( 282
283 #[test]
284 fn test_keywords_after_if() {
285 assert_snapshot!(
286 get_keyword_completions(
392 r" 287 r"
393 fn quux() { 288 fn quux() {
394 <|> 289 if true {
395 92 290 ()
291 } <|>
396 } 292 }
397 ", 293 ",
398 ), 294 ),
399 @r###" 295 @r###"
400 [ 296 kw const
401 CompletionItem { 297 kw else
402 label: "if", 298 kw else if
403 source_range: 49..49, 299 kw extern
404 delete: 49..49, 300 kw fn
405 insert: "if $0 {}", 301 kw if
406 kind: Keyword, 302 kw if let
407 }, 303 kw impl
408 CompletionItem { 304 kw let
409 label: "loop", 305 kw loop
410 source_range: 49..49, 306 kw match
411 delete: 49..49, 307 kw mod
412 insert: "loop {$0}", 308 kw return
413 kind: Keyword, 309 kw static
414 }, 310 kw trait
415 CompletionItem { 311 kw type
416 label: "match", 312 kw unsafe
417 source_range: 49..49, 313 kw use
418 delete: 49..49, 314 kw while
419 insert: "match $0 {}",
420 kind: Keyword,
421 },
422 CompletionItem {
423 label: "return",
424 source_range: 49..49,
425 delete: 49..49,
426 insert: "return;",
427 kind: Keyword,
428 },
429 CompletionItem {
430 label: "while",
431 source_range: 49..49,
432 delete: 49..49,
433 insert: "while $0 {}",
434 kind: Keyword,
435 },
436 ]
437 "### 315 "###
438 ); 316 );
439 } 317 }
440 318
441 #[test] 319 #[test]
442 fn dont_add_semi_after_return_if_not_a_statement() { 320 fn test_keywords_in_match_arm() {
443 assert_debug_snapshot!( 321 assert_snapshot!(
444 do_keyword_completion( 322 get_keyword_completions(
445 r" 323 r"
446 fn quux() -> i32 { 324 fn quux() -> i32 {
447 match () { 325 match () {
@@ -451,336 +329,130 @@ mod tests {
451 ", 329 ",
452 ), 330 ),
453 @r###" 331 @r###"
454 [ 332 kw if
455 CompletionItem { 333 kw if let
456 label: "if", 334 kw loop
457 source_range: 97..97, 335 kw match
458 delete: 97..97, 336 kw return
459 insert: "if $0 {}", 337 kw unsafe
460 kind: Keyword,
461 },
462 CompletionItem {
463 label: "loop",
464 source_range: 97..97,
465 delete: 97..97,
466 insert: "loop {$0}",
467 kind: Keyword,
468 },
469 CompletionItem {
470 label: "match",
471 source_range: 97..97,
472 delete: 97..97,
473 insert: "match $0 {}",
474 kind: Keyword,
475 },
476 CompletionItem {
477 label: "return",
478 source_range: 97..97,
479 delete: 97..97,
480 insert: "return $0",
481 kind: Keyword,
482 },
483 CompletionItem {
484 label: "while",
485 source_range: 97..97,
486 delete: 97..97,
487 insert: "while $0 {}",
488 kind: Keyword,
489 },
490 ]
491 "### 338 "###
492 ); 339 );
493 } 340 }
494 341
495 #[test] 342 #[test]
496 fn last_return_in_block_has_semi() { 343 fn test_keywords_in_trait_def() {
497 assert_debug_snapshot!( 344 assert_snapshot!(
498 do_keyword_completion( 345 get_keyword_completions(r"trait My { <|> }"),
499 r"
500 fn quux() -> i32 {
501 if condition {
502 <|>
503 }
504 }
505 ",
506 ),
507 @r###" 346 @r###"
508 [ 347 kw const
509 CompletionItem { 348 kw fn
510 label: "if", 349 kw type
511 source_range: 95..95, 350 kw unsafe
512 delete: 95..95,
513 insert: "if $0 {}",
514 kind: Keyword,
515 },
516 CompletionItem {
517 label: "loop",
518 source_range: 95..95,
519 delete: 95..95,
520 insert: "loop {$0}",
521 kind: Keyword,
522 },
523 CompletionItem {
524 label: "match",
525 source_range: 95..95,
526 delete: 95..95,
527 insert: "match $0 {}",
528 kind: Keyword,
529 },
530 CompletionItem {
531 label: "return",
532 source_range: 95..95,
533 delete: 95..95,
534 insert: "return $0;",
535 kind: Keyword,
536 },
537 CompletionItem {
538 label: "while",
539 source_range: 95..95,
540 delete: 95..95,
541 insert: "while $0 {}",
542 kind: Keyword,
543 },
544 ]
545 "### 351 "###
546 ); 352 );
547 assert_debug_snapshot!( 353 }
548 do_keyword_completion( 354
549 r" 355 #[test]
550 fn quux() -> i32 { 356 fn test_keywords_in_impl_def() {
551 if condition { 357 assert_snapshot!(
552 <|> 358 get_keyword_completions(r"impl My { <|> }"),
553 }
554 let x = 92;
555 x
556 }
557 ",
558 ),
559 @r###" 359 @r###"
560 [ 360 kw const
561 CompletionItem { 361 kw fn
562 label: "if", 362 kw pub
563 source_range: 95..95, 363 kw type
564 delete: 95..95, 364 kw unsafe
565 insert: "if $0 {}",
566 kind: Keyword,
567 },
568 CompletionItem {
569 label: "loop",
570 source_range: 95..95,
571 delete: 95..95,
572 insert: "loop {$0}",
573 kind: Keyword,
574 },
575 CompletionItem {
576 label: "match",
577 source_range: 95..95,
578 delete: 95..95,
579 insert: "match $0 {}",
580 kind: Keyword,
581 },
582 CompletionItem {
583 label: "return",
584 source_range: 95..95,
585 delete: 95..95,
586 insert: "return $0;",
587 kind: Keyword,
588 },
589 CompletionItem {
590 label: "while",
591 source_range: 95..95,
592 delete: 95..95,
593 insert: "while $0 {}",
594 kind: Keyword,
595 },
596 ]
597 "### 365 "###
598 ); 366 );
599 } 367 }
600 368
601 #[test] 369 #[test]
602 fn completes_break_and_continue_in_loops() { 370 fn test_keywords_in_loop() {
603 assert_debug_snapshot!( 371 assert_snapshot!(
604 do_keyword_completion( 372 get_keyword_completions(r"fn my() { loop { <|> } }"),
605 r"
606 fn quux() -> i32 {
607 loop { <|> }
608 }
609 ",
610 ),
611 @r###" 373 @r###"
612 [ 374 kw break
613 CompletionItem { 375 kw const
614 label: "break", 376 kw continue
615 source_range: 63..63, 377 kw extern
616 delete: 63..63, 378 kw fn
617 insert: "break;", 379 kw if
618 kind: Keyword, 380 kw if let
619 }, 381 kw impl
620 CompletionItem { 382 kw let
621 label: "continue", 383 kw loop
622 source_range: 63..63, 384 kw match
623 delete: 63..63, 385 kw mod
624 insert: "continue;", 386 kw return
625 kind: Keyword, 387 kw static
626 }, 388 kw trait
627 CompletionItem { 389 kw type
628 label: "if", 390 kw unsafe
629 source_range: 63..63, 391 kw use
630 delete: 63..63, 392 kw while
631 insert: "if $0 {}",
632 kind: Keyword,
633 },
634 CompletionItem {
635 label: "loop",
636 source_range: 63..63,
637 delete: 63..63,
638 insert: "loop {$0}",
639 kind: Keyword,
640 },
641 CompletionItem {
642 label: "match",
643 source_range: 63..63,
644 delete: 63..63,
645 insert: "match $0 {}",
646 kind: Keyword,
647 },
648 CompletionItem {
649 label: "return",
650 source_range: 63..63,
651 delete: 63..63,
652 insert: "return $0;",
653 kind: Keyword,
654 },
655 CompletionItem {
656 label: "while",
657 source_range: 63..63,
658 delete: 63..63,
659 insert: "while $0 {}",
660 kind: Keyword,
661 },
662 ]
663 "### 393 "###
664 ); 394 );
395 }
665 396
666 // No completion: lambda isolates control flow 397 #[test]
667 assert_debug_snapshot!( 398 fn test_keywords_after_unsafe_in_item_list() {
668 do_keyword_completion( 399 assert_snapshot!(
669 r" 400 get_keyword_completions(r"unsafe <|>"),
670 fn quux() -> i32 {
671 loop { || { <|> } }
672 }
673 ",
674 ),
675 @r###" 401 @r###"
676 [ 402 kw fn
677 CompletionItem { 403 kw impl
678 label: "if", 404 kw trait
679 source_range: 68..68,
680 delete: 68..68,
681 insert: "if $0 {}",
682 kind: Keyword,
683 },
684 CompletionItem {
685 label: "loop",
686 source_range: 68..68,
687 delete: 68..68,
688 insert: "loop {$0}",
689 kind: Keyword,
690 },
691 CompletionItem {
692 label: "match",
693 source_range: 68..68,
694 delete: 68..68,
695 insert: "match $0 {}",
696 kind: Keyword,
697 },
698 CompletionItem {
699 label: "return",
700 source_range: 68..68,
701 delete: 68..68,
702 insert: "return $0;",
703 kind: Keyword,
704 },
705 CompletionItem {
706 label: "while",
707 source_range: 68..68,
708 delete: 68..68,
709 insert: "while $0 {}",
710 kind: Keyword,
711 },
712 ]
713 "### 405 "###
714 ); 406 );
715 } 407 }
716 408
717 #[test] 409 #[test]
718 fn no_semi_after_break_continue_in_expr() { 410 fn test_keywords_after_unsafe_in_block_expr() {
719 assert_debug_snapshot!( 411 assert_snapshot!(
720 do_keyword_completion( 412 get_keyword_completions(r"fn my_fn() { unsafe <|> }"),
721 r"
722 fn f() {
723 loop {
724 match () {
725 () => br<|>
726 }
727 }
728 }
729 ",
730 ),
731 @r###" 413 @r###"
732 [ 414 kw fn
733 CompletionItem { 415 kw impl
734 label: "break", 416 kw trait
735 source_range: 122..124,
736 delete: 122..124,
737 insert: "break",
738 kind: Keyword,
739 },
740 CompletionItem {
741 label: "continue",
742 source_range: 122..124,
743 delete: 122..124,
744 insert: "continue",
745 kind: Keyword,
746 },
747 CompletionItem {
748 label: "if",
749 source_range: 122..124,
750 delete: 122..124,
751 insert: "if $0 {}",
752 kind: Keyword,
753 },
754 CompletionItem {
755 label: "loop",
756 source_range: 122..124,
757 delete: 122..124,
758 insert: "loop {$0}",
759 kind: Keyword,
760 },
761 CompletionItem {
762 label: "match",
763 source_range: 122..124,
764 delete: 122..124,
765 insert: "match $0 {}",
766 kind: Keyword,
767 },
768 CompletionItem {
769 label: "return",
770 source_range: 122..124,
771 delete: 122..124,
772 insert: "return",
773 kind: Keyword,
774 },
775 CompletionItem {
776 label: "while",
777 source_range: 122..124,
778 delete: 122..124,
779 insert: "while $0 {}",
780 kind: Keyword,
781 },
782 ]
783 "### 417 "###
784 ) 418 );
419 }
420
421 #[test]
422 fn test_mut_in_ref_and_in_fn_parameters_list() {
423 assert_snapshot!(
424 get_keyword_completions(r"fn my_fn(&<|>) {}"),
425 @r###"
426 kw mut
427 "###
428 );
429 assert_snapshot!(
430 get_keyword_completions(r"fn my_fn(<|>) {}"),
431 @r###"
432 kw mut
433 "###
434 );
435 assert_snapshot!(
436 get_keyword_completions(r"fn my_fn() { let &<|> }"),
437 @r###"
438 kw mut
439 "###
440 );
441 }
442
443 #[test]
444 fn test_where_keyword() {
445 assert_snapshot!(
446 get_keyword_completions(r"trait A <|>"),
447 @r###"
448 kw where
449 "###
450 );
451 assert_snapshot!(
452 get_keyword_completions(r"impl A <|>"),
453 @r###"
454 kw where
455 "###
456 );
785 } 457 }
786} 458}
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs
index 59b58bf98..b878aeb0a 100644
--- a/crates/ra_ide/src/completion/complete_postfix.rs
+++ b/crates/ra_ide/src/completion/complete_postfix.rs
@@ -91,7 +91,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
91 &dot_receiver, 91 &dot_receiver,
92 "if", 92 "if",
93 "if expr {}", 93 "if expr {}",
94 &format!("if {} {{$0}}", receiver_text), 94 &format!("if {} {{\n $0\n}}", receiver_text),
95 ) 95 )
96 .add_to(acc); 96 .add_to(acc);
97 postfix_snippet( 97 postfix_snippet(
@@ -100,7 +100,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) {
100 &dot_receiver, 100 &dot_receiver,
101 "while", 101 "while",
102 "while expr {}", 102 "while expr {}",
103 &format!("while {} {{\n$0\n}}", receiver_text), 103 &format!("while {} {{\n $0\n}}", receiver_text),
104 ) 104 )
105 .add_to(acc); 105 .add_to(acc);
106 } 106 }
@@ -283,7 +283,7 @@ mod tests {
283 label: "if", 283 label: "if",
284 source_range: 89..89, 284 source_range: 89..89,
285 delete: 85..89, 285 delete: 85..89,
286 insert: "if bar {$0}", 286 insert: "if bar {\n $0\n}",
287 detail: "if expr {}", 287 detail: "if expr {}",
288 }, 288 },
289 CompletionItem { 289 CompletionItem {
@@ -318,7 +318,7 @@ mod tests {
318 label: "while", 318 label: "while",
319 source_range: 89..89, 319 source_range: 89..89,
320 delete: 85..89, 320 delete: 85..89,
321 insert: "while bar {\n$0\n}", 321 insert: "while bar {\n $0\n}",
322 detail: "while expr {}", 322 detail: "while expr {}",
323 }, 323 },
324 ] 324 ]
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index c4646b727..560fb19e6 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -5,12 +5,17 @@ use ra_db::SourceDatabase;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::{find_covering_element, find_node_at_offset}, 7 algo::{find_covering_element, find_node_at_offset},
8 ast, match_ast, AstNode, 8 ast, match_ast, AstNode, NodeOrToken,
9 SyntaxKind::*, 9 SyntaxKind::*,
10 SyntaxNode, SyntaxToken, TextRange, TextSize, 10 SyntaxNode, SyntaxToken, TextRange, TextSize,
11}; 11};
12use ra_text_edit::Indel; 12use ra_text_edit::Indel;
13 13
14use super::patterns::{
15 has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent,
16 has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling,
17 has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev,
18};
14use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; 19use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition};
15use test_utils::mark; 20use test_utils::mark;
16 21
@@ -60,6 +65,18 @@ pub(crate) struct CompletionContext<'a> {
60 pub(super) is_path_type: bool, 65 pub(super) is_path_type: bool,
61 pub(super) has_type_args: bool, 66 pub(super) has_type_args: bool,
62 pub(super) attribute_under_caret: Option<ast::Attr>, 67 pub(super) attribute_under_caret: Option<ast::Attr>,
68 pub(super) unsafe_is_prev: bool,
69 pub(super) if_is_prev: bool,
70 pub(super) block_expr_parent: bool,
71 pub(super) bind_pat_parent: bool,
72 pub(super) ref_pat_parent: bool,
73 pub(super) in_loop_body: bool,
74 pub(super) has_trait_parent: bool,
75 pub(super) has_impl_parent: bool,
76 pub(super) trait_as_prev_sibling: bool,
77 pub(super) impl_as_prev_sibling: bool,
78 pub(super) is_match_arm: bool,
79 pub(super) has_item_list_or_source_file_parent: bool,
63} 80}
64 81
65impl<'a> CompletionContext<'a> { 82impl<'a> CompletionContext<'a> {
@@ -118,6 +135,18 @@ impl<'a> CompletionContext<'a> {
118 has_type_args: false, 135 has_type_args: false,
119 dot_receiver_is_ambiguous_float_literal: false, 136 dot_receiver_is_ambiguous_float_literal: false,
120 attribute_under_caret: None, 137 attribute_under_caret: None,
138 unsafe_is_prev: false,
139 in_loop_body: false,
140 ref_pat_parent: false,
141 bind_pat_parent: false,
142 block_expr_parent: false,
143 has_trait_parent: false,
144 has_impl_parent: false,
145 trait_as_prev_sibling: false,
146 impl_as_prev_sibling: false,
147 if_is_prev: false,
148 is_match_arm: false,
149 has_item_list_or_source_file_parent: false,
121 }; 150 };
122 151
123 let mut original_file = original_file.syntax().clone(); 152 let mut original_file = original_file.syntax().clone();
@@ -159,7 +188,7 @@ impl<'a> CompletionContext<'a> {
159 break; 188 break;
160 } 189 }
161 } 190 }
162 191 ctx.fill_keyword_patterns(&hypothetical_file, offset);
163 ctx.fill(&original_file, hypothetical_file, offset); 192 ctx.fill(&original_file, hypothetical_file, offset);
164 Some(ctx) 193 Some(ctx)
165 } 194 }
@@ -188,6 +217,24 @@ impl<'a> CompletionContext<'a> {
188 self.sema.scope_at_offset(&self.token.parent(), self.offset) 217 self.sema.scope_at_offset(&self.token.parent(), self.offset)
189 } 218 }
190 219
220 fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) {
221 let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap();
222 let syntax_element = NodeOrToken::Token(fake_ident_token.clone());
223 self.block_expr_parent = has_block_expr_parent(syntax_element.clone());
224 self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone());
225 self.if_is_prev = if_is_prev(syntax_element.clone());
226 self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone());
227 self.ref_pat_parent = has_ref_parent(syntax_element.clone());
228 self.in_loop_body = is_in_loop_body(syntax_element.clone());
229 self.has_trait_parent = has_trait_parent(syntax_element.clone());
230 self.has_impl_parent = has_impl_parent(syntax_element.clone());
231 self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone());
232 self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone());
233 self.is_match_arm = is_match_arm(syntax_element.clone());
234 self.has_item_list_or_source_file_parent =
235 has_item_list_or_source_file_parent(syntax_element.clone());
236 }
237
191 fn fill( 238 fn fill(
192 &mut self, 239 &mut self,
193 original_file: &SyntaxNode, 240 original_file: &SyntaxNode,
@@ -334,6 +381,7 @@ impl<'a> CompletionContext<'a> {
334 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some(); 381 self.is_path_type = path.syntax().parent().and_then(ast::PathType::cast).is_some();
335 self.has_type_args = segment.type_arg_list().is_some(); 382 self.has_type_args = segment.type_arg_list().is_some();
336 383
384 #[allow(deprecated)]
337 if let Some(path) = hir::Path::from_ast(path.clone()) { 385 if let Some(path) = hir::Path::from_ast(path.clone()) {
338 if let Some(path_prefix) = path.qualifier() { 386 if let Some(path_prefix) = path.qualifier() {
339 self.path_prefix = Some(path_prefix); 387 self.path_prefix = Some(path_prefix);
diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs
index cfb7c1e38..98348b349 100644
--- a/crates/ra_ide/src/completion/completion_item.rs
+++ b/crates/ra_ide/src/completion/completion_item.rs
@@ -125,6 +125,32 @@ pub enum CompletionItemKind {
125 Attribute, 125 Attribute,
126} 126}
127 127
128impl CompletionItemKind {
129 #[cfg(test)]
130 pub(crate) fn tag(&self) -> &'static str {
131 match self {
132 CompletionItemKind::Snippet => "sn",
133 CompletionItemKind::Keyword => "kw",
134 CompletionItemKind::Module => "md",
135 CompletionItemKind::Function => "fn",
136 CompletionItemKind::BuiltinType => "bt",
137 CompletionItemKind::Struct => "st",
138 CompletionItemKind::Enum => "en",
139 CompletionItemKind::EnumVariant => "ev",
140 CompletionItemKind::Binding => "bn",
141 CompletionItemKind::Field => "fd",
142 CompletionItemKind::Static => "sc",
143 CompletionItemKind::Const => "ct",
144 CompletionItemKind::Trait => "tt",
145 CompletionItemKind::TypeAlias => "ta",
146 CompletionItemKind::Method => "me",
147 CompletionItemKind::TypeParam => "tp",
148 CompletionItemKind::Macro => "ma",
149 CompletionItemKind::Attribute => "at",
150 }
151 }
152}
153
128#[derive(Debug, PartialEq, Eq, Copy, Clone)] 154#[derive(Debug, PartialEq, Eq, Copy, Clone)]
129pub(crate) enum CompletionKind { 155pub(crate) enum CompletionKind {
130 /// Parser-based keyword completion. 156 /// Parser-based keyword completion.
diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs
new file mode 100644
index 000000000..b2fe13280
--- /dev/null
+++ b/crates/ra_ide/src/completion/patterns.rs
@@ -0,0 +1,194 @@
1//! Patterns telling us certain facts about current syntax element, they are used in completion context
2
3use ra_syntax::{
4 algo::non_trivia_sibling,
5 ast::{self, LoopBodyOwner},
6 match_ast, AstNode, Direction, NodeOrToken, SyntaxElement,
7 SyntaxKind::*,
8 SyntaxNode, SyntaxToken,
9};
10
11#[cfg(test)]
12use crate::completion::test_utils::check_pattern_is_applicable;
13
14pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool {
15 not_same_range_ancestor(element)
16 .filter(|it| it.kind() == ITEM_LIST)
17 .and_then(|it| it.parent())
18 .filter(|it| it.kind() == TRAIT_DEF)
19 .is_some()
20}
21#[test]
22fn test_has_trait_parent() {
23 check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent);
24}
25
26pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool {
27 not_same_range_ancestor(element)
28 .filter(|it| it.kind() == ITEM_LIST)
29 .and_then(|it| it.parent())
30 .filter(|it| it.kind() == IMPL_DEF)
31 .is_some()
32}
33#[test]
34fn test_has_impl_parent() {
35 check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent);
36}
37
38pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool {
39 not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some()
40}
41#[test]
42fn test_has_block_expr_parent() {
43 check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent);
44}
45
46pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool {
47 element.ancestors().find(|it| it.kind() == BIND_PAT).is_some()
48}
49#[test]
50fn test_has_bind_pat_parent() {
51 check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent);
52 check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent);
53}
54
55pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool {
56 not_same_range_ancestor(element)
57 .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR)
58 .is_some()
59}
60#[test]
61fn test_has_ref_parent() {
62 check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent);
63 check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent);
64}
65
66pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool {
67 let ancestor = not_same_range_ancestor(element);
68 if !ancestor.is_some() {
69 return true;
70 }
71 ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some()
72}
73#[test]
74fn test_has_item_list_or_source_file_parent() {
75 check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent);
76 check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent);
77}
78
79pub(crate) fn is_match_arm(element: SyntaxElement) -> bool {
80 not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some()
81 && previous_sibling_or_ancestor_sibling(element)
82 .and_then(|it| it.into_token())
83 .filter(|it| it.kind() == FAT_ARROW)
84 .is_some()
85}
86#[test]
87fn test_is_match_arm() {
88 check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm);
89}
90
91pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool {
92 element
93 .into_token()
94 .and_then(|it| previous_non_trivia_token(it))
95 .filter(|it| it.kind() == UNSAFE_KW)
96 .is_some()
97}
98#[test]
99fn test_unsafe_is_prev() {
100 check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev);
101}
102
103pub(crate) fn if_is_prev(element: SyntaxElement) -> bool {
104 element
105 .into_token()
106 .and_then(|it| previous_non_trivia_token(it))
107 .filter(|it| it.kind() == IF_KW)
108 .is_some()
109}
110#[test]
111fn test_if_is_prev() {
112 check_pattern_is_applicable(r"if l<|>", if_is_prev);
113}
114
115pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool {
116 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some()
117}
118#[test]
119fn test_has_trait_as_prev_sibling() {
120 check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling);
121}
122
123pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool {
124 previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some()
125}
126#[test]
127fn test_has_impl_as_prev_sibling() {
128 check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling);
129}
130
131pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool {
132 let leaf = match element {
133 NodeOrToken::Node(node) => node,
134 NodeOrToken::Token(token) => token.parent(),
135 };
136 for node in leaf.ancestors() {
137 if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR {
138 break;
139 }
140 let loop_body = match_ast! {
141 match node {
142 ast::ForExpr(it) => it.loop_body(),
143 ast::WhileExpr(it) => it.loop_body(),
144 ast::LoopExpr(it) => it.loop_body(),
145 _ => None,
146 }
147 };
148 if let Some(body) = loop_body {
149 if body.syntax().text_range().contains_range(leaf.text_range()) {
150 return true;
151 }
152 }
153 }
154 false
155}
156
157fn not_same_range_ancestor(element: SyntaxElement) -> Option<SyntaxNode> {
158 element
159 .ancestors()
160 .take_while(|it| it.text_range() == element.text_range())
161 .last()
162 .and_then(|it| it.parent())
163}
164
165fn previous_non_trivia_token(token: SyntaxToken) -> Option<SyntaxToken> {
166 let mut token = token.prev_token();
167 while let Some(inner) = token.clone() {
168 if !inner.kind().is_trivia() {
169 return Some(inner);
170 } else {
171 token = inner.prev_token();
172 }
173 }
174 None
175}
176
177fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option<SyntaxElement> {
178 let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev);
179 if let Some(sibling) = token_sibling {
180 Some(sibling)
181 } else {
182 // if not trying to find first ancestor which has such a sibling
183 let node = match element {
184 NodeOrToken::Node(node) => node,
185 NodeOrToken::Token(token) => token.parent(),
186 };
187 let range = node.text_range();
188 let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?;
189 let prev_sibling_node = top_node.ancestors().find(|it| {
190 non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some()
191 })?;
192 non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)
193 }
194}
diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs
index bf22452a2..1e16a43ca 100644
--- a/crates/ra_ide/src/completion/test_utils.rs
+++ b/crates/ra_ide/src/completion/test_utils.rs
@@ -5,25 +5,63 @@ use crate::{
5 mock_analysis::{analysis_and_position, single_file_with_position}, 5 mock_analysis::{analysis_and_position, single_file_with_position},
6 CompletionItem, 6 CompletionItem,
7}; 7};
8use hir::Semantics;
9use ra_syntax::{AstNode, NodeOrToken, SyntaxElement};
8 10
9pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> { 11pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec<CompletionItem> {
10 do_completion_with_options(code, kind, &CompletionConfig::default()) 12 do_completion_with_options(code, kind, &CompletionConfig::default())
11} 13}
12 14
15pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String {
16 completion_list_with_options(code, kind, &CompletionConfig::default())
17}
18
13pub(crate) fn do_completion_with_options( 19pub(crate) fn do_completion_with_options(
14 code: &str, 20 code: &str,
15 kind: CompletionKind, 21 kind: CompletionKind,
16 options: &CompletionConfig, 22 options: &CompletionConfig,
17) -> Vec<CompletionItem> { 23) -> Vec<CompletionItem> {
24 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, options)
25 .into_iter()
26 .filter(|c| c.completion_kind == kind)
27 .collect();
28 kind_completions.sort_by(|l, r| l.label().cmp(r.label()));
29 kind_completions
30}
31
32fn get_all_completion_items(code: &str, options: &CompletionConfig) -> Vec<CompletionItem> {
18 let (analysis, position) = if code.contains("//-") { 33 let (analysis, position) = if code.contains("//-") {
19 analysis_and_position(code) 34 analysis_and_position(code)
20 } else { 35 } else {
21 single_file_with_position(code) 36 single_file_with_position(code)
22 }; 37 };
23 let completions = analysis.completions(options, position).unwrap().unwrap(); 38 analysis.completions(options, position).unwrap().unwrap().into()
24 let completion_items: Vec<CompletionItem> = completions.into(); 39}
25 let mut kind_completions: Vec<CompletionItem> = 40
26 completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); 41pub(crate) fn completion_list_with_options(
42 code: &str,
43 kind: CompletionKind,
44 options: &CompletionConfig,
45) -> String {
46 let mut kind_completions: Vec<CompletionItem> = get_all_completion_items(code, options)
47 .into_iter()
48 .filter(|c| c.completion_kind == kind)
49 .collect();
27 kind_completions.sort_by_key(|c| c.label().to_owned()); 50 kind_completions.sort_by_key(|c| c.label().to_owned());
28 kind_completions 51 kind_completions
52 .into_iter()
53 .map(|it| format!("{} {}\n", it.kind().unwrap().tag(), it.label()))
54 .collect()
55}
56
57pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) {
58 let (analysis, pos) = single_file_with_position(code);
59 analysis
60 .with_db(|db| {
61 let sema = Semantics::new(db);
62 let original_file = sema.parse(pos.file_id);
63 let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap();
64 assert!(check(NodeOrToken::Token(token)));
65 })
66 .unwrap();
29} 67}
diff --git a/crates/ra_ide/src/diagnostics.rs b/crates/ra_ide/src/diagnostics.rs
index bf14a467f..fd9abb55b 100644
--- a/crates/ra_ide/src/diagnostics.rs
+++ b/crates/ra_ide/src/diagnostics.rs
@@ -11,7 +11,7 @@ use hir::{
11 Semantics, 11 Semantics,
12}; 12};
13use itertools::Itertools; 13use itertools::Itertools;
14use ra_db::{RelativePath, SourceDatabase, SourceDatabaseExt}; 14use ra_db::SourceDatabase;
15use ra_ide_db::RootDatabase; 15use ra_ide_db::RootDatabase;
16use ra_prof::profile; 16use ra_prof::profile;
17use ra_syntax::{ 17use ra_syntax::{
@@ -57,14 +57,10 @@ pub(crate) fn diagnostics(db: &RootDatabase, file_id: FileId) -> Vec<Diagnostic>
57 }) 57 })
58 .on::<hir::diagnostics::UnresolvedModule, _>(|d| { 58 .on::<hir::diagnostics::UnresolvedModule, _>(|d| {
59 let original_file = d.source().file_id.original_file(db); 59 let original_file = d.source().file_id.original_file(db);
60 let source_root = db.file_source_root(original_file); 60 let fix = Fix::new(
61 let path = db 61 "Create module",
62 .file_relative_path(original_file) 62 FileSystemEdit::CreateFile { anchor: original_file, dst: d.candidate.clone() }.into(),
63 .parent() 63 );
64 .unwrap_or_else(|| RelativePath::new(""))
65 .join(&d.candidate);
66 let fix =
67 Fix::new("Create module", FileSystemEdit::CreateFile { source_root, path }.into());
68 res.borrow_mut().push(Diagnostic { 64 res.borrow_mut().push(Diagnostic {
69 range: sema.diagnostics_range(d).range, 65 range: sema.diagnostics_range(d).range,
70 message: d.message(), 66 message: d.message(),
@@ -187,7 +183,8 @@ fn check_struct_shorthand_initialization(
187 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) { 183 if let (Some(name_ref), Some(expr)) = (record_field.name_ref(), record_field.expr()) {
188 let field_name = name_ref.syntax().text().to_string(); 184 let field_name = name_ref.syntax().text().to_string();
189 let field_expr = expr.syntax().text().to_string(); 185 let field_expr = expr.syntax().text().to_string();
190 if field_name == field_expr { 186 let field_name_is_tup_index = name_ref.as_tuple_field().is_some();
187 if field_name == field_expr && !field_name_is_tup_index {
191 let mut edit_builder = TextEditBuilder::default(); 188 let mut edit_builder = TextEditBuilder::default();
192 edit_builder.delete(record_field.syntax().text_range()); 189 edit_builder.delete(record_field.syntax().text_range());
193 edit_builder.insert(record_field.syntax().text_range().start(), field_name); 190 edit_builder.insert(record_field.syntax().text_range().start(), field_name);
@@ -321,29 +318,26 @@ mod tests {
321 fn test_wrap_return_type() { 318 fn test_wrap_return_type() {
322 let before = r#" 319 let before = r#"
323 //- /main.rs 320 //- /main.rs
324 use std::{string::String, result::Result::{self, Ok, Err}}; 321 use core::result::Result::{self, Ok, Err};
325 322
326 fn div(x: i32, y: i32) -> Result<i32, String> { 323 fn div(x: i32, y: i32) -> Result<i32, ()> {
327 if y == 0 { 324 if y == 0 {
328 return Err("div by zero".into()); 325 return Err(());
329 } 326 }
330 x / y<|> 327 x / y<|>
331 } 328 }
332 329
333 //- /std/lib.rs 330 //- /core/lib.rs
334 pub mod string {
335 pub struct String { }
336 }
337 pub mod result { 331 pub mod result {
338 pub enum Result<T, E> { Ok(T), Err(E) } 332 pub enum Result<T, E> { Ok(T), Err(E) }
339 } 333 }
340 "#; 334 "#;
341 let after = r#" 335 let after = r#"
342 use std::{string::String, result::Result::{self, Ok, Err}}; 336 use core::result::Result::{self, Ok, Err};
343 337
344 fn div(x: i32, y: i32) -> Result<i32, String> { 338 fn div(x: i32, y: i32) -> Result<i32, ()> {
345 if y == 0 { 339 if y == 0 {
346 return Err("div by zero".into()); 340 return Err(());
347 } 341 }
348 Ok(x / y) 342 Ok(x / y)
349 } 343 }
@@ -355,7 +349,7 @@ mod tests {
355 fn test_wrap_return_type_handles_generic_functions() { 349 fn test_wrap_return_type_handles_generic_functions() {
356 let before = r#" 350 let before = r#"
357 //- /main.rs 351 //- /main.rs
358 use std::result::Result::{self, Ok, Err}; 352 use core::result::Result::{self, Ok, Err};
359 353
360 fn div<T>(x: T) -> Result<T, i32> { 354 fn div<T>(x: T) -> Result<T, i32> {
361 if x == 0 { 355 if x == 0 {
@@ -364,13 +358,13 @@ mod tests {
364 <|>x 358 <|>x
365 } 359 }
366 360
367 //- /std/lib.rs 361 //- /core/lib.rs
368 pub mod result { 362 pub mod result {
369 pub enum Result<T, E> { Ok(T), Err(E) } 363 pub enum Result<T, E> { Ok(T), Err(E) }
370 } 364 }
371 "#; 365 "#;
372 let after = r#" 366 let after = r#"
373 use std::result::Result::{self, Ok, Err}; 367 use core::result::Result::{self, Ok, Err};
374 368
375 fn div<T>(x: T) -> Result<T, i32> { 369 fn div<T>(x: T) -> Result<T, i32> {
376 if x == 0 { 370 if x == 0 {
@@ -386,32 +380,29 @@ mod tests {
386 fn test_wrap_return_type_handles_type_aliases() { 380 fn test_wrap_return_type_handles_type_aliases() {
387 let before = r#" 381 let before = r#"
388 //- /main.rs 382 //- /main.rs
389 use std::{string::String, result::Result::{self, Ok, Err}}; 383 use core::result::Result::{self, Ok, Err};
390 384
391 type MyResult<T> = Result<T, String>; 385 type MyResult<T> = Result<T, ()>;
392 386
393 fn div(x: i32, y: i32) -> MyResult<i32> { 387 fn div(x: i32, y: i32) -> MyResult<i32> {
394 if y == 0 { 388 if y == 0 {
395 return Err("div by zero".into()); 389 return Err(());
396 } 390 }
397 x <|>/ y 391 x <|>/ y
398 } 392 }
399 393
400 //- /std/lib.rs 394 //- /core/lib.rs
401 pub mod string {
402 pub struct String { }
403 }
404 pub mod result { 395 pub mod result {
405 pub enum Result<T, E> { Ok(T), Err(E) } 396 pub enum Result<T, E> { Ok(T), Err(E) }
406 } 397 }
407 "#; 398 "#;
408 let after = r#" 399 let after = r#"
409 use std::{string::String, result::Result::{self, Ok, Err}}; 400 use core::result::Result::{self, Ok, Err};
410 401
411 type MyResult<T> = Result<T, String>; 402 type MyResult<T> = Result<T, ()>;
412 fn div(x: i32, y: i32) -> MyResult<i32> { 403 fn div(x: i32, y: i32) -> MyResult<i32> {
413 if y == 0 { 404 if y == 0 {
414 return Err("div by zero".into()); 405 return Err(());
415 } 406 }
416 Ok(x / y) 407 Ok(x / y)
417 } 408 }
@@ -423,16 +414,13 @@ mod tests {
423 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() { 414 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
424 let content = r#" 415 let content = r#"
425 //- /main.rs 416 //- /main.rs
426 use std::{string::String, result::Result::{self, Ok, Err}}; 417 use core::result::Result::{self, Ok, Err};
427 418
428 fn foo() -> Result<String, i32> { 419 fn foo() -> Result<(), i32> {
429 0<|> 420 0<|>
430 } 421 }
431 422
432 //- /std/lib.rs 423 //- /core/lib.rs
433 pub mod string {
434 pub struct String { }
435 }
436 pub mod result { 424 pub mod result {
437 pub enum Result<T, E> { Ok(T), Err(E) } 425 pub enum Result<T, E> { Ok(T), Err(E) }
438 } 426 }
@@ -444,7 +432,7 @@ mod tests {
444 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() { 432 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result() {
445 let content = r#" 433 let content = r#"
446 //- /main.rs 434 //- /main.rs
447 use std::{string::String, result::Result::{self, Ok, Err}}; 435 use core::result::Result::{self, Ok, Err};
448 436
449 enum SomeOtherEnum { 437 enum SomeOtherEnum {
450 Ok(i32), 438 Ok(i32),
@@ -455,10 +443,7 @@ mod tests {
455 0<|> 443 0<|>
456 } 444 }
457 445
458 //- /std/lib.rs 446 //- /core/lib.rs
459 pub mod string {
460 pub struct String { }
461 }
462 pub mod result { 447 pub mod result {
463 pub enum Result<T, E> { Ok(T), Err(E) } 448 pub enum Result<T, E> { Ok(T), Err(E) }
464 } 449 }
@@ -623,10 +608,10 @@ mod tests {
623 source_file_edits: [], 608 source_file_edits: [],
624 file_system_edits: [ 609 file_system_edits: [
625 CreateFile { 610 CreateFile {
626 source_root: SourceRootId( 611 anchor: FileId(
627 0, 612 1,
628 ), 613 ),
629 path: "foo.rs", 614 dst: "foo.rs",
630 }, 615 },
631 ], 616 ],
632 is_snippet: false, 617 is_snippet: false,
@@ -731,6 +716,18 @@ mod tests {
731 "#, 716 "#,
732 check_struct_shorthand_initialization, 717 check_struct_shorthand_initialization,
733 ); 718 );
719 check_not_applicable(
720 r#"
721 struct A(usize);
722
723 fn main() {
724 A {
725 0: 0
726 }
727 }
728 "#,
729 check_struct_shorthand_initialization,
730 );
734 731
735 check_apply( 732 check_apply(
736 r#" 733 r#"
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 28f686767..51dc1f041 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -82,7 +82,7 @@ pub use ra_db::{
82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId, 82 Canceled, CrateGraph, CrateId, Edition, FileId, FilePosition, FileRange, SourceRootId,
83}; 83};
84pub use ra_ide_db::{ 84pub use ra_ide_db::{
85 change::{AnalysisChange, LibraryData}, 85 change::AnalysisChange,
86 line_index::{LineCol, LineIndex}, 86 line_index::{LineCol, LineIndex},
87 search::SearchScope, 87 search::SearchScope,
88 source_change::{FileSystemEdit, SourceChange, SourceFileEdit}, 88 source_change::{FileSystemEdit, SourceChange, SourceFileEdit},
@@ -440,12 +440,14 @@ impl Analysis {
440 440
441 /// Computes syntax highlighting for the given file 441 /// Computes syntax highlighting for the given file
442 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 442 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
443 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None)) 443 self.with_db(|db| syntax_highlighting::highlight(db, file_id, None, false))
444 } 444 }
445 445
446 /// Computes syntax highlighting for the given file range. 446 /// Computes syntax highlighting for the given file range.
447 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> { 447 pub fn highlight_range(&self, frange: FileRange) -> Cancelable<Vec<HighlightedRange>> {
448 self.with_db(|db| syntax_highlighting::highlight(db, frange.file_id, Some(frange.range))) 448 self.with_db(|db| {
449 syntax_highlighting::highlight(db, frange.file_id, Some(frange.range), false)
450 })
449 } 451 }
450 452
451 /// Computes syntax highlighting for the given file. 453 /// Computes syntax highlighting for the given file.
diff --git a/crates/ra_ide/src/prime_caches.rs b/crates/ra_ide/src/prime_caches.rs
index 90bf7d25f..c5ab5a1d8 100644
--- a/crates/ra_ide/src/prime_caches.rs
+++ b/crates/ra_ide/src/prime_caches.rs
@@ -7,6 +7,6 @@ use crate::{FileId, RootDatabase};
7 7
8pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) { 8pub(crate) fn prime_caches(db: &RootDatabase, files: Vec<FileId>) {
9 for file in files { 9 for file in files {
10 let _ = crate::syntax_highlighting::highlight(db, file, None); 10 let _ = crate::syntax_highlighting::highlight(db, file, None, false);
11 } 11 }
12} 12}
diff --git a/crates/ra_ide/src/references/rename.rs b/crates/ra_ide/src/references/rename.rs
index 915d4f4d3..c4f07f905 100644
--- a/crates/ra_ide/src/references/rename.rs
+++ b/crates/ra_ide/src/references/rename.rs
@@ -1,7 +1,7 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3use hir::{ModuleSource, Semantics}; 3use hir::{ModuleSource, Semantics};
4use ra_db::{RelativePath, RelativePathBuf, SourceDatabaseExt}; 4use ra_db::{RelativePathBuf, SourceDatabaseExt};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind, 7 algo::find_node_at_offset, ast, ast::TypeAscriptionOwner, lex_single_valid_syntax_kind,
@@ -92,23 +92,14 @@ fn rename_mod(
92 ModuleSource::SourceFile(..) => { 92 ModuleSource::SourceFile(..) => {
93 let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id); 93 let mod_path: RelativePathBuf = sema.db.file_relative_path(file_id);
94 // mod is defined in path/to/dir/mod.rs 94 // mod is defined in path/to/dir/mod.rs
95 let dst_path = if mod_path.file_stem() == Some("mod") { 95 let dst = if mod_path.file_stem() == Some("mod") {
96 mod_path 96 format!("../{}/mod.rs", new_name)
97 .parent()
98 .and_then(|p| p.parent())
99 .or_else(|| Some(RelativePath::new("")))
100 .map(|p| p.join(new_name).join("mod.rs"))
101 } else { 97 } else {
102 Some(mod_path.with_file_name(new_name).with_extension("rs")) 98 format!("{}.rs", new_name)
103 }; 99 };
104 if let Some(path) = dst_path { 100 let move_file =
105 let move_file = FileSystemEdit::MoveFile { 101 FileSystemEdit::MoveFile { src: file_id, anchor: position.file_id, dst };
106 src: file_id, 102 file_system_edits.push(move_file);
107 dst_source_root: sema.db.file_source_root(position.file_id),
108 dst_path: path,
109 };
110 file_system_edits.push(move_file);
111 }
112 } 103 }
113 ModuleSource::Module(..) => {} 104 ModuleSource::Module(..) => {}
114 } 105 }
@@ -623,16 +614,16 @@ mod tests {
623 #[test] 614 #[test]
624 fn test_rename_mod() { 615 fn test_rename_mod() {
625 let (analysis, position) = analysis_and_position( 616 let (analysis, position) = analysis_and_position(
626 " 617 r#"
627 //- /lib.rs 618//- /lib.rs
628 mod bar; 619mod bar;
629 620
630 //- /bar.rs 621//- /bar.rs
631 mod foo<|>; 622mod foo<|>;
632 623
633 //- /bar/foo.rs 624//- /bar/foo.rs
634 // emtpy 625// emtpy
635 ", 626 "#,
636 ); 627 );
637 let new_name = "foo2"; 628 let new_name = "foo2";
638 let source_change = analysis.rename(position, new_name).unwrap(); 629 let source_change = analysis.rename(position, new_name).unwrap();
@@ -662,10 +653,10 @@ mod tests {
662 src: FileId( 653 src: FileId(
663 3, 654 3,
664 ), 655 ),
665 dst_source_root: SourceRootId( 656 anchor: FileId(
666 0, 657 2,
667 ), 658 ),
668 dst_path: "bar/foo2.rs", 659 dst: "foo2.rs",
669 }, 660 },
670 ], 661 ],
671 is_snippet: false, 662 is_snippet: false,
@@ -678,12 +669,12 @@ mod tests {
678 #[test] 669 #[test]
679 fn test_rename_mod_in_dir() { 670 fn test_rename_mod_in_dir() {
680 let (analysis, position) = analysis_and_position( 671 let (analysis, position) = analysis_and_position(
681 " 672 r#"
682 //- /lib.rs 673//- /lib.rs
683 mod fo<|>o; 674mod fo<|>o;
684 //- /foo/mod.rs 675//- /foo/mod.rs
685 // emtpy 676// emtpy
686 ", 677 "#,
687 ); 678 );
688 let new_name = "foo2"; 679 let new_name = "foo2";
689 let source_change = analysis.rename(position, new_name).unwrap(); 680 let source_change = analysis.rename(position, new_name).unwrap();
@@ -713,10 +704,10 @@ mod tests {
713 src: FileId( 704 src: FileId(
714 2, 705 2,
715 ), 706 ),
716 dst_source_root: SourceRootId( 707 anchor: FileId(
717 0, 708 1,
718 ), 709 ),
719 dst_path: "foo2/mod.rs", 710 dst: "../foo2/mod.rs",
720 }, 711 },
721 ], 712 ],
722 is_snippet: false, 713 is_snippet: false,
@@ -753,19 +744,19 @@ mod tests {
753 #[test] 744 #[test]
754 fn test_rename_mod_filename_and_path() { 745 fn test_rename_mod_filename_and_path() {
755 let (analysis, position) = analysis_and_position( 746 let (analysis, position) = analysis_and_position(
756 " 747 r#"
757 //- /lib.rs 748//- /lib.rs
758 mod bar; 749mod bar;
759 fn f() { 750fn f() {
760 bar::foo::fun() 751 bar::foo::fun()
761 } 752}
762 753
763 //- /bar.rs 754//- /bar.rs
764 pub mod foo<|>; 755pub mod foo<|>;
765 756
766 //- /bar/foo.rs 757//- /bar/foo.rs
767 // pub fn fun() {} 758// pub fn fun() {}
768 ", 759 "#,
769 ); 760 );
770 let new_name = "foo2"; 761 let new_name = "foo2";
771 let source_change = analysis.rename(position, new_name).unwrap(); 762 let source_change = analysis.rename(position, new_name).unwrap();
@@ -808,10 +799,10 @@ mod tests {
808 src: FileId( 799 src: FileId(
809 3, 800 3,
810 ), 801 ),
811 dst_source_root: SourceRootId( 802 anchor: FileId(
812 0, 803 2,
813 ), 804 ),
814 dst_path: "bar/foo2.rs", 805 dst: "foo2.rs",
815 }, 806 },
816 ], 807 ],
817 is_snippet: false, 808 is_snippet: false,
diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html
index 0ae8c7efc..f92a0aba5 100644
--- a/crates/ra_ide/src/snapshots/highlight_doctest.html
+++ b/crates/ra_ide/src/snapshots/highlight_doctest.html
@@ -25,22 +25,30 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25.variable { color: #DCDCCC; } 25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; } 26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; } 27.mutable { text-decoration: underline; }
28.unresolved_reference { color: #FC5555; }
29.escape_sequence { color: #94BFF3; }
28 30
29.keyword { color: #F0DFAF; font-weight: bold; } 31.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; } 32.keyword.unsafe { color: #BC8383; font-weight: bold; }
31.control { font-style: italic; } 33.control { font-style: italic; }
32</style> 34</style>
33<pre><code><span class="keyword">impl</span> <span class="unresolved_reference">Foo</span> { 35<pre><code><span class="keyword">struct</span> <span class="struct declaration">Foo</span> {
36 <span class="field declaration">bar</span>: <span class="builtin_type">bool</span>,
37}
38
39<span class="keyword">impl</span> <span class="struct">Foo</span> {
40 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="constant declaration">bar</span>: <span class="builtin_type">bool</span> = <span class="bool_literal">true</span>;
41
34 <span class="comment">/// Constructs a new `Foo`.</span> 42 <span class="comment">/// Constructs a new `Foo`.</span>
35 <span class="comment">///</span> 43 <span class="comment">///</span>
36 <span class="comment">/// # Examples</span> 44 <span class="comment">/// # Examples</span>
37 <span class="comment">///</span> 45 <span class="comment">///</span>
38 <span class="comment">/// ```</span> 46 <span class="comment">/// ```</span>
39 <span class="comment">/// #</span> <span class="attribute">#![</span><span class="function attribute">allow</span><span class="attribute">(unused_mut)]</span> 47 <span class="comment">/// #</span> <span class="attribute">#![</span><span class="function attribute">allow</span><span class="attribute">(unused_mut)]</span>
40 <span class="comment">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="unresolved_reference">Foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>(); 48 <span class="comment">/// </span><span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">foo</span>: <span class="struct">Foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>();
41 <span class="comment">/// ```</span> 49 <span class="comment">/// ```</span>
42 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -&gt; <span class="unresolved_reference">Foo</span> { 50 <span class="keyword">pub</span> <span class="keyword">const</span> <span class="keyword">fn</span> <span class="function declaration">new</span>() -&gt; <span class="struct">Foo</span> {
43 <span class="unresolved_reference">Foo</span> { } 51 <span class="struct">Foo</span> { <span class="field">bar</span>: <span class="bool_literal">true</span> }
44 } 52 }
45 53
46 <span class="comment">/// `bar` method on `Foo`.</span> 54 <span class="comment">/// `bar` method on `Foo`.</span>
@@ -48,11 +56,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
48 <span class="comment">/// # Examples</span> 56 <span class="comment">/// # Examples</span>
49 <span class="comment">///</span> 57 <span class="comment">///</span>
50 <span class="comment">/// ```</span> 58 <span class="comment">/// ```</span>
51 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>(); 59 <span class="comment">/// </span><span class="keyword">use</span> <span class="module">x</span>::<span class="module">y</span>;
60 <span class="comment">///</span>
61 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foo</span> = <span class="struct">Foo</span>::<span class="function">new</span>();
52 <span class="comment">///</span> 62 <span class="comment">///</span>
53 <span class="comment">/// </span><span class="comment">// calls bar on foo</span> 63 <span class="comment">/// </span><span class="comment">// calls bar on foo</span>
54 <span class="comment">/// </span><span class="macro">assert!</span>(foo.bar()); 64 <span class="comment">/// </span><span class="macro">assert!</span>(foo.bar());
55 <span class="comment">///</span> 65 <span class="comment">///</span>
66 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">bar</span> = <span class="variable">foo</span>.<span class="field">bar</span> || <span class="struct">Foo</span>::<span class="constant">bar</span>;
67 <span class="comment">///</span>
56 <span class="comment">/// </span><span class="comment">/* multi-line 68 <span class="comment">/// </span><span class="comment">/* multi-line
57 </span><span class="comment">/// </span><span class="comment"> comment */</span> 69 </span><span class="comment">/// </span><span class="comment"> comment */</span>
58 <span class="comment">///</span> 70 <span class="comment">///</span>
@@ -62,8 +74,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
62 <span class="comment">///</span> 74 <span class="comment">///</span>
63 <span class="comment">/// ```</span> 75 <span class="comment">/// ```</span>
64 <span class="comment">///</span> 76 <span class="comment">///</span>
77 <span class="comment">/// ```rust,no_run</span>
78 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="struct">Foo</span>::<span class="function">new</span>().<span class="function">bar</span>();
65 <span class="comment">/// ```</span> 79 <span class="comment">/// ```</span>
66 <span class="comment">/// </span><span class="keyword">let</span> <span class="variable declaration">foobar</span> = <span class="unresolved_reference">Foo</span>::<span class="unresolved_reference">new</span>().<span class="unresolved_reference">bar</span>(); 80 <span class="comment">///</span>
81 <span class="comment">/// ```sh</span>
82 <span class="comment">/// echo 1</span>
67 <span class="comment">/// ```</span> 83 <span class="comment">/// ```</span>
68 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> { 84 <span class="keyword">pub</span> <span class="keyword">fn</span> <span class="function declaration">foo</span>(&<span class="self_keyword">self</span>) -&gt; <span class="builtin_type">bool</span> {
69 <span class="bool_literal">true</span> 85 <span class="bool_literal">true</span>
diff --git a/crates/ra_ide/src/snapshots/highlight_injection.html b/crates/ra_ide/src/snapshots/highlight_injection.html
index dec06eb51..47dbd7bc8 100644
--- a/crates/ra_ide/src/snapshots/highlight_injection.html
+++ b/crates/ra_ide/src/snapshots/highlight_injection.html
@@ -25,6 +25,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25.variable { color: #DCDCCC; } 25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; } 26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; } 27.mutable { text-decoration: underline; }
28.unresolved_reference { color: #FC5555; }
29.escape_sequence { color: #94BFF3; }
28 30
29.keyword { color: #F0DFAF; font-weight: bold; } 31.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; } 32.keyword.unsafe { color: #BC8383; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/highlight_strings.html b/crates/ra_ide/src/snapshots/highlight_strings.html
index 849eb3b73..b46fa44c6 100644
--- a/crates/ra_ide/src/snapshots/highlight_strings.html
+++ b/crates/ra_ide/src/snapshots/highlight_strings.html
@@ -25,6 +25,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25.variable { color: #DCDCCC; } 25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; } 26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; } 27.mutable { text-decoration: underline; }
28.unresolved_reference { color: #FC5555; }
29.escape_sequence { color: #94BFF3; }
28 30
29.keyword { color: #F0DFAF; font-weight: bold; } 31.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; } 32.keyword.unsafe { color: #BC8383; font-weight: bold; }
@@ -82,6 +84,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
82 84
83 <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>); 85 <span class="macro">println!</span>(<span class="string_literal">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal">!"</span>, <span class="string_literal">"world"</span>);
84 86
85 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>); 87 <span class="comment">// escape sequences</span>
88 <span class="macro">println!</span>(<span class="string_literal">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal">World"</span>);
89 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal"> World"</span>);
90
91 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal">"</span>, A = <span class="numeric_literal">92</span>);
86 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>); 92 <span class="macro">println!</span>(<span class="string_literal">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal">"</span>, ничоси = <span class="numeric_literal">92</span>);
87}</code></pre> \ No newline at end of file 93}</code></pre> \ No newline at end of file
diff --git a/crates/ra_ide/src/snapshots/highlight_unsafe.html b/crates/ra_ide/src/snapshots/highlight_unsafe.html
index bd24e6e38..73438fbb4 100644
--- a/crates/ra_ide/src/snapshots/highlight_unsafe.html
+++ b/crates/ra_ide/src/snapshots/highlight_unsafe.html
@@ -25,6 +25,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25.variable { color: #DCDCCC; } 25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; } 26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; } 27.mutable { text-decoration: underline; }
28.unresolved_reference { color: #FC5555; }
29.escape_sequence { color: #94BFF3; }
28 30
29.keyword { color: #F0DFAF; font-weight: bold; } 31.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; } 32.keyword.unsafe { color: #BC8383; font-weight: bold; }
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html
index e34ff5a7d..0c4f0a018 100644
--- a/crates/ra_ide/src/snapshots/highlighting.html
+++ b/crates/ra_ide/src/snapshots/highlighting.html
@@ -25,6 +25,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25.variable { color: #DCDCCC; } 25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; } 26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; } 27.mutable { text-decoration: underline; }
28.unresolved_reference { color: #FC5555; }
29.escape_sequence { color: #94BFF3; }
28 30
29.keyword { color: #F0DFAF; font-weight: bold; } 31.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; } 32.keyword.unsafe { color: #BC8383; font-weight: bold; }
@@ -62,6 +64,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
62 } 64 }
63} 65}
64 66
67<span class="macro">macro_rules!</span> <span class="macro declaration">noop</span> {
68 ($expr:expr) =&gt; {
69 $expr
70 }
71}
72
65<span class="comment">// comment</span> 73<span class="comment">// comment</span>
66<span class="keyword">fn</span> <span class="function declaration">main</span>() { 74<span class="keyword">fn</span> <span class="function declaration">main</span>() {
67 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>); 75 <span class="macro">println!</span>(<span class="string_literal">"Hello, {}!"</span>, <span class="numeric_literal">92</span>);
@@ -80,11 +88,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
80 <span class="comment">// Do nothing</span> 88 <span class="comment">// Do nothing</span>
81 } 89 }
82 90
91 <span class="macro">noop!</span>(<span class="macro">noop</span><span class="macro">!</span>(<span class="numeric_literal">1</span>));
92
83 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>; 93 <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>;
84 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; 94 <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>;
85 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>; 95 <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>;
86 96
87 <span class="variable mutable">y</span>; 97 <span class="keyword">let</span> <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable declaration">z</span>, <span class="field">y</span> } = <span class="struct">Foo</span> { <span class="field">x</span>: <span class="variable">z</span>, <span class="field">y</span> };
98
99 <span class="variable">y</span>;
88} 100}
89 101
90<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; { 102<span class="keyword">enum</span> <span class="enum declaration">Option</span>&lt;<span class="type_param declaration">T</span>&gt; {
diff --git a/crates/ra_ide/src/snapshots/rainbow_highlighting.html b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
index 1ab06182c..a74a70069 100644
--- a/crates/ra_ide/src/snapshots/rainbow_highlighting.html
+++ b/crates/ra_ide/src/snapshots/rainbow_highlighting.html
@@ -25,6 +25,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
25.variable { color: #DCDCCC; } 25.variable { color: #DCDCCC; }
26.format_specifier { color: #CC696B; } 26.format_specifier { color: #CC696B; }
27.mutable { text-decoration: underline; } 27.mutable { text-decoration: underline; }
28.unresolved_reference { color: #FC5555; }
29.escape_sequence { color: #94BFF3; }
28 30
29.keyword { color: #F0DFAF; font-weight: bold; } 31.keyword { color: #F0DFAF; font-weight: bold; }
30.keyword.unsafe { color: #BC8383; font-weight: bold; } 32.keyword.unsafe { color: #BC8383; font-weight: bold; }
diff --git a/crates/ra_ide/src/ssr.rs b/crates/ra_ide/src/ssr.rs
index 93e9aee1d..762aab962 100644
--- a/crates/ra_ide/src/ssr.rs
+++ b/crates/ra_ide/src/ssr.rs
@@ -27,11 +27,11 @@ impl std::error::Error for SsrError {}
27// 27//
28// Search and replace with named wildcards that will match any expression. 28// Search and replace with named wildcards that will match any expression.
29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`. 29// The syntax for a structural search replace command is `<search_pattern> ==>> <replace_pattern>`.
30// A `$<name>:expr` placeholder in the search pattern will match any expression and `$<name>` will reference it in the replacement. 30// A `$<name>` placeholder in the search pattern will match any AST node and `$<name>` will reference it in the replacement.
31// Available via the command `rust-analyzer.ssr`. 31// Available via the command `rust-analyzer.ssr`.
32// 32//
33// ```rust 33// ```rust
34// // Using structural search replace command [foo($a:expr, $b:expr) ==>> ($a).foo($b)] 34// // Using structural search replace command [foo($a, $b) ==>> ($a).foo($b)]
35// 35//
36// // BEFORE 36// // BEFORE
37// String::from(foo(y + 5, z)) 37// String::from(foo(y + 5, z))
@@ -79,7 +79,7 @@ struct SsrPattern {
79 vars: Vec<Var>, 79 vars: Vec<Var>,
80} 80}
81 81
82/// represents an `$var` in an SSR query 82/// Represents a `$var` in an SSR query.
83#[derive(Debug, Clone, PartialEq, Eq, Hash)] 83#[derive(Debug, Clone, PartialEq, Eq, Hash)]
84struct Var(String); 84struct Var(String);
85 85
@@ -122,8 +122,7 @@ impl FromStr for SsrQuery {
122 let mut pattern = it.next().expect("something").to_string(); 122 let mut pattern = it.next().expect("something").to_string();
123 123
124 for part in it.map(split_by_var) { 124 for part in it.map(split_by_var) {
125 let (var, var_type, remainder) = part?; 125 let (var, remainder) = part?;
126 is_expr(var_type)?;
127 let new_var = create_name(var, &mut vars)?; 126 let new_var = create_name(var, &mut vars)?;
128 pattern.push_str(new_var); 127 pattern.push_str(new_var);
129 pattern.push_str(remainder); 128 pattern.push_str(remainder);
@@ -166,15 +165,11 @@ fn traverse(node: &SyntaxNode, go: &mut impl FnMut(&SyntaxNode) -> bool) {
166 } 165 }
167} 166}
168 167
169fn split_by_var(s: &str) -> Result<(&str, &str, &str), SsrError> { 168fn split_by_var(s: &str) -> Result<(&str, &str), SsrError> {
170 let end_of_name = s.find(':').ok_or_else(|| SsrError("Use $<name>:expr".into()))?; 169 let end_of_name = s.find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
171 let name = &s[0..end_of_name]; 170 let name = &s[..end_of_name];
172 is_name(name)?; 171 is_name(name)?;
173 let type_begin = end_of_name + 1; 172 Ok((name, &s[end_of_name..]))
174 let type_length =
175 s[type_begin..].find(|c| !char::is_ascii_alphanumeric(&c)).unwrap_or_else(|| s.len());
176 let type_name = &s[type_begin..type_begin + type_length];
177 Ok((name, type_name, &s[type_begin + type_length..]))
178} 173}
179 174
180fn is_name(s: &str) -> Result<(), SsrError> { 175fn is_name(s: &str) -> Result<(), SsrError> {
@@ -185,14 +180,6 @@ fn is_name(s: &str) -> Result<(), SsrError> {
185 } 180 }
186} 181}
187 182
188fn is_expr(s: &str) -> Result<(), SsrError> {
189 if s == "expr" {
190 Ok(())
191 } else {
192 Err(SsrError("Only $<name>:expr is supported".into()))
193 }
194}
195
196fn replace_in_template(template: String, var: &str, new_var: &str) -> String { 183fn replace_in_template(template: String, var: &str, new_var: &str) -> String {
197 let name = format!("${}", var); 184 let name = format!("${}", var);
198 template.replace(&name, new_var) 185 template.replace(&name, new_var)
@@ -450,7 +437,7 @@ mod tests {
450 437
451 #[test] 438 #[test]
452 fn parser_happy_case() { 439 fn parser_happy_case() {
453 let result: SsrQuery = "foo($a:expr, $b:expr) ==>> bar($b, $a)".parse().unwrap(); 440 let result: SsrQuery = "foo($a, $b) ==>> bar($b, $a)".parse().unwrap();
454 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)"); 441 assert_eq!(&result.pattern.pattern.text(), "foo(__search_pattern_a, __search_pattern_b)");
455 assert_eq!(result.pattern.vars.len(), 2); 442 assert_eq!(result.pattern.vars.len(), 2);
456 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a"); 443 assert_eq!(result.pattern.vars[0].0, "__search_pattern_a");
@@ -477,30 +464,9 @@ mod tests {
477 } 464 }
478 465
479 #[test] 466 #[test]
480 fn parser_no_pattern_type() {
481 assert_eq!(parse_error_text("foo($a) ==>>"), "Parse error: Use $<name>:expr");
482 }
483
484 #[test]
485 fn parser_invalid_name() {
486 assert_eq!(
487 parse_error_text("foo($a+:expr) ==>>"),
488 "Parse error: Name can contain only alphanumerics and _"
489 );
490 }
491
492 #[test]
493 fn parser_invalid_type() {
494 assert_eq!(
495 parse_error_text("foo($a:ident) ==>>"),
496 "Parse error: Only $<name>:expr is supported"
497 );
498 }
499
500 #[test]
501 fn parser_repeated_name() { 467 fn parser_repeated_name() {
502 assert_eq!( 468 assert_eq!(
503 parse_error_text("foo($a:expr, $a:expr) ==>>"), 469 parse_error_text("foo($a, $a) ==>>"),
504 "Parse error: Name `a` repeats more than once" 470 "Parse error: Name `a` repeats more than once"
505 ); 471 );
506 } 472 }
@@ -517,7 +483,7 @@ mod tests {
517 483
518 #[test] 484 #[test]
519 fn parse_match_replace() { 485 fn parse_match_replace() {
520 let query: SsrQuery = "foo($x:expr) ==>> bar($x)".parse().unwrap(); 486 let query: SsrQuery = "foo($x) ==>> bar($x)".parse().unwrap();
521 let input = "fn main() { foo(1+2); }"; 487 let input = "fn main() { foo(1+2); }";
522 488
523 let code = SourceFile::parse(input).tree(); 489 let code = SourceFile::parse(input).tree();
@@ -549,7 +515,7 @@ mod tests {
549 #[test] 515 #[test]
550 fn ssr_function_to_method() { 516 fn ssr_function_to_method() {
551 assert_ssr_transform( 517 assert_ssr_transform(
552 "my_function($a:expr, $b:expr) ==>> ($a).my_method($b)", 518 "my_function($a, $b) ==>> ($a).my_method($b)",
553 "loop { my_function( other_func(x, y), z + w) }", 519 "loop { my_function( other_func(x, y), z + w) }",
554 "loop { (other_func(x, y)).my_method(z + w) }", 520 "loop { (other_func(x, y)).my_method(z + w) }",
555 ) 521 )
@@ -558,7 +524,7 @@ mod tests {
558 #[test] 524 #[test]
559 fn ssr_nested_function() { 525 fn ssr_nested_function() {
560 assert_ssr_transform( 526 assert_ssr_transform(
561 "foo($a:expr, $b:expr, $c:expr) ==>> bar($c, baz($a, $b))", 527 "foo($a, $b, $c) ==>> bar($c, baz($a, $b))",
562 "fn main { foo (x + value.method(b), x+y-z, true && false) }", 528 "fn main { foo (x + value.method(b), x+y-z, true && false) }",
563 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }", 529 "fn main { bar(true && false, baz(x + value.method(b), x+y-z)) }",
564 ) 530 )
@@ -567,7 +533,7 @@ mod tests {
567 #[test] 533 #[test]
568 fn ssr_expected_spacing() { 534 fn ssr_expected_spacing() {
569 assert_ssr_transform( 535 assert_ssr_transform(
570 "foo($x:expr) + bar() ==>> bar($x)", 536 "foo($x) + bar() ==>> bar($x)",
571 "fn main() { foo(5) + bar() }", 537 "fn main() { foo(5) + bar() }",
572 "fn main() { bar(5) }", 538 "fn main() { bar(5) }",
573 ); 539 );
@@ -576,7 +542,7 @@ mod tests {
576 #[test] 542 #[test]
577 fn ssr_with_extra_space() { 543 fn ssr_with_extra_space() {
578 assert_ssr_transform( 544 assert_ssr_transform(
579 "foo($x:expr ) + bar() ==>> bar($x)", 545 "foo($x ) + bar() ==>> bar($x)",
580 "fn main() { foo( 5 ) +bar( ) }", 546 "fn main() { foo( 5 ) +bar( ) }",
581 "fn main() { bar(5) }", 547 "fn main() { bar(5) }",
582 ); 548 );
@@ -585,7 +551,7 @@ mod tests {
585 #[test] 551 #[test]
586 fn ssr_keeps_nested_comment() { 552 fn ssr_keeps_nested_comment() {
587 assert_ssr_transform( 553 assert_ssr_transform(
588 "foo($x:expr) ==>> bar($x)", 554 "foo($x) ==>> bar($x)",
589 "fn main() { foo(other(5 /* using 5 */)) }", 555 "fn main() { foo(other(5 /* using 5 */)) }",
590 "fn main() { bar(other(5 /* using 5 */)) }", 556 "fn main() { bar(other(5 /* using 5 */)) }",
591 ) 557 )
@@ -594,7 +560,7 @@ mod tests {
594 #[test] 560 #[test]
595 fn ssr_keeps_comment() { 561 fn ssr_keeps_comment() {
596 assert_ssr_transform( 562 assert_ssr_transform(
597 "foo($x:expr) ==>> bar($x)", 563 "foo($x) ==>> bar($x)",
598 "fn main() { foo(5 /* using 5 */) }", 564 "fn main() { foo(5 /* using 5 */) }",
599 "fn main() { bar(5)/* using 5 */ }", 565 "fn main() { bar(5)/* using 5 */ }",
600 ) 566 )
@@ -603,7 +569,7 @@ mod tests {
603 #[test] 569 #[test]
604 fn ssr_struct_lit() { 570 fn ssr_struct_lit() {
605 assert_ssr_transform( 571 assert_ssr_transform(
606 "foo{a: $a:expr, b: $b:expr} ==>> foo::new($a, $b)", 572 "foo{a: $a, b: $b} ==>> foo::new($a, $b)",
607 "fn main() { foo{b:2, a:1} }", 573 "fn main() { foo{b:2, a:1} }",
608 "fn main() { foo::new(1, 2) }", 574 "fn main() { foo::new(1, 2) }",
609 ) 575 )
@@ -612,7 +578,7 @@ mod tests {
612 #[test] 578 #[test]
613 fn ssr_call_and_method_call() { 579 fn ssr_call_and_method_call() {
614 assert_ssr_transform( 580 assert_ssr_transform(
615 "foo::<'a>($a:expr, $b:expr)) ==>> foo2($a, $b)", 581 "foo::<'a>($a, $b)) ==>> foo2($a, $b)",
616 "fn main() { get().bar.foo::<'a>(1); }", 582 "fn main() { get().bar.foo::<'a>(1); }",
617 "fn main() { foo2(get().bar, 1); }", 583 "fn main() { foo2(get().bar, 1); }",
618 ) 584 )
@@ -621,7 +587,7 @@ mod tests {
621 #[test] 587 #[test]
622 fn ssr_method_call_and_call() { 588 fn ssr_method_call_and_call() {
623 assert_ssr_transform( 589 assert_ssr_transform(
624 "$o:expr.foo::<i32>($a:expr)) ==>> $o.foo2($a)", 590 "$o.foo::<i32>($a)) ==>> $o.foo2($a)",
625 "fn main() { X::foo::<i32>(x, 1); }", 591 "fn main() { X::foo::<i32>(x, 1); }",
626 "fn main() { x.foo2(1); }", 592 "fn main() { x.foo2(1); }",
627 ) 593 )
diff --git a/crates/ra_ide/src/status.rs b/crates/ra_ide/src/status.rs
index 5b7992920..45411b357 100644
--- a/crates/ra_ide/src/status.rs
+++ b/crates/ra_ide/src/status.rs
@@ -16,6 +16,7 @@ use ra_prof::{memory_usage, Bytes};
16use ra_syntax::{ast, Parse, SyntaxNode}; 16use ra_syntax::{ast, Parse, SyntaxNode};
17 17
18use crate::FileId; 18use crate::FileId;
19use rustc_hash::FxHashMap;
19 20
20fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { 21fn syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats {
21 db.query(ra_db::ParseQuery).entries::<SyntaxTreeStats>() 22 db.query(ra_db::ParseQuery).entries::<SyntaxTreeStats>()
@@ -123,20 +124,24 @@ struct LibrarySymbolsStats {
123 124
124impl fmt::Display for LibrarySymbolsStats { 125impl fmt::Display for LibrarySymbolsStats {
125 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 126 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
126 write!(fmt, "{} ({}) symbols", self.total, self.size,) 127 write!(fmt, "{} ({}) symbols", self.total, self.size)
127 } 128 }
128} 129}
129 130
130impl FromIterator<TableEntry<SourceRootId, Arc<SymbolIndex>>> for LibrarySymbolsStats { 131impl FromIterator<TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>
132 for LibrarySymbolsStats
133{
131 fn from_iter<T>(iter: T) -> LibrarySymbolsStats 134 fn from_iter<T>(iter: T) -> LibrarySymbolsStats
132 where 135 where
133 T: IntoIterator<Item = TableEntry<SourceRootId, Arc<SymbolIndex>>>, 136 T: IntoIterator<Item = TableEntry<(), Arc<FxHashMap<SourceRootId, SymbolIndex>>>>,
134 { 137 {
135 let mut res = LibrarySymbolsStats::default(); 138 let mut res = LibrarySymbolsStats::default();
136 for entry in iter { 139 for entry in iter {
137 let value = entry.value.unwrap(); 140 let value = entry.value.unwrap();
138 res.total += value.len(); 141 for symbols in value.values() {
139 res.size += value.memory_size(); 142 res.total += symbols.len();
143 res.size += symbols.memory_size();
144 }
140 } 145 }
141 res 146 res
142 } 147 }
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs
index ab45c364a..854b6cc6d 100644
--- a/crates/ra_ide/src/syntax_highlighting.rs
+++ b/crates/ra_ide/src/syntax_highlighting.rs
@@ -44,6 +44,7 @@ pub(crate) fn highlight(
44 db: &RootDatabase, 44 db: &RootDatabase,
45 file_id: FileId, 45 file_id: FileId,
46 range_to_highlight: Option<TextRange>, 46 range_to_highlight: Option<TextRange>,
47 syntactic_name_ref_highlighting: bool,
47) -> Vec<HighlightedRange> { 48) -> Vec<HighlightedRange> {
48 let _p = profile("highlight"); 49 let _p = profile("highlight");
49 let sema = Semantics::new(db); 50 let sema = Semantics::new(db);
@@ -104,6 +105,7 @@ pub(crate) fn highlight(
104 if let Some((highlight, binding_hash)) = highlight_element( 105 if let Some((highlight, binding_hash)) = highlight_element(
105 &sema, 106 &sema,
106 &mut bindings_shadow_count, 107 &mut bindings_shadow_count,
108 syntactic_name_ref_highlighting,
107 name.syntax().clone().into(), 109 name.syntax().clone().into(),
108 ) { 110 ) {
109 stack.add(HighlightedRange { 111 stack.add(HighlightedRange {
@@ -160,23 +162,25 @@ pub(crate) fn highlight(
160 // Check if macro takes a format string and remember it for highlighting later. 162 // Check if macro takes a format string and remember it for highlighting later.
161 // The macros that accept a format string expand to a compiler builtin macros 163 // The macros that accept a format string expand to a compiler builtin macros
162 // `format_args` and `format_args_nl`. 164 // `format_args` and `format_args_nl`.
163 if let Some(fmt_macro_call) = parent.parent().and_then(ast::MacroCall::cast) { 165 if let Some(name) = parent
164 if let Some(name) = 166 .parent()
165 fmt_macro_call.path().and_then(|p| p.segment()).and_then(|s| s.name_ref()) 167 .and_then(ast::MacroCall::cast)
166 { 168 .and_then(|mc| mc.path())
167 match name.text().as_str() { 169 .and_then(|p| p.segment())
168 "format_args" | "format_args_nl" => { 170 .and_then(|s| s.name_ref())
169 format_string = parent 171 {
170 .children_with_tokens() 172 match name.text().as_str() {
171 .filter(|t| t.kind() != WHITESPACE) 173 "format_args" | "format_args_nl" => {
172 .nth(1) 174 format_string = parent
173 .filter(|e| { 175 .children_with_tokens()
174 ast::String::can_cast(e.kind()) 176 .filter(|t| t.kind() != WHITESPACE)
175 || ast::RawString::can_cast(e.kind()) 177 .nth(1)
176 }) 178 .filter(|e| {
177 } 179 ast::String::can_cast(e.kind())
178 _ => {} 180 || ast::RawString::can_cast(e.kind())
181 })
179 } 182 }
183 _ => {}
180 } 184 }
181 } 185 }
182 186
@@ -198,15 +202,18 @@ pub(crate) fn highlight(
198 202
199 let is_format_string = format_string.as_ref() == Some(&element_to_highlight); 203 let is_format_string = format_string.as_ref() == Some(&element_to_highlight);
200 204
201 if let Some((highlight, binding_hash)) = 205 if let Some((highlight, binding_hash)) = highlight_element(
202 highlight_element(&sema, &mut bindings_shadow_count, element_to_highlight.clone()) 206 &sema,
203 { 207 &mut bindings_shadow_count,
208 syntactic_name_ref_highlighting,
209 element_to_highlight.clone(),
210 ) {
204 stack.add(HighlightedRange { range, highlight, binding_hash }); 211 stack.add(HighlightedRange { range, highlight, binding_hash });
205 if let Some(string) = 212 if let Some(string) =
206 element_to_highlight.as_token().cloned().and_then(ast::String::cast) 213 element_to_highlight.as_token().cloned().and_then(ast::String::cast)
207 { 214 {
208 stack.push();
209 if is_format_string { 215 if is_format_string {
216 stack.push();
210 string.lex_format_specifier(|piece_range, kind| { 217 string.lex_format_specifier(|piece_range, kind| {
211 if let Some(highlight) = highlight_format_specifier(kind) { 218 if let Some(highlight) = highlight_format_specifier(kind) {
212 stack.add(HighlightedRange { 219 stack.add(HighlightedRange {
@@ -216,13 +223,27 @@ pub(crate) fn highlight(
216 }); 223 });
217 } 224 }
218 }); 225 });
226 stack.pop();
227 }
228 // Highlight escape sequences
229 if let Some(char_ranges) = string.char_ranges() {
230 stack.push();
231 for (piece_range, _) in char_ranges.iter().filter(|(_, char)| char.is_ok()) {
232 if string.text()[piece_range.start().into()..].starts_with('\\') {
233 stack.add(HighlightedRange {
234 range: piece_range + range.start(),
235 highlight: HighlightTag::EscapeSequence.into(),
236 binding_hash: None,
237 });
238 }
239 }
240 stack.pop_and_inject(false);
219 } 241 }
220 stack.pop();
221 } else if let Some(string) = 242 } else if let Some(string) =
222 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast) 243 element_to_highlight.as_token().cloned().and_then(ast::RawString::cast)
223 { 244 {
224 stack.push();
225 if is_format_string { 245 if is_format_string {
246 stack.push();
226 string.lex_format_specifier(|piece_range, kind| { 247 string.lex_format_specifier(|piece_range, kind| {
227 if let Some(highlight) = highlight_format_specifier(kind) { 248 if let Some(highlight) = highlight_format_specifier(kind) {
228 stack.add(HighlightedRange { 249 stack.add(HighlightedRange {
@@ -232,8 +253,8 @@ pub(crate) fn highlight(
232 }); 253 });
233 } 254 }
234 }); 255 });
256 stack.pop();
235 } 257 }
236 stack.pop();
237 } 258 }
238 } 259 }
239 } 260 }
@@ -408,6 +429,7 @@ fn macro_call_range(macro_call: &ast::MacroCall) -> Option<TextRange> {
408fn highlight_element( 429fn highlight_element(
409 sema: &Semantics<RootDatabase>, 430 sema: &Semantics<RootDatabase>,
410 bindings_shadow_count: &mut FxHashMap<Name, u32>, 431 bindings_shadow_count: &mut FxHashMap<Name, u32>,
432 syntactic_name_ref_highlighting: bool,
411 element: SyntaxElement, 433 element: SyntaxElement,
412) -> Option<(Highlight, Option<u64>)> { 434) -> Option<(Highlight, Option<u64>)> {
413 let db = sema.db; 435 let db = sema.db;
@@ -461,6 +483,7 @@ fn highlight_element(
461 } 483 }
462 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(), 484 NameRefClass::FieldShorthand { .. } => HighlightTag::Field.into(),
463 }, 485 },
486 None if syntactic_name_ref_highlighting => highlight_name_ref_by_syntax(name_ref),
464 None => HighlightTag::UnresolvedReference.into(), 487 None => HighlightTag::UnresolvedReference.into(),
465 } 488 }
466 } 489 }
@@ -493,6 +516,9 @@ fn highlight_element(
493 h |= HighlightModifier::Unsafe; 516 h |= HighlightModifier::Unsafe;
494 h 517 h
495 } 518 }
519 T![!] if element.parent().and_then(ast::MacroCall::cast).is_some() => {
520 Highlight::new(HighlightTag::Macro)
521 }
496 522
497 k if k.is_keyword() => { 523 k if k.is_keyword() => {
498 let h = Highlight::new(HighlightTag::Keyword); 524 let h = Highlight::new(HighlightTag::Keyword);
@@ -609,3 +635,53 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight {
609 635
610 tag.into() 636 tag.into()
611} 637}
638
639fn highlight_name_ref_by_syntax(name: ast::NameRef) -> Highlight {
640 let default = HighlightTag::UnresolvedReference;
641
642 let parent = match name.syntax().parent() {
643 Some(it) => it,
644 _ => return default.into(),
645 };
646
647 let tag = match parent.kind() {
648 METHOD_CALL_EXPR => HighlightTag::Function,
649 FIELD_EXPR => HighlightTag::Field,
650 PATH_SEGMENT => {
651 let path = match parent.parent().and_then(ast::Path::cast) {
652 Some(it) => it,
653 _ => return default.into(),
654 };
655 let expr = match path.syntax().parent().and_then(ast::PathExpr::cast) {
656 Some(it) => it,
657 _ => {
658 // within path, decide whether it is module or adt by checking for uppercase name
659 return if name.text().chars().next().unwrap_or_default().is_uppercase() {
660 HighlightTag::Struct
661 } else {
662 HighlightTag::Module
663 }
664 .into();
665 }
666 };
667 let parent = match expr.syntax().parent() {
668 Some(it) => it,
669 None => return default.into(),
670 };
671
672 match parent.kind() {
673 CALL_EXPR => HighlightTag::Function,
674 _ => {
675 if name.text().chars().next().unwrap_or_default().is_uppercase() {
676 HighlightTag::Struct
677 } else {
678 HighlightTag::Constant
679 }
680 }
681 }
682 }
683 _ => default,
684 };
685
686 tag.into()
687}
diff --git a/crates/ra_ide/src/syntax_highlighting/html.rs b/crates/ra_ide/src/syntax_highlighting/html.rs
index 5bada6252..99b6b25ab 100644
--- a/crates/ra_ide/src/syntax_highlighting/html.rs
+++ b/crates/ra_ide/src/syntax_highlighting/html.rs
@@ -19,7 +19,7 @@ pub(crate) fn highlight_as_html(db: &RootDatabase, file_id: FileId, rainbow: boo
19 ) 19 )
20 } 20 }
21 21
22 let ranges = highlight(db, file_id, None); 22 let ranges = highlight(db, file_id, None, false);
23 let text = parse.tree().syntax().to_string(); 23 let text = parse.tree().syntax().to_string();
24 let mut prev_pos = TextSize::from(0); 24 let mut prev_pos = TextSize::from(0);
25 let mut buf = String::new(); 25 let mut buf = String::new();
@@ -84,6 +84,8 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
84.variable { color: #DCDCCC; } 84.variable { color: #DCDCCC; }
85.format_specifier { color: #CC696B; } 85.format_specifier { color: #CC696B; }
86.mutable { text-decoration: underline; } 86.mutable { text-decoration: underline; }
87.unresolved_reference { color: #FC5555; }
88.escape_sequence { color: #94BFF3; }
87 89
88.keyword { color: #F0DFAF; font-weight: bold; } 90.keyword { color: #F0DFAF; font-weight: bold; }
89.keyword.unsafe { color: #BC8383; font-weight: bold; } 91.keyword.unsafe { color: #BC8383; font-weight: bold; }
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
index 3575a0fc6..929a5cc5c 100644
--- a/crates/ra_ide/src/syntax_highlighting/injection.rs
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -53,6 +53,10 @@ pub(super) fn highlight_injection(
53/// Mapping from extracted documentation code to original code 53/// Mapping from extracted documentation code to original code
54type RangesMap = BTreeMap<TextSize, TextSize>; 54type RangesMap = BTreeMap<TextSize, TextSize>;
55 55
56const RUSTDOC_FENCE: &'static str = "```";
57const RUSTDOC_FENCE_TOKENS: &[&'static str] =
58 &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
59
56/// Extracts Rust code from documentation comments as well as a mapping from 60/// Extracts Rust code from documentation comments as well as a mapping from
57/// the extracted source code back to the original source ranges. 61/// the extracted source code back to the original source ranges.
58/// Lastly, a vector of new comment highlight ranges (spanning only the 62/// Lastly, a vector of new comment highlight ranges (spanning only the
@@ -67,6 +71,7 @@ pub(super) fn extract_doc_comments(
67 // Mapping from extracted documentation code to original code 71 // Mapping from extracted documentation code to original code
68 let mut range_mapping: RangesMap = BTreeMap::new(); 72 let mut range_mapping: RangesMap = BTreeMap::new();
69 let mut line_start = TextSize::try_from(prefix.len()).unwrap(); 73 let mut line_start = TextSize::try_from(prefix.len()).unwrap();
74 let mut is_codeblock = false;
70 let mut is_doctest = false; 75 let mut is_doctest = false;
71 // Replace the original, line-spanning comment ranges by new, only comment-prefix 76 // Replace the original, line-spanning comment ranges by new, only comment-prefix
72 // spanning comment ranges. 77 // spanning comment ranges.
@@ -76,8 +81,13 @@ pub(super) fn extract_doc_comments(
76 .filter_map(|el| el.into_token().and_then(ast::Comment::cast)) 81 .filter_map(|el| el.into_token().and_then(ast::Comment::cast))
77 .filter(|comment| comment.kind().doc.is_some()) 82 .filter(|comment| comment.kind().doc.is_some())
78 .filter(|comment| { 83 .filter(|comment| {
79 if comment.text().contains("```") { 84 if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
80 is_doctest = !is_doctest; 85 is_codeblock = !is_codeblock;
86 // Check whether code is rust by inspecting fence guards
87 let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
88 let is_rust =
89 guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
90 is_doctest = is_codeblock && is_rust;
81 false 91 false
82 } else { 92 } else {
83 is_doctest 93 is_doctest
@@ -137,7 +147,7 @@ pub(super) fn highlight_doc_comment(
137 let (analysis, tmp_file_id) = Analysis::from_single_file(text); 147 let (analysis, tmp_file_id) = Analysis::from_single_file(text);
138 148
139 stack.push(); 149 stack.push();
140 for mut h in analysis.highlight(tmp_file_id).unwrap() { 150 for mut h in analysis.with_db(|db| super::highlight(db, tmp_file_id, None, true)).unwrap() {
141 // Determine start offset and end offset in case of multi-line ranges 151 // Determine start offset and end offset in case of multi-line ranges
142 let mut start_offset = None; 152 let mut start_offset = None;
143 let mut end_offset = None; 153 let mut end_offset = None;
diff --git a/crates/ra_ide/src/syntax_highlighting/tags.rs b/crates/ra_ide/src/syntax_highlighting/tags.rs
index 94f466966..400d22fb6 100644
--- a/crates/ra_ide/src/syntax_highlighting/tags.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tags.rs
@@ -23,6 +23,7 @@ pub enum HighlightTag {
23 Constant, 23 Constant,
24 Enum, 24 Enum,
25 EnumVariant, 25 EnumVariant,
26 EscapeSequence,
26 Field, 27 Field,
27 FormatSpecifier, 28 FormatSpecifier,
28 Function, 29 Function,
@@ -71,6 +72,7 @@ impl HighlightTag {
71 HighlightTag::Constant => "constant", 72 HighlightTag::Constant => "constant",
72 HighlightTag::Enum => "enum", 73 HighlightTag::Enum => "enum",
73 HighlightTag::EnumVariant => "enum_variant", 74 HighlightTag::EnumVariant => "enum_variant",
75 HighlightTag::EscapeSequence => "escape_sequence",
74 HighlightTag::Field => "field", 76 HighlightTag::Field => "field",
75 HighlightTag::FormatSpecifier => "format_specifier", 77 HighlightTag::FormatSpecifier => "format_specifier",
76 HighlightTag::Function => "function", 78 HighlightTag::Function => "function",
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 021f8e7e2..b4d56a7a0 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -7,18 +7,6 @@ use crate::{
7 FileRange, TextRange, 7 FileRange, TextRange,
8}; 8};
9 9
10/// Highlights the code given by the `ra_fixture` argument, renders the
11/// result as HTML, and compares it with the HTML file given as `snapshot`.
12/// Note that the `snapshot` file is overwritten by the rendered HTML.
13fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
14 let (analysis, file_id) = single_file(ra_fixture);
15 let dst_file = project_dir().join(snapshot);
16 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
17 let expected_html = &read_text(&dst_file);
18 fs::write(dst_file, &actual_html).unwrap();
19 assert_eq_text!(expected_html, actual_html);
20}
21
22#[test] 10#[test]
23fn test_highlighting() { 11fn test_highlighting() {
24 check_highlighting( 12 check_highlighting(
@@ -55,6 +43,12 @@ def_fn! {
55 } 43 }
56} 44}
57 45
46macro_rules! noop {
47 ($expr:expr) => {
48 $expr
49 }
50}
51
58// comment 52// comment
59fn main() { 53fn main() {
60 println!("Hello, {}!", 92); 54 println!("Hello, {}!", 92);
@@ -73,10 +67,14 @@ fn main() {
73 // Do nothing 67 // Do nothing
74 } 68 }
75 69
70 noop!(noop!(1));
71
76 let mut x = 42; 72 let mut x = 42;
77 let y = &mut x; 73 let y = &mut x;
78 let z = &y; 74 let z = &y;
79 75
76 let Foo { x: z, y } = Foo { x: z, y };
77
80 y; 78 y;
81} 79}
82 80
@@ -248,6 +246,10 @@ fn main() {
248 246
249 println!(r"Hello, {}!", "world"); 247 println!(r"Hello, {}!", "world");
250 248
249 // escape sequences
250 println!("Hello\nWorld");
251 println!("\u{48}\x65\x6C\x6C\x6F World");
252
251 println!("{\x41}", A = 92); 253 println!("{\x41}", A = 92);
252 println!("{ничоси}", ничоси = 92); 254 println!("{ничоси}", ничоси = 92);
253}"# 255}"#
@@ -289,7 +291,13 @@ fn main() {
289fn test_highlight_doctest() { 291fn test_highlight_doctest() {
290 check_highlighting( 292 check_highlighting(
291 r#" 293 r#"
294struct Foo {
295 bar: bool,
296}
297
292impl Foo { 298impl Foo {
299 pub const bar: bool = true;
300
293 /// Constructs a new `Foo`. 301 /// Constructs a new `Foo`.
294 /// 302 ///
295 /// # Examples 303 /// # Examples
@@ -299,7 +307,7 @@ impl Foo {
299 /// let mut foo: Foo = Foo::new(); 307 /// let mut foo: Foo = Foo::new();
300 /// ``` 308 /// ```
301 pub const fn new() -> Foo { 309 pub const fn new() -> Foo {
302 Foo { } 310 Foo { bar: true }
303 } 311 }
304 312
305 /// `bar` method on `Foo`. 313 /// `bar` method on `Foo`.
@@ -307,11 +315,15 @@ impl Foo {
307 /// # Examples 315 /// # Examples
308 /// 316 ///
309 /// ``` 317 /// ```
318 /// use x::y;
319 ///
310 /// let foo = Foo::new(); 320 /// let foo = Foo::new();
311 /// 321 ///
312 /// // calls bar on foo 322 /// // calls bar on foo
313 /// assert!(foo.bar()); 323 /// assert!(foo.bar());
314 /// 324 ///
325 /// let bar = foo.bar || Foo::bar;
326 ///
315 /// /* multi-line 327 /// /* multi-line
316 /// comment */ 328 /// comment */
317 /// 329 ///
@@ -321,9 +333,13 @@ impl Foo {
321 /// 333 ///
322 /// ``` 334 /// ```
323 /// 335 ///
324 /// ``` 336 /// ```rust,no_run
325 /// let foobar = Foo::new().bar(); 337 /// let foobar = Foo::new().bar();
326 /// ``` 338 /// ```
339 ///
340 /// ```sh
341 /// echo 1
342 /// ```
327 pub fn foo(&self) -> bool { 343 pub fn foo(&self) -> bool {
328 true 344 true
329 } 345 }
@@ -332,5 +348,17 @@ impl Foo {
332 .trim(), 348 .trim(),
333 "crates/ra_ide/src/snapshots/highlight_doctest.html", 349 "crates/ra_ide/src/snapshots/highlight_doctest.html",
334 false, 350 false,
335 ) 351 );
352}
353
354/// Highlights the code given by the `ra_fixture` argument, renders the
355/// result as HTML, and compares it with the HTML file given as `snapshot`.
356/// Note that the `snapshot` file is overwritten by the rendered HTML.
357fn check_highlighting(ra_fixture: &str, snapshot: &str, rainbow: bool) {
358 let (analysis, file_id) = single_file(ra_fixture);
359 let dst_file = project_dir().join(snapshot);
360 let actual_html = &analysis.highlight_as_html(file_id, rainbow).unwrap();
361 let expected_html = &read_text(&dst_file);
362 fs::write(dst_file, &actual_html).unwrap();
363 assert_eq_text!(expected_html, actual_html);
336} 364}
diff --git a/crates/ra_ide/src/typing.rs b/crates/ra_ide/src/typing.rs
index 533306e2e..83776d2b6 100644
--- a/crates/ra_ide/src/typing.rs
+++ b/crates/ra_ide/src/typing.rs
@@ -21,7 +21,9 @@ use ra_ide_db::{source_change::SourceFileEdit, RootDatabase};
21use ra_syntax::{ 21use ra_syntax::{
22 algo::find_node_at_offset, 22 algo::find_node_at_offset,
23 ast::{self, AstToken}, 23 ast::{self, AstToken},
24 AstNode, SourceFile, TextRange, TextSize, 24 AstNode, SourceFile,
25 SyntaxKind::{FIELD_EXPR, METHOD_CALL_EXPR},
26 TextRange, TextSize,
25}; 27};
26 28
27use ra_text_edit::TextEdit; 29use ra_text_edit::TextEdit;
@@ -98,9 +100,12 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
98 }; 100 };
99 let current_indent_len = TextSize::of(current_indent); 101 let current_indent_len = TextSize::of(current_indent);
100 102
103 let parent = whitespace.syntax().parent();
101 // Make sure dot is a part of call chain 104 // Make sure dot is a part of call chain
102 let field_expr = ast::FieldExpr::cast(whitespace.syntax().parent())?; 105 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
103 let prev_indent = leading_indent(field_expr.syntax())?; 106 return None;
107 }
108 let prev_indent = leading_indent(&parent)?;
104 let target_indent = format!(" {}", prev_indent); 109 let target_indent = format!(" {}", prev_indent);
105 let target_indent_len = TextSize::of(&target_indent); 110 let target_indent_len = TextSize::of(&target_indent);
106 if current_indent_len == target_indent_len { 111 if current_indent_len == target_indent_len {
@@ -143,11 +148,11 @@ mod tests {
143 }) 148 })
144 } 149 }
145 150
146 fn type_char(char_typed: char, before: &str, after: &str) { 151 fn type_char(char_typed: char, ra_fixture_before: &str, ra_fixture_after: &str) {
147 let actual = do_type_char(char_typed, before) 152 let actual = do_type_char(char_typed, ra_fixture_before)
148 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed)); 153 .unwrap_or_else(|| panic!("typing `{}` did nothing", char_typed));
149 154
150 assert_eq_text!(after, &actual); 155 assert_eq_text!(ra_fixture_after, &actual);
151 } 156 }
152 157
153 fn type_char_noop(char_typed: char, before: &str) { 158 fn type_char_noop(char_typed: char, before: &str) {
@@ -249,6 +254,27 @@ fn foo() {
249 } 254 }
250 255
251 #[test] 256 #[test]
257 fn indents_new_chain_call_with_let() {
258 type_char(
259 '.',
260 r#"
261fn main() {
262 let _ = foo
263 <|>
264 bar()
265}
266"#,
267 r#"
268fn main() {
269 let _ = foo
270 .
271 bar()
272}
273"#,
274 );
275 }
276
277 #[test]
252 fn indents_continued_chain_call() { 278 fn indents_continued_chain_call() {
253 type_char( 279 type_char(
254 '.', 280 '.',
diff --git a/crates/ra_ide_db/src/change.rs b/crates/ra_ide_db/src/change.rs
index 5dbe1c1b7..78ee6a515 100644
--- a/crates/ra_ide_db/src/change.rs
+++ b/crates/ra_ide_db/src/change.rs
@@ -9,24 +9,16 @@ use ra_db::{
9 SourceRootId, 9 SourceRootId,
10}; 10};
11use ra_prof::{memory_usage, profile, Bytes}; 11use ra_prof::{memory_usage, profile, Bytes};
12use ra_syntax::SourceFile;
13#[cfg(not(feature = "wasm"))]
14use rayon::prelude::*;
15use rustc_hash::FxHashMap; 12use rustc_hash::FxHashMap;
16 13
17use crate::{ 14use crate::{symbol_index::SymbolsDatabase, RootDatabase};
18 symbol_index::{SymbolIndex, SymbolsDatabase},
19 DebugData, RootDatabase,
20};
21 15
22#[derive(Default)] 16#[derive(Default)]
23pub struct AnalysisChange { 17pub struct AnalysisChange {
24 new_roots: Vec<(SourceRootId, bool)>, 18 new_roots: Vec<(SourceRootId, bool)>,
25 roots_changed: FxHashMap<SourceRootId, RootChange>, 19 roots_changed: FxHashMap<SourceRootId, RootChange>,
26 files_changed: Vec<(FileId, Arc<String>)>, 20 files_changed: Vec<(FileId, Arc<String>)>,
27 libraries_added: Vec<LibraryData>,
28 crate_graph: Option<CrateGraph>, 21 crate_graph: Option<CrateGraph>,
29 debug_data: DebugData,
30} 22}
31 23
32impl fmt::Debug for AnalysisChange { 24impl fmt::Debug for AnalysisChange {
@@ -41,9 +33,6 @@ impl fmt::Debug for AnalysisChange {
41 if !self.files_changed.is_empty() { 33 if !self.files_changed.is_empty() {
42 d.field("files_changed", &self.files_changed.len()); 34 d.field("files_changed", &self.files_changed.len());
43 } 35 }
44 if !self.libraries_added.is_empty() {
45 d.field("libraries_added", &self.libraries_added.len());
46 }
47 if self.crate_graph.is_some() { 36 if self.crate_graph.is_some() {
48 d.field("crate_graph", &self.crate_graph); 37 d.field("crate_graph", &self.crate_graph);
49 } 38 }
@@ -80,17 +69,9 @@ impl AnalysisChange {
80 self.roots_changed.entry(root_id).or_default().removed.push(file); 69 self.roots_changed.entry(root_id).or_default().removed.push(file);
81 } 70 }
82 71
83 pub fn add_library(&mut self, data: LibraryData) {
84 self.libraries_added.push(data)
85 }
86
87 pub fn set_crate_graph(&mut self, graph: CrateGraph) { 72 pub fn set_crate_graph(&mut self, graph: CrateGraph) {
88 self.crate_graph = Some(graph); 73 self.crate_graph = Some(graph);
89 } 74 }
90
91 pub fn set_debug_root_path(&mut self, source_root_id: SourceRootId, path: String) {
92 self.debug_data.root_paths.insert(source_root_id, path);
93 }
94} 75}
95 76
96#[derive(Debug)] 77#[derive(Debug)]
@@ -121,47 +102,6 @@ impl fmt::Debug for RootChange {
121 } 102 }
122} 103}
123 104
124pub struct LibraryData {
125 root_id: SourceRootId,
126 root_change: RootChange,
127 symbol_index: SymbolIndex,
128}
129
130impl fmt::Debug for LibraryData {
131 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
132 f.debug_struct("LibraryData")
133 .field("root_id", &self.root_id)
134 .field("root_change", &self.root_change)
135 .field("n_symbols", &self.symbol_index.len())
136 .finish()
137 }
138}
139
140impl LibraryData {
141 pub fn prepare(
142 root_id: SourceRootId,
143 files: Vec<(FileId, RelativePathBuf, Arc<String>)>,
144 ) -> LibraryData {
145 let _p = profile("LibraryData::prepare");
146
147 #[cfg(not(feature = "wasm"))]
148 let iter = files.par_iter();
149 #[cfg(feature = "wasm")]
150 let iter = files.iter();
151
152 let symbol_index = SymbolIndex::for_files(iter.map(|(file_id, _, text)| {
153 let parse = SourceFile::parse(text);
154 (*file_id, parse)
155 }));
156 let mut root_change = RootChange::default();
157 root_change.added = files
158 .into_iter()
159 .map(|(file_id, path, text)| AddFile { file_id, path, text })
160 .collect();
161 LibraryData { root_id, root_change, symbol_index }
162 }
163}
164
165const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100); 105const GC_COOLDOWN: time::Duration = time::Duration::from_millis(100);
166 106
167impl RootDatabase { 107impl RootDatabase {
@@ -176,6 +116,7 @@ impl RootDatabase {
176 log::info!("apply_change {:?}", change); 116 log::info!("apply_change {:?}", change);
177 if !change.new_roots.is_empty() { 117 if !change.new_roots.is_empty() {
178 let mut local_roots = Vec::clone(&self.local_roots()); 118 let mut local_roots = Vec::clone(&self.local_roots());
119 let mut libraries = Vec::clone(&self.library_roots());
179 for (root_id, is_local) in change.new_roots { 120 for (root_id, is_local) in change.new_roots {
180 let root = 121 let root =
181 if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() }; 122 if is_local { SourceRoot::new_local() } else { SourceRoot::new_library() };
@@ -183,9 +124,12 @@ impl RootDatabase {
183 self.set_source_root_with_durability(root_id, Arc::new(root), durability); 124 self.set_source_root_with_durability(root_id, Arc::new(root), durability);
184 if is_local { 125 if is_local {
185 local_roots.push(root_id); 126 local_roots.push(root_id);
127 } else {
128 libraries.push(root_id)
186 } 129 }
187 } 130 }
188 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH); 131 self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
132 self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH);
189 } 133 }
190 134
191 for (root_id, root_change) in change.roots_changed { 135 for (root_id, root_change) in change.roots_changed {
@@ -197,29 +141,9 @@ impl RootDatabase {
197 let durability = durability(&source_root); 141 let durability = durability(&source_root);
198 self.set_file_text_with_durability(file_id, text, durability) 142 self.set_file_text_with_durability(file_id, text, durability)
199 } 143 }
200 if !change.libraries_added.is_empty() {
201 let mut libraries = Vec::clone(&self.library_roots());
202 for library in change.libraries_added {
203 libraries.push(library.root_id);
204 self.set_source_root_with_durability(
205 library.root_id,
206 Arc::new(SourceRoot::new_library()),
207 Durability::HIGH,
208 );
209 self.set_library_symbols_with_durability(
210 library.root_id,
211 Arc::new(library.symbol_index),
212 Durability::HIGH,
213 );
214 self.apply_root_change(library.root_id, library.root_change);
215 }
216 self.set_library_roots_with_durability(Arc::new(libraries), Durability::HIGH);
217 }
218 if let Some(crate_graph) = change.crate_graph { 144 if let Some(crate_graph) = change.crate_graph {
219 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH) 145 self.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH)
220 } 146 }
221
222 Arc::make_mut(&mut self.debug_data).merge(change.debug_data)
223 } 147 }
224 148
225 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) { 149 fn apply_root_change(&mut self, root_id: SourceRootId, root_change: RootChange) {
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs
index 1826f3ac6..3ef5e74b6 100644
--- a/crates/ra_ide_db/src/defs.rs
+++ b/crates/ra_ide_db/src/defs.rs
@@ -78,6 +78,7 @@ impl Definition {
78 } 78 }
79} 79}
80 80
81#[derive(Debug)]
81pub enum NameClass { 82pub enum NameClass {
82 Definition(Definition), 83 Definition(Definition),
83 /// `None` in `if let None = Some(82) {}` 84 /// `None` in `if let None = Some(82) {}`
@@ -131,9 +132,11 @@ pub fn classify_name(sema: &Semantics<RootDatabase>, name: &ast::Name) -> Option
131 let local = sema.to_def(&it)?; 132 let local = sema.to_def(&it)?;
132 133
133 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) { 134 if let Some(record_field_pat) = it.syntax().parent().and_then(ast::RecordFieldPat::cast) {
134 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) { 135 if record_field_pat.name_ref().is_none() {
135 let field = Definition::Field(field); 136 if let Some(field) = sema.resolve_record_field_pat(&record_field_pat) {
136 return Some(NameClass::FieldShorthand { local, field }); 137 let field = Definition::Field(field);
138 return Some(NameClass::FieldShorthand { local, field });
139 }
137 } 140 }
138 } 141 }
139 142
diff --git a/crates/ra_ide_db/src/imports_locator.rs b/crates/ra_ide_db/src/imports_locator.rs
index bf0d8db60..fff112e66 100644
--- a/crates/ra_ide_db/src/imports_locator.rs
+++ b/crates/ra_ide_db/src/imports_locator.rs
@@ -1,7 +1,7 @@
1//! This module contains an import search funcionality that is provided to the ra_assists module. 1//! This module contains an import search funcionality that is provided to the ra_assists module.
2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module. 2//! Later, this should be moved away to a separate crate that is accessible from the ra_assists module.
3 3
4use hir::{MacroDef, ModuleDef, Semantics}; 4use hir::{Crate, MacroDef, ModuleDef, Semantics};
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ast, AstNode, SyntaxKind::NAME}; 6use ra_syntax::{ast, AstNode, SyntaxKind::NAME};
7 7
@@ -11,44 +11,46 @@ use crate::{
11 RootDatabase, 11 RootDatabase,
12}; 12};
13use either::Either; 13use either::Either;
14use rustc_hash::FxHashSet;
14 15
15pub struct ImportsLocator<'a> { 16pub struct ImportsLocator<'a> {
16 sema: Semantics<'a, RootDatabase>, 17 sema: Semantics<'a, RootDatabase>,
18 krate: Crate,
17} 19}
18 20
19impl<'a> ImportsLocator<'a> { 21impl<'a> ImportsLocator<'a> {
20 pub fn new(db: &'a RootDatabase) -> Self { 22 pub fn new(db: &'a RootDatabase, krate: Crate) -> Self {
21 Self { sema: Semantics::new(db) } 23 Self { sema: Semantics::new(db), krate }
22 } 24 }
23 25
24 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> { 26 pub fn find_imports(&mut self, name_to_import: &str) -> Vec<Either<ModuleDef, MacroDef>> {
25 let _p = profile("search_for_imports"); 27 let _p = profile("search_for_imports");
26 let db = self.sema.db; 28 let db = self.sema.db;
27 29
28 let project_results = { 30 // Query dependencies first.
29 let mut query = Query::new(name_to_import.to_string()); 31 let mut candidates: FxHashSet<_> =
30 query.exact(); 32 self.krate.query_external_importables(db, name_to_import).collect();
31 query.limit(40); 33
32 symbol_index::world_symbols(db, query) 34 // Query the local crate using the symbol index.
33 }; 35 let local_results = {
34 let lib_results = {
35 let mut query = Query::new(name_to_import.to_string()); 36 let mut query = Query::new(name_to_import.to_string());
36 query.libs();
37 query.exact(); 37 query.exact();
38 query.limit(40); 38 query.limit(40);
39 symbol_index::world_symbols(db, query) 39 symbol_index::crate_symbols(db, self.krate.into(), query)
40 }; 40 };
41 41
42 project_results 42 candidates.extend(
43 .into_iter() 43 local_results
44 .chain(lib_results.into_iter()) 44 .into_iter()
45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate)) 45 .filter_map(|import_candidate| self.get_name_definition(&import_candidate))
46 .filter_map(|name_definition_to_import| match name_definition_to_import { 46 .filter_map(|name_definition_to_import| match name_definition_to_import {
47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)), 47 Definition::ModuleDef(module_def) => Some(Either::Left(module_def)),
48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)), 48 Definition::Macro(macro_def) => Some(Either::Right(macro_def)),
49 _ => None, 49 _ => None,
50 }) 50 }),
51 .collect() 51 );
52
53 candidates.into_iter().collect()
52 } 54 }
53 55
54 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> { 56 fn get_name_definition(&mut self, import_candidate: &FileSymbol) -> Option<Definition> {
diff --git a/crates/ra_ide_db/src/lib.rs b/crates/ra_ide_db/src/lib.rs
index 727d743b5..a808de4f1 100644
--- a/crates/ra_ide_db/src/lib.rs
+++ b/crates/ra_ide_db/src/lib.rs
@@ -17,9 +17,9 @@ use hir::db::{AstDatabase, DefDatabase};
17use ra_db::{ 17use ra_db::{
18 salsa::{self, Database, Durability}, 18 salsa::{self, Database, Durability},
19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase, 19 Canceled, CheckCanceled, CrateId, FileId, FileLoader, FileLoaderDelegate, SourceDatabase,
20 SourceRootId, Upcast, 20 Upcast,
21}; 21};
22use rustc_hash::FxHashMap; 22use rustc_hash::FxHashSet;
23 23
24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase}; 24use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
25 25
@@ -36,7 +36,6 @@ use crate::{line_index::LineIndex, symbol_index::SymbolsDatabase};
36#[derive(Debug)] 36#[derive(Debug)]
37pub struct RootDatabase { 37pub struct RootDatabase {
38 runtime: salsa::Runtime<RootDatabase>, 38 runtime: salsa::Runtime<RootDatabase>,
39 pub(crate) debug_data: Arc<DebugData>,
40 pub last_gc: crate::wasm_shims::Instant, 39 pub last_gc: crate::wasm_shims::Instant,
41 pub last_gc_check: crate::wasm_shims::Instant, 40 pub last_gc_check: crate::wasm_shims::Instant,
42} 41}
@@ -60,7 +59,7 @@ impl FileLoader for RootDatabase {
60 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> { 59 fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
61 FileLoaderDelegate(self).resolve_path(anchor, path) 60 FileLoaderDelegate(self).resolve_path(anchor, path)
62 } 61 }
63 fn relevant_crates(&self, file_id: FileId) -> Arc<Vec<CrateId>> { 62 fn relevant_crates(&self, file_id: FileId) -> Arc<FxHashSet<CrateId>> {
64 FileLoaderDelegate(self).relevant_crates(file_id) 63 FileLoaderDelegate(self).relevant_crates(file_id)
65 } 64 }
66} 65}
@@ -98,7 +97,6 @@ impl RootDatabase {
98 runtime: salsa::Runtime::default(), 97 runtime: salsa::Runtime::default(),
99 last_gc: crate::wasm_shims::Instant::now(), 98 last_gc: crate::wasm_shims::Instant::now(),
100 last_gc_check: crate::wasm_shims::Instant::now(), 99 last_gc_check: crate::wasm_shims::Instant::now(),
101 debug_data: Default::default(),
102 }; 100 };
103 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH); 101 db.set_crate_graph_with_durability(Default::default(), Durability::HIGH);
104 db.set_local_roots_with_durability(Default::default(), Durability::HIGH); 102 db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
@@ -121,7 +119,6 @@ impl salsa::ParallelDatabase for RootDatabase {
121 runtime: self.runtime.snapshot(self), 119 runtime: self.runtime.snapshot(self),
122 last_gc: self.last_gc, 120 last_gc: self.last_gc,
123 last_gc_check: self.last_gc_check, 121 last_gc_check: self.last_gc_check,
124 debug_data: Arc::clone(&self.debug_data),
125 }) 122 })
126 } 123 }
127} 124}
@@ -135,14 +132,3 @@ fn line_index(db: &impl LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
135 let text = db.file_text(file_id); 132 let text = db.file_text(file_id);
136 Arc::new(LineIndex::new(&*text)) 133 Arc::new(LineIndex::new(&*text))
137} 134}
138
139#[derive(Debug, Default, Clone)]
140pub(crate) struct DebugData {
141 pub(crate) root_paths: FxHashMap<SourceRootId, String>,
142}
143
144impl DebugData {
145 pub(crate) fn merge(&mut self, other: DebugData) {
146 self.root_paths.extend(other.root_paths.into_iter());
147 }
148}
diff --git a/crates/ra_ide_db/src/source_change.rs b/crates/ra_ide_db/src/source_change.rs
index f40ae8304..0bbd3c3e5 100644
--- a/crates/ra_ide_db/src/source_change.rs
+++ b/crates/ra_ide_db/src/source_change.rs
@@ -3,7 +3,7 @@
3//! 3//!
4//! It can be viewed as a dual for `AnalysisChange`. 4//! It can be viewed as a dual for `AnalysisChange`.
5 5
6use ra_db::{FileId, RelativePathBuf, SourceRootId}; 6use ra_db::FileId;
7use ra_text_edit::TextEdit; 7use ra_text_edit::TextEdit;
8 8
9#[derive(Debug, Clone)] 9#[derive(Debug, Clone)]
@@ -44,8 +44,8 @@ impl From<Vec<SourceFileEdit>> for SourceChange {
44 44
45#[derive(Debug, Clone)] 45#[derive(Debug, Clone)]
46pub enum FileSystemEdit { 46pub enum FileSystemEdit {
47 CreateFile { source_root: SourceRootId, path: RelativePathBuf }, 47 CreateFile { anchor: FileId, dst: String },
48 MoveFile { src: FileId, dst_source_root: SourceRootId, dst_path: RelativePathBuf }, 48 MoveFile { src: FileId, anchor: FileId, dst: String },
49} 49}
50 50
51impl From<FileSystemEdit> for SourceChange { 51impl From<FileSystemEdit> for SourceChange {
diff --git a/crates/ra_ide_db/src/symbol_index.rs b/crates/ra_ide_db/src/symbol_index.rs
index acc31fe3b..25c99813f 100644
--- a/crates/ra_ide_db/src/symbol_index.rs
+++ b/crates/ra_ide_db/src/symbol_index.rs
@@ -29,18 +29,20 @@ use std::{
29}; 29};
30 30
31use fst::{self, Streamer}; 31use fst::{self, Streamer};
32use hir::db::DefDatabase;
32use ra_db::{ 33use ra_db::{
33 salsa::{self, ParallelDatabase}, 34 salsa::{self, ParallelDatabase},
34 FileId, SourceDatabaseExt, SourceRootId, 35 CrateId, FileId, SourceDatabaseExt, SourceRootId,
35}; 36};
37use ra_prof::profile;
36use ra_syntax::{ 38use ra_syntax::{
37 ast::{self, NameOwner}, 39 ast::{self, NameOwner},
38 match_ast, AstNode, Parse, SmolStr, SourceFile, 40 match_ast, AstNode, Parse, SmolStr, SourceFile,
39 SyntaxKind::{self, *}, 41 SyntaxKind::{self, *},
40 SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent, 42 SyntaxNode, SyntaxNodePtr, TextRange, WalkEvent,
41}; 43};
42#[cfg(not(feature = "wasm"))]
43use rayon::prelude::*; 44use rayon::prelude::*;
45use rustc_hash::FxHashMap;
44 46
45use crate::RootDatabase; 47use crate::RootDatabase;
46 48
@@ -85,10 +87,9 @@ impl Query {
85} 87}
86 88
87#[salsa::query_group(SymbolsDatabaseStorage)] 89#[salsa::query_group(SymbolsDatabaseStorage)]
88pub trait SymbolsDatabase: hir::db::HirDatabase { 90pub trait SymbolsDatabase: hir::db::HirDatabase + SourceDatabaseExt + ParallelDatabase {
89 fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>; 91 fn file_symbols(&self, file_id: FileId) -> Arc<SymbolIndex>;
90 #[salsa::input] 92 fn library_symbols(&self) -> Arc<FxHashMap<SourceRootId, SymbolIndex>>;
91 fn library_symbols(&self, id: SourceRootId) -> Arc<SymbolIndex>;
92 /// The set of "local" (that is, from the current workspace) roots. 93 /// The set of "local" (that is, from the current workspace) roots.
93 /// Files in local roots are assumed to change frequently. 94 /// Files in local roots are assumed to change frequently.
94 #[salsa::input] 95 #[salsa::input]
@@ -99,6 +100,29 @@ pub trait SymbolsDatabase: hir::db::HirDatabase {
99 fn library_roots(&self) -> Arc<Vec<SourceRootId>>; 100 fn library_roots(&self) -> Arc<Vec<SourceRootId>>;
100} 101}
101 102
103fn library_symbols(
104 db: &(impl SymbolsDatabase + ParallelDatabase),
105) -> Arc<FxHashMap<SourceRootId, SymbolIndex>> {
106 let _p = profile("library_symbols");
107
108 let roots = db.library_roots();
109 let res = roots
110 .iter()
111 .map(|&root_id| {
112 let root = db.source_root(root_id);
113 let files = root
114 .walk()
115 .map(|it| (it, SourceDatabaseExt::file_text(db, it)))
116 .collect::<Vec<_>>();
117 let symbol_index = SymbolIndex::for_files(
118 files.into_par_iter().map(|(file, text)| (file, SourceFile::parse(&text))),
119 );
120 (root_id, symbol_index)
121 })
122 .collect();
123 Arc::new(res)
124}
125
102fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> { 126fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex> {
103 db.check_canceled(); 127 db.check_canceled();
104 let parse = db.parse(file_id); 128 let parse = db.parse(file_id);
@@ -110,6 +134,14 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
110 Arc::new(SymbolIndex::new(symbols)) 134 Arc::new(SymbolIndex::new(symbols))
111} 135}
112 136
137/// Need to wrap Snapshot to provide `Clone` impl for `map_with`
138struct Snap<DB>(DB);
139impl<DB: ParallelDatabase> Clone for Snap<salsa::Snapshot<DB>> {
140 fn clone(&self) -> Snap<salsa::Snapshot<DB>> {
141 Snap(self.0.snapshot())
142 }
143}
144
113// Feature: Workspace Symbol 145// Feature: Workspace Symbol
114// 146//
115// Uses fuzzy-search to find types, modules and functions by name across your 147// Uses fuzzy-search to find types, modules and functions by name across your
@@ -132,27 +164,13 @@ fn file_symbols(db: &impl SymbolsDatabase, file_id: FileId) -> Arc<SymbolIndex>
132// | VS Code | kbd:[Ctrl+T] 164// | VS Code | kbd:[Ctrl+T]
133// |=== 165// |===
134pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> { 166pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
135 /// Need to wrap Snapshot to provide `Clone` impl for `map_with` 167 let _p = ra_prof::profile("world_symbols").detail(|| query.query.clone());
136 struct Snap(salsa::Snapshot<RootDatabase>);
137 impl Clone for Snap {
138 fn clone(&self) -> Snap {
139 Snap(self.0.snapshot())
140 }
141 }
142 168
143 let buf: Vec<Arc<SymbolIndex>> = if query.libs { 169 let tmp1;
144 let snap = Snap(db.snapshot()); 170 let tmp2;
145 #[cfg(not(feature = "wasm"))] 171 let buf: Vec<&SymbolIndex> = if query.libs {
146 let buf = db 172 tmp1 = db.library_symbols();
147 .library_roots() 173 tmp1.values().collect()
148 .par_iter()
149 .map_with(snap, |db, &lib_id| db.0.library_symbols(lib_id))
150 .collect();
151
152 #[cfg(feature = "wasm")]
153 let buf = db.library_roots().iter().map(|&lib_id| snap.0.library_symbols(lib_id)).collect();
154
155 buf
156 } else { 174 } else {
157 let mut files = Vec::new(); 175 let mut files = Vec::new();
158 for &root in db.local_roots().iter() { 176 for &root in db.local_roots().iter() {
@@ -161,15 +179,36 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec<FileSymbol> {
161 } 179 }
162 180
163 let snap = Snap(db.snapshot()); 181 let snap = Snap(db.snapshot());
164 #[cfg(not(feature = "wasm"))] 182 tmp2 = files
165 let buf = 183 .par_iter()
166 files.par_iter().map_with(snap, |db, &file_id| db.0.file_symbols(file_id)).collect(); 184 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
185 .collect::<Vec<_>>();
186 tmp2.iter().map(|it| &**it).collect()
187 };
188 query.search(&buf)
189}
167 190
168 #[cfg(feature = "wasm")] 191pub fn crate_symbols(db: &RootDatabase, krate: CrateId, query: Query) -> Vec<FileSymbol> {
169 let buf = files.iter().map(|&file_id| snap.0.file_symbols(file_id)).collect(); 192 // FIXME(#4842): This now depends on CrateDefMap, why not build the entire symbol index from
193 // that instead?
194
195 let def_map = db.crate_def_map(krate);
196 let mut files = Vec::new();
197 let mut modules = vec![def_map.root];
198 while let Some(module) = modules.pop() {
199 let data = &def_map[module];
200 files.extend(data.origin.file_id());
201 modules.extend(data.children.values());
202 }
203
204 let snap = Snap(db.snapshot());
205
206 let buf = files
207 .par_iter()
208 .map_with(snap, |db, &file_id| db.0.file_symbols(file_id))
209 .collect::<Vec<_>>();
210 let buf = buf.iter().map(|it| &**it).collect::<Vec<_>>();
170 211
171 buf
172 };
173 query.search(&buf) 212 query.search(&buf)
174} 213}
175 214
@@ -215,12 +254,8 @@ impl SymbolIndex {
215 lhs_chars.cmp(rhs_chars) 254 lhs_chars.cmp(rhs_chars)
216 } 255 }
217 256
218 #[cfg(not(feature = "wasm"))]
219 symbols.par_sort_by(cmp); 257 symbols.par_sort_by(cmp);
220 258
221 #[cfg(feature = "wasm")]
222 symbols.sort_by(cmp);
223
224 let mut builder = fst::MapBuilder::memory(); 259 let mut builder = fst::MapBuilder::memory();
225 260
226 let mut last_batch_start = 0; 261 let mut last_batch_start = 0;
@@ -254,7 +289,6 @@ impl SymbolIndex {
254 self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>() 289 self.map.as_fst().size() + self.symbols.len() * mem::size_of::<FileSymbol>()
255 } 290 }
256 291
257 #[cfg(not(feature = "wasm"))]
258 pub(crate) fn for_files( 292 pub(crate) fn for_files(
259 files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>, 293 files: impl ParallelIterator<Item = (FileId, Parse<ast::SourceFile>)>,
260 ) -> SymbolIndex { 294 ) -> SymbolIndex {
@@ -264,16 +298,6 @@ impl SymbolIndex {
264 SymbolIndex::new(symbols) 298 SymbolIndex::new(symbols)
265 } 299 }
266 300
267 #[cfg(feature = "wasm")]
268 pub(crate) fn for_files(
269 files: impl Iterator<Item = (FileId, Parse<ast::SourceFile>)>,
270 ) -> SymbolIndex {
271 let symbols = files
272 .flat_map(|(file_id, file)| source_file_to_file_symbols(&file.tree(), file_id))
273 .collect::<Vec<_>>();
274 SymbolIndex::new(symbols)
275 }
276
277 fn range_to_map_value(start: usize, end: usize) -> u64 { 301 fn range_to_map_value(start: usize, end: usize) -> u64 {
278 debug_assert![start <= (std::u32::MAX as usize)]; 302 debug_assert![start <= (std::u32::MAX as usize)];
279 debug_assert![end <= (std::u32::MAX as usize)]; 303 debug_assert![end <= (std::u32::MAX as usize)];
@@ -289,7 +313,7 @@ impl SymbolIndex {
289} 313}
290 314
291impl Query { 315impl Query {
292 pub(crate) fn search(self, indices: &[Arc<SymbolIndex>]) -> Vec<FileSymbol> { 316 pub(crate) fn search(self, indices: &[&SymbolIndex]) -> Vec<FileSymbol> {
293 let mut op = fst::map::OpBuilder::new(); 317 let mut op = fst::map::OpBuilder::new();
294 for file_symbols in indices.iter() { 318 for file_symbols in indices.iter() {
295 let automaton = fst::automaton::Subsequence::new(&self.lowercased); 319 let automaton = fst::automaton::Subsequence::new(&self.lowercased);
@@ -298,9 +322,6 @@ impl Query {
298 let mut stream = op.union(); 322 let mut stream = op.union();
299 let mut res = Vec::new(); 323 let mut res = Vec::new();
300 while let Some((_, indexed_values)) = stream.next() { 324 while let Some((_, indexed_values)) = stream.next() {
301 if res.len() >= self.limit {
302 break;
303 }
304 for indexed_value in indexed_values { 325 for indexed_value in indexed_values {
305 let symbol_index = &indices[indexed_value.index]; 326 let symbol_index = &indices[indexed_value.index];
306 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value); 327 let (start, end) = SymbolIndex::map_value_to_range(indexed_value.value);
@@ -312,7 +333,11 @@ impl Query {
312 if self.exact && symbol.name != self.query { 333 if self.exact && symbol.name != self.query {
313 continue; 334 continue;
314 } 335 }
336
315 res.push(symbol.clone()); 337 res.push(symbol.clone());
338 if res.len() >= self.limit {
339 return res;
340 }
316 } 341 }
317 } 342 }
318 } 343 }
diff --git a/crates/ra_parser/src/grammar/expressions.rs b/crates/ra_parser/src/grammar/expressions.rs
index d6e8df32a..6e72eea66 100644
--- a/crates/ra_parser/src/grammar/expressions.rs
+++ b/crates/ra_parser/src/grammar/expressions.rs
@@ -50,10 +50,8 @@ fn expr_no_struct(p: &mut Parser) {
50} 50}
51 51
52fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool { 52fn is_expr_stmt_attr_allowed(kind: SyntaxKind) -> bool {
53 match kind { 53 let forbid = matches!(kind, BIN_EXPR | RANGE_EXPR);
54 BIN_EXPR | RANGE_EXPR | IF_EXPR => false, 54 !forbid
55 _ => true,
56 }
57} 55}
58 56
59pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) { 57pub(super) fn stmt(p: &mut Parser, with_semi: StmtWithSemi) {
diff --git a/crates/ra_parser/src/grammar/type_params.rs b/crates/ra_parser/src/grammar/type_params.rs
index 50e4900c3..325d566ad 100644
--- a/crates/ra_parser/src/grammar/type_params.rs
+++ b/crates/ra_parser/src/grammar/type_params.rs
@@ -191,10 +191,14 @@ fn where_predicate(p: &mut Parser) {
191 } 191 }
192 _ => { 192 _ => {
193 // test where_pred_for 193 // test where_pred_for
194 // fn test<F>() 194 // fn for_trait<F>()
195 // where 195 // where
196 // for<'a> F: Fn(&'a str) 196 // for<'a> F: Fn(&'a str)
197 // { } 197 // { }
198 if p.at(T![for]) {
199 types::for_binder(p);
200 }
201
198 types::type_(p); 202 types::type_(p);
199 203
200 if p.at(T![:]) { 204 if p.at(T![:]) {
diff --git a/crates/ra_parser/src/grammar/types.rs b/crates/ra_parser/src/grammar/types.rs
index fe1a039cb..9e8e3bd97 100644
--- a/crates/ra_parser/src/grammar/types.rs
+++ b/crates/ra_parser/src/grammar/types.rs
@@ -216,19 +216,21 @@ pub(super) fn for_binder(p: &mut Parser) {
216 216
217// test for_type 217// test for_type
218// type A = for<'a> fn() -> (); 218// type A = for<'a> fn() -> ();
219// fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 219// type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
220// fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 220// type Obj = for<'a> PartialEq<&'a i32>;
221// fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
222pub(super) fn for_type(p: &mut Parser) { 221pub(super) fn for_type(p: &mut Parser) {
223 assert!(p.at(T![for])); 222 assert!(p.at(T![for]));
224 let m = p.start(); 223 let m = p.start();
225 for_binder(p); 224 for_binder(p);
226 match p.current() { 225 match p.current() {
227 T![fn] | T![unsafe] | T![extern] => fn_pointer_type(p), 226 T![fn] | T![unsafe] | T![extern] => {}
228 T![&] => reference_type(p), 227 // OK: legacy trait object format
229 _ if paths::is_path_start(p) => path_type_(p, false), 228 _ if paths::is_use_path_start(p) => {}
230 _ => p.error("expected a path"), 229 _ => {
230 p.error("expected a function pointer or path");
231 }
231 } 232 }
233 type_no_bounds(p);
232 m.complete(p, FOR_TYPE); 234 m.complete(p, FOR_TYPE);
233} 235}
234 236
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 47fa34ddf..9541362f5 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -29,13 +29,7 @@ pub enum ProjectWorkspace {
29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. 29 /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`.
30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot }, 30 Cargo { cargo: CargoWorkspace, sysroot: Sysroot },
31 /// Project workspace was manually specified using a `rust-project.json` file. 31 /// Project workspace was manually specified using a `rust-project.json` file.
32 Json { project: JsonProject }, 32 Json { project: JsonProject, project_location: PathBuf },
33}
34
35impl From<JsonProject> for ProjectWorkspace {
36 fn from(project: JsonProject) -> ProjectWorkspace {
37 ProjectWorkspace::Json { project }
38 }
39} 33}
40 34
41/// `PackageRoot` describes a package root folder. 35/// `PackageRoot` describes a package root folder.
@@ -47,17 +41,21 @@ pub struct PackageRoot {
47 path: PathBuf, 41 path: PathBuf,
48 /// Is a member of the current workspace 42 /// Is a member of the current workspace
49 is_member: bool, 43 is_member: bool,
44 out_dir: Option<PathBuf>,
50} 45}
51impl PackageRoot { 46impl PackageRoot {
52 pub fn new_member(path: PathBuf) -> PackageRoot { 47 pub fn new_member(path: PathBuf) -> PackageRoot {
53 Self { path, is_member: true } 48 Self { path, is_member: true, out_dir: None }
54 } 49 }
55 pub fn new_non_member(path: PathBuf) -> PackageRoot { 50 pub fn new_non_member(path: PathBuf) -> PackageRoot {
56 Self { path, is_member: false } 51 Self { path, is_member: false, out_dir: None }
57 } 52 }
58 pub fn path(&self) -> &Path { 53 pub fn path(&self) -> &Path {
59 &self.path 54 &self.path
60 } 55 }
56 pub fn out_dir(&self) -> Option<&Path> {
57 self.out_dir.as_deref()
58 }
61 pub fn is_member(&self) -> bool { 59 pub fn is_member(&self) -> bool {
62 self.is_member 60 self.is_member
63 } 61 }
@@ -160,10 +158,15 @@ impl ProjectWorkspace {
160 format!("Failed to open json file {}", project_json.display()) 158 format!("Failed to open json file {}", project_json.display())
161 })?; 159 })?;
162 let reader = BufReader::new(file); 160 let reader = BufReader::new(file);
161 let project_location = match project_json.parent() {
162 Some(parent) => PathBuf::from(parent),
163 None => PathBuf::new(),
164 };
163 ProjectWorkspace::Json { 165 ProjectWorkspace::Json {
164 project: from_reader(reader).with_context(|| { 166 project: from_reader(reader).with_context(|| {
165 format!("Failed to deserialize json file {}", project_json.display()) 167 format!("Failed to deserialize json file {}", project_json.display())
166 })?, 168 })?,
169 project_location: project_location,
167 } 170 }
168 } 171 }
169 ProjectManifest::CargoToml(cargo_toml) => { 172 ProjectManifest::CargoToml(cargo_toml) => {
@@ -196,14 +199,17 @@ impl ProjectWorkspace {
196 /// the root is a member of the current workspace 199 /// the root is a member of the current workspace
197 pub fn to_roots(&self) -> Vec<PackageRoot> { 200 pub fn to_roots(&self) -> Vec<PackageRoot> {
198 match self { 201 match self {
199 ProjectWorkspace::Json { project } => { 202 ProjectWorkspace::Json { project, project_location } => project
200 project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect() 203 .roots
201 } 204 .iter()
205 .map(|r| PackageRoot::new_member(project_location.join(&r.path)))
206 .collect(),
202 ProjectWorkspace::Cargo { cargo, sysroot } => cargo 207 ProjectWorkspace::Cargo { cargo, sysroot } => cargo
203 .packages() 208 .packages()
204 .map(|pkg| PackageRoot { 209 .map(|pkg| PackageRoot {
205 path: cargo[pkg].root().to_path_buf(), 210 path: cargo[pkg].root().to_path_buf(),
206 is_member: cargo[pkg].is_member, 211 is_member: cargo[pkg].is_member,
212 out_dir: cargo[pkg].out_dir.clone(),
207 }) 213 })
208 .chain(sysroot.crates().map(|krate| { 214 .chain(sysroot.crates().map(|krate| {
209 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf()) 215 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
@@ -212,20 +218,9 @@ impl ProjectWorkspace {
212 } 218 }
213 } 219 }
214 220
215 pub fn out_dirs(&self) -> Vec<PathBuf> {
216 match self {
217 ProjectWorkspace::Json { project } => {
218 project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect()
219 }
220 ProjectWorkspace::Cargo { cargo, sysroot: _ } => {
221 cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect()
222 }
223 }
224 }
225
226 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { 221 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
227 match self { 222 match self {
228 ProjectWorkspace::Json { project } => project 223 ProjectWorkspace::Json { project, .. } => project
229 .crates 224 .crates
230 .iter() 225 .iter()
231 .filter_map(|krate| krate.proc_macro_dylib_path.as_ref()) 226 .filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
@@ -241,7 +236,7 @@ impl ProjectWorkspace {
241 236
242 pub fn n_packages(&self) -> usize { 237 pub fn n_packages(&self) -> usize {
243 match self { 238 match self {
244 ProjectWorkspace::Json { project } => project.crates.len(), 239 ProjectWorkspace::Json { project, .. } => project.crates.len(),
245 ProjectWorkspace::Cargo { cargo, sysroot } => { 240 ProjectWorkspace::Cargo { cargo, sysroot } => {
246 cargo.packages().len() + sysroot.crates().len() 241 cargo.packages().len() + sysroot.crates().len()
247 } 242 }
@@ -257,13 +252,14 @@ impl ProjectWorkspace {
257 ) -> CrateGraph { 252 ) -> CrateGraph {
258 let mut crate_graph = CrateGraph::default(); 253 let mut crate_graph = CrateGraph::default();
259 match self { 254 match self {
260 ProjectWorkspace::Json { project } => { 255 ProjectWorkspace::Json { project, project_location } => {
261 let crates: FxHashMap<_, _> = project 256 let crates: FxHashMap<_, _> = project
262 .crates 257 .crates
263 .iter() 258 .iter()
264 .enumerate() 259 .enumerate()
265 .filter_map(|(seq_index, krate)| { 260 .filter_map(|(seq_index, krate)| {
266 let file_id = load(&krate.root_module)?; 261 let file_path = project_location.join(&krate.root_module);
262 let file_id = load(&file_path)?;
267 let edition = match krate.edition { 263 let edition = match krate.edition {
268 json_project::Edition::Edition2015 => Edition::Edition2015, 264 json_project::Edition::Edition2015 => Edition::Edition2015,
269 json_project::Edition::Edition2018 => Edition::Edition2018, 265 json_project::Edition::Edition2018 => Edition::Edition2018,
@@ -546,7 +542,7 @@ impl ProjectWorkspace {
546 ProjectWorkspace::Cargo { cargo, .. } => { 542 ProjectWorkspace::Cargo { cargo, .. } => {
547 Some(cargo.workspace_root()).filter(|root| path.starts_with(root)) 543 Some(cargo.workspace_root()).filter(|root| path.starts_with(root))
548 } 544 }
549 ProjectWorkspace::Json { project: JsonProject { roots, .. } } => roots 545 ProjectWorkspace::Json { project: JsonProject { roots, .. }, .. } => roots
550 .iter() 546 .iter()
551 .find(|root| path.starts_with(&root.path)) 547 .find(|root| path.starts_with(&root.path))
552 .map(|root| root.path.as_ref()), 548 .map(|root| root.path.as_ref()),
@@ -594,7 +590,7 @@ fn get_rustc_cfg_options(target: Option<&str>) -> CfgOptions {
594 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e), 590 Err(e) => log::error!("failed to get rustc cfgs: {:#}", e),
595 } 591 }
596 592
597 cfg_options.insert_atom("debug_assertion".into()); 593 cfg_options.insert_atom("debug_assertions".into());
598 594
599 cfg_options 595 cfg_options
600} 596}
diff --git a/crates/ra_syntax/src/algo.rs b/crates/ra_syntax/src/algo.rs
index 664894d1f..f7a885eb3 100644
--- a/crates/ra_syntax/src/algo.rs
+++ b/crates/ra_syntax/src/algo.rs
@@ -290,6 +290,11 @@ impl<'a> SyntaxRewriter<'a> {
290 N::cast(self.rewrite(node.syntax())).unwrap() 290 N::cast(self.rewrite(node.syntax())).unwrap()
291 } 291 }
292 292
293 /// Returns a node that encompasses all replacements to be done by this rewriter.
294 ///
295 /// Passing the returned node to `rewrite` will apply all replacements queued up in `self`.
296 ///
297 /// Returns `None` when there are no replacements.
293 pub fn rewrite_root(&self) -> Option<SyntaxNode> { 298 pub fn rewrite_root(&self) -> Option<SyntaxNode> {
294 assert!(self.f.is_none()); 299 assert!(self.f.is_none());
295 self.replacements 300 self.replacements
@@ -298,6 +303,9 @@ impl<'a> SyntaxRewriter<'a> {
298 SyntaxElement::Node(it) => it.clone(), 303 SyntaxElement::Node(it) => it.clone(),
299 SyntaxElement::Token(it) => it.parent(), 304 SyntaxElement::Token(it) => it.parent(),
300 }) 305 })
306 // If we only have one replacement, we must return its parent node, since `rewrite` does
307 // not replace the node passed to it.
308 .map(|it| it.parent().unwrap_or(it))
301 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap()) 309 .fold1(|a, b| least_common_ancestor(&a, &b).unwrap())
302 } 310 }
303 311
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs
index eddc807d5..9d02aeef3 100644
--- a/crates/ra_syntax/src/ast.rs
+++ b/crates/ra_syntax/src/ast.rs
@@ -285,6 +285,8 @@ where
285 let pred = predicates.next().unwrap(); 285 let pred = predicates.next().unwrap();
286 let mut bounds = pred.type_bound_list().unwrap().bounds(); 286 let mut bounds = pred.type_bound_list().unwrap().bounds();
287 287
288 assert!(pred.for_token().is_none());
289 assert!(pred.type_param_list().is_none());
288 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string()); 290 assert_eq!("T", pred.type_ref().unwrap().syntax().text().to_string());
289 assert_bound("Clone", bounds.next()); 291 assert_bound("Clone", bounds.next());
290 assert_bound("Copy", bounds.next()); 292 assert_bound("Copy", bounds.next());
@@ -322,6 +324,8 @@ where
322 let pred = predicates.next().unwrap(); 324 let pred = predicates.next().unwrap();
323 let mut bounds = pred.type_bound_list().unwrap().bounds(); 325 let mut bounds = pred.type_bound_list().unwrap().bounds();
324 326
325 assert_eq!("for<'a> F", pred.type_ref().unwrap().syntax().text().to_string()); 327 assert!(pred.for_token().is_some());
328 assert_eq!("<'a>", pred.type_param_list().unwrap().syntax().text().to_string());
329 assert_eq!("F", pred.type_ref().unwrap().syntax().text().to_string());
326 assert_bound("Fn(&'a str)", bounds.next()); 330 assert_bound("Fn(&'a str)", bounds.next());
327} 331}
diff --git a/crates/ra_syntax/src/ast/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs
index cb430ca01..58141da11 100644
--- a/crates/ra_syntax/src/ast/generated/nodes.rs
+++ b/crates/ra_syntax/src/ast/generated/nodes.rs
@@ -2052,6 +2052,8 @@ pub struct WherePred {
2052} 2052}
2053impl ast::TypeBoundsOwner for WherePred {} 2053impl ast::TypeBoundsOwner for WherePred {}
2054impl WherePred { 2054impl WherePred {
2055 pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) }
2056 pub fn type_param_list(&self) -> Option<TypeParamList> { support::child(&self.syntax) }
2055 pub fn lifetime_token(&self) -> Option<SyntaxToken> { 2057 pub fn lifetime_token(&self) -> Option<SyntaxToken> {
2056 support::token(&self.syntax, T![lifetime]) 2058 support::token(&self.syntax, T![lifetime])
2057 } 2059 }
@@ -4849,687 +4851,687 @@ impl AstNode for FieldDefList {
4849 } 4851 }
4850} 4852}
4851impl std::fmt::Display for NominalDef { 4853impl std::fmt::Display for NominalDef {
4852 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4854 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4853 std::fmt::Display::fmt(self.syntax(), f) 4855 std::fmt::Display::fmt(self.syntax(), f)
4854 } 4856 }
4855} 4857}
4856impl std::fmt::Display for GenericParam { 4858impl std::fmt::Display for GenericParam {
4857 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4859 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4858 std::fmt::Display::fmt(self.syntax(), f) 4860 std::fmt::Display::fmt(self.syntax(), f)
4859 } 4861 }
4860} 4862}
4861impl std::fmt::Display for GenericArg { 4863impl std::fmt::Display for GenericArg {
4862 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4864 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4863 std::fmt::Display::fmt(self.syntax(), f) 4865 std::fmt::Display::fmt(self.syntax(), f)
4864 } 4866 }
4865} 4867}
4866impl std::fmt::Display for TypeRef { 4868impl std::fmt::Display for TypeRef {
4867 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4869 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4868 std::fmt::Display::fmt(self.syntax(), f) 4870 std::fmt::Display::fmt(self.syntax(), f)
4869 } 4871 }
4870} 4872}
4871impl std::fmt::Display for ModuleItem { 4873impl std::fmt::Display for ModuleItem {
4872 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4874 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4873 std::fmt::Display::fmt(self.syntax(), f) 4875 std::fmt::Display::fmt(self.syntax(), f)
4874 } 4876 }
4875} 4877}
4876impl std::fmt::Display for AssocItem { 4878impl std::fmt::Display for AssocItem {
4877 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4879 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4878 std::fmt::Display::fmt(self.syntax(), f) 4880 std::fmt::Display::fmt(self.syntax(), f)
4879 } 4881 }
4880} 4882}
4881impl std::fmt::Display for ExternItem { 4883impl std::fmt::Display for ExternItem {
4882 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4884 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4883 std::fmt::Display::fmt(self.syntax(), f) 4885 std::fmt::Display::fmt(self.syntax(), f)
4884 } 4886 }
4885} 4887}
4886impl std::fmt::Display for Expr { 4888impl std::fmt::Display for Expr {
4887 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4889 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4888 std::fmt::Display::fmt(self.syntax(), f) 4890 std::fmt::Display::fmt(self.syntax(), f)
4889 } 4891 }
4890} 4892}
4891impl std::fmt::Display for Pat { 4893impl std::fmt::Display for Pat {
4892 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4894 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4893 std::fmt::Display::fmt(self.syntax(), f) 4895 std::fmt::Display::fmt(self.syntax(), f)
4894 } 4896 }
4895} 4897}
4896impl std::fmt::Display for RecordInnerPat { 4898impl std::fmt::Display for RecordInnerPat {
4897 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4898 std::fmt::Display::fmt(self.syntax(), f) 4900 std::fmt::Display::fmt(self.syntax(), f)
4899 } 4901 }
4900} 4902}
4901impl std::fmt::Display for AttrInput { 4903impl std::fmt::Display for AttrInput {
4902 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4904 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4903 std::fmt::Display::fmt(self.syntax(), f) 4905 std::fmt::Display::fmt(self.syntax(), f)
4904 } 4906 }
4905} 4907}
4906impl std::fmt::Display for Stmt { 4908impl std::fmt::Display for Stmt {
4907 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4909 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4908 std::fmt::Display::fmt(self.syntax(), f) 4910 std::fmt::Display::fmt(self.syntax(), f)
4909 } 4911 }
4910} 4912}
4911impl std::fmt::Display for FieldDefList { 4913impl std::fmt::Display for FieldDefList {
4912 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4914 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4913 std::fmt::Display::fmt(self.syntax(), f) 4915 std::fmt::Display::fmt(self.syntax(), f)
4914 } 4916 }
4915} 4917}
4916impl std::fmt::Display for SourceFile { 4918impl std::fmt::Display for SourceFile {
4917 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4919 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4918 std::fmt::Display::fmt(self.syntax(), f) 4920 std::fmt::Display::fmt(self.syntax(), f)
4919 } 4921 }
4920} 4922}
4921impl std::fmt::Display for FnDef { 4923impl std::fmt::Display for FnDef {
4922 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4924 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4923 std::fmt::Display::fmt(self.syntax(), f) 4925 std::fmt::Display::fmt(self.syntax(), f)
4924 } 4926 }
4925} 4927}
4926impl std::fmt::Display for RetType { 4928impl std::fmt::Display for RetType {
4927 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4929 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4928 std::fmt::Display::fmt(self.syntax(), f) 4930 std::fmt::Display::fmt(self.syntax(), f)
4929 } 4931 }
4930} 4932}
4931impl std::fmt::Display for StructDef { 4933impl std::fmt::Display for StructDef {
4932 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4934 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4933 std::fmt::Display::fmt(self.syntax(), f) 4935 std::fmt::Display::fmt(self.syntax(), f)
4934 } 4936 }
4935} 4937}
4936impl std::fmt::Display for UnionDef { 4938impl std::fmt::Display for UnionDef {
4937 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4939 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4938 std::fmt::Display::fmt(self.syntax(), f) 4940 std::fmt::Display::fmt(self.syntax(), f)
4939 } 4941 }
4940} 4942}
4941impl std::fmt::Display for RecordFieldDefList { 4943impl std::fmt::Display for RecordFieldDefList {
4942 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4944 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4943 std::fmt::Display::fmt(self.syntax(), f) 4945 std::fmt::Display::fmt(self.syntax(), f)
4944 } 4946 }
4945} 4947}
4946impl std::fmt::Display for RecordFieldDef { 4948impl std::fmt::Display for RecordFieldDef {
4947 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4949 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4948 std::fmt::Display::fmt(self.syntax(), f) 4950 std::fmt::Display::fmt(self.syntax(), f)
4949 } 4951 }
4950} 4952}
4951impl std::fmt::Display for TupleFieldDefList { 4953impl std::fmt::Display for TupleFieldDefList {
4952 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4954 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4953 std::fmt::Display::fmt(self.syntax(), f) 4955 std::fmt::Display::fmt(self.syntax(), f)
4954 } 4956 }
4955} 4957}
4956impl std::fmt::Display for TupleFieldDef { 4958impl std::fmt::Display for TupleFieldDef {
4957 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4958 std::fmt::Display::fmt(self.syntax(), f) 4960 std::fmt::Display::fmt(self.syntax(), f)
4959 } 4961 }
4960} 4962}
4961impl std::fmt::Display for EnumDef { 4963impl std::fmt::Display for EnumDef {
4962 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4964 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4963 std::fmt::Display::fmt(self.syntax(), f) 4965 std::fmt::Display::fmt(self.syntax(), f)
4964 } 4966 }
4965} 4967}
4966impl std::fmt::Display for EnumVariantList { 4968impl std::fmt::Display for EnumVariantList {
4967 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4969 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4968 std::fmt::Display::fmt(self.syntax(), f) 4970 std::fmt::Display::fmt(self.syntax(), f)
4969 } 4971 }
4970} 4972}
4971impl std::fmt::Display for EnumVariant { 4973impl std::fmt::Display for EnumVariant {
4972 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4974 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4973 std::fmt::Display::fmt(self.syntax(), f) 4975 std::fmt::Display::fmt(self.syntax(), f)
4974 } 4976 }
4975} 4977}
4976impl std::fmt::Display for TraitDef { 4978impl std::fmt::Display for TraitDef {
4977 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4979 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4978 std::fmt::Display::fmt(self.syntax(), f) 4980 std::fmt::Display::fmt(self.syntax(), f)
4979 } 4981 }
4980} 4982}
4981impl std::fmt::Display for Module { 4983impl std::fmt::Display for Module {
4982 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4984 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4983 std::fmt::Display::fmt(self.syntax(), f) 4985 std::fmt::Display::fmt(self.syntax(), f)
4984 } 4986 }
4985} 4987}
4986impl std::fmt::Display for ItemList { 4988impl std::fmt::Display for ItemList {
4987 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4989 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4988 std::fmt::Display::fmt(self.syntax(), f) 4990 std::fmt::Display::fmt(self.syntax(), f)
4989 } 4991 }
4990} 4992}
4991impl std::fmt::Display for ConstDef { 4993impl std::fmt::Display for ConstDef {
4992 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4994 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4993 std::fmt::Display::fmt(self.syntax(), f) 4995 std::fmt::Display::fmt(self.syntax(), f)
4994 } 4996 }
4995} 4997}
4996impl std::fmt::Display for StaticDef { 4998impl std::fmt::Display for StaticDef {
4997 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 4999 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4998 std::fmt::Display::fmt(self.syntax(), f) 5000 std::fmt::Display::fmt(self.syntax(), f)
4999 } 5001 }
5000} 5002}
5001impl std::fmt::Display for TypeAliasDef { 5003impl std::fmt::Display for TypeAliasDef {
5002 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5004 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5003 std::fmt::Display::fmt(self.syntax(), f) 5005 std::fmt::Display::fmt(self.syntax(), f)
5004 } 5006 }
5005} 5007}
5006impl std::fmt::Display for ImplDef { 5008impl std::fmt::Display for ImplDef {
5007 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5009 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5008 std::fmt::Display::fmt(self.syntax(), f) 5010 std::fmt::Display::fmt(self.syntax(), f)
5009 } 5011 }
5010} 5012}
5011impl std::fmt::Display for ParenType { 5013impl std::fmt::Display for ParenType {
5012 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5014 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5013 std::fmt::Display::fmt(self.syntax(), f) 5015 std::fmt::Display::fmt(self.syntax(), f)
5014 } 5016 }
5015} 5017}
5016impl std::fmt::Display for TupleType { 5018impl std::fmt::Display for TupleType {
5017 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5019 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5018 std::fmt::Display::fmt(self.syntax(), f) 5020 std::fmt::Display::fmt(self.syntax(), f)
5019 } 5021 }
5020} 5022}
5021impl std::fmt::Display for NeverType { 5023impl std::fmt::Display for NeverType {
5022 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5024 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5023 std::fmt::Display::fmt(self.syntax(), f) 5025 std::fmt::Display::fmt(self.syntax(), f)
5024 } 5026 }
5025} 5027}
5026impl std::fmt::Display for PathType { 5028impl std::fmt::Display for PathType {
5027 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5029 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5028 std::fmt::Display::fmt(self.syntax(), f) 5030 std::fmt::Display::fmt(self.syntax(), f)
5029 } 5031 }
5030} 5032}
5031impl std::fmt::Display for PointerType { 5033impl std::fmt::Display for PointerType {
5032 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5034 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5033 std::fmt::Display::fmt(self.syntax(), f) 5035 std::fmt::Display::fmt(self.syntax(), f)
5034 } 5036 }
5035} 5037}
5036impl std::fmt::Display for ArrayType { 5038impl std::fmt::Display for ArrayType {
5037 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5039 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5038 std::fmt::Display::fmt(self.syntax(), f) 5040 std::fmt::Display::fmt(self.syntax(), f)
5039 } 5041 }
5040} 5042}
5041impl std::fmt::Display for SliceType { 5043impl std::fmt::Display for SliceType {
5042 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5044 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5043 std::fmt::Display::fmt(self.syntax(), f) 5045 std::fmt::Display::fmt(self.syntax(), f)
5044 } 5046 }
5045} 5047}
5046impl std::fmt::Display for ReferenceType { 5048impl std::fmt::Display for ReferenceType {
5047 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5049 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5048 std::fmt::Display::fmt(self.syntax(), f) 5050 std::fmt::Display::fmt(self.syntax(), f)
5049 } 5051 }
5050} 5052}
5051impl std::fmt::Display for PlaceholderType { 5053impl std::fmt::Display for PlaceholderType {
5052 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5054 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5053 std::fmt::Display::fmt(self.syntax(), f) 5055 std::fmt::Display::fmt(self.syntax(), f)
5054 } 5056 }
5055} 5057}
5056impl std::fmt::Display for FnPointerType { 5058impl std::fmt::Display for FnPointerType {
5057 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5059 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5058 std::fmt::Display::fmt(self.syntax(), f) 5060 std::fmt::Display::fmt(self.syntax(), f)
5059 } 5061 }
5060} 5062}
5061impl std::fmt::Display for ForType { 5063impl std::fmt::Display for ForType {
5062 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5064 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5063 std::fmt::Display::fmt(self.syntax(), f) 5065 std::fmt::Display::fmt(self.syntax(), f)
5064 } 5066 }
5065} 5067}
5066impl std::fmt::Display for ImplTraitType { 5068impl std::fmt::Display for ImplTraitType {
5067 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5068 std::fmt::Display::fmt(self.syntax(), f) 5070 std::fmt::Display::fmt(self.syntax(), f)
5069 } 5071 }
5070} 5072}
5071impl std::fmt::Display for DynTraitType { 5073impl std::fmt::Display for DynTraitType {
5072 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5074 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5073 std::fmt::Display::fmt(self.syntax(), f) 5075 std::fmt::Display::fmt(self.syntax(), f)
5074 } 5076 }
5075} 5077}
5076impl std::fmt::Display for TupleExpr { 5078impl std::fmt::Display for TupleExpr {
5077 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5079 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5078 std::fmt::Display::fmt(self.syntax(), f) 5080 std::fmt::Display::fmt(self.syntax(), f)
5079 } 5081 }
5080} 5082}
5081impl std::fmt::Display for ArrayExpr { 5083impl std::fmt::Display for ArrayExpr {
5082 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5084 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5083 std::fmt::Display::fmt(self.syntax(), f) 5085 std::fmt::Display::fmt(self.syntax(), f)
5084 } 5086 }
5085} 5087}
5086impl std::fmt::Display for ParenExpr { 5088impl std::fmt::Display for ParenExpr {
5087 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5089 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5088 std::fmt::Display::fmt(self.syntax(), f) 5090 std::fmt::Display::fmt(self.syntax(), f)
5089 } 5091 }
5090} 5092}
5091impl std::fmt::Display for PathExpr { 5093impl std::fmt::Display for PathExpr {
5092 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5094 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5093 std::fmt::Display::fmt(self.syntax(), f) 5095 std::fmt::Display::fmt(self.syntax(), f)
5094 } 5096 }
5095} 5097}
5096impl std::fmt::Display for LambdaExpr { 5098impl std::fmt::Display for LambdaExpr {
5097 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5099 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5098 std::fmt::Display::fmt(self.syntax(), f) 5100 std::fmt::Display::fmt(self.syntax(), f)
5099 } 5101 }
5100} 5102}
5101impl std::fmt::Display for IfExpr { 5103impl std::fmt::Display for IfExpr {
5102 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5103 std::fmt::Display::fmt(self.syntax(), f) 5105 std::fmt::Display::fmt(self.syntax(), f)
5104 } 5106 }
5105} 5107}
5106impl std::fmt::Display for LoopExpr { 5108impl std::fmt::Display for LoopExpr {
5107 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5108 std::fmt::Display::fmt(self.syntax(), f) 5110 std::fmt::Display::fmt(self.syntax(), f)
5109 } 5111 }
5110} 5112}
5111impl std::fmt::Display for EffectExpr { 5113impl std::fmt::Display for EffectExpr {
5112 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5113 std::fmt::Display::fmt(self.syntax(), f) 5115 std::fmt::Display::fmt(self.syntax(), f)
5114 } 5116 }
5115} 5117}
5116impl std::fmt::Display for ForExpr { 5118impl std::fmt::Display for ForExpr {
5117 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5118 std::fmt::Display::fmt(self.syntax(), f) 5120 std::fmt::Display::fmt(self.syntax(), f)
5119 } 5121 }
5120} 5122}
5121impl std::fmt::Display for WhileExpr { 5123impl std::fmt::Display for WhileExpr {
5122 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5124 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5123 std::fmt::Display::fmt(self.syntax(), f) 5125 std::fmt::Display::fmt(self.syntax(), f)
5124 } 5126 }
5125} 5127}
5126impl std::fmt::Display for ContinueExpr { 5128impl std::fmt::Display for ContinueExpr {
5127 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5128 std::fmt::Display::fmt(self.syntax(), f) 5130 std::fmt::Display::fmt(self.syntax(), f)
5129 } 5131 }
5130} 5132}
5131impl std::fmt::Display for BreakExpr { 5133impl std::fmt::Display for BreakExpr {
5132 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5133 std::fmt::Display::fmt(self.syntax(), f) 5135 std::fmt::Display::fmt(self.syntax(), f)
5134 } 5136 }
5135} 5137}
5136impl std::fmt::Display for Label { 5138impl std::fmt::Display for Label {
5137 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5138 std::fmt::Display::fmt(self.syntax(), f) 5140 std::fmt::Display::fmt(self.syntax(), f)
5139 } 5141 }
5140} 5142}
5141impl std::fmt::Display for BlockExpr { 5143impl std::fmt::Display for BlockExpr {
5142 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5144 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5143 std::fmt::Display::fmt(self.syntax(), f) 5145 std::fmt::Display::fmt(self.syntax(), f)
5144 } 5146 }
5145} 5147}
5146impl std::fmt::Display for ReturnExpr { 5148impl std::fmt::Display for ReturnExpr {
5147 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5148 std::fmt::Display::fmt(self.syntax(), f) 5150 std::fmt::Display::fmt(self.syntax(), f)
5149 } 5151 }
5150} 5152}
5151impl std::fmt::Display for CallExpr { 5153impl std::fmt::Display for CallExpr {
5152 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5153 std::fmt::Display::fmt(self.syntax(), f) 5155 std::fmt::Display::fmt(self.syntax(), f)
5154 } 5156 }
5155} 5157}
5156impl std::fmt::Display for MethodCallExpr { 5158impl std::fmt::Display for MethodCallExpr {
5157 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5158 std::fmt::Display::fmt(self.syntax(), f) 5160 std::fmt::Display::fmt(self.syntax(), f)
5159 } 5161 }
5160} 5162}
5161impl std::fmt::Display for IndexExpr { 5163impl std::fmt::Display for IndexExpr {
5162 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5163 std::fmt::Display::fmt(self.syntax(), f) 5165 std::fmt::Display::fmt(self.syntax(), f)
5164 } 5166 }
5165} 5167}
5166impl std::fmt::Display for FieldExpr { 5168impl std::fmt::Display for FieldExpr {
5167 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5169 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5168 std::fmt::Display::fmt(self.syntax(), f) 5170 std::fmt::Display::fmt(self.syntax(), f)
5169 } 5171 }
5170} 5172}
5171impl std::fmt::Display for AwaitExpr { 5173impl std::fmt::Display for AwaitExpr {
5172 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5174 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5173 std::fmt::Display::fmt(self.syntax(), f) 5175 std::fmt::Display::fmt(self.syntax(), f)
5174 } 5176 }
5175} 5177}
5176impl std::fmt::Display for TryExpr { 5178impl std::fmt::Display for TryExpr {
5177 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5179 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5178 std::fmt::Display::fmt(self.syntax(), f) 5180 std::fmt::Display::fmt(self.syntax(), f)
5179 } 5181 }
5180} 5182}
5181impl std::fmt::Display for CastExpr { 5183impl std::fmt::Display for CastExpr {
5182 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5183 std::fmt::Display::fmt(self.syntax(), f) 5185 std::fmt::Display::fmt(self.syntax(), f)
5184 } 5186 }
5185} 5187}
5186impl std::fmt::Display for RefExpr { 5188impl std::fmt::Display for RefExpr {
5187 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5188 std::fmt::Display::fmt(self.syntax(), f) 5190 std::fmt::Display::fmt(self.syntax(), f)
5189 } 5191 }
5190} 5192}
5191impl std::fmt::Display for PrefixExpr { 5193impl std::fmt::Display for PrefixExpr {
5192 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5193 std::fmt::Display::fmt(self.syntax(), f) 5195 std::fmt::Display::fmt(self.syntax(), f)
5194 } 5196 }
5195} 5197}
5196impl std::fmt::Display for BoxExpr { 5198impl std::fmt::Display for BoxExpr {
5197 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5199 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5198 std::fmt::Display::fmt(self.syntax(), f) 5200 std::fmt::Display::fmt(self.syntax(), f)
5199 } 5201 }
5200} 5202}
5201impl std::fmt::Display for RangeExpr { 5203impl std::fmt::Display for RangeExpr {
5202 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5203 std::fmt::Display::fmt(self.syntax(), f) 5205 std::fmt::Display::fmt(self.syntax(), f)
5204 } 5206 }
5205} 5207}
5206impl std::fmt::Display for BinExpr { 5208impl std::fmt::Display for BinExpr {
5207 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5209 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5208 std::fmt::Display::fmt(self.syntax(), f) 5210 std::fmt::Display::fmt(self.syntax(), f)
5209 } 5211 }
5210} 5212}
5211impl std::fmt::Display for Literal { 5213impl std::fmt::Display for Literal {
5212 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5214 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5213 std::fmt::Display::fmt(self.syntax(), f) 5215 std::fmt::Display::fmt(self.syntax(), f)
5214 } 5216 }
5215} 5217}
5216impl std::fmt::Display for MatchExpr { 5218impl std::fmt::Display for MatchExpr {
5217 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5219 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5218 std::fmt::Display::fmt(self.syntax(), f) 5220 std::fmt::Display::fmt(self.syntax(), f)
5219 } 5221 }
5220} 5222}
5221impl std::fmt::Display for MatchArmList { 5223impl std::fmt::Display for MatchArmList {
5222 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5223 std::fmt::Display::fmt(self.syntax(), f) 5225 std::fmt::Display::fmt(self.syntax(), f)
5224 } 5226 }
5225} 5227}
5226impl std::fmt::Display for MatchArm { 5228impl std::fmt::Display for MatchArm {
5227 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5228 std::fmt::Display::fmt(self.syntax(), f) 5230 std::fmt::Display::fmt(self.syntax(), f)
5229 } 5231 }
5230} 5232}
5231impl std::fmt::Display for MatchGuard { 5233impl std::fmt::Display for MatchGuard {
5232 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5233 std::fmt::Display::fmt(self.syntax(), f) 5235 std::fmt::Display::fmt(self.syntax(), f)
5234 } 5236 }
5235} 5237}
5236impl std::fmt::Display for RecordLit { 5238impl std::fmt::Display for RecordLit {
5237 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5238 std::fmt::Display::fmt(self.syntax(), f) 5240 std::fmt::Display::fmt(self.syntax(), f)
5239 } 5241 }
5240} 5242}
5241impl std::fmt::Display for RecordFieldList { 5243impl std::fmt::Display for RecordFieldList {
5242 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5243 std::fmt::Display::fmt(self.syntax(), f) 5245 std::fmt::Display::fmt(self.syntax(), f)
5244 } 5246 }
5245} 5247}
5246impl std::fmt::Display for RecordField { 5248impl std::fmt::Display for RecordField {
5247 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5248 std::fmt::Display::fmt(self.syntax(), f) 5250 std::fmt::Display::fmt(self.syntax(), f)
5249 } 5251 }
5250} 5252}
5251impl std::fmt::Display for OrPat { 5253impl std::fmt::Display for OrPat {
5252 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5254 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5253 std::fmt::Display::fmt(self.syntax(), f) 5255 std::fmt::Display::fmt(self.syntax(), f)
5254 } 5256 }
5255} 5257}
5256impl std::fmt::Display for ParenPat { 5258impl std::fmt::Display for ParenPat {
5257 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5258 std::fmt::Display::fmt(self.syntax(), f) 5260 std::fmt::Display::fmt(self.syntax(), f)
5259 } 5261 }
5260} 5262}
5261impl std::fmt::Display for RefPat { 5263impl std::fmt::Display for RefPat {
5262 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5263 std::fmt::Display::fmt(self.syntax(), f) 5265 std::fmt::Display::fmt(self.syntax(), f)
5264 } 5266 }
5265} 5267}
5266impl std::fmt::Display for BoxPat { 5268impl std::fmt::Display for BoxPat {
5267 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5268 std::fmt::Display::fmt(self.syntax(), f) 5270 std::fmt::Display::fmt(self.syntax(), f)
5269 } 5271 }
5270} 5272}
5271impl std::fmt::Display for BindPat { 5273impl std::fmt::Display for BindPat {
5272 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5273 std::fmt::Display::fmt(self.syntax(), f) 5275 std::fmt::Display::fmt(self.syntax(), f)
5274 } 5276 }
5275} 5277}
5276impl std::fmt::Display for PlaceholderPat { 5278impl std::fmt::Display for PlaceholderPat {
5277 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5279 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5278 std::fmt::Display::fmt(self.syntax(), f) 5280 std::fmt::Display::fmt(self.syntax(), f)
5279 } 5281 }
5280} 5282}
5281impl std::fmt::Display for DotDotPat { 5283impl std::fmt::Display for DotDotPat {
5282 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5284 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5283 std::fmt::Display::fmt(self.syntax(), f) 5285 std::fmt::Display::fmt(self.syntax(), f)
5284 } 5286 }
5285} 5287}
5286impl std::fmt::Display for PathPat { 5288impl std::fmt::Display for PathPat {
5287 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5289 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5288 std::fmt::Display::fmt(self.syntax(), f) 5290 std::fmt::Display::fmt(self.syntax(), f)
5289 } 5291 }
5290} 5292}
5291impl std::fmt::Display for SlicePat { 5293impl std::fmt::Display for SlicePat {
5292 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5294 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5293 std::fmt::Display::fmt(self.syntax(), f) 5295 std::fmt::Display::fmt(self.syntax(), f)
5294 } 5296 }
5295} 5297}
5296impl std::fmt::Display for RangePat { 5298impl std::fmt::Display for RangePat {
5297 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5299 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5298 std::fmt::Display::fmt(self.syntax(), f) 5300 std::fmt::Display::fmt(self.syntax(), f)
5299 } 5301 }
5300} 5302}
5301impl std::fmt::Display for LiteralPat { 5303impl std::fmt::Display for LiteralPat {
5302 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5303 std::fmt::Display::fmt(self.syntax(), f) 5305 std::fmt::Display::fmt(self.syntax(), f)
5304 } 5306 }
5305} 5307}
5306impl std::fmt::Display for MacroPat { 5308impl std::fmt::Display for MacroPat {
5307 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5309 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5308 std::fmt::Display::fmt(self.syntax(), f) 5310 std::fmt::Display::fmt(self.syntax(), f)
5309 } 5311 }
5310} 5312}
5311impl std::fmt::Display for RecordPat { 5313impl std::fmt::Display for RecordPat {
5312 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5314 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5313 std::fmt::Display::fmt(self.syntax(), f) 5315 std::fmt::Display::fmt(self.syntax(), f)
5314 } 5316 }
5315} 5317}
5316impl std::fmt::Display for RecordFieldPatList { 5318impl std::fmt::Display for RecordFieldPatList {
5317 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5319 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5318 std::fmt::Display::fmt(self.syntax(), f) 5320 std::fmt::Display::fmt(self.syntax(), f)
5319 } 5321 }
5320} 5322}
5321impl std::fmt::Display for RecordFieldPat { 5323impl std::fmt::Display for RecordFieldPat {
5322 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5323 std::fmt::Display::fmt(self.syntax(), f) 5325 std::fmt::Display::fmt(self.syntax(), f)
5324 } 5326 }
5325} 5327}
5326impl std::fmt::Display for TupleStructPat { 5328impl std::fmt::Display for TupleStructPat {
5327 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5329 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5328 std::fmt::Display::fmt(self.syntax(), f) 5330 std::fmt::Display::fmt(self.syntax(), f)
5329 } 5331 }
5330} 5332}
5331impl std::fmt::Display for TuplePat { 5333impl std::fmt::Display for TuplePat {
5332 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5334 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5333 std::fmt::Display::fmt(self.syntax(), f) 5335 std::fmt::Display::fmt(self.syntax(), f)
5334 } 5336 }
5335} 5337}
5336impl std::fmt::Display for Visibility { 5338impl std::fmt::Display for Visibility {
5337 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5338 std::fmt::Display::fmt(self.syntax(), f) 5340 std::fmt::Display::fmt(self.syntax(), f)
5339 } 5341 }
5340} 5342}
5341impl std::fmt::Display for Name { 5343impl std::fmt::Display for Name {
5342 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5344 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5343 std::fmt::Display::fmt(self.syntax(), f) 5345 std::fmt::Display::fmt(self.syntax(), f)
5344 } 5346 }
5345} 5347}
5346impl std::fmt::Display for NameRef { 5348impl std::fmt::Display for NameRef {
5347 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5348 std::fmt::Display::fmt(self.syntax(), f) 5350 std::fmt::Display::fmt(self.syntax(), f)
5349 } 5351 }
5350} 5352}
5351impl std::fmt::Display for MacroCall { 5353impl std::fmt::Display for MacroCall {
5352 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5354 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5353 std::fmt::Display::fmt(self.syntax(), f) 5355 std::fmt::Display::fmt(self.syntax(), f)
5354 } 5356 }
5355} 5357}
5356impl std::fmt::Display for Attr { 5358impl std::fmt::Display for Attr {
5357 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5359 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5358 std::fmt::Display::fmt(self.syntax(), f) 5360 std::fmt::Display::fmt(self.syntax(), f)
5359 } 5361 }
5360} 5362}
5361impl std::fmt::Display for TokenTree { 5363impl std::fmt::Display for TokenTree {
5362 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5364 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5363 std::fmt::Display::fmt(self.syntax(), f) 5365 std::fmt::Display::fmt(self.syntax(), f)
5364 } 5366 }
5365} 5367}
5366impl std::fmt::Display for TypeParamList { 5368impl std::fmt::Display for TypeParamList {
5367 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5369 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5368 std::fmt::Display::fmt(self.syntax(), f) 5370 std::fmt::Display::fmt(self.syntax(), f)
5369 } 5371 }
5370} 5372}
5371impl std::fmt::Display for TypeParam { 5373impl std::fmt::Display for TypeParam {
5372 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5374 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5373 std::fmt::Display::fmt(self.syntax(), f) 5375 std::fmt::Display::fmt(self.syntax(), f)
5374 } 5376 }
5375} 5377}
5376impl std::fmt::Display for ConstParam { 5378impl std::fmt::Display for ConstParam {
5377 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5379 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5378 std::fmt::Display::fmt(self.syntax(), f) 5380 std::fmt::Display::fmt(self.syntax(), f)
5379 } 5381 }
5380} 5382}
5381impl std::fmt::Display for LifetimeParam { 5383impl std::fmt::Display for LifetimeParam {
5382 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5383 std::fmt::Display::fmt(self.syntax(), f) 5385 std::fmt::Display::fmt(self.syntax(), f)
5384 } 5386 }
5385} 5387}
5386impl std::fmt::Display for TypeBound { 5388impl std::fmt::Display for TypeBound {
5387 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5389 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5388 std::fmt::Display::fmt(self.syntax(), f) 5390 std::fmt::Display::fmt(self.syntax(), f)
5389 } 5391 }
5390} 5392}
5391impl std::fmt::Display for TypeBoundList { 5393impl std::fmt::Display for TypeBoundList {
5392 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5394 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5393 std::fmt::Display::fmt(self.syntax(), f) 5395 std::fmt::Display::fmt(self.syntax(), f)
5394 } 5396 }
5395} 5397}
5396impl std::fmt::Display for WherePred { 5398impl std::fmt::Display for WherePred {
5397 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5399 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5398 std::fmt::Display::fmt(self.syntax(), f) 5400 std::fmt::Display::fmt(self.syntax(), f)
5399 } 5401 }
5400} 5402}
5401impl std::fmt::Display for WhereClause { 5403impl std::fmt::Display for WhereClause {
5402 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5404 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5403 std::fmt::Display::fmt(self.syntax(), f) 5405 std::fmt::Display::fmt(self.syntax(), f)
5404 } 5406 }
5405} 5407}
5406impl std::fmt::Display for Abi { 5408impl std::fmt::Display for Abi {
5407 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5408 std::fmt::Display::fmt(self.syntax(), f) 5410 std::fmt::Display::fmt(self.syntax(), f)
5409 } 5411 }
5410} 5412}
5411impl std::fmt::Display for ExprStmt { 5413impl std::fmt::Display for ExprStmt {
5412 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5414 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5413 std::fmt::Display::fmt(self.syntax(), f) 5415 std::fmt::Display::fmt(self.syntax(), f)
5414 } 5416 }
5415} 5417}
5416impl std::fmt::Display for LetStmt { 5418impl std::fmt::Display for LetStmt {
5417 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5418 std::fmt::Display::fmt(self.syntax(), f) 5420 std::fmt::Display::fmt(self.syntax(), f)
5419 } 5421 }
5420} 5422}
5421impl std::fmt::Display for Condition { 5423impl std::fmt::Display for Condition {
5422 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5424 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5423 std::fmt::Display::fmt(self.syntax(), f) 5425 std::fmt::Display::fmt(self.syntax(), f)
5424 } 5426 }
5425} 5427}
5426impl std::fmt::Display for ParamList { 5428impl std::fmt::Display for ParamList {
5427 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5429 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5428 std::fmt::Display::fmt(self.syntax(), f) 5430 std::fmt::Display::fmt(self.syntax(), f)
5429 } 5431 }
5430} 5432}
5431impl std::fmt::Display for SelfParam { 5433impl std::fmt::Display for SelfParam {
5432 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5434 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5433 std::fmt::Display::fmt(self.syntax(), f) 5435 std::fmt::Display::fmt(self.syntax(), f)
5434 } 5436 }
5435} 5437}
5436impl std::fmt::Display for Param { 5438impl std::fmt::Display for Param {
5437 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5439 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5438 std::fmt::Display::fmt(self.syntax(), f) 5440 std::fmt::Display::fmt(self.syntax(), f)
5439 } 5441 }
5440} 5442}
5441impl std::fmt::Display for UseItem { 5443impl std::fmt::Display for UseItem {
5442 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5444 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5443 std::fmt::Display::fmt(self.syntax(), f) 5445 std::fmt::Display::fmt(self.syntax(), f)
5444 } 5446 }
5445} 5447}
5446impl std::fmt::Display for UseTree { 5448impl std::fmt::Display for UseTree {
5447 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5449 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5448 std::fmt::Display::fmt(self.syntax(), f) 5450 std::fmt::Display::fmt(self.syntax(), f)
5449 } 5451 }
5450} 5452}
5451impl std::fmt::Display for Alias { 5453impl std::fmt::Display for Alias {
5452 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5454 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5453 std::fmt::Display::fmt(self.syntax(), f) 5455 std::fmt::Display::fmt(self.syntax(), f)
5454 } 5456 }
5455} 5457}
5456impl std::fmt::Display for UseTreeList { 5458impl std::fmt::Display for UseTreeList {
5457 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5459 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5458 std::fmt::Display::fmt(self.syntax(), f) 5460 std::fmt::Display::fmt(self.syntax(), f)
5459 } 5461 }
5460} 5462}
5461impl std::fmt::Display for ExternCrateItem { 5463impl std::fmt::Display for ExternCrateItem {
5462 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5464 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5463 std::fmt::Display::fmt(self.syntax(), f) 5465 std::fmt::Display::fmt(self.syntax(), f)
5464 } 5466 }
5465} 5467}
5466impl std::fmt::Display for ArgList { 5468impl std::fmt::Display for ArgList {
5467 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5469 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5468 std::fmt::Display::fmt(self.syntax(), f) 5470 std::fmt::Display::fmt(self.syntax(), f)
5469 } 5471 }
5470} 5472}
5471impl std::fmt::Display for Path { 5473impl std::fmt::Display for Path {
5472 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5473 std::fmt::Display::fmt(self.syntax(), f) 5475 std::fmt::Display::fmt(self.syntax(), f)
5474 } 5476 }
5475} 5477}
5476impl std::fmt::Display for PathSegment { 5478impl std::fmt::Display for PathSegment {
5477 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5479 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5478 std::fmt::Display::fmt(self.syntax(), f) 5480 std::fmt::Display::fmt(self.syntax(), f)
5479 } 5481 }
5480} 5482}
5481impl std::fmt::Display for TypeArgList { 5483impl std::fmt::Display for TypeArgList {
5482 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5483 std::fmt::Display::fmt(self.syntax(), f) 5485 std::fmt::Display::fmt(self.syntax(), f)
5484 } 5486 }
5485} 5487}
5486impl std::fmt::Display for TypeArg { 5488impl std::fmt::Display for TypeArg {
5487 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5489 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5488 std::fmt::Display::fmt(self.syntax(), f) 5490 std::fmt::Display::fmt(self.syntax(), f)
5489 } 5491 }
5490} 5492}
5491impl std::fmt::Display for AssocTypeArg { 5493impl std::fmt::Display for AssocTypeArg {
5492 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5494 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5493 std::fmt::Display::fmt(self.syntax(), f) 5495 std::fmt::Display::fmt(self.syntax(), f)
5494 } 5496 }
5495} 5497}
5496impl std::fmt::Display for LifetimeArg { 5498impl std::fmt::Display for LifetimeArg {
5497 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5499 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5498 std::fmt::Display::fmt(self.syntax(), f) 5500 std::fmt::Display::fmt(self.syntax(), f)
5499 } 5501 }
5500} 5502}
5501impl std::fmt::Display for ConstArg { 5503impl std::fmt::Display for ConstArg {
5502 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5504 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5503 std::fmt::Display::fmt(self.syntax(), f) 5505 std::fmt::Display::fmt(self.syntax(), f)
5504 } 5506 }
5505} 5507}
5506impl std::fmt::Display for MacroItems { 5508impl std::fmt::Display for MacroItems {
5507 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5508 std::fmt::Display::fmt(self.syntax(), f) 5510 std::fmt::Display::fmt(self.syntax(), f)
5509 } 5511 }
5510} 5512}
5511impl std::fmt::Display for MacroStmts { 5513impl std::fmt::Display for MacroStmts {
5512 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5514 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5513 std::fmt::Display::fmt(self.syntax(), f) 5515 std::fmt::Display::fmt(self.syntax(), f)
5514 } 5516 }
5515} 5517}
5516impl std::fmt::Display for ExternItemList { 5518impl std::fmt::Display for ExternItemList {
5517 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5519 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5518 std::fmt::Display::fmt(self.syntax(), f) 5520 std::fmt::Display::fmt(self.syntax(), f)
5519 } 5521 }
5520} 5522}
5521impl std::fmt::Display for ExternBlock { 5523impl std::fmt::Display for ExternBlock {
5522 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5524 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5523 std::fmt::Display::fmt(self.syntax(), f) 5525 std::fmt::Display::fmt(self.syntax(), f)
5524 } 5526 }
5525} 5527}
5526impl std::fmt::Display for MetaItem { 5528impl std::fmt::Display for MetaItem {
5527 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5529 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5528 std::fmt::Display::fmt(self.syntax(), f) 5530 std::fmt::Display::fmt(self.syntax(), f)
5529 } 5531 }
5530} 5532}
5531impl std::fmt::Display for MacroDef { 5533impl std::fmt::Display for MacroDef {
5532 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5534 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5533 std::fmt::Display::fmt(self.syntax(), f) 5535 std::fmt::Display::fmt(self.syntax(), f)
5534 } 5536 }
5535} 5537}
diff --git a/crates/ra_syntax/src/ast/generated/tokens.rs b/crates/ra_syntax/src/ast/generated/tokens.rs
index f91befaac..abadd0b61 100644
--- a/crates/ra_syntax/src/ast/generated/tokens.rs
+++ b/crates/ra_syntax/src/ast/generated/tokens.rs
@@ -11,7 +11,7 @@ pub struct Whitespace {
11 pub(crate) syntax: SyntaxToken, 11 pub(crate) syntax: SyntaxToken,
12} 12}
13impl std::fmt::Display for Whitespace { 13impl std::fmt::Display for Whitespace {
14 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 std::fmt::Display::fmt(&self.syntax, f) 15 std::fmt::Display::fmt(&self.syntax, f)
16 } 16 }
17} 17}
@@ -32,7 +32,7 @@ pub struct Comment {
32 pub(crate) syntax: SyntaxToken, 32 pub(crate) syntax: SyntaxToken,
33} 33}
34impl std::fmt::Display for Comment { 34impl std::fmt::Display for Comment {
35 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 std::fmt::Display::fmt(&self.syntax, f) 36 std::fmt::Display::fmt(&self.syntax, f)
37 } 37 }
38} 38}
@@ -53,7 +53,7 @@ pub struct String {
53 pub(crate) syntax: SyntaxToken, 53 pub(crate) syntax: SyntaxToken,
54} 54}
55impl std::fmt::Display for String { 55impl std::fmt::Display for String {
56 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 std::fmt::Display::fmt(&self.syntax, f) 57 std::fmt::Display::fmt(&self.syntax, f)
58 } 58 }
59} 59}
@@ -74,7 +74,7 @@ pub struct RawString {
74 pub(crate) syntax: SyntaxToken, 74 pub(crate) syntax: SyntaxToken,
75} 75}
76impl std::fmt::Display for RawString { 76impl std::fmt::Display for RawString {
77 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 77 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78 std::fmt::Display::fmt(&self.syntax, f) 78 std::fmt::Display::fmt(&self.syntax, f)
79 } 79 }
80} 80}
diff --git a/crates/ra_syntax/src/ast/tokens.rs b/crates/ra_syntax/src/ast/tokens.rs
index 56378385a..2e72d4927 100644
--- a/crates/ra_syntax/src/ast/tokens.rs
+++ b/crates/ra_syntax/src/ast/tokens.rs
@@ -84,7 +84,7 @@ impl Whitespace {
84} 84}
85 85
86pub struct QuoteOffsets { 86pub struct QuoteOffsets {
87 pub quotes: [TextRange; 2], 87 pub quotes: (TextRange, TextRange),
88 pub contents: TextRange, 88 pub contents: TextRange,
89} 89}
90 90
@@ -103,7 +103,7 @@ impl QuoteOffsets {
103 let end = TextSize::of(literal); 103 let end = TextSize::of(literal);
104 104
105 let res = QuoteOffsets { 105 let res = QuoteOffsets {
106 quotes: [TextRange::new(start, left_quote), TextRange::new(right_quote, end)], 106 quotes: (TextRange::new(start, left_quote), TextRange::new(right_quote, end)),
107 contents: TextRange::new(left_quote, right_quote), 107 contents: TextRange::new(left_quote, right_quote),
108 }; 108 };
109 Some(res) 109 Some(res)
@@ -116,17 +116,17 @@ pub trait HasQuotes: AstToken {
116 let offsets = QuoteOffsets::new(text)?; 116 let offsets = QuoteOffsets::new(text)?;
117 let o = self.syntax().text_range().start(); 117 let o = self.syntax().text_range().start();
118 let offsets = QuoteOffsets { 118 let offsets = QuoteOffsets {
119 quotes: [offsets.quotes[0] + o, offsets.quotes[1] + o], 119 quotes: (offsets.quotes.0 + o, offsets.quotes.1 + o),
120 contents: offsets.contents + o, 120 contents: offsets.contents + o,
121 }; 121 };
122 Some(offsets) 122 Some(offsets)
123 } 123 }
124 fn open_quote_text_range(&self) -> Option<TextRange> { 124 fn open_quote_text_range(&self) -> Option<TextRange> {
125 self.quote_offsets().map(|it| it.quotes[0]) 125 self.quote_offsets().map(|it| it.quotes.0)
126 } 126 }
127 127
128 fn close_quote_text_range(&self) -> Option<TextRange> { 128 fn close_quote_text_range(&self) -> Option<TextRange> {
129 self.quote_offsets().map(|it| it.quotes[1]) 129 self.quote_offsets().map(|it| it.quotes.1)
130 } 130 }
131 131
132 fn text_range_between_quotes(&self) -> Option<TextRange> { 132 fn text_range_between_quotes(&self) -> Option<TextRange> {
diff --git a/crates/ra_syntax/src/lib.rs b/crates/ra_syntax/src/lib.rs
index 61e686da5..a33a35cc1 100644
--- a/crates/ra_syntax/src/lib.rs
+++ b/crates/ra_syntax/src/lib.rs
@@ -51,7 +51,8 @@ pub use crate::{
51 ptr::{AstPtr, SyntaxNodePtr}, 51 ptr::{AstPtr, SyntaxNodePtr},
52 syntax_error::SyntaxError, 52 syntax_error::SyntaxError,
53 syntax_node::{ 53 syntax_node::{
54 Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, SyntaxTreeBuilder, 54 Direction, NodeOrToken, SyntaxElement, SyntaxElementChildren, SyntaxNode,
55 SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder,
55 }, 56 },
56}; 57};
57pub use ra_parser::{SyntaxKind, T}; 58pub use ra_parser::{SyntaxKind, T};
diff --git a/crates/ra_syntax/src/parsing/text_token_source.rs b/crates/ra_syntax/src/parsing/text_token_source.rs
index 7ddc2c2c3..97aa3e795 100644
--- a/crates/ra_syntax/src/parsing/text_token_source.rs
+++ b/crates/ra_syntax/src/parsing/text_token_source.rs
@@ -1,40 +1,35 @@
1//! FIXME: write short doc here 1//! See `TextTokenSource` docs.
2 2
3use ra_parser::Token as PToken;
4use ra_parser::TokenSource; 3use ra_parser::TokenSource;
5 4
6use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize}; 5use crate::{parsing::lexer::Token, SyntaxKind::EOF, TextRange, TextSize};
7 6
7/// Implementation of `ra_parser::TokenSource` that takes tokens from source code text.
8pub(crate) struct TextTokenSource<'t> { 8pub(crate) struct TextTokenSource<'t> {
9 text: &'t str, 9 text: &'t str,
10 /// start position of each token(expect whitespace and comment) 10 /// token and its start position (non-whitespace/comment tokens)
11 /// ```non-rust 11 /// ```non-rust
12 /// struct Foo; 12 /// struct Foo;
13 /// ^------^--- 13 /// ^------^--^-
14 /// | | ^- 14 /// | | \________
15 /// 0 7 10 15 /// | \____ \
16 /// | \ |
17 /// (struct, 0) (Foo, 7) (;, 10)
16 /// ``` 18 /// ```
17 /// (token, start_offset): `[(struct, 0), (Foo, 7), (;, 10)]` 19 /// `[(struct, 0), (Foo, 7), (;, 10)]`
18 start_offsets: Vec<TextSize>, 20 token_offset_pairs: Vec<(Token, TextSize)>,
19 /// non-whitespace/comment tokens
20 /// ```non-rust
21 /// struct Foo {}
22 /// ^^^^^^ ^^^ ^^
23 /// ```
24 /// tokens: `[struct, Foo, {, }]`
25 tokens: Vec<Token>,
26 21
27 /// Current token and position 22 /// Current token and position
28 curr: (PToken, usize), 23 curr: (ra_parser::Token, usize),
29} 24}
30 25
31impl<'t> TokenSource for TextTokenSource<'t> { 26impl<'t> TokenSource for TextTokenSource<'t> {
32 fn current(&self) -> PToken { 27 fn current(&self) -> ra_parser::Token {
33 self.curr.0 28 self.curr.0
34 } 29 }
35 30
36 fn lookahead_nth(&self, n: usize) -> PToken { 31 fn lookahead_nth(&self, n: usize) -> ra_parser::Token {
37 mk_token(self.curr.1 + n, &self.start_offsets, &self.tokens) 32 mk_token(self.curr.1 + n, &self.token_offset_pairs)
38 } 33 }
39 34
40 fn bump(&mut self) { 35 fn bump(&mut self) {
@@ -43,45 +38,47 @@ impl<'t> TokenSource for TextTokenSource<'t> {
43 } 38 }
44 39
45 let pos = self.curr.1 + 1; 40 let pos = self.curr.1 + 1;
46 self.curr = (mk_token(pos, &self.start_offsets, &self.tokens), pos); 41 self.curr = (mk_token(pos, &self.token_offset_pairs), pos);
47 } 42 }
48 43
49 fn is_keyword(&self, kw: &str) -> bool { 44 fn is_keyword(&self, kw: &str) -> bool {
50 let pos = self.curr.1; 45 self.token_offset_pairs
51 if pos >= self.tokens.len() { 46 .get(self.curr.1)
52 return false; 47 .map(|(token, offset)| &self.text[TextRange::at(*offset, token.len)] == kw)
53 } 48 .unwrap_or(false)
54 let range = TextRange::at(self.start_offsets[pos], self.tokens[pos].len);
55 self.text[range] == *kw
56 } 49 }
57} 50}
58 51
59fn mk_token(pos: usize, start_offsets: &[TextSize], tokens: &[Token]) -> PToken { 52fn mk_token(pos: usize, token_offset_pairs: &[(Token, TextSize)]) -> ra_parser::Token {
60 let kind = tokens.get(pos).map(|t| t.kind).unwrap_or(EOF); 53 let (kind, is_jointed_to_next) = match token_offset_pairs.get(pos) {
61 let is_jointed_to_next = if pos + 1 < start_offsets.len() { 54 Some((token, offset)) => (
62 start_offsets[pos] + tokens[pos].len == start_offsets[pos + 1] 55 token.kind,
63 } else { 56 token_offset_pairs
64 false 57 .get(pos + 1)
58 .map(|(_, next_offset)| offset + token.len == *next_offset)
59 .unwrap_or(false),
60 ),
61 None => (EOF, false),
65 }; 62 };
66 63 ra_parser::Token { kind, is_jointed_to_next }
67 PToken { kind, is_jointed_to_next }
68} 64}
69 65
70impl<'t> TextTokenSource<'t> { 66impl<'t> TextTokenSource<'t> {
71 /// Generate input from tokens(expect comment and whitespace). 67 /// Generate input from tokens(expect comment and whitespace).
72 pub fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> { 68 pub fn new(text: &'t str, raw_tokens: &'t [Token]) -> TextTokenSource<'t> {
73 let mut tokens = Vec::new(); 69 let token_offset_pairs: Vec<_> = raw_tokens
74 let mut start_offsets = Vec::new(); 70 .iter()
75 let mut len = 0.into(); 71 .filter_map({
76 for &token in raw_tokens.iter() { 72 let mut len = 0.into();
77 if !token.kind.is_trivia() { 73 move |token| {
78 tokens.push(token); 74 let pair = if token.kind.is_trivia() { None } else { Some((*token, len)) };
79 start_offsets.push(len); 75 len += token.len;
80 } 76 pair
81 len += token.len; 77 }
82 } 78 })
79 .collect();
83 80
84 let first = mk_token(0, &start_offsets, &tokens); 81 let first = mk_token(0, &token_offset_pairs);
85 TextTokenSource { text, start_offsets, tokens, curr: (first, 0) } 82 TextTokenSource { text, token_offset_pairs, curr: (first, 0) }
86 } 83 }
87} 84}
diff --git a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
index 568a4cc02..4d6461d1e 100644
--- a/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
+++ b/crates/ra_syntax/test_data/parser/err/0027_incomplere_where_for.rast
@@ -12,17 +12,16 @@ [email protected]
12 [email protected] "where" 12 [email protected] "where"
13 [email protected] " " 13 [email protected] " "
14 [email protected] 14 [email protected]
15 [email protected] 15 [email protected] "for"
16 [email protected] "for" 16 [email protected]
17 [email protected] 17 [email protected] "<"
18 [email protected] "<" 18 [email protected]
19 [email protected] 19 [email protected] "\'a"
20 [email protected] "\'a" 20 [email protected] ">"
21 [email protected] ">"
22 [email protected] "\n" 21 [email protected] "\n"
23 [email protected] 22 [email protected]
24 [email protected] "{" 23 [email protected] "{"
25 [email protected] "}" 24 [email protected] "}"
26 [email protected] "\n" 25 [email protected] "\n"
27error 26..26: expected a path 26error 26..26: expected type
28error 26..26: expected colon 27error 26..26: expected colon
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
new file mode 100644
index 000000000..cb90f28bc
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rast
@@ -0,0 +1,240 @@
1[email protected]
2 [email protected]
3 [email protected] "type"
4 [email protected] " "
5 [email protected]
6 [email protected] "ForRef"
7 [email protected] " "
8 [email protected] "="
9 [email protected] " "
10 [email protected]
11 [email protected] "for"
12 [email protected]
13 [email protected] "<"
14 [email protected]
15 [email protected] "\'a"
16 [email protected] ">"
17 [email protected] " "
18 [email protected]
19 [email protected] "&"
20 [email protected] "\'a"
21 [email protected] " "
22 [email protected]
23 [email protected]
24 [email protected]
25 [email protected]
26 [email protected] "u32"
27 [email protected] ";"
28 [email protected] "\n"
29 [email protected]
30 [email protected] "type"
31 [email protected] " "
32 [email protected]
33 [email protected] "ForTup"
34 [email protected] " "
35 [email protected] "="
36 [email protected] " "
37 [email protected]
38 [email protected] "for"
39 [email protected]
40 [email protected] "<"
41 [email protected]
42 [email protected] "\'a"
43 [email protected] ">"
44 [email protected] " "
45 [email protected]
46 [email protected] "("
47 [email protected]
48 [email protected] "&"
49 [email protected] "\'a"
50 [email protected] " "
51 [email protected]
52 [email protected]
53 [email protected]
54 [email protected]
55 [email protected] "u32"
56 [email protected] ","
57 [email protected] ")"
58 [email protected] ";"
59 [email protected] "\n"
60 [email protected]
61 [email protected] "type"
62 [email protected] " "
63 [email protected]
64 [email protected] "ForSlice"
65 [email protected] " "
66 [email protected] "="
67 [email protected] " "
68 [email protected]
69 [email protected] "for"
70 [email protected]
71 [email protected] "<"
72 [email protected]
73 [email protected] "\'a"
74 [email protected] ">"
75 [email protected] " "
76 [email protected]
77 [email protected] "["
78 [email protected]
79 [email protected]
80 [email protected]
81 [email protected]
82 [email protected] "u32"
83 [email protected] "]"
84 [email protected] ";"
85 [email protected] "\n"
86 [email protected]
87 [email protected] "type"
88 [email protected] " "
89 [email protected]
90 [email protected] "ForForFn"
91 [email protected] " "
92 [email protected] "="
93 [email protected] " "
94 [email protected]
95 [email protected] "for"
96 [email protected]
97 [email protected] "<"
98 [email protected]
99 [email protected] "\'a"
100 [email protected] ">"
101 [email protected] " "
102 [email protected]
103 [email protected] "for"
104 [email protected]
105 [email protected] "<"
106 [email protected]
107 [email protected] "\'b"
108 [email protected] ">"
109 [email protected] " "
110 [email protected]
111 [email protected] "fn"
112 [email protected]
113 [email protected] "("
114 [email protected]
115 [email protected]
116 [email protected] "&"
117 [email protected] "\'a"
118 [email protected] " "
119 [email protected]
120 [email protected]
121 [email protected]
122 [email protected]
123 [email protected] "i32"
124 [email protected] ","
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "&"
129 [email protected] "\'b"
130 [email protected] " "
131 [email protected]
132 [email protected]
133 [email protected]
134 [email protected]
135 [email protected] "i32"
136 [email protected] ")"
137 [email protected] ";"
138 [email protected] "\n"
139 [email protected]
140 [email protected] "fn"
141 [email protected] " "
142 [email protected]
143 [email protected] "for_for_for"
144 [email protected]
145 [email protected] "<"
146 [email protected]
147 [email protected]
148 [email protected] "T"
149 [email protected] ">"
150 [email protected]
151 [email protected] "("
152 [email protected] ")"
153 [email protected] "\n"
154 [email protected]
155 [email protected] "where"
156 [email protected] "\n "
157 [email protected]
158 [email protected] "for"
159 [email protected]
160 [email protected] "<"
161 [email protected]
162 [email protected] "\'a"
163 [email protected] ">"
164 [email protected] " "
165 [email protected]
166 [email protected] "for"
167 [email protected]
168 [email protected] "<"
169 [email protected]
170 [email protected] "\'b"
171 [email protected] ">"
172 [email protected] " "
173 [email protected]
174 [email protected] "for"
175 [email protected]
176 [email protected] "<"
177 [email protected]
178 [email protected] "\'c"
179 [email protected] ">"
180 [email protected] " "
181 [email protected]
182 [email protected] "fn"
183 [email protected]
184 [email protected] "("
185 [email protected]
186 [email protected]
187 [email protected] "&"
188 [email protected] "\'a"
189 [email protected] " "
190 [email protected]
191 [email protected]
192 [email protected]
193 [email protected]
194 [email protected] "T"
195 [email protected] ","
196 [email protected] " "
197 [email protected]
198 [email protected]
199 [email protected] "&"
200 [email protected] "\'b"
201 [email protected] " "
202 [email protected]
203 [email protected]
204 [email protected]
205 [email protected]
206 [email protected] "T"
207 [email protected] ","
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected] "&"
212 [email protected] "\'c"
213 [email protected] " "
214 [email protected]
215 [email protected]
216 [email protected]
217 [email protected]
218 [email protected] "T"
219 [email protected] ")"
220 [email protected] ":"
221 [email protected] " "
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected]
227 [email protected]
228 [email protected] "Copy"
229 [email protected] ","
230 [email protected] "\n"
231 [email protected]
232 [email protected] "{"
233 [email protected] "\n"
234 [email protected] "}"
235 [email protected] "\n"
236error 21..21: expected a function pointer or path
237error 52..52: expected a function pointer or path
238error 88..88: expected a function pointer or path
239error 119..119: expected a function pointer or path
240error 195..195: expected a function pointer or path
diff --git a/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
new file mode 100644
index 000000000..0e9f8ccb4
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/err/0044_unexpected_for_type.rs
@@ -0,0 +1,9 @@
1type ForRef = for<'a> &'a u32;
2type ForTup = for<'a> (&'a u32,);
3type ForSlice = for<'a> [u32];
4type ForForFn = for<'a> for<'b> fn(&'a i32, &'b i32);
5fn for_for_for<T>()
6where
7 for<'a> for<'b> for<'c> fn(&'a T, &'b T, &'c T): Copy,
8{
9}
diff --git a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
index 0656fdf73..4e3fa704e 100644
--- a/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
+++ b/crates/ra_syntax/test_data/parser/inline/err/0009_attr_on_expr_not_allowed.rast
@@ -56,4 +56,3 @@ [email protected]
56 [email protected] "}" 56 [email protected] "}"
57 [email protected] "\n" 57 [email protected] "\n"
58error 24..24: attributes are not allowed on BIN_EXPR 58error 24..24: attributes are not allowed on BIN_EXPR
59error 44..44: attributes are not allowed on IF_EXPR
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
index 9dc473e43..cd0892451 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rast
@@ -1,61 +1,60 @@
1[email protected]9 1SOURCE_FILE@0..54
2 FN_DEF@0..48 2 FN_DEF@0..53
3 [email protected] "fn" 3 [email protected] "fn"
4 [email protected] " " 4 [email protected] " "
5 [email protected] 5 [email protected]
6 [email protected] "test" 6 [email protected] "for_trait"
7 [email protected] 7 [email protected]
8 [email protected] "<" 8 [email protected] "<"
9 [email protected] 9 [email protected]
10 [email protected] 10 [email protected]
11 [email protected] "F" 11 [email protected] "F"
12 [email protected] ">" 12 [email protected] ">"
13 [email protected] 13 [email protected]
14 [email protected] "(" 14 [email protected] "("
15 [email protected] ")" 15 [email protected] ")"
16 [email protected] "\n" 16 [email protected] "\n"
17 [email protected] 17 [email protected]
18 [email protected] "where" 18 [email protected] "where"
19 [email protected] "\n " 19 [email protected] "\n "
20 [email protected] 20 [email protected]
21 [email protected] 21 [email protected] "for"
22 [email protected] "for" 22 [email protected]
23 [email protected] 23 [email protected] "<"
24 [email protected] "<" 24 [email protected]
25 [email protected] 25 [email protected] "\'a"
26 [email protected] "\'a" 26 [email protected] ">"
27 [email protected] ">" 27 [email protected] " "
28 [email protected] " " 28 [email protected]
29 [email protected] 29 [email protected]
30 [email protected] 30 [email protected]
31 [email protected] 31 [email protected]
32 [email protected] 32 [email protected] "F"
33 [email protected] "F" 33 [email protected] ":"
34 [email protected] ":" 34 [email protected] " "
35 [email protected] " " 35 [email protected]
36 [email protected] 36 [email protected]
37 [email protected] 37 [email protected]
38 [email protected] 38 [email protected]
39 [email protected] 39 [email protected]
40 [email protected] 40 [email protected]
41 [email protected] 41 [email protected] "Fn"
42 [email protected] "Fn" 42 [email protected]
43 [email protected] 43 [email protected] "("
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected]
46 [email protected] 46 [email protected] "&"
47 [email protected] "&" 47 [email protected] "\'a"
48 [email protected] "\'a" 48 [email protected] " "
49 [email protected] " " 49 [email protected]
50 [email protected] 50 [email protected]
51 [email protected] 51 [email protected]
52 [email protected] 52 [email protected]
53 [email protected] 53 [email protected] "str"
54 [email protected] "str" 54 [email protected] ")"
55 [email protected] ")" 55 [email protected] "\n"
56 [email protected] "\n" 56 [email protected]
57 [email protected] 57 [email protected] "{"
58 [email protected] "{" 58 [email protected] " "
59 [email protected] " " 59 [email protected] "}"
60 [email protected] "}" 60 [email protected] "\n"
61 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
index b448c6178..423bc105b 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0003_where_pred_for.rs
@@ -1,4 +1,4 @@
1fn test<F>() 1fn for_trait<F>()
2where 2where
3 for<'a> F: Fn(&'a str) 3 for<'a> F: Fn(&'a str)
4{ } 4{ }
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
index dfb8d57ad..b26ac2d36 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rast
@@ -1,4 +1,4 @@
1[email protected]00 1SOURCE_FILE@0..121
2 [email protected] 2 [email protected]
3 [email protected] "type" 3 [email protected] "type"
4 [email protected] " " 4 [email protected] " "
@@ -29,212 +29,84 @@ [email protected]
29 [email protected] ")" 29 [email protected] ")"
30 [email protected] ";" 30 [email protected] ";"
31 [email protected] "\n" 31 [email protected] "\n"
32 [email protected] 32 [email protected]
33 [email protected] "fn" 33 [email protected] "type"
34 [email protected] " " 34 [email protected] " "
35 [email protected] 35 [email protected]
36 [email protected] "foo" 36 [email protected] "B"
37 [email protected] 37 [email protected] " "
38 [email protected] "<" 38 [email protected] "="
39 [email protected] 39 [email protected] " "
40 [email protected] 40 [email protected]
41 [email protected] "T" 41 [email protected] "for"
42 [email protected] ">" 42 [email protected]
43 [email protected] 43 [email protected] "<"
44 [email protected] "(" 44 [email protected]
45 [email protected] 45 [email protected] "\'a"
46 [email protected] 46 [email protected] ">"
47 [email protected] 47 [email protected] " "
48 [email protected] "_t" 48 [email protected]
49 [email protected] ":" 49 [email protected] "unsafe"
50 [email protected] " " 50 [email protected] " "
51 [email protected] 51 [email protected]
52 [email protected] "&" 52 [email protected] "extern"
53 [email protected] 53 [email protected] " "
54 [email protected] 54 [email protected] "\"C\""
55 [email protected] 55 [email protected] " "
56 [email protected] 56 [email protected] "fn"
57 [email protected] "T" 57 [email protected]
58 [email protected] ")" 58 [email protected] "("
59 [email protected] " " 59 [email protected]
60 [email protected] 60 [email protected]
61 [email protected] "where" 61 [email protected] "&"
62 [email protected] " " 62 [email protected] "\'a"
63 [email protected] 63 [email protected] " "
64 [email protected] 64 [email protected]
65 [email protected] "for" 65 [email protected] "("
66 [email protected] 66 [email protected] ")"
67 [email protected] "<" 67 [email protected] ")"
68 [email protected] 68 [email protected] " "
69 [email protected] "\'a" 69 [email protected]
70 [email protected] ">" 70 [email protected] "->"
71 [email protected] " " 71 [email protected] " "
72 [email protected] 72 [email protected]
73 [email protected] "&" 73 [email protected] "("
74 [email protected] "\'a" 74 [email protected] ")"
75 [email protected] " " 75 [email protected] ";"
76 [email protected] 76 [email protected] "\n"
77 [email protected] 77 [email protected]
78 [email protected] 78 [email protected] "type"
79 [email protected] 79 [email protected] " "
80 [email protected] "T" 80 [email protected]
81 [email protected] ":" 81 [email protected] "Obj"
82 [email protected] " " 82 [email protected] " "
83 [email protected] 83 [email protected] "="
84 [email protected] 84 [email protected] " "
85 [email protected] 85 [email protected]
86 [email protected] 86 [email protected] "for"
87 [email protected] 87 [email protected]
88 [email protected] 88 [email protected] "<"
89 [email protected] "Iterator" 89 [email protected]
90 [email protected] " " 90 [email protected] "\'a"
91 [email protected] 91 [email protected] ">"
92 [email protected] "{" 92 [email protected] " "
93 [email protected] "}" 93 [email protected]
94 [email protected] "\n" 94 [email protected]
95 [email protected] 95 [email protected]
96 [email protected] "fn" 96 [email protected]
97 [email protected] " " 97 [email protected] "PartialEq"
98 [email protected] 98 [email protected]
99 [email protected] "bar" 99 [email protected] "<"
100 [email protected] 100 [email protected]
101 [email protected] "<" 101 [email protected]
102 [email protected] 102 [email protected] "&"
103 [email protected] 103 [email protected] "\'a"
104 [email protected] "T" 104 [email protected] " "
105 [email protected] ">" 105 [email protected]
106 [email protected] 106 [email protected]
107 [email protected] "(" 107 [email protected]
108 [email protected] 108 [email protected]
109 [email protected] 109 [email protected] "i32"
110 [email protected] 110 [email protected] ">"
111 [email protected] "_t" 111 [email protected] ";"
112 [email protected] ":" 112 [email protected] "\n"
113 [email protected] " "
114 [email protected]
115 [email protected] "&"
116 [email protected]
117 [email protected]
118 [email protected]
119 [email protected]
120 [email protected] "T"
121 [email protected] ")"
122 [email protected] " "
123 [email protected]
124 [email protected] "where"
125 [email protected] " "
126 [email protected]
127 [email protected]
128 [email protected] "for"
129 [email protected]
130 [email protected] "<"
131 [email protected]
132 [email protected] "\'a"
133 [email protected] ">"
134 [email protected] " "
135 [email protected]
136 [email protected] "&"
137 [email protected] "\'a"
138 [email protected] " "
139 [email protected] "mut"
140 [email protected] " "
141 [email protected]
142 [email protected]
143 [email protected]
144 [email protected]
145 [email protected] "T"
146 [email protected] ":"
147 [email protected] " "
148 [email protected]
149 [email protected]
150 [email protected]
151 [email protected]
152 [email protected]
153 [email protected]
154 [email protected] "Iterator"
155 [email protected] " "
156 [email protected]
157 [email protected] "{"
158 [email protected] "}"
159 [email protected] "\n"
160 [email protected]
161 [email protected] "fn"
162 [email protected] " "
163 [email protected]
164 [email protected] "baz"
165 [email protected]
166 [email protected] "<"
167 [email protected]
168 [email protected]
169 [email protected] "T"
170 [email protected] ">"
171 [email protected]
172 [email protected] "("
173 [email protected]
174 [email protected]
175 [email protected]
176 [email protected] "_t"
177 [email protected] ":"
178 [email protected] " "
179 [email protected]
180 [email protected] "&"
181 [email protected]
182 [email protected]
183 [email protected]
184 [email protected]
185 [email protected] "T"
186 [email protected] ")"
187 [email protected] " "
188 [email protected]
189 [email protected] "where"
190 [email protected] " "
191 [email protected]
192 [email protected]
193 [email protected] "for"
194 [email protected]
195 [email protected] "<"
196 [email protected]
197 [email protected] "\'a"
198 [email protected] ">"
199 [email protected] " "
200 [email protected]
201 [email protected]
202 [email protected]
203 [email protected]
204 [email protected] "<"
205 [email protected]
206 [email protected] "&"
207 [email protected] "\'a"
208 [email protected] " "
209 [email protected]
210 [email protected]
211 [email protected]
212 [email protected]
213 [email protected] "T"
214 [email protected] " "
215 [email protected] "as"
216 [email protected] " "
217 [email protected]
218 [email protected]
219 [email protected]
220 [email protected]
221 [email protected] "Baz"
222 [email protected] ">"
223 [email protected] "::"
224 [email protected]
225 [email protected]
226 [email protected] "Foo"
227 [email protected] ":"
228 [email protected] " "
229 [email protected]
230 [email protected]
231 [email protected]
232 [email protected]
233 [email protected]
234 [email protected]
235 [email protected] "Iterator"
236 [email protected] " "
237 [email protected]
238 [email protected] "{"
239 [email protected] "}"
240 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
index d6774d438..8ac7b9e10 100644
--- a/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
+++ b/crates/ra_syntax/test_data/parser/inline/ok/0081_for_type.rs
@@ -1,4 +1,3 @@
1type A = for<'a> fn() -> (); 1type A = for<'a> fn() -> ();
2fn foo<T>(_t: &T) where for<'a> &'a T: Iterator {} 2type B = for<'a> unsafe extern "C" fn(&'a ()) -> ();
3fn bar<T>(_t: &T) where for<'a> &'a mut T: Iterator {} 3type Obj = for<'a> PartialEq<&'a i32>;
4fn baz<T>(_t: &T) where for<'a> <&'a T as Baz>::Foo: Iterator {}
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
new file mode 100644
index 000000000..503585103
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rast
@@ -0,0 +1,392 @@
1[email protected]
2 [email protected]
3 [email protected] "fn"
4 [email protected] " "
5 [email protected]
6 [email protected] "for_trait"
7 [email protected]
8 [email protected] "<"
9 [email protected]
10 [email protected]
11 [email protected] "F"
12 [email protected] ">"
13 [email protected]
14 [email protected] "("
15 [email protected] ")"
16 [email protected] "\n"
17 [email protected]
18 [email protected] "where"
19 [email protected] "\n "
20 [email protected]
21 [email protected] "for"
22 [email protected]
23 [email protected] "<"
24 [email protected]
25 [email protected] "\'a"
26 [email protected] ">"
27 [email protected] " "
28 [email protected]
29 [email protected]
30 [email protected]
31 [email protected]
32 [email protected] "F"
33 [email protected] ":"
34 [email protected] " "
35 [email protected]
36 [email protected]
37 [email protected]
38 [email protected]
39 [email protected]
40 [email protected]
41 [email protected] "Fn"
42 [email protected]
43 [email protected] "("
44 [email protected]
45 [email protected]
46 [email protected] "&"
47 [email protected] "\'a"
48 [email protected] " "
49 [email protected]
50 [email protected]
51 [email protected]
52 [email protected]
53 [email protected] "str"
54 [email protected] ")"
55 [email protected] ","
56 [email protected] "\n"
57 [email protected]
58 [email protected] "{"
59 [email protected] "\n"
60 [email protected] "}"
61 [email protected] "\n"
62 [email protected]
63 [email protected] "fn"
64 [email protected] " "
65 [email protected]
66 [email protected] "for_ref"
67 [email protected]
68 [email protected] "<"
69 [email protected]
70 [email protected]
71 [email protected] "F"
72 [email protected] ">"
73 [email protected]
74 [email protected] "("
75 [email protected] ")"
76 [email protected] "\n"
77 [email protected]
78 [email protected] "where"
79 [email protected] "\n "
80 [email protected]
81 [email protected] "for"
82 [email protected]
83 [email protected] "<"
84 [email protected]
85 [email protected] "\'a"
86 [email protected] ">"
87 [email protected] " "
88 [email protected]
89 [email protected] "&"
90 [email protected] "\'a"
91 [email protected] " "
92 [email protected]
93 [email protected]
94 [email protected]
95 [email protected]
96 [email protected] "F"
97 [email protected] ":"
98 [email protected] " "
99 [email protected]
100 [email protected]
101 [email protected]
102 [email protected]
103 [email protected]
104 [email protected]
105 [email protected] "Debug"
106 [email protected] ","
107 [email protected] "\n"
108 [email protected]
109 [email protected] "{"
110 [email protected] "\n"
111 [email protected] "}"
112 [email protected] "\n"
113 [email protected]
114 [email protected] "fn"
115 [email protected] " "
116 [email protected]
117 [email protected] "for_parens"
118 [email protected]
119 [email protected] "<"
120 [email protected]
121 [email protected]
122 [email protected] "F"
123 [email protected] ">"
124 [email protected]
125 [email protected] "("
126 [email protected] ")"
127 [email protected] "\n"
128 [email protected]
129 [email protected] "where"
130 [email protected] "\n "
131 [email protected]
132 [email protected] "for"
133 [email protected]
134 [email protected] "<"
135 [email protected]
136 [email protected] "\'a"
137 [email protected] ">"
138 [email protected] " "
139 [email protected]
140 [email protected] "("
141 [email protected]
142 [email protected] "&"
143 [email protected] "\'a"
144 [email protected] " "
145 [email protected]
146 [email protected]
147 [email protected]
148 [email protected]
149 [email protected] "F"
150 [email protected] ")"
151 [email protected] ":"
152 [email protected] " "
153 [email protected]
154 [email protected]
155 [email protected]
156 [email protected]
157 [email protected]
158 [email protected]
159 [email protected] "Fn"
160 [email protected]
161 [email protected] "("
162 [email protected]
163 [email protected]
164 [email protected] "&"
165 [email protected] "\'a"
166 [email protected] " "
167 [email protected]
168 [email protected]
169 [email protected]
170 [email protected]
171 [email protected] "str"
172 [email protected] ")"
173 [email protected] ","
174 [email protected] "\n"
175 [email protected]
176 [email protected] "{"
177 [email protected] "\n"
178 [email protected] "}"
179 [email protected] "\n"
180 [email protected]
181 [email protected] "fn"
182 [email protected] " "
183 [email protected]
184 [email protected] "for_slice"
185 [email protected]
186 [email protected] "<"
187 [email protected]
188 [email protected]
189 [email protected] "F"
190 [email protected] ">"
191 [email protected]
192 [email protected] "("
193 [email protected] ")"
194 [email protected] "\n"
195 [email protected]
196 [email protected] "where"
197 [email protected] "\n "
198 [email protected]
199 [email protected] "for"
200 [email protected]
201 [email protected] "<"
202 [email protected]
203 [email protected] "\'a"
204 [email protected] ">"
205 [email protected] " "
206 [email protected]
207 [email protected] "["
208 [email protected]
209 [email protected] "&"
210 [email protected] "\'a"
211 [email protected] " "
212 [email protected]
213 [email protected]
214 [email protected]
215 [email protected]
216 [email protected] "F"
217 [email protected] "]"
218 [email protected] ":"
219 [email protected] " "
220 [email protected]
221 [email protected]
222 [email protected]
223 [email protected]
224 [email protected]
225 [email protected]
226 [email protected] "Eq"
227 [email protected] ","
228 [email protected] "\n"
229 [email protected]
230 [email protected] "{"
231 [email protected] "\n"
232 [email protected] "}"
233 [email protected] "\n"
234 [email protected]
235 [email protected] "fn"
236 [email protected] " "
237 [email protected]
238 [email protected] "for_qpath"
239 [email protected]
240 [email protected] "<"
241 [email protected]
242 [email protected]
243 [email protected] "T"
244 [email protected] ">"
245 [email protected]
246 [email protected] "("
247 [email protected]
248 [email protected]
249 [email protected]
250 [email protected] "_t"
251 [email protected] ":"
252 [email protected] " "
253 [email protected]
254 [email protected] "&"
255 [email protected]
256 [email protected]
257 [email protected]
258 [email protected]
259 [email protected] "T"
260 [email protected] ")"
261 [email protected] "\n"
262 [email protected]
263 [email protected] "where"
264 [email protected] "\n "
265 [email protected]
266 [email protected] "for"
267 [email protected]
268 [email protected] "<"
269 [email protected]
270 [email protected] "\'a"
271 [email protected] ">"
272 [email protected] " "
273 [email protected]
274 [email protected]
275 [email protected]
276 [email protected]
277 [email protected] "<"
278 [email protected]
279 [email protected] "&"
280 [email protected] "\'a"
281 [email protected] " "
282 [email protected]
283 [email protected]
284 [email protected]
285 [email protected]
286 [email protected] "T"
287 [email protected] " "
288 [email protected] "as"
289 [email protected] " "
290 [email protected]
291 [email protected]
292 [email protected]
293 [email protected]
294 [email protected] "Baz"
295 [email protected] ">"
296 [email protected] "::"
297 [email protected]
298 [email protected]
299 [email protected] "Foo"
300 [email protected] ":"
301 [email protected] " "
302 [email protected]
303 [email protected]
304 [email protected]
305 [email protected]
306 [email protected]
307 [email protected]
308 [email protected] "Iterator"
309 [email protected] ","
310 [email protected] "\n"
311 [email protected]
312 [email protected] "{"
313 [email protected] "\n"
314 [email protected] "}"
315 [email protected] "\n"
316 [email protected]
317 [email protected] "fn"
318 [email protected] " "
319 [email protected]
320 [email protected] "for_for_fn"
321 [email protected]
322 [email protected] "<"
323 [email protected]
324 [email protected]
325 [email protected] "T"
326 [email protected] ">"
327 [email protected]
328 [email protected] "("
329 [email protected] ")"
330 [email protected] "\n"
331 [email protected]
332 [email protected] "where"
333 [email protected] "\n "
334 [email protected]
335 [email protected] "for"
336 [email protected]
337 [email protected] "<"
338 [email protected]
339 [email protected] "\'a"
340 [email protected] ">"
341 [email protected] " "
342 [email protected]
343 [email protected] "for"
344 [email protected]
345 [email protected] "<"
346 [email protected]
347 [email protected] "\'b"
348 [email protected] ">"
349 [email protected] " "
350 [email protected]
351 [email protected] "fn"
352 [email protected]
353 [email protected] "("
354 [email protected]
355 [email protected]
356 [email protected] "&"
357 [email protected] "\'a"
358 [email protected] " "
359 [email protected]
360 [email protected]
361 [email protected]
362 [email protected]
363 [email protected] "T"
364 [email protected] ","
365 [email protected] " "
366 [email protected]
367 [email protected]
368 [email protected] "&"
369 [email protected] "\'b"
370 [email protected] " "
371 [email protected]
372 [email protected]
373 [email protected]
374 [email protected]
375 [email protected] "T"
376 [email protected] ")"
377 [email protected] ":"
378 [email protected] " "
379 [email protected]
380 [email protected]
381 [email protected]
382 [email protected]
383 [email protected]
384 [email protected]
385 [email protected] "Copy"
386 [email protected] ","
387 [email protected] "\n"
388 [email protected]
389 [email protected] "{"
390 [email protected] "\n"
391 [email protected] "}"
392 [email protected] "\n"
diff --git a/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
new file mode 100644
index 000000000..9058c4619
--- /dev/null
+++ b/crates/ra_syntax/test_data/parser/ok/0067_where_for_pred.rs
@@ -0,0 +1,30 @@
1fn for_trait<F>()
2where
3 for<'a> F: Fn(&'a str),
4{
5}
6fn for_ref<F>()
7where
8 for<'a> &'a F: Debug,
9{
10}
11fn for_parens<F>()
12where
13 for<'a> (&'a F): Fn(&'a str),
14{
15}
16fn for_slice<F>()
17where
18 for<'a> [&'a F]: Eq,
19{
20}
21fn for_qpath<T>(_t: &T)
22where
23 for<'a> <&'a T as Baz>::Foo: Iterator,
24{
25}
26fn for_for_fn<T>()
27where
28 for<'a> for<'b> fn(&'a T, &'b T): Copy,
29{
30}
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs
index 8d071ab1c..99e3f7173 100644
--- a/crates/rust-analyzer/src/bin/main.rs
+++ b/crates/rust-analyzer/src/bin/main.rs
@@ -108,11 +108,11 @@ fn run_server() -> Result<()> {
108 config.update(value); 108 config.update(value);
109 } 109 }
110 config.update_caps(&initialize_params.capabilities); 110 config.update_caps(&initialize_params.capabilities);
111 let cwd = std::env::current_dir()?;
112 config.root_path =
113 initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
111 114
112 if config.linked_projects.is_empty() { 115 if config.linked_projects.is_empty() {
113 let cwd = std::env::current_dir()?;
114 let root =
115 initialize_params.root_uri.and_then(|it| it.to_file_path().ok()).unwrap_or(cwd);
116 let workspace_roots = initialize_params 116 let workspace_roots = initialize_params
117 .workspace_folders 117 .workspace_folders
118 .map(|workspaces| { 118 .map(|workspaces| {
@@ -122,7 +122,7 @@ fn run_server() -> Result<()> {
122 .collect::<Vec<_>>() 122 .collect::<Vec<_>>()
123 }) 123 })
124 .filter(|workspaces| !workspaces.is_empty()) 124 .filter(|workspaces| !workspaces.is_empty())
125 .unwrap_or_else(|| vec![root]); 125 .unwrap_or_else(|| vec![config.root_path.clone()]);
126 126
127 config.linked_projects = ProjectManifest::discover_all(&workspace_roots) 127 config.linked_projects = ProjectManifest::discover_all(&workspace_roots)
128 .into_iter() 128 .into_iter()
diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs
index 44f856f6b..5c22dce0d 100644
--- a/crates/rust-analyzer/src/cargo_target_spec.rs
+++ b/crates/rust-analyzer/src/cargo_target_spec.rs
@@ -2,7 +2,7 @@
2 2
3use ra_cfg::CfgExpr; 3use ra_cfg::CfgExpr;
4use ra_ide::{FileId, RunnableKind, TestId}; 4use ra_ide::{FileId, RunnableKind, TestId};
5use ra_project_model::{self, ProjectWorkspace, TargetKind}; 5use ra_project_model::{self, TargetKind};
6 6
7use crate::{global_state::GlobalStateSnapshot, Result}; 7use crate::{global_state::GlobalStateSnapshot, Result};
8 8
@@ -89,27 +89,23 @@ impl CargoTargetSpec {
89 } 89 }
90 90
91 pub(crate) fn for_file( 91 pub(crate) fn for_file(
92 world: &GlobalStateSnapshot, 92 global_state_snapshot: &GlobalStateSnapshot,
93 file_id: FileId, 93 file_id: FileId,
94 ) -> Result<Option<CargoTargetSpec>> { 94 ) -> Result<Option<CargoTargetSpec>> {
95 let &crate_id = match world.analysis().crate_for(file_id)?.first() { 95 let crate_id = match global_state_snapshot.analysis().crate_for(file_id)?.first() {
96 Some(crate_id) => crate_id, 96 Some(crate_id) => *crate_id,
97 None => return Ok(None), 97 None => return Ok(None),
98 }; 98 };
99 let file_id = world.analysis().crate_root(crate_id)?; 99 let (cargo_ws, target) = match global_state_snapshot.cargo_target_for_crate_root(crate_id) {
100 let path = world.file_id_to_path(file_id); 100 Some(it) => it,
101 let res = world.workspaces.iter().find_map(|ws| match ws { 101 None => return Ok(None),
102 ProjectWorkspace::Cargo { cargo, .. } => { 102 };
103 let tgt = cargo.target_by_root(&path)?; 103 let res = CargoTargetSpec {
104 Some(CargoTargetSpec { 104 package: cargo_ws.package_flag(&cargo_ws[cargo_ws[target].package]),
105 package: cargo.package_flag(&cargo[cargo[tgt].package]), 105 target: cargo_ws[target].name.clone(),
106 target: cargo[tgt].name.clone(), 106 target_kind: cargo_ws[target].kind,
107 target_kind: cargo[tgt].kind, 107 };
108 }) 108 Ok(Some(res))
109 }
110 ProjectWorkspace::Json { .. } => None,
111 });
112 Ok(res)
113 } 109 }
114 110
115 pub(crate) fn push_to(self, buf: &mut Vec<String>, kind: &RunnableKind) { 111 pub(crate) fn push_to(self, buf: &mut Vec<String>, kind: &RunnableKind) {
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 8f2aeac77..97367d7c6 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -36,28 +36,28 @@ pub fn load_cargo(
36 )?; 36 )?;
37 37
38 let mut extern_dirs = FxHashSet::default(); 38 let mut extern_dirs = FxHashSet::default();
39 extern_dirs.extend(ws.out_dirs());
40
41 let mut project_roots = ws.to_roots();
42 project_roots.extend(extern_dirs.iter().cloned().map(PackageRoot::new_non_member));
43 39
44 let (sender, receiver) = unbounded(); 40 let (sender, receiver) = unbounded();
45 let sender = Box::new(move |t| sender.send(t).unwrap()); 41 let sender = Box::new(move |t| sender.send(t).unwrap());
46 let (mut vfs, roots) = Vfs::new( 42
47 project_roots 43 let mut roots = Vec::new();
48 .iter() 44 let project_roots = ws.to_roots();
49 .map(|pkg_root| { 45 for root in &project_roots {
50 RootEntry::new( 46 roots.push(RootEntry::new(
51 pkg_root.path().to_owned(), 47 root.path().to_owned(),
52 RustPackageFilterBuilder::default() 48 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
53 .set_member(pkg_root.is_member()) 49 ));
54 .into_vfs_filter(), 50
55 ) 51 if let Some(out_dir) = root.out_dir() {
56 }) 52 extern_dirs.insert(out_dir.to_path_buf());
57 .collect(), 53 roots.push(RootEntry::new(
58 sender, 54 out_dir.to_owned(),
59 Watch(false), 55 RustPackageFilterBuilder::default().set_member(root.is_member()).into_vfs_filter(),
60 ); 56 ))
57 }
58 }
59
60 let (mut vfs, roots) = Vfs::new(roots, sender, Watch(false));
61 61
62 let source_roots = roots 62 let source_roots = roots
63 .into_iter() 63 .into_iter()
@@ -111,10 +111,6 @@ pub(crate) fn load(
111 vfs.root2path(root) 111 vfs.root2path(root)
112 ); 112 );
113 analysis_change.add_root(source_root_id, is_local); 113 analysis_change.add_root(source_root_id, is_local);
114 analysis_change.set_debug_root_path(
115 source_root_id,
116 source_roots[&source_root_id].path().display().to_string(),
117 );
118 114
119 let vfs_root_path = vfs.root2path(root); 115 let vfs_root_path = vfs.root2path(root);
120 if extern_dirs.contains(&vfs_root_path) { 116 if extern_dirs.contains(&vfs_root_path) {
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 17671f89e..0df7427cb 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -9,6 +9,7 @@
9 9
10use std::{ffi::OsString, path::PathBuf}; 10use std::{ffi::OsString, path::PathBuf};
11 11
12use crate::diagnostics::DiagnosticsConfig;
12use lsp_types::ClientCapabilities; 13use lsp_types::ClientCapabilities;
13use ra_flycheck::FlycheckConfig; 14use ra_flycheck::FlycheckConfig;
14use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig}; 15use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
@@ -20,6 +21,7 @@ pub struct Config {
20 pub client_caps: ClientCapsConfig, 21 pub client_caps: ClientCapsConfig,
21 22
22 pub publish_diagnostics: bool, 23 pub publish_diagnostics: bool,
24 pub diagnostics: DiagnosticsConfig,
23 pub lru_capacity: Option<usize>, 25 pub lru_capacity: Option<usize>,
24 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>, 26 pub proc_macro_srv: Option<(PathBuf, Vec<OsString>)>,
25 pub files: FilesConfig, 27 pub files: FilesConfig,
@@ -38,12 +40,13 @@ pub struct Config {
38 40
39 pub with_sysroot: bool, 41 pub with_sysroot: bool,
40 pub linked_projects: Vec<LinkedProject>, 42 pub linked_projects: Vec<LinkedProject>,
43 pub root_path: PathBuf,
41} 44}
42 45
43#[derive(Debug, Clone)] 46#[derive(Debug, Clone)]
44pub enum LinkedProject { 47pub enum LinkedProject {
45 ProjectManifest(ProjectManifest), 48 ProjectManifest(ProjectManifest),
46 JsonProject(JsonProject), 49 InlineJsonProject(JsonProject),
47} 50}
48 51
49impl From<ProjectManifest> for LinkedProject { 52impl From<ProjectManifest> for LinkedProject {
@@ -54,7 +57,7 @@ impl From<ProjectManifest> for LinkedProject {
54 57
55impl From<JsonProject> for LinkedProject { 58impl From<JsonProject> for LinkedProject {
56 fn from(v: JsonProject) -> Self { 59 fn from(v: JsonProject) -> Self {
57 LinkedProject::JsonProject(v) 60 LinkedProject::InlineJsonProject(v)
58 } 61 }
59} 62}
60 63
@@ -135,6 +138,7 @@ impl Default for Config {
135 138
136 with_sysroot: true, 139 with_sysroot: true,
137 publish_diagnostics: true, 140 publish_diagnostics: true,
141 diagnostics: DiagnosticsConfig::default(),
138 lru_capacity: None, 142 lru_capacity: None,
139 proc_macro_srv: None, 143 proc_macro_srv: None,
140 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() }, 144 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
@@ -147,6 +151,7 @@ impl Default for Config {
147 all_targets: true, 151 all_targets: true,
148 all_features: false, 152 all_features: false,
149 extra_args: Vec::new(), 153 extra_args: Vec::new(),
154 features: Vec::new(),
150 }), 155 }),
151 156
152 inlay_hints: InlayHintsConfig { 157 inlay_hints: InlayHintsConfig {
@@ -166,6 +171,7 @@ impl Default for Config {
166 lens: LensConfig::default(), 171 lens: LensConfig::default(),
167 hover: HoverConfig::default(), 172 hover: HoverConfig::default(),
168 linked_projects: Vec::new(), 173 linked_projects: Vec::new(),
174 root_path: PathBuf::new(),
169 } 175 }
170 } 176 }
171} 177}
@@ -181,6 +187,8 @@ impl Config {
181 187
182 set(value, "/withSysroot", &mut self.with_sysroot); 188 set(value, "/withSysroot", &mut self.with_sysroot);
183 set(value, "/diagnostics/enable", &mut self.publish_diagnostics); 189 set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
190 set(value, "/diagnostics/warningsAsInfo", &mut self.diagnostics.warnings_as_info);
191 set(value, "/diagnostics/warningsAsHint", &mut self.diagnostics.warnings_as_hint);
184 set(value, "/lruCapacity", &mut self.lru_capacity); 192 set(value, "/lruCapacity", &mut self.lru_capacity);
185 self.files.watcher = match get(value, "/files/watcher") { 193 self.files.watcher = match get(value, "/files/watcher") {
186 Some("client") => FilesWatcher::Client, 194 Some("client") => FilesWatcher::Client,
@@ -234,13 +242,14 @@ impl Config {
234 } 242 }
235 // otherwise configure command customizations 243 // otherwise configure command customizations
236 _ => { 244 _ => {
237 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features }) 245 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets, all_features, features })
238 = &mut self.check 246 = &mut self.check
239 { 247 {
240 set(value, "/checkOnSave/extraArgs", extra_args); 248 set(value, "/checkOnSave/extraArgs", extra_args);
241 set(value, "/checkOnSave/command", command); 249 set(value, "/checkOnSave/command", command);
242 set(value, "/checkOnSave/allTargets", all_targets); 250 set(value, "/checkOnSave/allTargets", all_targets);
243 set(value, "/checkOnSave/allFeatures", all_features); 251 *all_features = get(value, "/checkOnSave/allFeatures").unwrap_or(self.cargo.all_features);
252 *features = get(value, "/checkOnSave/features").unwrap_or(self.cargo.features.clone());
244 } 253 }
245 } 254 }
246 }; 255 };
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 25856c543..290609e7f 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -11,6 +11,12 @@ use crate::lsp_ext;
11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>; 11pub type CheckFixes = Arc<HashMap<FileId, Vec<Fix>>>;
12 12
13#[derive(Debug, Default, Clone)] 13#[derive(Debug, Default, Clone)]
14pub struct DiagnosticsConfig {
15 pub warnings_as_info: Vec<String>,
16 pub warnings_as_hint: Vec<String>,
17}
18
19#[derive(Debug, Default, Clone)]
14pub struct DiagnosticCollection { 20pub struct DiagnosticCollection {
15 pub native: HashMap<FileId, Vec<Diagnostic>>, 21 pub native: HashMap<FileId, Vec<Diagnostic>>,
16 pub check: HashMap<FileId, Vec<Diagnostic>>, 22 pub check: HashMap<FileId, Vec<Diagnostic>>,
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index f0273315e..9a7972ff5 100644
--- a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -29,7 +29,7 @@ expression: diag
29 }, 29 },
30 }, 30 },
31 severity: Some( 31 severity: Some(
32 Hint, 32 Warning,
33 ), 33 ),
34 code: Some( 34 code: Some(
35 String( 35 String(
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap
new file mode 100644
index 000000000..f0273315e
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap
@@ -0,0 +1,86 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic {
21 range: Range {
22 start: Position {
23 line: 290,
24 character: 8,
25 },
26 end: Position {
27 line: 290,
28 character: 11,
29 },
30 },
31 severity: Some(
32 Hint,
33 ),
34 code: Some(
35 String(
36 "unused_variables",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
43 related_information: None,
44 tags: Some(
45 [
46 Unnecessary,
47 ],
48 ),
49 },
50 fixes: [
51 CodeAction {
52 title: "consider prefixing with an underscore",
53 id: None,
54 group: None,
55 kind: Some(
56 "quickfix",
57 ),
58 command: None,
59 edit: Some(
60 SnippetWorkspaceEdit {
61 changes: Some(
62 {
63 "file:///test/driver/subcommand/repl.rs": [
64 TextEdit {
65 range: Range {
66 start: Position {
67 line: 290,
68 character: 8,
69 },
70 end: Position {
71 line: 290,
72 character: 11,
73 },
74 },
75 new_text: "_foo",
76 },
77 ],
78 },
79 ),
80 document_changes: None,
81 },
82 ),
83 },
84 ],
85 },
86]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap
new file mode 100644
index 000000000..85fd050fd
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap
@@ -0,0 +1,86 @@
1---
2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag
4---
5[
6 MappedRustDiagnostic {
7 location: Location {
8 uri: "file:///test/driver/subcommand/repl.rs",
9 range: Range {
10 start: Position {
11 line: 290,
12 character: 8,
13 },
14 end: Position {
15 line: 290,
16 character: 11,
17 },
18 },
19 },
20 diagnostic: Diagnostic {
21 range: Range {
22 start: Position {
23 line: 290,
24 character: 8,
25 },
26 end: Position {
27 line: 290,
28 character: 11,
29 },
30 },
31 severity: Some(
32 Information,
33 ),
34 code: Some(
35 String(
36 "unused_variables",
37 ),
38 ),
39 source: Some(
40 "rustc",
41 ),
42 message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
43 related_information: None,
44 tags: Some(
45 [
46 Unnecessary,
47 ],
48 ),
49 },
50 fixes: [
51 CodeAction {
52 title: "consider prefixing with an underscore",
53 id: None,
54 group: None,
55 kind: Some(
56 "quickfix",
57 ),
58 command: None,
59 edit: Some(
60 SnippetWorkspaceEdit {
61 changes: Some(
62 {
63 "file:///test/driver/subcommand/repl.rs": [
64 TextEdit {
65 range: Range {
66 start: Position {
67 line: 290,
68 character: 8,
69 },
70 end: Position {
71 line: 290,
72 character: 11,
73 },
74 },
75 new_text: "_foo",
76 },
77 ],
78 },
79 ),
80 document_changes: None,
81 },
82 ),
83 },
84 ],
85 },
86]
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 04e286780..ba74f15f3 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -1,10 +1,6 @@
1//! This module provides the functionality needed to convert diagnostics from 1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format. 2//! `cargo check` json format to the LSP diagnostic format.
3use std::{ 3use std::{collections::HashMap, path::Path};
4 collections::HashMap,
5 path::{Component, Path, Prefix},
6 str::FromStr,
7};
8 4
9use lsp_types::{ 5use lsp_types::{
10 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location, 6 Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, Location,
@@ -13,14 +9,24 @@ use lsp_types::{
13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion}; 9use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
14use stdx::format_to; 10use stdx::format_to;
15 11
16use crate::{lsp_ext, Result}; 12use super::DiagnosticsConfig;
13use crate::{lsp_ext, to_proto::url_from_abs_path};
17 14
18/// Converts a Rust level string to a LSP severity 15/// Determines the LSP severity from a diagnostic
19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> { 16fn map_diagnostic_to_severity(
20 let res = match val { 17 config: &DiagnosticsConfig,
18 val: &ra_flycheck::Diagnostic,
19) -> Option<DiagnosticSeverity> {
20 let res = match val.level {
21 DiagnosticLevel::Ice => DiagnosticSeverity::Error, 21 DiagnosticLevel::Ice => DiagnosticSeverity::Error,
22 DiagnosticLevel::Error => DiagnosticSeverity::Error, 22 DiagnosticLevel::Error => DiagnosticSeverity::Error,
23 DiagnosticLevel::Warning => DiagnosticSeverity::Warning, 23 DiagnosticLevel::Warning => match &val.code {
24 Some(code) if config.warnings_as_hint.contains(&code.code) => DiagnosticSeverity::Hint,
25 Some(code) if config.warnings_as_info.contains(&code.code) => {
26 DiagnosticSeverity::Information
27 }
28 _ => DiagnosticSeverity::Warning,
29 },
24 DiagnosticLevel::Note => DiagnosticSeverity::Information, 30 DiagnosticLevel::Note => DiagnosticSeverity::Information,
25 DiagnosticLevel::Help => DiagnosticSeverity::Hint, 31 DiagnosticLevel::Help => DiagnosticSeverity::Hint,
26 DiagnosticLevel::Unknown => return None, 32 DiagnosticLevel::Unknown => return None,
@@ -65,7 +71,7 @@ fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &Path) -> Locatio
65fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &Path) -> Location { 71fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &Path) -> Location {
66 let mut file_name = workspace_root.to_path_buf(); 72 let mut file_name = workspace_root.to_path_buf();
67 file_name.push(&span.file_name); 73 file_name.push(&span.file_name);
68 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap(); 74 let uri = url_from_abs_path(&file_name);
69 75
70 // FIXME: this doesn't handle UTF16 offsets correctly 76 // FIXME: this doesn't handle UTF16 offsets correctly
71 let range = Range::new( 77 let range = Range::new(
@@ -176,6 +182,7 @@ pub(crate) struct MappedRustDiagnostic {
176/// 182///
177/// If the diagnostic has no primary span this will return `None` 183/// If the diagnostic has no primary span this will return `None`
178pub(crate) fn map_rust_diagnostic_to_lsp( 184pub(crate) fn map_rust_diagnostic_to_lsp(
185 config: &DiagnosticsConfig,
179 rd: &ra_flycheck::Diagnostic, 186 rd: &ra_flycheck::Diagnostic,
180 workspace_root: &Path, 187 workspace_root: &Path,
181) -> Vec<MappedRustDiagnostic> { 188) -> Vec<MappedRustDiagnostic> {
@@ -184,7 +191,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
184 return Vec::new(); 191 return Vec::new();
185 } 192 }
186 193
187 let mut severity = map_level_to_severity(rd.level); 194 let severity = map_diagnostic_to_severity(config, rd);
188 195
189 let mut source = String::from("rustc"); 196 let mut source = String::from("rustc");
190 let mut code = rd.code.as_ref().map(|c| c.code.clone()); 197 let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -226,7 +233,6 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
226 } 233 }
227 234
228 if is_unused_or_unnecessary(rd) { 235 if is_unused_or_unnecessary(rd) {
229 severity = Some(DiagnosticSeverity::Hint);
230 tags.push(DiagnosticTag::Unnecessary); 236 tags.push(DiagnosticTag::Unnecessary);
231 } 237 }
232 238
@@ -275,70 +281,16 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
275 .collect() 281 .collect()
276} 282}
277 283
278/// Returns a `Url` object from a given path, will lowercase drive letters if present.
279/// This will only happen when processing windows paths.
280///
281/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
282pub fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
283 let component_has_windows_drive = path.as_ref().components().any(|comp| {
284 if let Component::Prefix(c) = comp {
285 return matches!(c.kind(), Prefix::Disk(_) | Prefix::VerbatimDisk(_));
286 }
287 false
288 });
289
290 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
291 let res = if component_has_windows_drive {
292 let url_original = Url::from_file_path(&path)
293 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
294
295 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
296
297 // There is a drive partition, but we never found a colon.
298 // This should not happen, but in this case we just pass it through.
299 if drive_partition.len() == 1 {
300 return Ok(url_original);
301 }
302
303 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
304 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
305
306 url
307 } else {
308 Url::from_file_path(&path)
309 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?
310 };
311 Ok(res)
312}
313
314#[cfg(test)] 284#[cfg(test)]
285#[cfg(not(windows))]
315mod tests { 286mod tests {
316 use super::*; 287 use super::*;
317 288
318 // `Url` is not able to parse windows paths on unix machines.
319 #[test]
320 #[cfg(target_os = "windows")]
321 fn test_lowercase_drive_letter_with_drive() {
322 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
323
324 assert_eq!(url.to_string(), "file:///c:/Test");
325 }
326
327 #[test]
328 #[cfg(target_os = "windows")]
329 fn test_drive_without_colon_passthrough() {
330 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
331
332 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
333 }
334
335 #[cfg(not(windows))]
336 fn parse_diagnostic(val: &str) -> ra_flycheck::Diagnostic { 289 fn parse_diagnostic(val: &str) -> ra_flycheck::Diagnostic {
337 serde_json::from_str::<ra_flycheck::Diagnostic>(val).unwrap() 290 serde_json::from_str::<ra_flycheck::Diagnostic>(val).unwrap()
338 } 291 }
339 292
340 #[test] 293 #[test]
341 #[cfg(not(windows))]
342 fn snap_rustc_incompatible_type_for_trait() { 294 fn snap_rustc_incompatible_type_for_trait() {
343 let diag = parse_diagnostic( 295 let diag = parse_diagnostic(
344 r##"{ 296 r##"{
@@ -387,12 +339,11 @@ mod tests {
387 ); 339 );
388 340
389 let workspace_root = Path::new("/test/"); 341 let workspace_root = Path::new("/test/");
390 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 342 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
391 insta::assert_debug_snapshot!(diag); 343 insta::assert_debug_snapshot!(diag);
392 } 344 }
393 345
394 #[test] 346 #[test]
395 #[cfg(not(windows))]
396 fn snap_rustc_unused_variable() { 347 fn snap_rustc_unused_variable() {
397 let diag = parse_diagnostic( 348 let diag = parse_diagnostic(
398 r##"{ 349 r##"{
@@ -470,12 +421,187 @@ mod tests {
470 ); 421 );
471 422
472 let workspace_root = Path::new("/test/"); 423 let workspace_root = Path::new("/test/");
473 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 424 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
425 insta::assert_debug_snapshot!(diag);
426 }
427
428 #[test]
429 #[cfg(not(windows))]
430 fn snap_rustc_unused_variable_as_info() {
431 let diag = parse_diagnostic(
432 r##"{
433 "message": "unused variable: `foo`",
434 "code": {
435 "code": "unused_variables",
436 "explanation": null
437 },
438 "level": "warning",
439 "spans": [
440 {
441 "file_name": "driver/subcommand/repl.rs",
442 "byte_start": 9228,
443 "byte_end": 9231,
444 "line_start": 291,
445 "line_end": 291,
446 "column_start": 9,
447 "column_end": 12,
448 "is_primary": true,
449 "text": [
450 {
451 "text": " let foo = 42;",
452 "highlight_start": 9,
453 "highlight_end": 12
454 }
455 ],
456 "label": null,
457 "suggested_replacement": null,
458 "suggestion_applicability": null,
459 "expansion": null
460 }
461 ],
462 "children": [
463 {
464 "message": "#[warn(unused_variables)] on by default",
465 "code": null,
466 "level": "note",
467 "spans": [],
468 "children": [],
469 "rendered": null
470 },
471 {
472 "message": "consider prefixing with an underscore",
473 "code": null,
474 "level": "help",
475 "spans": [
476 {
477 "file_name": "driver/subcommand/repl.rs",
478 "byte_start": 9228,
479 "byte_end": 9231,
480 "line_start": 291,
481 "line_end": 291,
482 "column_start": 9,
483 "column_end": 12,
484 "is_primary": true,
485 "text": [
486 {
487 "text": " let foo = 42;",
488 "highlight_start": 9,
489 "highlight_end": 12
490 }
491 ],
492 "label": null,
493 "suggested_replacement": "_foo",
494 "suggestion_applicability": "MachineApplicable",
495 "expansion": null
496 }
497 ],
498 "children": [],
499 "rendered": null
500 }
501 ],
502 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
503 }"##,
504 );
505
506 let config = DiagnosticsConfig {
507 warnings_as_info: vec!["unused_variables".to_string()],
508 ..DiagnosticsConfig::default()
509 };
510
511 let workspace_root = Path::new("/test/");
512 let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
474 insta::assert_debug_snapshot!(diag); 513 insta::assert_debug_snapshot!(diag);
475 } 514 }
476 515
477 #[test] 516 #[test]
478 #[cfg(not(windows))] 517 #[cfg(not(windows))]
518 fn snap_rustc_unused_variable_as_hint() {
519 let diag = parse_diagnostic(
520 r##"{
521 "message": "unused variable: `foo`",
522 "code": {
523 "code": "unused_variables",
524 "explanation": null
525 },
526 "level": "warning",
527 "spans": [
528 {
529 "file_name": "driver/subcommand/repl.rs",
530 "byte_start": 9228,
531 "byte_end": 9231,
532 "line_start": 291,
533 "line_end": 291,
534 "column_start": 9,
535 "column_end": 12,
536 "is_primary": true,
537 "text": [
538 {
539 "text": " let foo = 42;",
540 "highlight_start": 9,
541 "highlight_end": 12
542 }
543 ],
544 "label": null,
545 "suggested_replacement": null,
546 "suggestion_applicability": null,
547 "expansion": null
548 }
549 ],
550 "children": [
551 {
552 "message": "#[warn(unused_variables)] on by default",
553 "code": null,
554 "level": "note",
555 "spans": [],
556 "children": [],
557 "rendered": null
558 },
559 {
560 "message": "consider prefixing with an underscore",
561 "code": null,
562 "level": "help",
563 "spans": [
564 {
565 "file_name": "driver/subcommand/repl.rs",
566 "byte_start": 9228,
567 "byte_end": 9231,
568 "line_start": 291,
569 "line_end": 291,
570 "column_start": 9,
571 "column_end": 12,
572 "is_primary": true,
573 "text": [
574 {
575 "text": " let foo = 42;",
576 "highlight_start": 9,
577 "highlight_end": 12
578 }
579 ],
580 "label": null,
581 "suggested_replacement": "_foo",
582 "suggestion_applicability": "MachineApplicable",
583 "expansion": null
584 }
585 ],
586 "children": [],
587 "rendered": null
588 }
589 ],
590 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
591 }"##,
592 );
593
594 let config = DiagnosticsConfig {
595 warnings_as_hint: vec!["unused_variables".to_string()],
596 ..DiagnosticsConfig::default()
597 };
598
599 let workspace_root = Path::new("/test/");
600 let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
601 insta::assert_debug_snapshot!(diag);
602 }
603
604 #[test]
479 fn snap_rustc_wrong_number_of_parameters() { 605 fn snap_rustc_wrong_number_of_parameters() {
480 let diag = parse_diagnostic( 606 let diag = parse_diagnostic(
481 r##"{ 607 r##"{
@@ -595,12 +721,11 @@ mod tests {
595 ); 721 );
596 722
597 let workspace_root = Path::new("/test/"); 723 let workspace_root = Path::new("/test/");
598 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 724 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
599 insta::assert_debug_snapshot!(diag); 725 insta::assert_debug_snapshot!(diag);
600 } 726 }
601 727
602 #[test] 728 #[test]
603 #[cfg(not(windows))]
604 fn snap_clippy_pass_by_ref() { 729 fn snap_clippy_pass_by_ref() {
605 let diag = parse_diagnostic( 730 let diag = parse_diagnostic(
606 r##"{ 731 r##"{
@@ -716,12 +841,11 @@ mod tests {
716 ); 841 );
717 842
718 let workspace_root = Path::new("/test/"); 843 let workspace_root = Path::new("/test/");
719 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 844 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
720 insta::assert_debug_snapshot!(diag); 845 insta::assert_debug_snapshot!(diag);
721 } 846 }
722 847
723 #[test] 848 #[test]
724 #[cfg(not(windows))]
725 fn snap_rustc_mismatched_type() { 849 fn snap_rustc_mismatched_type() {
726 let diag = parse_diagnostic( 850 let diag = parse_diagnostic(
727 r##"{ 851 r##"{
@@ -760,12 +884,11 @@ mod tests {
760 ); 884 );
761 885
762 let workspace_root = Path::new("/test/"); 886 let workspace_root = Path::new("/test/");
763 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 887 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
764 insta::assert_debug_snapshot!(diag); 888 insta::assert_debug_snapshot!(diag);
765 } 889 }
766 890
767 #[test] 891 #[test]
768 #[cfg(not(windows))]
769 fn snap_handles_macro_location() { 892 fn snap_handles_macro_location() {
770 let diag = parse_diagnostic( 893 let diag = parse_diagnostic(
771 r##"{ 894 r##"{
@@ -1032,12 +1155,11 @@ mod tests {
1032 ); 1155 );
1033 1156
1034 let workspace_root = Path::new("/test/"); 1157 let workspace_root = Path::new("/test/");
1035 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 1158 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1036 insta::assert_debug_snapshot!(diag); 1159 insta::assert_debug_snapshot!(diag);
1037 } 1160 }
1038 1161
1039 #[test] 1162 #[test]
1040 #[cfg(not(windows))]
1041 fn snap_macro_compiler_error() { 1163 fn snap_macro_compiler_error() {
1042 let diag = parse_diagnostic( 1164 let diag = parse_diagnostic(
1043 r##"{ 1165 r##"{
@@ -1262,12 +1384,11 @@ mod tests {
1262 ); 1384 );
1263 1385
1264 let workspace_root = Path::new("/test/"); 1386 let workspace_root = Path::new("/test/");
1265 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 1387 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1266 insta::assert_debug_snapshot!(diag); 1388 insta::assert_debug_snapshot!(diag);
1267 } 1389 }
1268 1390
1269 #[test] 1391 #[test]
1270 #[cfg(not(windows))]
1271 fn snap_multi_line_fix() { 1392 fn snap_multi_line_fix() {
1272 let diag = parse_diagnostic( 1393 let diag = parse_diagnostic(
1273 r##"{ 1394 r##"{
@@ -1396,7 +1517,7 @@ mod tests {
1396 ); 1517 );
1397 1518
1398 let workspace_root = Path::new("/test/"); 1519 let workspace_root = Path::new("/test/");
1399 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root); 1520 let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
1400 insta::assert_debug_snapshot!(diag); 1521 insta::assert_debug_snapshot!(diag);
1401 } 1522 }
1402} 1523}
diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs
index 206673829..40f856e6e 100644
--- a/crates/rust-analyzer/src/from_proto.rs
+++ b/crates/rust-analyzer/src/from_proto.rs
@@ -17,7 +17,7 @@ pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Tex
17} 17}
18 18
19pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> { 19pub(crate) fn file_id(world: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result<FileId> {
20 world.uri_to_file_id(url) 20 world.url_to_file_id(url)
21} 21}
22 22
23pub(crate) fn file_position( 23pub(crate) fn file_position(
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 73b0f881d..ca95d776a 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -12,42 +12,34 @@ use crossbeam_channel::{unbounded, Receiver};
12use lsp_types::Url; 12use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_flycheck::{Flycheck, FlycheckConfig}; 14use ra_flycheck::{Flycheck, FlycheckConfig};
15use ra_ide::{ 15use ra_ide::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, SourceRootId};
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 16use ra_project_model::{CargoWorkspace, ProcMacroClient, ProjectWorkspace, Target};
17}; 17use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsTask, Watch};
18use ra_project_model::{ProcMacroClient, ProjectWorkspace};
19use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch};
20use relative_path::RelativePathBuf;
21use stdx::format_to; 18use stdx::format_to;
22 19
23use crate::{ 20use crate::{
24 config::Config, 21 config::Config,
25 diagnostics::{ 22 diagnostics::{CheckFixes, DiagnosticCollection},
26 to_proto::url_from_path_with_drive_lowercasing, CheckFixes, DiagnosticCollection,
27 },
28 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 23 main_loop::pending_requests::{CompletedRequest, LatestRequests},
24 to_proto::url_from_abs_path,
29 vfs_glob::{Glob, RustPackageFilterBuilder}, 25 vfs_glob::{Glob, RustPackageFilterBuilder},
30 LspError, Result, 26 LspError, Result,
31}; 27};
32use ra_db::ExternSourceId; 28use ra_db::{CrateId, ExternSourceId};
33use rustc_hash::{FxHashMap, FxHashSet}; 29use rustc_hash::{FxHashMap, FxHashSet};
34 30
35fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { 31fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> {
36 // FIXME: Figure out the multi-workspace situation 32 // FIXME: Figure out the multi-workspace situation
37 workspaces 33 workspaces.iter().find_map(|w| match w {
38 .iter() 34 ProjectWorkspace::Cargo { cargo, .. } => {
39 .find_map(|w| match w {
40 ProjectWorkspace::Cargo { cargo, .. } => Some(cargo),
41 ProjectWorkspace::Json { .. } => None,
42 })
43 .map(|cargo| {
44 let cargo_project_root = cargo.workspace_root().to_path_buf(); 35 let cargo_project_root = cargo.workspace_root().to_path_buf();
45 Some(Flycheck::new(config.clone(), cargo_project_root)) 36 Some(Flycheck::new(config.clone(), cargo_project_root))
46 }) 37 }
47 .unwrap_or_else(|| { 38 ProjectWorkspace::Json { .. } => {
48 log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); 39 log::warn!("Cargo check watching only supported for cargo workspaces, disabling");
49 None 40 None
50 }) 41 }
42 })
51} 43}
52 44
53/// `GlobalState` is the primary mutable state of the language server 45/// `GlobalState` is the primary mutable state of the language server
@@ -89,8 +81,7 @@ impl GlobalState {
89 ) -> GlobalState { 81 ) -> GlobalState {
90 let mut change = AnalysisChange::new(); 82 let mut change = AnalysisChange::new();
91 83
92 let extern_dirs: FxHashSet<_> = 84 let mut extern_dirs: FxHashSet<PathBuf> = FxHashSet::default();
93 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect();
94 85
95 let mut local_roots = Vec::new(); 86 let mut local_roots = Vec::new();
96 let roots: Vec<_> = { 87 let roots: Vec<_> = {
@@ -100,22 +91,22 @@ impl GlobalState {
100 .exclude(exclude_globs.iter().cloned()) 91 .exclude(exclude_globs.iter().cloned())
101 .into_vfs_filter() 92 .into_vfs_filter()
102 }; 93 };
103 workspaces 94 let mut roots = Vec::new();
104 .iter() 95 for root in workspaces.iter().flat_map(ProjectWorkspace::to_roots) {
105 .flat_map(ProjectWorkspace::to_roots) 96 let path = root.path().to_owned();
106 .map(|pkg_root| { 97 if root.is_member() {
107 let path = pkg_root.path().to_owned(); 98 local_roots.push(path.clone());
108 if pkg_root.is_member() { 99 }
109 local_roots.push(path.clone()); 100 roots.push(RootEntry::new(path, create_filter(root.is_member())));
110 } 101 if let Some(out_dir) = root.out_dir() {
111 RootEntry::new(path, create_filter(pkg_root.is_member())) 102 extern_dirs.insert(out_dir.to_path_buf());
112 }) 103 roots.push(RootEntry::new(
113 .chain( 104 out_dir.to_path_buf(),
114 extern_dirs 105 create_filter(root.is_member()),
115 .iter() 106 ))
116 .map(|path| RootEntry::new(path.to_owned(), create_filter(false))), 107 }
117 ) 108 }
118 .collect() 109 roots
119 }; 110 };
120 111
121 let (task_sender, task_receiver) = unbounded(); 112 let (task_sender, task_receiver) = unbounded();
@@ -127,7 +118,6 @@ impl GlobalState {
127 let vfs_root_path = vfs.root2path(r); 118 let vfs_root_path = vfs.root2path(r);
128 let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it)); 119 let is_local = local_roots.iter().any(|it| vfs_root_path.starts_with(it));
129 change.add_root(SourceRootId(r.0), is_local); 120 change.add_root(SourceRootId(r.0), is_local);
130 change.set_debug_root_path(SourceRootId(r.0), vfs_root_path.display().to_string());
131 121
132 // FIXME: add path2root in vfs to simpily this logic 122 // FIXME: add path2root in vfs to simpily this logic
133 if extern_dirs.contains(&vfs_root_path) { 123 if extern_dirs.contains(&vfs_root_path) {
@@ -198,32 +188,18 @@ impl GlobalState {
198 188
199 /// Returns a vec of libraries 189 /// Returns a vec of libraries
200 /// FIXME: better API here 190 /// FIXME: better API here
201 pub fn process_changes( 191 pub fn process_changes(&mut self, roots_scanned: &mut usize) -> bool {
202 &mut self,
203 roots_scanned: &mut usize,
204 ) -> Option<Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>> {
205 let changes = self.vfs.write().commit_changes(); 192 let changes = self.vfs.write().commit_changes();
206 if changes.is_empty() { 193 if changes.is_empty() {
207 return None; 194 return false;
208 } 195 }
209 let mut libs = Vec::new();
210 let mut change = AnalysisChange::new(); 196 let mut change = AnalysisChange::new();
211 for c in changes { 197 for c in changes {
212 match c { 198 match c {
213 VfsChange::AddRoot { root, files } => { 199 VfsChange::AddRoot { root, files } => {
214 let root_path = self.vfs.read().root2path(root); 200 *roots_scanned += 1;
215 let is_local = self.local_roots.iter().any(|r| root_path.starts_with(r)); 201 for (file, path, text) in files {
216 if is_local { 202 change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
217 *roots_scanned += 1;
218 for (file, path, text) in files {
219 change.add_file(SourceRootId(root.0), FileId(file.0), path, text);
220 }
221 } else {
222 let files = files
223 .into_iter()
224 .map(|(vfsfile, path, text)| (FileId(vfsfile.0), path, text))
225 .collect();
226 libs.push((SourceRootId(root.0), files));
227 } 203 }
228 } 204 }
229 VfsChange::AddFile { root, file, path, text } => { 205 VfsChange::AddFile { root, file, path, text } => {
@@ -238,13 +214,7 @@ impl GlobalState {
238 } 214 }
239 } 215 }
240 self.analysis_host.apply_change(change); 216 self.analysis_host.apply_change(change);
241 Some(libs) 217 true
242 }
243
244 pub fn add_lib(&mut self, data: LibraryData) {
245 let mut change = AnalysisChange::new();
246 change.add_library(data);
247 self.analysis_host.apply_change(change);
248 } 218 }
249 219
250 pub fn snapshot(&self) -> GlobalStateSnapshot { 220 pub fn snapshot(&self) -> GlobalStateSnapshot {
@@ -276,8 +246,8 @@ impl GlobalStateSnapshot {
276 &self.analysis 246 &self.analysis
277 } 247 }
278 248
279 pub fn uri_to_file_id(&self, uri: &Url) -> Result<FileId> { 249 pub fn url_to_file_id(&self, url: &Url) -> Result<FileId> {
280 let path = uri.to_file_path().map_err(|()| format!("invalid uri: {}", uri))?; 250 let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?;
281 let file = self.vfs.read().path2file(&path).ok_or_else(|| { 251 let file = self.vfs.read().path2file(&path).ok_or_else(|| {
282 // Show warning as this file is outside current workspace 252 // Show warning as this file is outside current workspace
283 // FIXME: just handle such files, and remove `LspError::UNKNOWN_FILE`. 253 // FIXME: just handle such files, and remove `LspError::UNKNOWN_FILE`.
@@ -289,27 +259,33 @@ impl GlobalStateSnapshot {
289 Ok(FileId(file.0)) 259 Ok(FileId(file.0))
290 } 260 }
291 261
292 pub fn file_id_to_uri(&self, id: FileId) -> Result<Url> { 262 pub fn file_id_to_url(&self, id: FileId) -> Url {
293 let path = self.vfs.read().file2path(VfsFile(id.0)); 263 file_id_to_url(&self.vfs.read(), id)
294 let url = url_from_path_with_drive_lowercasing(path)?;
295
296 Ok(url)
297 }
298
299 pub fn file_id_to_path(&self, id: FileId) -> PathBuf {
300 self.vfs.read().file2path(VfsFile(id.0))
301 } 264 }
302 265
303 pub fn file_line_endings(&self, id: FileId) -> LineEndings { 266 pub fn file_line_endings(&self, id: FileId) -> LineEndings {
304 self.vfs.read().file_line_endings(VfsFile(id.0)) 267 self.vfs.read().file_line_endings(VfsFile(id.0))
305 } 268 }
306 269
307 pub fn path_to_uri(&self, root: SourceRootId, path: &RelativePathBuf) -> Result<Url> { 270 pub fn anchored_path(&self, file_id: FileId, path: &str) -> Url {
308 let base = self.vfs.read().root2path(VfsRoot(root.0)); 271 let mut base = self.vfs.read().file2path(VfsFile(file_id.0));
309 let path = path.to_path(base); 272 base.pop();
310 let url = Url::from_file_path(&path) 273 let path = base.join(path);
311 .map_err(|_| format!("can't convert path to url: {}", path.display()))?; 274 url_from_abs_path(&path)
312 Ok(url) 275 }
276
277 pub(crate) fn cargo_target_for_crate_root(
278 &self,
279 crate_id: CrateId,
280 ) -> Option<(&CargoWorkspace, Target)> {
281 let file_id = self.analysis().crate_root(crate_id).ok()?;
282 let path = self.vfs.read().file2path(VfsFile(file_id.0));
283 self.workspaces.iter().find_map(|ws| match ws {
284 ProjectWorkspace::Cargo { cargo, .. } => {
285 cargo.target_by_root(&path).map(|it| (cargo, it))
286 }
287 ProjectWorkspace::Json { .. } => None,
288 })
313 } 289 }
314 290
315 pub fn status(&self) -> String { 291 pub fn status(&self) -> String {
@@ -337,3 +313,8 @@ impl GlobalStateSnapshot {
337 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path)) 313 self.workspaces.iter().find_map(|ws| ws.workspace_root_for(&path))
338 } 314 }
339} 315}
316
317pub(crate) fn file_id_to_url(vfs: &Vfs, id: FileId) -> Url {
318 let path = vfs.file2path(VfsFile(id.0));
319 url_from_abs_path(&path)
320}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 752dbf145..08b0a5a16 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -24,20 +24,19 @@ use lsp_types::{
24 WorkDoneProgressReport, 24 WorkDoneProgressReport,
25}; 25};
26use ra_flycheck::{CheckTask, Status}; 26use ra_flycheck::{CheckTask, Status};
27use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; 27use ra_ide::{Canceled, FileId, LineIndex};
28use ra_prof::profile; 28use ra_prof::profile;
29use ra_project_model::{PackageRoot, ProjectWorkspace}; 29use ra_project_model::{PackageRoot, ProjectWorkspace};
30use ra_vfs::{VfsFile, VfsTask, Watch}; 30use ra_vfs::{VfsTask, Watch};
31use relative_path::RelativePathBuf;
32use rustc_hash::FxHashSet; 31use rustc_hash::FxHashSet;
33use serde::{de::DeserializeOwned, Serialize}; 32use serde::{de::DeserializeOwned, Serialize};
34use threadpool::ThreadPool; 33use threadpool::ThreadPool;
35 34
36use crate::{ 35use crate::{
37 config::{Config, FilesWatcher, LinkedProject}, 36 config::{Config, FilesWatcher, LinkedProject},
38 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask}, 37 diagnostics::DiagnosticTask,
39 from_proto, 38 from_proto,
40 global_state::{GlobalState, GlobalStateSnapshot}, 39 global_state::{file_id_to_url, GlobalState, GlobalStateSnapshot},
41 lsp_ext, 40 lsp_ext,
42 main_loop::{ 41 main_loop::{
43 pending_requests::{PendingRequest, PendingRequests}, 42 pending_requests::{PendingRequest, PendingRequests},
@@ -121,7 +120,12 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
121 }) 120 })
122 .ok() 121 .ok()
123 } 122 }
124 LinkedProject::JsonProject(it) => Some(it.clone().into()), 123 LinkedProject::InlineJsonProject(it) => {
124 Some(ra_project_model::ProjectWorkspace::Json {
125 project: it.clone(),
126 project_location: config.root_path.clone(),
127 })
128 }
125 }) 129 })
126 .collect::<Vec<_>>() 130 .collect::<Vec<_>>()
127 }; 131 };
@@ -169,12 +173,10 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
169 173
170 let pool = ThreadPool::default(); 174 let pool = ThreadPool::default();
171 let (task_sender, task_receiver) = unbounded::<Task>(); 175 let (task_sender, task_receiver) = unbounded::<Task>();
172 let (libdata_sender, libdata_receiver) = unbounded::<LibraryData>();
173 176
174 log::info!("server initialized, serving requests"); 177 log::info!("server initialized, serving requests");
175 { 178 {
176 let task_sender = task_sender; 179 let task_sender = task_sender;
177 let libdata_sender = libdata_sender;
178 loop { 180 loop {
179 log::trace!("selecting"); 181 log::trace!("selecting");
180 let event = select! { 182 let event = select! {
@@ -187,7 +189,6 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
187 Ok(task) => Event::Vfs(task), 189 Ok(task) => Event::Vfs(task),
188 Err(RecvError) => return Err("vfs died".into()), 190 Err(RecvError) => return Err("vfs died".into()),
189 }, 191 },
190 recv(libdata_receiver) -> data => Event::Lib(data.unwrap()),
191 recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task { 192 recv(global_state.flycheck.as_ref().map_or(&never(), |it| &it.task_recv)) -> task => match task {
192 Ok(task) => Event::CheckWatcher(task), 193 Ok(task) => Event::CheckWatcher(task),
193 Err(RecvError) => return Err("check watcher died".into()), 194 Err(RecvError) => return Err("check watcher died".into()),
@@ -198,15 +199,7 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
198 break; 199 break;
199 }; 200 };
200 } 201 }
201 loop_turn( 202 loop_turn(&pool, &task_sender, &connection, &mut global_state, &mut loop_state, event)?;
202 &pool,
203 &task_sender,
204 &libdata_sender,
205 &connection,
206 &mut global_state,
207 &mut loop_state,
208 event,
209 )?;
210 } 203 }
211 } 204 }
212 global_state.analysis_host.request_cancellation(); 205 global_state.analysis_host.request_cancellation();
@@ -214,7 +207,6 @@ pub fn main_loop(config: Config, connection: Connection) -> Result<()> {
214 task_receiver.into_iter().for_each(|task| { 207 task_receiver.into_iter().for_each(|task| {
215 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state) 208 on_task(task, &connection.sender, &mut loop_state.pending_requests, &mut global_state)
216 }); 209 });
217 libdata_receiver.into_iter().for_each(drop);
218 log::info!("...tasks have finished"); 210 log::info!("...tasks have finished");
219 log::info!("joining threadpool..."); 211 log::info!("joining threadpool...");
220 pool.join(); 212 pool.join();
@@ -238,7 +230,6 @@ enum Event {
238 Msg(Message), 230 Msg(Message),
239 Task(Task), 231 Task(Task),
240 Vfs(VfsTask), 232 Vfs(VfsTask),
241 Lib(LibraryData),
242 CheckWatcher(CheckTask), 233 CheckWatcher(CheckTask),
243} 234}
244 235
@@ -274,7 +265,6 @@ impl fmt::Debug for Event {
274 Event::Msg(it) => fmt::Debug::fmt(it, f), 265 Event::Msg(it) => fmt::Debug::fmt(it, f),
275 Event::Task(it) => fmt::Debug::fmt(it, f), 266 Event::Task(it) => fmt::Debug::fmt(it, f),
276 Event::Vfs(it) => fmt::Debug::fmt(it, f), 267 Event::Vfs(it) => fmt::Debug::fmt(it, f),
277 Event::Lib(it) => fmt::Debug::fmt(it, f),
278 Event::CheckWatcher(it) => fmt::Debug::fmt(it, f), 268 Event::CheckWatcher(it) => fmt::Debug::fmt(it, f),
279 } 269 }
280 } 270 }
@@ -286,10 +276,6 @@ struct LoopState {
286 pending_responses: FxHashSet<RequestId>, 276 pending_responses: FxHashSet<RequestId>,
287 pending_requests: PendingRequests, 277 pending_requests: PendingRequests,
288 subscriptions: Subscriptions, 278 subscriptions: Subscriptions,
289 // We try not to index more than MAX_IN_FLIGHT_LIBS libraries at the same
290 // time to always have a thread ready to react to input.
291 in_flight_libraries: usize,
292 pending_libraries: Vec<(SourceRootId, Vec<(FileId, RelativePathBuf, Arc<String>)>)>,
293 workspace_loaded: bool, 279 workspace_loaded: bool,
294 roots_progress_reported: Option<usize>, 280 roots_progress_reported: Option<usize>,
295 roots_scanned: usize, 281 roots_scanned: usize,
@@ -310,7 +296,6 @@ impl LoopState {
310fn loop_turn( 296fn loop_turn(
311 pool: &ThreadPool, 297 pool: &ThreadPool,
312 task_sender: &Sender<Task>, 298 task_sender: &Sender<Task>,
313 libdata_sender: &Sender<LibraryData>,
314 connection: &Connection, 299 connection: &Connection,
315 global_state: &mut GlobalState, 300 global_state: &mut GlobalState,
316 loop_state: &mut LoopState, 301 loop_state: &mut LoopState,
@@ -334,12 +319,6 @@ fn loop_turn(
334 Event::Vfs(task) => { 319 Event::Vfs(task) => {
335 global_state.vfs.write().handle_task(task); 320 global_state.vfs.write().handle_task(task);
336 } 321 }
337 Event::Lib(lib) => {
338 global_state.add_lib(lib);
339 global_state.maybe_collect_garbage();
340 loop_state.in_flight_libraries -= 1;
341 loop_state.roots_scanned += 1;
342 }
343 Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?, 322 Event::CheckWatcher(task) => on_check_task(task, global_state, task_sender)?,
344 Event::Msg(msg) => match msg { 323 Event::Msg(msg) => match msg {
345 Message::Request(req) => on_request( 324 Message::Request(req) => on_request(
@@ -385,36 +364,12 @@ fn loop_turn(
385 }, 364 },
386 }; 365 };
387 366
388 let mut state_changed = false; 367 let mut state_changed = global_state.process_changes(&mut loop_state.roots_scanned);
389 if let Some(changes) = global_state.process_changes(&mut loop_state.roots_scanned) {
390 state_changed = true;
391 loop_state.pending_libraries.extend(changes);
392 }
393
394 let max_in_flight_libs = pool.max_count().saturating_sub(2).max(1);
395 while loop_state.in_flight_libraries < max_in_flight_libs {
396 let (root, files) = match loop_state.pending_libraries.pop() {
397 Some(it) => it,
398 None => break,
399 };
400
401 loop_state.in_flight_libraries += 1;
402 let sender = libdata_sender.clone();
403 pool.execute(move || {
404 log::info!("indexing {:?} ... ", root);
405 let data = LibraryData::prepare(root, files);
406 sender.send(data).unwrap();
407 });
408 }
409 368
410 let show_progress = 369 let show_progress =
411 !loop_state.workspace_loaded && global_state.config.client_caps.work_done_progress; 370 !loop_state.workspace_loaded && global_state.config.client_caps.work_done_progress;
412 371
413 if !loop_state.workspace_loaded 372 if !loop_state.workspace_loaded && loop_state.roots_scanned == loop_state.roots_total {
414 && loop_state.roots_scanned == loop_state.roots_total
415 && loop_state.pending_libraries.is_empty()
416 && loop_state.in_flight_libraries == 0
417 {
418 state_changed = true; 373 state_changed = true;
419 loop_state.workspace_loaded = true; 374 loop_state.workspace_loaded = true;
420 if let Some(flycheck) = &global_state.flycheck { 375 if let Some(flycheck) = &global_state.flycheck {
@@ -664,14 +619,11 @@ fn apply_document_changes(
664 mut line_index: Cow<'_, LineIndex>, 619 mut line_index: Cow<'_, LineIndex>,
665 content_changes: Vec<TextDocumentContentChangeEvent>, 620 content_changes: Vec<TextDocumentContentChangeEvent>,
666) { 621) {
667 // Remove when https://github.com/rust-analyzer/rust-analyzer/issues/4263 is fixed.
668 let backup_text = old_text.clone();
669 let backup_changes = content_changes.clone();
670
671 // The changes we got must be applied sequentially, but can cross lines so we 622 // The changes we got must be applied sequentially, but can cross lines so we
672 // have to keep our line index updated. 623 // have to keep our line index updated.
673 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we 624 // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
674 // remember the last valid line in the index and only rebuild it if needed. 625 // remember the last valid line in the index and only rebuild it if needed.
626 // The VFS will normalize the end of lines to `\n`.
675 enum IndexValid { 627 enum IndexValid {
676 All, 628 All,
677 UpToLineExclusive(u64), 629 UpToLineExclusive(u64),
@@ -695,19 +647,7 @@ fn apply_document_changes(
695 } 647 }
696 index_valid = IndexValid::UpToLineExclusive(range.start.line); 648 index_valid = IndexValid::UpToLineExclusive(range.start.line);
697 let range = from_proto::text_range(&line_index, range); 649 let range = from_proto::text_range(&line_index, range);
698 let mut text = old_text.to_owned(); 650 old_text.replace_range(Range::<usize>::from(range), &change.text);
699 match std::panic::catch_unwind(move || {
700 text.replace_range(Range::<usize>::from(range), &change.text);
701 text
702 }) {
703 Ok(t) => *old_text = t,
704 Err(e) => {
705 eprintln!("Bug in incremental text synchronization. Please report the following output on https://github.com/rust-analyzer/rust-analyzer/issues/4263");
706 dbg!(&backup_text);
707 dbg!(&backup_changes);
708 std::panic::resume_unwind(e);
709 }
710 }
711 } 651 }
712 None => { 652 None => {
713 *old_text = change.text; 653 *old_text = change.text;
@@ -729,6 +669,7 @@ fn on_check_task(
729 669
730 CheckTask::AddDiagnostic { workspace_root, diagnostic } => { 670 CheckTask::AddDiagnostic { workspace_root, diagnostic } => {
731 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( 671 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
672 &global_state.config.diagnostics,
732 &diagnostic, 673 &diagnostic,
733 &workspace_root, 674 &workspace_root,
734 ); 675 );
@@ -801,17 +742,9 @@ fn on_diagnostic_task(task: DiagnosticTask, msg_sender: &Sender<Message>, state:
801 let subscriptions = state.diagnostics.handle_task(task); 742 let subscriptions = state.diagnostics.handle_task(task);
802 743
803 for file_id in subscriptions { 744 for file_id in subscriptions {
804 let path = state.vfs.read().file2path(VfsFile(file_id.0)); 745 let url = file_id_to_url(&state.vfs.read(), file_id);
805 let uri = match url_from_path_with_drive_lowercasing(&path) {
806 Ok(uri) => uri,
807 Err(err) => {
808 log::error!("Couldn't convert path to url ({}): {}", err, path.display());
809 continue;
810 }
811 };
812
813 let diagnostics = state.diagnostics.diagnostics_for(file_id).cloned().collect(); 746 let diagnostics = state.diagnostics.diagnostics_for(file_id).cloned().collect();
814 let params = lsp_types::PublishDiagnosticsParams { uri, diagnostics, version: None }; 747 let params = lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version: None };
815 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params); 748 let not = notification_new::<lsp_types::notification::PublishDiagnostics>(params);
816 msg_sender.send(not.into()).unwrap(); 749 msg_sender.send(not.into()).unwrap();
817 } 750 }
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index a41adf8b0..b34b529b5 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -258,7 +258,7 @@ pub fn handle_document_symbol(
258 let res = if snap.config.client_caps.hierarchical_symbols { 258 let res = if snap.config.client_caps.hierarchical_symbols {
259 document_symbols.into() 259 document_symbols.into()
260 } else { 260 } else {
261 let url = to_proto::url(&snap, file_id)?; 261 let url = to_proto::url(&snap, file_id);
262 let mut symbol_information = Vec::<SymbolInformation>::new(); 262 let mut symbol_information = Vec::<SymbolInformation>::new();
263 for symbol in document_symbols { 263 for symbol in document_symbols {
264 flatten_document_symbol(&symbol, None, &url, &mut symbol_information); 264 flatten_document_symbol(&symbol, None, &url, &mut symbol_information);
@@ -1160,7 +1160,7 @@ fn show_impl_command_link(
1160) -> Option<lsp_ext::CommandLinkGroup> { 1160) -> Option<lsp_ext::CommandLinkGroup> {
1161 if snap.config.hover.implementations { 1161 if snap.config.hover.implementations {
1162 if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) { 1162 if let Some(nav_data) = snap.analysis().goto_implementation(*position).unwrap_or(None) {
1163 let uri = to_proto::url(snap, position.file_id).ok()?; 1163 let uri = to_proto::url(snap, position.file_id);
1164 let line_index = snap.analysis().file_line_index(position.file_id).ok()?; 1164 let line_index = snap.analysis().file_line_index(position.file_id).ok()?;
1165 let position = to_proto::position(&line_index, position.offset); 1165 let position = to_proto::position(&line_index, position.offset);
1166 let locations: Vec<_> = nav_data 1166 let locations: Vec<_> = nav_data
diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs
index 6f125c37c..2ea63d33b 100644
--- a/crates/rust-analyzer/src/semantic_tokens.rs
+++ b/crates/rust-analyzer/src/semantic_tokens.rs
@@ -45,6 +45,7 @@ define_semantic_token_types![
45 (UNION, "union"), 45 (UNION, "union"),
46 (UNRESOLVED_REFERENCE, "unresolvedReference"), 46 (UNRESOLVED_REFERENCE, "unresolvedReference"),
47 (FORMAT_SPECIFIER, "formatSpecifier"), 47 (FORMAT_SPECIFIER, "formatSpecifier"),
48 (ESCAPE_SEQUENCE, "escapeSequence"),
48]; 49];
49 50
50macro_rules! define_semantic_token_modifiers { 51macro_rules! define_semantic_token_modifiers {
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 710df1fbd..055c97455 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -1,4 +1,7 @@
1//! Conversion of rust-analyzer specific types to lsp_types equivalents. 1//! Conversion of rust-analyzer specific types to lsp_types equivalents.
2use std::path::{self, Path};
3
4use itertools::Itertools;
2use ra_db::{FileId, FileRange}; 5use ra_db::{FileId, FileRange};
3use ra_ide::{ 6use ra_ide::{
4 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind, 7 Assist, CompletionItem, CompletionItemKind, Documentation, FileSystemEdit, Fold, FoldKind,
@@ -321,6 +324,7 @@ fn semantic_token_type_and_modifiers(
321 HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE, 324 HighlightTag::UnresolvedReference => semantic_tokens::UNRESOLVED_REFERENCE,
322 HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER, 325 HighlightTag::FormatSpecifier => semantic_tokens::FORMAT_SPECIFIER,
323 HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR, 326 HighlightTag::Operator => lsp_types::SemanticTokenType::OPERATOR,
327 HighlightTag::EscapeSequence => semantic_tokens::ESCAPE_SEQUENCE,
324 }; 328 };
325 329
326 for modifier in highlight.modifiers.iter() { 330 for modifier in highlight.modifiers.iter() {
@@ -385,24 +389,55 @@ pub(crate) fn folding_range(
385 } 389 }
386} 390}
387 391
388pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> Result<lsp_types::Url> { 392pub(crate) fn url(snap: &GlobalStateSnapshot, file_id: FileId) -> lsp_types::Url {
389 snap.file_id_to_uri(file_id) 393 snap.file_id_to_url(file_id)
394}
395
396/// Returns a `Url` object from a given path, will lowercase drive letters if present.
397/// This will only happen when processing windows paths.
398///
399/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
400pub(crate) fn url_from_abs_path(path: &Path) -> lsp_types::Url {
401 assert!(path.is_absolute());
402 let url = lsp_types::Url::from_file_path(path).unwrap();
403 match path.components().next() {
404 Some(path::Component::Prefix(prefix)) if matches!(prefix.kind(), path::Prefix::Disk(_) | path::Prefix::VerbatimDisk(_)) =>
405 {
406 // Need to lowercase driver letter
407 }
408 _ => return url,
409 }
410
411 let driver_letter_range = {
412 let (scheme, drive_letter, _rest) = match url.as_str().splitn(3, ':').collect_tuple() {
413 Some(it) => it,
414 None => return url,
415 };
416 let start = scheme.len() + ':'.len_utf8();
417 start..(start + drive_letter.len())
418 };
419
420 // Note: lowercasing the `path` itself doesn't help, the `Url::parse`
421 // machinery *also* canonicalizes the drive letter. So, just massage the
422 // string in place.
423 let mut url = url.into_string();
424 url[driver_letter_range].make_ascii_lowercase();
425 lsp_types::Url::parse(&url).unwrap()
390} 426}
391 427
392pub(crate) fn versioned_text_document_identifier( 428pub(crate) fn versioned_text_document_identifier(
393 snap: &GlobalStateSnapshot, 429 snap: &GlobalStateSnapshot,
394 file_id: FileId, 430 file_id: FileId,
395 version: Option<i64>, 431 version: Option<i64>,
396) -> Result<lsp_types::VersionedTextDocumentIdentifier> { 432) -> lsp_types::VersionedTextDocumentIdentifier {
397 let res = lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id)?, version }; 433 lsp_types::VersionedTextDocumentIdentifier { uri: url(snap, file_id), version }
398 Ok(res)
399} 434}
400 435
401pub(crate) fn location( 436pub(crate) fn location(
402 snap: &GlobalStateSnapshot, 437 snap: &GlobalStateSnapshot,
403 frange: FileRange, 438 frange: FileRange,
404) -> Result<lsp_types::Location> { 439) -> Result<lsp_types::Location> {
405 let url = url(snap, frange.file_id)?; 440 let url = url(snap, frange.file_id);
406 let line_index = snap.analysis().file_line_index(frange.file_id)?; 441 let line_index = snap.analysis().file_line_index(frange.file_id)?;
407 let range = range(&line_index, frange.range); 442 let range = range(&line_index, frange.range);
408 let loc = lsp_types::Location::new(url, range); 443 let loc = lsp_types::Location::new(url, range);
@@ -438,7 +473,7 @@ fn location_info(
438) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { 473) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> {
439 let line_index = snap.analysis().file_line_index(target.file_id())?; 474 let line_index = snap.analysis().file_line_index(target.file_id())?;
440 475
441 let target_uri = url(snap, target.file_id())?; 476 let target_uri = url(snap, target.file_id());
442 let target_range = range(&line_index, target.full_range()); 477 let target_range = range(&line_index, target.full_range());
443 let target_selection_range = 478 let target_selection_range =
444 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range); 479 target.focus_range().map(|it| range(&line_index, it)).unwrap_or(target_range);
@@ -478,7 +513,7 @@ pub(crate) fn snippet_text_document_edit(
478 is_snippet: bool, 513 is_snippet: bool,
479 source_file_edit: SourceFileEdit, 514 source_file_edit: SourceFileEdit,
480) -> Result<lsp_ext::SnippetTextDocumentEdit> { 515) -> Result<lsp_ext::SnippetTextDocumentEdit> {
481 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None)?; 516 let text_document = versioned_text_document_identifier(snap, source_file_edit.file_id, None);
482 let line_index = snap.analysis().file_line_index(source_file_edit.file_id)?; 517 let line_index = snap.analysis().file_line_index(source_file_edit.file_id)?;
483 let line_endings = snap.file_line_endings(source_file_edit.file_id); 518 let line_endings = snap.file_line_endings(source_file_edit.file_id);
484 let edits = source_file_edit 519 let edits = source_file_edit
@@ -492,19 +527,18 @@ pub(crate) fn snippet_text_document_edit(
492pub(crate) fn resource_op( 527pub(crate) fn resource_op(
493 snap: &GlobalStateSnapshot, 528 snap: &GlobalStateSnapshot,
494 file_system_edit: FileSystemEdit, 529 file_system_edit: FileSystemEdit,
495) -> Result<lsp_types::ResourceOp> { 530) -> lsp_types::ResourceOp {
496 let res = match file_system_edit { 531 match file_system_edit {
497 FileSystemEdit::CreateFile { source_root, path } => { 532 FileSystemEdit::CreateFile { anchor, dst } => {
498 let uri = snap.path_to_uri(source_root, &path)?; 533 let uri = snap.anchored_path(anchor, &dst);
499 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None }) 534 lsp_types::ResourceOp::Create(lsp_types::CreateFile { uri, options: None })
500 } 535 }
501 FileSystemEdit::MoveFile { src, dst_source_root, dst_path } => { 536 FileSystemEdit::MoveFile { src, anchor, dst } => {
502 let old_uri = snap.file_id_to_uri(src)?; 537 let old_uri = snap.file_id_to_url(src);
503 let new_uri = snap.path_to_uri(dst_source_root, &dst_path)?; 538 let new_uri = snap.anchored_path(anchor, &dst);
504 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None }) 539 lsp_types::ResourceOp::Rename(lsp_types::RenameFile { old_uri, new_uri, options: None })
505 } 540 }
506 }; 541 }
507 Ok(res)
508} 542}
509 543
510pub(crate) fn snippet_workspace_edit( 544pub(crate) fn snippet_workspace_edit(
@@ -513,7 +547,7 @@ pub(crate) fn snippet_workspace_edit(
513) -> Result<lsp_ext::SnippetWorkspaceEdit> { 547) -> Result<lsp_ext::SnippetWorkspaceEdit> {
514 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); 548 let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new();
515 for op in source_change.file_system_edits { 549 for op in source_change.file_system_edits {
516 let op = resource_op(&snap, op)?; 550 let op = resource_op(&snap, op);
517 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op)); 551 document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Op(op));
518 } 552 }
519 for edit in source_change.source_file_edits { 553 for edit in source_change.source_file_edits {
@@ -568,7 +602,7 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit {
568 } 602 }
569} 603}
570 604
571pub fn call_hierarchy_item( 605pub(crate) fn call_hierarchy_item(
572 snap: &GlobalStateSnapshot, 606 snap: &GlobalStateSnapshot,
573 target: NavigationTarget, 607 target: NavigationTarget,
574) -> Result<lsp_types::CallHierarchyItem> { 608) -> Result<lsp_types::CallHierarchyItem> {
@@ -579,50 +613,6 @@ pub fn call_hierarchy_item(
579 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range }) 613 Ok(lsp_types::CallHierarchyItem { name, kind, tags: None, detail, uri, range, selection_range })
580} 614}
581 615
582#[cfg(test)]
583mod tests {
584 use test_utils::extract_ranges;
585
586 use super::*;
587
588 #[test]
589 fn conv_fold_line_folding_only_fixup() {
590 let text = r#"<fold>mod a;
591mod b;
592mod c;</fold>
593
594fn main() <fold>{
595 if cond <fold>{
596 a::do_a();
597 }</fold> else <fold>{
598 b::do_b();
599 }</fold>
600}</fold>"#;
601
602 let (ranges, text) = extract_ranges(text, "fold");
603 assert_eq!(ranges.len(), 4);
604 let folds = vec![
605 Fold { range: ranges[0], kind: FoldKind::Mods },
606 Fold { range: ranges[1], kind: FoldKind::Block },
607 Fold { range: ranges[2], kind: FoldKind::Block },
608 Fold { range: ranges[3], kind: FoldKind::Block },
609 ];
610
611 let line_index = LineIndex::new(&text);
612 let converted: Vec<lsp_types::FoldingRange> =
613 folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect();
614
615 let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
616 assert_eq!(converted.len(), expected_lines.len());
617 for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) {
618 assert_eq!(folding_range.start_line, *start_line);
619 assert_eq!(folding_range.start_character, None);
620 assert_eq!(folding_range.end_line, *end_line);
621 assert_eq!(folding_range.end_character, None);
622 }
623 }
624}
625
626pub(crate) fn unresolved_code_action( 616pub(crate) fn unresolved_code_action(
627 snap: &GlobalStateSnapshot, 617 snap: &GlobalStateSnapshot,
628 assist: Assist, 618 assist: Assist,
@@ -676,3 +666,62 @@ pub(crate) fn runnable(
676 }, 666 },
677 }) 667 })
678} 668}
669
670#[cfg(test)]
671mod tests {
672 use test_utils::extract_ranges;
673
674 use super::*;
675
676 #[test]
677 fn conv_fold_line_folding_only_fixup() {
678 let text = r#"<fold>mod a;
679mod b;
680mod c;</fold>
681
682fn main() <fold>{
683 if cond <fold>{
684 a::do_a();
685 }</fold> else <fold>{
686 b::do_b();
687 }</fold>
688}</fold>"#;
689
690 let (ranges, text) = extract_ranges(text, "fold");
691 assert_eq!(ranges.len(), 4);
692 let folds = vec![
693 Fold { range: ranges[0], kind: FoldKind::Mods },
694 Fold { range: ranges[1], kind: FoldKind::Block },
695 Fold { range: ranges[2], kind: FoldKind::Block },
696 Fold { range: ranges[3], kind: FoldKind::Block },
697 ];
698
699 let line_index = LineIndex::new(&text);
700 let converted: Vec<lsp_types::FoldingRange> =
701 folds.into_iter().map(|it| folding_range(&text, &line_index, true, it)).collect();
702
703 let expected_lines = [(0, 2), (4, 10), (5, 6), (7, 9)];
704 assert_eq!(converted.len(), expected_lines.len());
705 for (folding_range, (start_line, end_line)) in converted.iter().zip(expected_lines.iter()) {
706 assert_eq!(folding_range.start_line, *start_line);
707 assert_eq!(folding_range.start_character, None);
708 assert_eq!(folding_range.end_line, *end_line);
709 assert_eq!(folding_range.end_character, None);
710 }
711 }
712
713 // `Url` is not able to parse windows paths on unix machines.
714 #[test]
715 #[cfg(target_os = "windows")]
716 fn test_lowercase_drive_letter_with_drive() {
717 let url = url_from_abs_path(Path::new("C:\\Test"));
718 assert_eq!(url.to_string(), "file:///c:/Test");
719 }
720
721 #[test]
722 #[cfg(target_os = "windows")]
723 fn test_drive_without_colon_passthrough() {
724 let url = url_from_abs_path(Path::new(r#"\\localhost\C$\my_dir"#));
725 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
726 }
727}
diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs
index 2141bfc20..981565cd7 100644
--- a/crates/test_utils/src/lib.rs
+++ b/crates/test_utils/src/lib.rs
@@ -10,17 +10,17 @@
10pub mod mark; 10pub mod mark;
11 11
12use std::{ 12use std::{
13 fs, 13 env, fs,
14 path::{Path, PathBuf}, 14 path::{Path, PathBuf},
15}; 15};
16 16
17pub use ra_cfg::CfgOptions; 17use serde_json::Value;
18use stdx::split1; 18use stdx::split1;
19use text_size::{TextRange, TextSize};
19 20
21pub use ra_cfg::CfgOptions;
20pub use relative_path::{RelativePath, RelativePathBuf}; 22pub use relative_path::{RelativePath, RelativePathBuf};
21pub use rustc_hash::FxHashMap; 23pub use rustc_hash::FxHashMap;
22use serde_json::Value;
23use text_size::{TextRange, TextSize};
24 24
25pub use difference::Changeset as __Changeset; 25pub use difference::Changeset as __Changeset;
26 26
@@ -625,8 +625,6 @@ pub fn skip_slow_tests() -> bool {
625 should_skip 625 should_skip
626} 626}
627 627
628const REWRITE: bool = false;
629
630/// Asserts that `expected` and `actual` strings are equal. If they differ only 628/// Asserts that `expected` and `actual` strings are equal. If they differ only
631/// in trailing or leading whitespace the test won't fail and 629/// in trailing or leading whitespace the test won't fail and
632/// the contents of `actual` will be written to the file located at `path`. 630/// the contents of `actual` will be written to the file located at `path`.
@@ -642,7 +640,7 @@ fn assert_equal_text(expected: &str, actual: &str, path: &Path) {
642 fs::write(path, actual).unwrap(); 640 fs::write(path, actual).unwrap();
643 return; 641 return;
644 } 642 }
645 if REWRITE { 643 if env::var("UPDATE_EXPECTATIONS").is_ok() {
646 println!("rewriting {}", pretty_path.display()); 644 println!("rewriting {}", pretty_path.display());
647 fs::write(path, actual).unwrap(); 645 fs::write(path, actual).unwrap();
648 return; 646 return;
diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml
new file mode 100644
index 000000000..c03e6363b
--- /dev/null
+++ b/crates/vfs/Cargo.toml
@@ -0,0 +1,14 @@
1[package]
2name = "vfs"
3version = "0.1.0"
4authors = ["rust-analyzer developers"]
5edition = "2018"
6
7[dependencies]
8rustc-hash = "1.0"
9jod-thread = "0.1.0"
10walkdir = "2.3.1"
11globset = "0.4.5"
12crossbeam-channel = "0.4.0"
13
14paths = { path = "../paths" }
diff --git a/crates/vfs/src/file_set.rs b/crates/vfs/src/file_set.rs
new file mode 100644
index 000000000..7dc721f7e
--- /dev/null
+++ b/crates/vfs/src/file_set.rs
@@ -0,0 +1,99 @@
1//! Partitions a list of files into disjoint subsets.
2//!
3//! Files which do not belong to any explicitly configured `FileSet` belong to
4//! the default `FileSet`.
5use std::{cmp, fmt, iter};
6
7use paths::AbsPathBuf;
8use rustc_hash::FxHashMap;
9
10use crate::{FileId, Vfs, VfsPath};
11
12#[derive(Default, Clone, Eq, PartialEq)]
13pub struct FileSet {
14 files: FxHashMap<VfsPath, FileId>,
15 paths: FxHashMap<FileId, VfsPath>,
16}
17
18impl FileSet {
19 pub fn resolve_path(&self, anchor: FileId, path: &str) -> Option<FileId> {
20 let mut base = self.paths[&anchor].clone();
21 base.pop();
22 let path = base.join(path);
23 let res = self.files.get(&path).copied();
24 res
25 }
26 pub fn insert(&mut self, file_id: FileId, path: VfsPath) {
27 self.files.insert(path.clone(), file_id);
28 self.paths.insert(file_id, path);
29 }
30 pub fn iter(&self) -> impl Iterator<Item = FileId> + '_ {
31 self.paths.keys().copied()
32 }
33}
34
35impl fmt::Debug for FileSet {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 f.debug_struct("FileSet").field("n_files", &self.files.len()).finish()
38 }
39}
40
41#[derive(Debug)]
42pub struct FileSetConfig {
43 n_file_sets: usize,
44 roots: Vec<(AbsPathBuf, usize)>,
45}
46
47impl FileSetConfig {
48 pub fn builder() -> FileSetConfigBuilder {
49 FileSetConfigBuilder::default()
50 }
51 pub fn partition(&self, vfs: &Vfs) -> Vec<FileSet> {
52 let mut res = vec![FileSet::default(); self.len()];
53 for (file_id, path) in vfs.iter() {
54 let root = self.classify(&path);
55 res[root].insert(file_id, path)
56 }
57 res
58 }
59 fn len(&self) -> usize {
60 self.n_file_sets
61 }
62 fn classify(&self, path: &VfsPath) -> usize {
63 for (root, idx) in self.roots.iter() {
64 if let Some(path) = path.as_path() {
65 if path.starts_with(root) {
66 return *idx;
67 }
68 }
69 }
70 self.len() - 1
71 }
72}
73
74pub struct FileSetConfigBuilder {
75 roots: Vec<Vec<AbsPathBuf>>,
76}
77
78impl Default for FileSetConfigBuilder {
79 fn default() -> Self {
80 FileSetConfigBuilder { roots: Vec::new() }
81 }
82}
83
84impl FileSetConfigBuilder {
85 pub fn add_file_set(&mut self, roots: Vec<AbsPathBuf>) {
86 self.roots.push(roots)
87 }
88 pub fn build(self) -> FileSetConfig {
89 let n_file_sets = self.roots.len() + 1;
90 let mut roots: Vec<(AbsPathBuf, usize)> = self
91 .roots
92 .into_iter()
93 .enumerate()
94 .flat_map(|(i, paths)| paths.into_iter().zip(iter::repeat(i)))
95 .collect();
96 roots.sort_by_key(|(path, _)| cmp::Reverse(path.to_string_lossy().len()));
97 FileSetConfig { n_file_sets, roots }
98 }
99}
diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs
new file mode 100644
index 000000000..75ce61cf9
--- /dev/null
+++ b/crates/vfs/src/lib.rs
@@ -0,0 +1,138 @@
1//! # Virtual File System
2//!
3//! VFS stores all files read by rust-analyzer. Reading file contents from VFS
4//! always returns the same contents, unless VFS was explicitly modified with
5//! `set_file_contents`. All changes to VFS are logged, and can be retrieved via
6//! `take_changes` method. The pack of changes is then pushed to `salsa` and
7//! triggers incremental recomputation.
8//!
9//! Files in VFS are identified with `FileId`s -- interned paths. The notion of
10//! the path, `VfsPath` is somewhat abstract: at the moment, it is represented
11//! as an `std::path::PathBuf` internally, but this is an implementation detail.
12//!
13//! VFS doesn't do IO or file watching itself. For that, see the `loader`
14//! module. `loader::Handle` is an object-safe trait which abstracts both file
15//! loading and file watching. `Handle` is dynamically configured with a set of
16//! directory entries which should be scanned and watched. `Handle` then
17//! asynchronously pushes file changes. Directory entries are configured in
18//! free-form via list of globs, it's up to the `Handle` to interpret the globs
19//! in any specific way.
20//!
21//! A simple `WalkdirLoaderHandle` is provided, which doesn't implement watching
22//! and just scans the directory using walkdir.
23//!
24//! VFS stores a flat list of files. `FileSet` can partition this list of files
25//! into disjoint sets of files. Traversal-like operations (including getting
26//! the neighbor file by the relative path) are handled by the `FileSet`.
27//! `FileSet`s are also pushed to salsa and cause it to re-check `mod foo;`
28//! declarations when files are created or deleted.
29//!
30//! `file_set::FileSet` and `loader::Entry` play similar, but different roles.
31//! Both specify the "set of paths/files", one is geared towards file watching,
32//! the other towards salsa changes. In particular, single `file_set::FileSet`
33//! may correspond to several `loader::Entry`. For example, a crate from
34//! crates.io which uses code generation would have two `Entries` -- for sources
35//! in `~/.cargo`, and for generated code in `./target/debug/build`. It will
36//! have a single `FileSet` which unions the two sources.
37mod vfs_path;
38mod path_interner;
39pub mod file_set;
40pub mod loader;
41pub mod walkdir_loader;
42
43use std::{fmt, mem};
44
45use crate::path_interner::PathInterner;
46
47pub use crate::vfs_path::VfsPath;
48pub use paths::{AbsPath, AbsPathBuf};
49
50#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
51pub struct FileId(pub u32);
52
53#[derive(Default)]
54pub struct Vfs {
55 interner: PathInterner,
56 data: Vec<Option<Vec<u8>>>,
57 changes: Vec<ChangedFile>,
58}
59
60pub struct ChangedFile {
61 pub file_id: FileId,
62 pub change_kind: ChangeKind,
63}
64
65impl ChangedFile {
66 pub fn exists(&self) -> bool {
67 self.change_kind != ChangeKind::Delete
68 }
69 pub fn is_created_or_deleted(&self) -> bool {
70 matches!(self.change_kind, ChangeKind::Create | ChangeKind::Delete)
71 }
72}
73
74#[derive(Eq, PartialEq)]
75pub enum ChangeKind {
76 Create,
77 Modify,
78 Delete,
79}
80
81impl Vfs {
82 pub fn file_id(&self, path: &VfsPath) -> Option<FileId> {
83 self.interner.get(path).filter(|&it| self.get(it).is_some())
84 }
85 pub fn file_path(&self, file_id: FileId) -> VfsPath {
86 self.interner.lookup(file_id).clone()
87 }
88 pub fn file_contents(&self, file_id: FileId) -> &[u8] {
89 self.get(file_id).as_deref().unwrap()
90 }
91 pub fn iter(&self) -> impl Iterator<Item = (FileId, VfsPath)> + '_ {
92 (0..self.data.len())
93 .map(|it| FileId(it as u32))
94 .filter(move |&file_id| self.get(file_id).is_some())
95 .map(move |file_id| {
96 let path = self.interner.lookup(file_id).clone();
97 (file_id, path)
98 })
99 }
100 pub fn set_file_contents(&mut self, path: VfsPath, contents: Option<Vec<u8>>) {
101 let file_id = self.alloc_file_id(path);
102 let change_kind = match (&self.get(file_id), &contents) {
103 (None, None) => return,
104 (None, Some(_)) => ChangeKind::Create,
105 (Some(_), None) => ChangeKind::Delete,
106 (Some(old), Some(new)) if old == new => return,
107 (Some(_), Some(_)) => ChangeKind::Modify,
108 };
109
110 *self.get_mut(file_id) = contents;
111 self.changes.push(ChangedFile { file_id, change_kind })
112 }
113 pub fn has_changes(&self) -> bool {
114 !self.changes.is_empty()
115 }
116 pub fn take_changes(&mut self) -> Vec<ChangedFile> {
117 mem::take(&mut self.changes)
118 }
119 fn alloc_file_id(&mut self, path: VfsPath) -> FileId {
120 let file_id = self.interner.intern(path);
121 let idx = file_id.0 as usize;
122 let len = self.data.len().max(idx + 1);
123 self.data.resize_with(len, || None);
124 file_id
125 }
126 fn get(&self, file_id: FileId) -> &Option<Vec<u8>> {
127 &self.data[file_id.0 as usize]
128 }
129 fn get_mut(&mut self, file_id: FileId) -> &mut Option<Vec<u8>> {
130 &mut self.data[file_id.0 as usize]
131 }
132}
133
134impl fmt::Debug for Vfs {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 f.debug_struct("Vfs").field("n_files", &self.data.len()).finish()
137 }
138}
diff --git a/crates/vfs/src/loader.rs b/crates/vfs/src/loader.rs
new file mode 100644
index 000000000..5a0ca68f3
--- /dev/null
+++ b/crates/vfs/src/loader.rs
@@ -0,0 +1,69 @@
1//! Object safe interface for file watching and reading.
2use std::fmt;
3
4use paths::AbsPathBuf;
5
6pub enum Entry {
7 Files(Vec<AbsPathBuf>),
8 Directory { path: AbsPathBuf, globs: Vec<String> },
9}
10
11pub struct Config {
12 pub load: Vec<Entry>,
13 pub watch: Vec<usize>,
14}
15
16pub enum Message {
17 DidSwitchConfig { n_entries: usize },
18 DidLoadAllEntries,
19 Loaded { files: Vec<(AbsPathBuf, Option<Vec<u8>>)> },
20}
21
22pub type Sender = Box<dyn Fn(Message) + Send>;
23
24pub trait Handle: fmt::Debug {
25 fn spawn(sender: Sender) -> Self
26 where
27 Self: Sized;
28 fn set_config(&mut self, config: Config);
29 fn invalidate(&mut self, path: AbsPathBuf);
30 fn load_sync(&mut self, path: &AbsPathBuf) -> Option<Vec<u8>>;
31}
32
33impl Entry {
34 pub fn rs_files_recursively(base: AbsPathBuf) -> Entry {
35 Entry::Directory { path: base, globs: globs(&["*.rs"]) }
36 }
37 pub fn local_cargo_package(base: AbsPathBuf) -> Entry {
38 Entry::Directory { path: base, globs: globs(&["*.rs", "!/target/"]) }
39 }
40 pub fn cargo_package_dependency(base: AbsPathBuf) -> Entry {
41 Entry::Directory {
42 path: base,
43 globs: globs(&["*.rs", "!/tests/", "!/examples/", "!/benches/"]),
44 }
45 }
46}
47
48fn globs(globs: &[&str]) -> Vec<String> {
49 globs.iter().map(|it| it.to_string()).collect()
50}
51
52impl fmt::Debug for Message {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 match self {
55 Message::Loaded { files } => {
56 f.debug_struct("Loaded").field("n_files", &files.len()).finish()
57 }
58 Message::DidSwitchConfig { n_entries } => {
59 f.debug_struct("DidSwitchConfig").field("n_entries", n_entries).finish()
60 }
61 Message::DidLoadAllEntries => f.debug_struct("DidLoadAllEntries").finish(),
62 }
63 }
64}
65
66#[test]
67fn handle_is_object_safe() {
68 fn _assert(_: &dyn Handle) {}
69}
diff --git a/crates/vfs/src/path_interner.rs b/crates/vfs/src/path_interner.rs
new file mode 100644
index 000000000..4f70d61e8
--- /dev/null
+++ b/crates/vfs/src/path_interner.rs
@@ -0,0 +1,31 @@
1//! Maps paths to compact integer ids. We don't care about clearings paths which
2//! no longer exist -- the assumption is total size of paths we ever look at is
3//! not too big.
4use rustc_hash::FxHashMap;
5
6use crate::{FileId, VfsPath};
7
8#[derive(Default)]
9pub(crate) struct PathInterner {
10 map: FxHashMap<VfsPath, FileId>,
11 vec: Vec<VfsPath>,
12}
13
14impl PathInterner {
15 pub(crate) fn get(&self, path: &VfsPath) -> Option<FileId> {
16 self.map.get(path).copied()
17 }
18 pub(crate) fn intern(&mut self, path: VfsPath) -> FileId {
19 if let Some(id) = self.get(&path) {
20 return id;
21 }
22 let id = FileId(self.vec.len() as u32);
23 self.map.insert(path.clone(), id);
24 self.vec.push(path);
25 id
26 }
27
28 pub(crate) fn lookup(&self, id: FileId) -> &VfsPath {
29 &self.vec[id.0 as usize]
30 }
31}
diff --git a/crates/vfs/src/vfs_path.rs b/crates/vfs/src/vfs_path.rs
new file mode 100644
index 000000000..de5dc0bf3
--- /dev/null
+++ b/crates/vfs/src/vfs_path.rs
@@ -0,0 +1,49 @@
1//! Abstract-ish representation of paths for VFS.
2use std::fmt;
3
4use paths::{AbsPath, AbsPathBuf};
5
6/// Long-term, we want to support files which do not reside in the file-system,
7/// so we treat VfsPaths as opaque identifiers.
8#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
9pub struct VfsPath(VfsPathRepr);
10
11impl VfsPath {
12 pub fn as_path(&self) -> Option<&AbsPath> {
13 match &self.0 {
14 VfsPathRepr::PathBuf(it) => Some(it.as_path()),
15 }
16 }
17 pub fn join(&self, path: &str) -> VfsPath {
18 match &self.0 {
19 VfsPathRepr::PathBuf(it) => {
20 let res = it.join(path).normalize();
21 VfsPath(VfsPathRepr::PathBuf(res))
22 }
23 }
24 }
25 pub fn pop(&mut self) -> bool {
26 match &mut self.0 {
27 VfsPathRepr::PathBuf(it) => it.pop(),
28 }
29 }
30}
31
32#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
33enum VfsPathRepr {
34 PathBuf(AbsPathBuf),
35}
36
37impl From<AbsPathBuf> for VfsPath {
38 fn from(v: AbsPathBuf) -> Self {
39 VfsPath(VfsPathRepr::PathBuf(v))
40 }
41}
42
43impl fmt::Display for VfsPath {
44 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
45 match &self.0 {
46 VfsPathRepr::PathBuf(it) => fmt::Display::fmt(&it.display(), f),
47 }
48 }
49}
diff --git a/crates/vfs/src/walkdir_loader.rs b/crates/vfs/src/walkdir_loader.rs
new file mode 100644
index 000000000..13e59e3f3
--- /dev/null
+++ b/crates/vfs/src/walkdir_loader.rs
@@ -0,0 +1,108 @@
1//! A walkdir-based implementation of `loader::Handle`, which doesn't try to
2//! watch files.
3use std::convert::TryFrom;
4
5use globset::{Glob, GlobSetBuilder};
6use paths::{AbsPath, AbsPathBuf};
7use walkdir::WalkDir;
8
9use crate::loader;
10
11#[derive(Debug)]
12pub struct WalkdirLoaderHandle {
13 // Relative order of fields below is significant.
14 sender: crossbeam_channel::Sender<Message>,
15 _thread: jod_thread::JoinHandle,
16}
17
18enum Message {
19 Config(loader::Config),
20 Invalidate(AbsPathBuf),
21}
22
23impl loader::Handle for WalkdirLoaderHandle {
24 fn spawn(sender: loader::Sender) -> WalkdirLoaderHandle {
25 let actor = WalkdirLoaderActor { sender };
26 let (sender, receiver) = crossbeam_channel::unbounded::<Message>();
27 let thread = jod_thread::spawn(move || actor.run(receiver));
28 WalkdirLoaderHandle { sender, _thread: thread }
29 }
30 fn set_config(&mut self, config: loader::Config) {
31 self.sender.send(Message::Config(config)).unwrap()
32 }
33 fn invalidate(&mut self, path: AbsPathBuf) {
34 self.sender.send(Message::Invalidate(path)).unwrap();
35 }
36 fn load_sync(&mut self, path: &AbsPathBuf) -> Option<Vec<u8>> {
37 read(path)
38 }
39}
40
41struct WalkdirLoaderActor {
42 sender: loader::Sender,
43}
44
45impl WalkdirLoaderActor {
46 fn run(mut self, receiver: crossbeam_channel::Receiver<Message>) {
47 for msg in receiver {
48 match msg {
49 Message::Config(config) => {
50 self.send(loader::Message::DidSwitchConfig { n_entries: config.load.len() });
51 for entry in config.load.into_iter() {
52 let files = self.load_entry(entry);
53 self.send(loader::Message::Loaded { files });
54 }
55 drop(config.watch);
56 self.send(loader::Message::DidLoadAllEntries);
57 }
58 Message::Invalidate(path) => {
59 let contents = read(path.as_path());
60 let files = vec![(path, contents)];
61 self.send(loader::Message::Loaded { files });
62 }
63 }
64 }
65 }
66 fn load_entry(&mut self, entry: loader::Entry) -> Vec<(AbsPathBuf, Option<Vec<u8>>)> {
67 match entry {
68 loader::Entry::Files(files) => files
69 .into_iter()
70 .map(|file| {
71 let contents = read(file.as_path());
72 (file, contents)
73 })
74 .collect::<Vec<_>>(),
75 loader::Entry::Directory { path, globs } => {
76 let globset = {
77 let mut builder = GlobSetBuilder::new();
78 for glob in &globs {
79 builder.add(Glob::new(glob).unwrap());
80 }
81 builder.build().unwrap()
82 };
83
84 let files = WalkDir::new(path)
85 .into_iter()
86 .filter_map(|it| it.ok())
87 .filter(|it| it.file_type().is_file())
88 .map(|it| it.into_path())
89 .map(|it| AbsPathBuf::try_from(it).unwrap())
90 .filter(|it| globset.is_match(&it));
91
92 files
93 .map(|file| {
94 let contents = read(file.as_path());
95 (file, contents)
96 })
97 .collect()
98 }
99 }
100 }
101 fn send(&mut self, msg: loader::Message) {
102 (self.sender)(msg)
103 }
104}
105
106fn read(path: &AbsPath) -> Option<Vec<u8>> {
107 std::fs::read(path).ok()
108}