aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/gen_lsp_server/Cargo.toml4
-rw-r--r--crates/ra_analysis/Cargo.toml4
-rw-r--r--crates/ra_analysis/src/completion/complete_dot.rs17
-rw-r--r--crates/ra_analysis/src/completion/complete_keyword.rs82
-rw-r--r--crates/ra_analysis/src/completion/complete_path.rs51
-rw-r--r--crates/ra_analysis/src/completion/complete_scope.rs41
-rw-r--r--crates/ra_analysis/src/completion/complete_snippet.rs2
-rw-r--r--crates/ra_analysis/src/completion/completion_context.rs37
-rw-r--r--crates/ra_analysis/src/completion/completion_item.rs30
-rw-r--r--crates/ra_analysis/src/db.rs1
-rw-r--r--crates/ra_analysis/src/extend_selection.rs51
-rw-r--r--crates/ra_analysis/src/imp.rs86
-rw-r--r--crates/ra_analysis/src/lib.rs37
-rw-r--r--crates/ra_analysis/src/macros.rs75
-rw-r--r--crates/ra_analysis/src/mock_analysis.rs17
-rw-r--r--crates/ra_analysis/src/syntax_highlighting.rs63
-rw-r--r--crates/ra_db/Cargo.toml4
-rw-r--r--crates/ra_db/src/lib.rs10
-rw-r--r--crates/ra_editor/Cargo.toml2
-rw-r--r--crates/ra_editor/src/diagnostics.rs276
-rw-r--r--crates/ra_editor/src/lib.rs149
-rw-r--r--crates/ra_hir/Cargo.toml7
-rw-r--r--crates/ra_hir/src/adt.rs45
-rw-r--r--crates/ra_hir/src/db.rs25
-rw-r--r--crates/ra_hir/src/function.rs24
-rw-r--r--crates/ra_hir/src/function/scope.rs24
-rw-r--r--crates/ra_hir/src/krate.rs7
-rw-r--r--crates/ra_hir/src/lib.rs3
-rw-r--r--crates/ra_hir/src/mock.rs1
-rw-r--r--crates/ra_hir/src/module.rs30
-rw-r--r--crates/ra_hir/src/module/imp.rs19
-rw-r--r--crates/ra_hir/src/module/nameres.rs25
-rw-r--r--crates/ra_hir/src/module/nameres/tests.rs66
-rw-r--r--crates/ra_hir/src/name.rs105
-rw-r--r--crates/ra_hir/src/path.rs18
-rw-r--r--crates/ra_hir/src/query_definitions.rs43
-rw-r--r--crates/ra_hir/src/source_binder.rs23
-rw-r--r--crates/ra_hir/src/ty.rs557
-rw-r--r--crates/ra_hir/src/ty/primitive.rs42
-rw-r--r--crates/ra_hir/src/ty/tests.rs21
-rw-r--r--crates/ra_hir/src/ty/tests/data/0002_let.txt2
-rw-r--r--crates/ra_hir/src/ty/tests/data/0003_paths.txt8
-rw-r--r--crates/ra_hir/src/ty/tests/data/0004_struct.txt4
-rw-r--r--crates/ra_hir/src/ty/tests/data/0006_backwards.txt20
-rw-r--r--crates/ra_lsp_server/Cargo.toml5
-rw-r--r--crates/ra_lsp_server/src/caps.rs2
-rw-r--r--crates/ra_lsp_server/src/conv.rs14
-rw-r--r--crates/ra_lsp_server/src/main_loop.rs1
-rw-r--r--crates/ra_lsp_server/src/main_loop/handlers.rs57
-rw-r--r--crates/ra_lsp_server/tests/heavy_tests/main.rs58
-rw-r--r--crates/ra_syntax/Cargo.toml6
-rw-r--r--crates/ra_syntax/fuzz/Cargo.lock530
-rw-r--r--crates/ra_syntax/src/ast/generated.rs45
-rw-r--r--crates/ra_syntax/src/grammar.ron1
-rw-r--r--crates/ra_syntax/src/string_lexing/parser.rs2
-rw-r--r--crates/ra_syntax/src/validation/char.rs2
-rw-r--r--crates/ra_syntax/tests/data/parser/fuzz-failures/0002.rs1
-rw-r--r--crates/ra_vfs/src/io.rs4
-rw-r--r--crates/ra_vfs/src/lib.rs16
-rw-r--r--crates/test_utils/Cargo.toml2
-rw-r--r--crates/tools/Cargo.toml2
-rw-r--r--crates/tools/src/lib.rs2
62 files changed, 2273 insertions, 635 deletions
diff --git a/crates/gen_lsp_server/Cargo.toml b/crates/gen_lsp_server/Cargo.toml
index 9776a82e3..afeeb1513 100644
--- a/crates/gen_lsp_server/Cargo.toml
+++ b/crates/gen_lsp_server/Cargo.toml
@@ -8,9 +8,9 @@ license = "MIT OR Apache-2.0"
8description = "Generic LSP server scaffold." 8description = "Generic LSP server scaffold."
9 9
10[dependencies] 10[dependencies]
11languageserver-types = "0.53.0" 11languageserver-types = "0.53.1"
12log = "0.4.3" 12log = "0.4.3"
13failure = "0.1.2" 13failure = "0.1.2"
14serde_json = "1.0.24" 14serde_json = "1.0.24"
15serde = { version = "1.0.71", features = ["derive"] } 15serde = { version = "1.0.83", features = ["derive"] }
16crossbeam-channel = "0.2.4" 16crossbeam-channel = "0.2.4"
diff --git a/crates/ra_analysis/Cargo.toml b/crates/ra_analysis/Cargo.toml
index 4a7b99947..a5d4f65ab 100644
--- a/crates/ra_analysis/Cargo.toml
+++ b/crates/ra_analysis/Cargo.toml
@@ -9,9 +9,9 @@ log = "0.4.5"
9relative-path = "0.4.0" 9relative-path = "0.4.0"
10rayon = "1.0.2" 10rayon = "1.0.2"
11fst = "0.3.1" 11fst = "0.3.1"
12salsa = "0.8.0" 12salsa = "0.9.0"
13rustc-hash = "1.0" 13rustc-hash = "1.0"
14parking_lot = "0.6.4" 14parking_lot = "0.7.0"
15ra_syntax = { path = "../ra_syntax" } 15ra_syntax = { path = "../ra_syntax" }
16ra_editor = { path = "../ra_editor" } 16ra_editor = { path = "../ra_editor" }
17ra_text_edit = { path = "../ra_text_edit" } 17ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_analysis/src/completion/complete_dot.rs b/crates/ra_analysis/src/completion/complete_dot.rs
index 93d657576..f24835d17 100644
--- a/crates/ra_analysis/src/completion/complete_dot.rs
+++ b/crates/ra_analysis/src/completion/complete_dot.rs
@@ -6,20 +6,9 @@ use crate::completion::{CompletionContext, Completions, CompletionKind, Completi
6 6
7/// Complete dot accesses, i.e. fields or methods (currently only fields). 7/// Complete dot accesses, i.e. fields or methods (currently only fields).
8pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { 8pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> {
9 let module = if let Some(module) = &ctx.module { 9 let (function, receiver) = match (&ctx.function, ctx.dot_receiver) {
10 module 10 (Some(function), Some(receiver)) => (function, receiver),
11 } else { 11 _ => return Ok(()),
12 return Ok(());
13 };
14 let function = if let Some(fn_def) = ctx.enclosing_fn {
15 hir::source_binder::function_from_module(ctx.db, module, fn_def)
16 } else {
17 return Ok(());
18 };
19 let receiver = if let Some(receiver) = ctx.dot_receiver {
20 receiver
21 } else {
22 return Ok(());
23 }; 12 };
24 let infer_result = function.infer(ctx.db)?; 13 let infer_result = function.infer(ctx.db)?;
25 let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) { 14 let receiver_ty = if let Some(ty) = infer_result.type_of_node(receiver.syntax()) {
diff --git a/crates/ra_analysis/src/completion/complete_keyword.rs b/crates/ra_analysis/src/completion/complete_keyword.rs
index 5427fcb11..b2486104a 100644
--- a/crates/ra_analysis/src/completion/complete_keyword.rs
+++ b/crates/ra_analysis/src/completion/complete_keyword.rs
@@ -18,7 +18,7 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
18 if !ctx.is_trivial_path { 18 if !ctx.is_trivial_path {
19 return; 19 return;
20 } 20 }
21 let fn_def = match ctx.enclosing_fn { 21 let fn_def = match ctx.function_syntax {
22 Some(it) => it, 22 Some(it) => it,
23 None => return, 23 None => return,
24 }; 24 };
@@ -32,10 +32,15 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte
32 acc.add(keyword("else if", "else if $0 {}")); 32 acc.add(keyword("else if", "else if $0 {}"));
33 } 33 }
34 if is_in_loop_body(ctx.leaf) { 34 if is_in_loop_body(ctx.leaf) {
35 acc.add(keyword("continue", "continue")); 35 if ctx.can_be_stmt {
36 acc.add(keyword("break", "break")); 36 acc.add(keyword("continue", "continue;"));
37 acc.add(keyword("break", "break;"));
38 } else {
39 acc.add(keyword("continue", "continue"));
40 acc.add(keyword("break", "break"));
41 }
37 } 42 }
38 acc.add_all(complete_return(fn_def, ctx.is_stmt)); 43 acc.add_all(complete_return(fn_def, ctx.can_be_stmt));
39} 44}
40 45
41fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool { 46fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
@@ -57,8 +62,8 @@ fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool {
57 false 62 false
58} 63}
59 64
60fn complete_return(fn_def: ast::FnDef, is_stmt: bool) -> Option<CompletionItem> { 65fn complete_return(fn_def: ast::FnDef, can_be_stmt: bool) -> Option<CompletionItem> {
61 let snip = match (is_stmt, fn_def.ret_type().is_some()) { 66 let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
62 (true, true) => "return $0;", 67 (true, true) => "return $0;",
63 (true, false) => "return;", 68 (true, false) => "return;",
64 (false, true) => "return $0", 69 (false, true) => "return $0",
@@ -75,7 +80,7 @@ mod tests {
75 } 80 }
76 81
77 #[test] 82 #[test]
78 fn test_completion_kewords() { 83 fn completes_various_keywords_in_function() {
79 check_keyword_completion( 84 check_keyword_completion(
80 r" 85 r"
81 fn quux() { 86 fn quux() {
@@ -87,13 +92,13 @@ mod tests {
87 match "match $0 {}" 92 match "match $0 {}"
88 while "while $0 {}" 93 while "while $0 {}"
89 loop "loop {$0}" 94 loop "loop {$0}"
90 return "return" 95 return "return;"
91 "#, 96 "#,
92 ); 97 );
93 } 98 }
94 99
95 #[test] 100 #[test]
96 fn test_completion_else() { 101 fn completes_else_after_if() {
97 check_keyword_completion( 102 check_keyword_completion(
98 r" 103 r"
99 fn quux() { 104 fn quux() {
@@ -109,7 +114,7 @@ mod tests {
109 loop "loop {$0}" 114 loop "loop {$0}"
110 else "else {$0}" 115 else "else {$0}"
111 else if "else if $0 {}" 116 else if "else if $0 {}"
112 return "return" 117 return "return;"
113 "#, 118 "#,
114 ); 119 );
115 } 120 }
@@ -149,7 +154,7 @@ mod tests {
149 } 154 }
150 155
151 #[test] 156 #[test]
152 fn test_completion_return_no_stmt() { 157 fn dont_add_semi_after_return_if_not_a_statement() {
153 check_keyword_completion( 158 check_keyword_completion(
154 r" 159 r"
155 fn quux() -> i32 { 160 fn quux() -> i32 {
@@ -169,7 +174,27 @@ mod tests {
169 } 174 }
170 175
171 #[test] 176 #[test]
172 fn test_continue_break_completion() { 177 fn last_return_in_block_has_semi() {
178 check_keyword_completion(
179 r"
180 fn quux() -> i32 {
181 if condition {
182 <|>
183 }
184 }
185 ",
186 r#"
187 if "if $0 {}"
188 match "match $0 {}"
189 while "while $0 {}"
190 loop "loop {$0}"
191 return "return $0;"
192 "#,
193 );
194 }
195
196 #[test]
197 fn completes_break_and_continue_in_loops() {
173 check_keyword_completion( 198 check_keyword_completion(
174 r" 199 r"
175 fn quux() -> i32 { 200 fn quux() -> i32 {
@@ -181,11 +206,12 @@ mod tests {
181 match "match $0 {}" 206 match "match $0 {}"
182 while "while $0 {}" 207 while "while $0 {}"
183 loop "loop {$0}" 208 loop "loop {$0}"
184 continue "continue" 209 continue "continue;"
185 break "break" 210 break "break;"
186 return "return $0" 211 return "return $0;"
187 "#, 212 "#,
188 ); 213 );
214 // No completion: lambda isolates control flow
189 check_keyword_completion( 215 check_keyword_completion(
190 r" 216 r"
191 fn quux() -> i32 { 217 fn quux() -> i32 {
@@ -197,8 +223,32 @@ mod tests {
197 match "match $0 {}" 223 match "match $0 {}"
198 while "while $0 {}" 224 while "while $0 {}"
199 loop "loop {$0}" 225 loop "loop {$0}"
200 return "return $0" 226 return "return $0;"
201 "#, 227 "#,
202 ); 228 );
203 } 229 }
230
231 #[test]
232 fn no_semi_after_break_continue_in_expr() {
233 check_keyword_completion(
234 r"
235 fn f() {
236 loop {
237 match () {
238 () => br<|>
239 }
240 }
241 }
242 ",
243 r#"
244 if "if $0 {}"
245 match "match $0 {}"
246 while "while $0 {}"
247 loop "loop {$0}"
248 continue "continue"
249 break "break"
250 return "return"
251 "#,
252 )
253 }
204} 254}
diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs
index aaa2c7cee..4723a65a6 100644
--- a/crates/ra_analysis/src/completion/complete_path.rs
+++ b/crates/ra_analysis/src/completion/complete_path.rs
@@ -1,6 +1,6 @@
1use crate::{ 1use crate::{
2 Cancelable, 2 Cancelable,
3 completion::{CompletionItem, Completions, CompletionKind, CompletionContext}, 3 completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext},
4}; 4};
5 5
6pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { 6pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> {
@@ -12,16 +12,25 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C
12 Some(it) => it, 12 Some(it) => it,
13 None => return Ok(()), 13 None => return Ok(()),
14 }; 14 };
15 let target_module = match def_id.resolve(ctx.db)? { 15 match def_id.resolve(ctx.db)? {
16 hir::Def::Module(it) => it, 16 hir::Def::Module(module) => {
17 let module_scope = module.scope(ctx.db)?;
18 module_scope.entries().for_each(|(name, res)| {
19 CompletionItem::new(CompletionKind::Reference, name.to_string())
20 .from_resolution(ctx, res)
21 .add_to(acc)
22 });
23 }
24 hir::Def::Enum(e) => e
25 .variants(ctx.db)?
26 .into_iter()
27 .for_each(|(name, _variant)| {
28 CompletionItem::new(CompletionKind::Reference, name.to_string())
29 .kind(CompletionItemKind::EnumVariant)
30 .add_to(acc)
31 }),
17 _ => return Ok(()), 32 _ => return Ok(()),
18 }; 33 };
19 let module_scope = target_module.scope(ctx.db)?;
20 module_scope.entries().for_each(|(name, res)| {
21 CompletionItem::new(CompletionKind::Reference, name.to_string())
22 .from_resolution(ctx.db, res)
23 .add_to(acc)
24 });
25 Ok(()) 34 Ok(())
26} 35}
27 36
@@ -92,4 +101,28 @@ mod tests {
92 "Spam", 101 "Spam",
93 ); 102 );
94 } 103 }
104
105 #[test]
106 fn completes_enum_variant() {
107 check_reference_completion(
108 "
109 //- /lib.rs
110 enum E { Foo, Bar(i32) }
111 fn foo() { let _ = E::<|> }
112 ",
113 "Foo;Bar",
114 );
115 }
116
117 #[test]
118 fn dont_render_function_parens_in_use_item() {
119 check_reference_completion(
120 "
121 //- /lib.rs
122 mod m { pub fn foo() {} }
123 use crate::m::f<|>;
124 ",
125 "foo",
126 )
127 }
95} 128}
diff --git a/crates/ra_analysis/src/completion/complete_scope.rs b/crates/ra_analysis/src/completion/complete_scope.rs
index a57670e3b..daf666505 100644
--- a/crates/ra_analysis/src/completion/complete_scope.rs
+++ b/crates/ra_analysis/src/completion/complete_scope.rs
@@ -14,8 +14,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
14 Some(it) => it, 14 Some(it) => it,
15 None => return Ok(()), 15 None => return Ok(()),
16 }; 16 };
17 if let Some(fn_def) = ctx.enclosing_fn { 17 if let Some(function) = &ctx.function {
18 let function = hir::source_binder::function_from_module(ctx.db, module, fn_def);
19 let scopes = function.scopes(ctx.db); 18 let scopes = function.scopes(ctx.db);
20 complete_fn(acc, &scopes, ctx.offset); 19 complete_fn(acc, &scopes, ctx.offset);
21 } 20 }
@@ -35,7 +34,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
35 }) 34 })
36 .for_each(|(name, res)| { 35 .for_each(|(name, res)| {
37 CompletionItem::new(CompletionKind::Reference, name.to_string()) 36 CompletionItem::new(CompletionKind::Reference, name.to_string())
38 .from_resolution(ctx.db, res) 37 .from_resolution(ctx, res)
39 .add_to(acc) 38 .add_to(acc)
40 }); 39 });
41 Ok(()) 40 Ok(())
@@ -75,7 +74,7 @@ mod tests {
75 let z = (); 74 let z = ();
76 } 75 }
77 ", 76 ",
78 "y;x;quux", 77 r#"y;x;quux "quux($0)""#,
79 ); 78 );
80 } 79 }
81 80
@@ -93,7 +92,7 @@ mod tests {
93 } 92 }
94 } 93 }
95 ", 94 ",
96 "b;a;quux", 95 r#"b;a;quux "quux()$0""#,
97 ); 96 );
98 } 97 }
99 98
@@ -107,7 +106,7 @@ mod tests {
107 } 106 }
108 } 107 }
109 ", 108 ",
110 "x;quux", 109 r#"x;quux "quux()$0""#,
111 ); 110 );
112 } 111 }
113 112
@@ -121,7 +120,7 @@ mod tests {
121 <|> 120 <|>
122 } 121 }
123 ", 122 ",
124 "quux;Foo;Baz", 123 r#"quux "quux()$0";Foo;Baz"#,
125 ); 124 );
126 } 125 }
127 126
@@ -135,7 +134,7 @@ mod tests {
135 fn quux() { <|> } 134 fn quux() { <|> }
136 } 135 }
137 ", 136 ",
138 "quux;Bar", 137 r#"quux "quux()$0";Bar"#,
139 ); 138 );
140 } 139 }
141 140
@@ -146,12 +145,12 @@ mod tests {
146 struct Foo; 145 struct Foo;
147 fn x() -> <|> 146 fn x() -> <|>
148 ", 147 ",
149 "Foo;x", 148 r#"Foo;x "x()$0""#,
150 ) 149 )
151 } 150 }
152 151
153 #[test] 152 #[test]
154 fn dont_show_to_completions_for_shadowing() { 153 fn dont_show_both_completions_for_shadowing() {
155 check_reference_completion( 154 check_reference_completion(
156 r" 155 r"
157 fn foo() -> { 156 fn foo() -> {
@@ -162,7 +161,7 @@ mod tests {
162 } 161 }
163 } 162 }
164 ", 163 ",
165 "bar;foo", 164 r#"bar;foo "foo()$0""#,
166 ) 165 )
167 } 166 }
168 167
@@ -170,4 +169,24 @@ mod tests {
170 fn completes_self_in_methods() { 169 fn completes_self_in_methods() {
171 check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self") 170 check_reference_completion(r"impl S { fn foo(&self) { <|> } }", "self")
172 } 171 }
172
173 #[test]
174 fn inserts_parens_for_function_calls() {
175 check_reference_completion(
176 r"
177 fn no_args() {}
178 fn main() { no_<|> }
179 ",
180 r#"no_args "no_args()$0"
181 main "main()$0""#,
182 );
183 check_reference_completion(
184 r"
185 fn with_args(x: i32, y: String) {}
186 fn main() { with_<|> }
187 ",
188 r#"main "main()$0"
189 with_args "with_args($0)""#,
190 );
191 }
173} 192}
diff --git a/crates/ra_analysis/src/completion/complete_snippet.rs b/crates/ra_analysis/src/completion/complete_snippet.rs
index fb9da0a4f..a495751dd 100644
--- a/crates/ra_analysis/src/completion/complete_snippet.rs
+++ b/crates/ra_analysis/src/completion/complete_snippet.rs
@@ -7,7 +7,7 @@ fn snippet(label: &str, snippet: &str) -> Builder {
7} 7}
8 8
9pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { 9pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) {
10 if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) { 10 if !(ctx.is_trivial_path && ctx.function_syntax.is_some()) {
11 return; 11 return;
12 } 12 }
13 snippet("pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc); 13 snippet("pd", "eprintln!(\"$0 = {:?}\", $0);").add_to(acc);
diff --git a/crates/ra_analysis/src/completion/completion_context.rs b/crates/ra_analysis/src/completion/completion_context.rs
index 978772fd4..4685c9328 100644
--- a/crates/ra_analysis/src/completion/completion_context.rs
+++ b/crates/ra_analysis/src/completion/completion_context.rs
@@ -22,14 +22,17 @@ pub(super) struct CompletionContext<'a> {
22 pub(super) offset: TextUnit, 22 pub(super) offset: TextUnit,
23 pub(super) leaf: SyntaxNodeRef<'a>, 23 pub(super) leaf: SyntaxNodeRef<'a>,
24 pub(super) module: Option<hir::Module>, 24 pub(super) module: Option<hir::Module>,
25 pub(super) enclosing_fn: Option<ast::FnDef<'a>>, 25 pub(super) function: Option<hir::Function>,
26 pub(super) function_syntax: Option<ast::FnDef<'a>>,
27 pub(super) use_item_syntax: Option<ast::UseItem<'a>>,
26 pub(super) is_param: bool, 28 pub(super) is_param: bool,
27 /// A single-indent path, like `foo`. 29 /// A single-indent path, like `foo`.
28 pub(super) is_trivial_path: bool, 30 pub(super) is_trivial_path: bool,
29 /// If not a trivial, path, the prefix (qualifier). 31 /// If not a trivial, path, the prefix (qualifier).
30 pub(super) path_prefix: Option<hir::Path>, 32 pub(super) path_prefix: Option<hir::Path>,
31 pub(super) after_if: bool, 33 pub(super) after_if: bool,
32 pub(super) is_stmt: bool, 34 /// `true` if we are a statement or a last expr in the block.
35 pub(super) can_be_stmt: bool,
33 /// Something is typed at the "top" level, in module or impl/trait. 36 /// Something is typed at the "top" level, in module or impl/trait.
34 pub(super) is_new_item: bool, 37 pub(super) is_new_item: bool,
35 /// The receiver if this is a field or method access, i.e. writing something.<|> 38 /// The receiver if this is a field or method access, i.e. writing something.<|>
@@ -52,12 +55,14 @@ impl<'a> CompletionContext<'a> {
52 leaf, 55 leaf,
53 offset: position.offset, 56 offset: position.offset,
54 module, 57 module,
55 enclosing_fn: None, 58 function: None,
59 function_syntax: None,
60 use_item_syntax: None,
56 is_param: false, 61 is_param: false,
57 is_trivial_path: false, 62 is_trivial_path: false,
58 path_prefix: None, 63 path_prefix: None,
59 after_if: false, 64 after_if: false,
60 is_stmt: false, 65 can_be_stmt: false,
61 is_new_item: false, 66 is_new_item: false,
62 dot_receiver: None, 67 dot_receiver: None,
63 is_method_call: false, 68 is_method_call: false,
@@ -112,11 +117,20 @@ impl<'a> CompletionContext<'a> {
112 _ => (), 117 _ => (),
113 } 118 }
114 119
115 self.enclosing_fn = self 120 self.use_item_syntax = self.leaf.ancestors().find_map(ast::UseItem::cast);
121
122 self.function_syntax = self
116 .leaf 123 .leaf
117 .ancestors() 124 .ancestors()
118 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE) 125 .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
119 .find_map(ast::FnDef::cast); 126 .find_map(ast::FnDef::cast);
127 match (&self.module, self.function_syntax) {
128 (Some(module), Some(fn_def)) => {
129 let function = source_binder::function_from_module(self.db, module, fn_def);
130 self.function = Some(function);
131 }
132 _ => (),
133 }
120 134
121 let parent = match name_ref.syntax().parent() { 135 let parent = match name_ref.syntax().parent() {
122 Some(it) => it, 136 Some(it) => it,
@@ -134,13 +148,22 @@ impl<'a> CompletionContext<'a> {
134 if path.qualifier().is_none() { 148 if path.qualifier().is_none() {
135 self.is_trivial_path = true; 149 self.is_trivial_path = true;
136 150
137 self.is_stmt = match name_ref 151 self.can_be_stmt = match name_ref
138 .syntax() 152 .syntax()
139 .ancestors() 153 .ancestors()
140 .filter_map(ast::ExprStmt::cast) 154 .filter_map(ast::ExprStmt::cast)
141 .next() 155 .next()
142 { 156 {
143 None => false, 157 None => {
158 name_ref
159 .syntax()
160 .ancestors()
161 .filter_map(ast::Block::cast)
162 .next()
163 .and_then(|block| block.expr())
164 .map(|e| e.syntax().range())
165 == Some(name_ref.syntax().range())
166 }
144 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(), 167 Some(expr_stmt) => expr_stmt.syntax().range() == name_ref.syntax().range(),
145 }; 168 };
146 169
diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs
index c9f9f495d..cd4d529f9 100644
--- a/crates/ra_analysis/src/completion/completion_item.rs
+++ b/crates/ra_analysis/src/completion/completion_item.rs
@@ -1,7 +1,7 @@
1use crate::db;
2
3use hir::PerNs; 1use hir::PerNs;
4 2
3use crate::completion::CompletionContext;
4
5/// `CompletionItem` describes a single completion variant in the editor pop-up. 5/// `CompletionItem` describes a single completion variant in the editor pop-up.
6/// It is basically a POD with various properties. To construct a 6/// It is basically a POD with various properties. To construct a
7/// `CompletionItem`, use `new` method and the `Builder` struct. 7/// `CompletionItem`, use `new` method and the `Builder` struct.
@@ -29,6 +29,7 @@ pub enum CompletionItemKind {
29 Function, 29 Function,
30 Struct, 30 Struct,
31 Enum, 31 Enum,
32 EnumVariant,
32 Binding, 33 Binding,
33 Field, 34 Field,
34} 35}
@@ -117,12 +118,12 @@ impl Builder {
117 self.kind = Some(kind); 118 self.kind = Some(kind);
118 self 119 self
119 } 120 }
120 pub(crate) fn from_resolution( 121 pub(super) fn from_resolution(
121 mut self, 122 mut self,
122 db: &db::RootDatabase, 123 ctx: &CompletionContext,
123 resolution: &hir::Resolution, 124 resolution: &hir::Resolution,
124 ) -> Builder { 125 ) -> Builder {
125 let resolved = resolution.def_id.and_then(|d| d.resolve(db).ok()); 126 let resolved = resolution.def_id.and_then(|d| d.resolve(ctx.db).ok());
126 let kind = match resolved { 127 let kind = match resolved {
127 PerNs { 128 PerNs {
128 types: Some(hir::Def::Module(..)), 129 types: Some(hir::Def::Module(..)),
@@ -137,14 +138,29 @@ impl Builder {
137 .. 138 ..
138 } => CompletionItemKind::Enum, 139 } => CompletionItemKind::Enum,
139 PerNs { 140 PerNs {
140 values: Some(hir::Def::Function(..)), 141 values: Some(hir::Def::Function(function)),
141 .. 142 ..
142 } => CompletionItemKind::Function, 143 } => return self.from_function(ctx, function),
143 _ => return self, 144 _ => return self,
144 }; 145 };
145 self.kind = Some(kind); 146 self.kind = Some(kind);
146 self 147 self
147 } 148 }
149
150 fn from_function(mut self, ctx: &CompletionContext, function: hir::Function) -> Builder {
151 // If not an import, add parenthesis automatically.
152 if ctx.use_item_syntax.is_none() {
153 if let Some(sig_info) = function.signature_info(ctx.db) {
154 if sig_info.params.is_empty() {
155 self.snippet = Some(format!("{}()$0", self.label));
156 } else {
157 self.snippet = Some(format!("{}($0)", self.label));
158 }
159 }
160 }
161 self.kind = Some(CompletionItemKind::Function);
162 self
163 }
148} 164}
149 165
150impl Into<CompletionItem> for Builder { 166impl Into<CompletionItem> for Builder {
diff --git a/crates/ra_analysis/src/db.rs b/crates/ra_analysis/src/db.rs
index 036e284bf..b072a5eba 100644
--- a/crates/ra_analysis/src/db.rs
+++ b/crates/ra_analysis/src/db.rs
@@ -91,7 +91,6 @@ salsa::database_storage! {
91 fn file_item() for hir::db::FileItemQuery; 91 fn file_item() for hir::db::FileItemQuery;
92 fn input_module_items() for hir::db::InputModuleItemsQuery; 92 fn input_module_items() for hir::db::InputModuleItemsQuery;
93 fn item_map() for hir::db::ItemMapQuery; 93 fn item_map() for hir::db::ItemMapQuery;
94 fn fn_syntax() for hir::db::FnSyntaxQuery;
95 fn submodules() for hir::db::SubmodulesQuery; 94 fn submodules() for hir::db::SubmodulesQuery;
96 fn infer() for hir::db::InferQuery; 95 fn infer() for hir::db::InferQuery;
97 fn type_for_def() for hir::db::TypeForDefQuery; 96 fn type_for_def() for hir::db::TypeForDefQuery;
diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs
new file mode 100644
index 000000000..cde6ee101
--- /dev/null
+++ b/crates/ra_analysis/src/extend_selection.rs
@@ -0,0 +1,51 @@
1use ra_db::SyntaxDatabase;
2use ra_syntax::{
3 SyntaxNodeRef, AstNode,
4 ast, algo::find_covering_node,
5};
6
7use crate::{
8 TextRange, FileRange,
9 db::RootDatabase,
10};
11
12pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange {
13 let source_file = db.source_file(frange.file_id);
14 if let Some(macro_call) = find_macro_call(source_file.syntax(), frange.range) {
15 if let Some(exp) = crate::macros::expand(db, frange.file_id, macro_call) {
16 if let Some(dst_range) = exp.map_range_forward(frange.range) {
17 if let Some(dst_range) = ra_editor::extend_selection(exp.source_file(), dst_range) {
18 if let Some(src_range) = exp.map_range_back(dst_range) {
19 return src_range;
20 }
21 }
22 }
23 }
24 }
25 ra_editor::extend_selection(&source_file, frange.range).unwrap_or(frange.range)
26}
27
28fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option<ast::MacroCall> {
29 find_covering_node(node, range)
30 .ancestors()
31 .find_map(ast::MacroCall::cast)
32}
33
34#[cfg(test)]
35mod tests {
36 use crate::mock_analysis::single_file_with_range;
37 use test_utils::assert_eq_dbg;
38
39 #[test]
40 fn extend_selection_inside_macros() {
41 let (analysis, frange) = single_file_with_range(
42 "
43 fn main() {
44 ctry!(foo(|x| <|>x<|>));
45 }
46 ",
47 );
48 let r = analysis.extend_selection(frange);
49 assert_eq_dbg("[51; 56)", &r);
50 }
51}
diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs
index 38a5c1a7d..bff2e00c9 100644
--- a/crates/ra_analysis/src/imp.rs
+++ b/crates/ra_analysis/src/imp.rs
@@ -7,10 +7,7 @@ use rayon::prelude::*;
7use salsa::{Database, ParallelDatabase}; 7use salsa::{Database, ParallelDatabase};
8 8
9use hir::{ 9use hir::{
10 self, 10 self, FnSignatureInfo, Problem, source_binder,
11 FnSignatureInfo,
12 Problem,
13 source_binder,
14}; 11};
15use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; 12use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase};
16use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; 13use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity};
@@ -26,7 +23,7 @@ use crate::{
26 AnalysisChange, 23 AnalysisChange,
27 Cancelable, 24 Cancelable,
28 completion::{CompletionItem, completions}, 25 completion::{CompletionItem, completions},
29 CrateId, db, Diagnostic, FileId, FilePosition, FileSystemEdit, 26 CrateId, db, Diagnostic, FileId, FilePosition, FileRange, FileSystemEdit,
30 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit, 27 Query, ReferenceResolution, RootChange, SourceChange, SourceFileEdit,
31 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase}, 28 symbol_index::{LibrarySymbolsQuery, SymbolIndex, SymbolsDatabase},
32}; 29};
@@ -123,9 +120,6 @@ impl AnalysisHostImpl {
123 .query(ra_db::SourceFileQuery) 120 .query(ra_db::SourceFileQuery)
124 .sweep(salsa::SweepStrategy::default().discard_values()); 121 .sweep(salsa::SweepStrategy::default().discard_values());
125 self.db 122 self.db
126 .query(hir::db::FnSyntaxQuery)
127 .sweep(salsa::SweepStrategy::default().discard_values());
128 self.db
129 .query(hir::db::SourceFileItemsQuery) 123 .query(hir::db::SourceFileItemsQuery)
130 .sweep(salsa::SweepStrategy::default().discard_values()); 124 .sweep(salsa::SweepStrategy::default().discard_values());
131 self.db 125 self.db
@@ -146,6 +140,9 @@ impl fmt::Debug for AnalysisImpl {
146} 140}
147 141
148impl AnalysisImpl { 142impl AnalysisImpl {
143 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
144 self.db.file_text(file_id)
145 }
149 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 146 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
150 self.db.source_file(file_id) 147 self.db.source_file(file_id)
151 } 148 }
@@ -243,7 +240,7 @@ impl AnalysisImpl {
243 rr.add_resolution( 240 rr.add_resolution(
244 position.file_id, 241 position.file_id,
245 FileSymbol { 242 FileSymbol {
246 name: entry.name().clone(), 243 name: entry.name().to_string().into(),
247 node_range: entry.ptr().range(), 244 node_range: entry.ptr().range(),
248 kind: NAME, 245 kind: NAME,
249 }, 246 },
@@ -261,23 +258,21 @@ impl AnalysisImpl {
261 let mut rr = ReferenceResolution::new(name.syntax().range()); 258 let mut rr = ReferenceResolution::new(name.syntax().range());
262 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) { 259 if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
263 if module.has_semi() { 260 if module.has_semi() {
264 let parent_module = 261 if let Some(child_module) =
265 source_binder::module_from_file_id(&*self.db, position.file_id)?; 262 source_binder::module_from_declaration(&*self.db, position.file_id, module)?
266 let child_name = module.name(); 263 {
267 match (parent_module, child_name) { 264 let file_id = child_module.source().file_id();
268 (Some(parent_module), Some(child_name)) => { 265 let name = match child_module.name() {
269 if let Some(child) = parent_module.child(&child_name.text()) { 266 Some(name) => name.to_string().into(),
270 let file_id = child.source().file_id(); 267 None => "".into(),
271 let symbol = FileSymbol { 268 };
272 name: child_name.text(), 269 let symbol = FileSymbol {
273 node_range: TextRange::offset_len(0.into(), 0.into()), 270 name,
274 kind: MODULE, 271 node_range: TextRange::offset_len(0.into(), 0.into()),
275 }; 272 kind: MODULE,
276 rr.add_resolution(file_id, symbol); 273 };
277 return Ok(Some(rr)); 274 rr.add_resolution(file_id, symbol);
278 } 275 return Ok(Some(rr));
279 }
280 _ => (),
281 } 276 }
282 } 277 }
283 } 278 }
@@ -334,16 +329,6 @@ impl AnalysisImpl {
334 Ok(Some((binding, descr))) 329 Ok(Some((binding, descr)))
335 } 330 }
336 } 331 }
337
338 pub fn doc_comment_for(
339 &self,
340 file_id: FileId,
341 symbol: FileSymbol,
342 ) -> Cancelable<Option<String>> {
343 let file = self.db.source_file(file_id);
344
345 Ok(symbol.docs(&file))
346 }
347 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { 332 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> {
348 let file = self.db.source_file(file_id); 333 let file = self.db.source_file(file_id);
349 let result = match (symbol.description(&file), symbol.docs(&file)) { 334 let result = match (symbol.description(&file), symbol.docs(&file)) {
@@ -422,19 +407,21 @@ impl AnalysisImpl {
422 Ok(res) 407 Ok(res)
423 } 408 }
424 409
425 pub fn assists(&self, file_id: FileId, range: TextRange) -> Vec<SourceChange> { 410 pub fn assists(&self, frange: FileRange) -> Vec<SourceChange> {
426 let file = self.file_syntax(file_id); 411 let file = self.file_syntax(frange.file_id);
427 let offset = range.start(); 412 let offset = frange.range.start();
428 let actions = vec![ 413 let actions = vec![
429 ra_editor::flip_comma(&file, offset).map(|f| f()), 414 ra_editor::flip_comma(&file, offset).map(|f| f()),
430 ra_editor::add_derive(&file, offset).map(|f| f()), 415 ra_editor::add_derive(&file, offset).map(|f| f()),
431 ra_editor::add_impl(&file, offset).map(|f| f()), 416 ra_editor::add_impl(&file, offset).map(|f| f()),
432 ra_editor::make_pub_crate(&file, offset).map(|f| f()), 417 ra_editor::make_pub_crate(&file, offset).map(|f| f()),
433 ra_editor::introduce_variable(&file, range).map(|f| f()), 418 ra_editor::introduce_variable(&file, frange.range).map(|f| f()),
434 ]; 419 ];
435 actions 420 actions
436 .into_iter() 421 .into_iter()
437 .filter_map(|local_edit| Some(SourceChange::from_local_edit(file_id, local_edit?))) 422 .filter_map(|local_edit| {
423 Some(SourceChange::from_local_edit(frange.file_id, local_edit?))
424 })
438 .collect() 425 .collect()
439 } 426 }
440 427
@@ -505,18 +492,15 @@ impl AnalysisImpl {
505 Ok(None) 492 Ok(None)
506 } 493 }
507 494
508 pub fn type_of(&self, file_id: FileId, range: TextRange) -> Cancelable<Option<String>> { 495 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
509 let file = self.db.source_file(file_id); 496 let file = self.db.source_file(frange.file_id);
510 let syntax = file.syntax(); 497 let syntax = file.syntax();
511 let node = find_covering_node(syntax, range); 498 let node = find_covering_node(syntax, frange.range);
512 let parent_fn = node.ancestors().filter_map(FnDef::cast).next(); 499 let parent_fn = ctry!(node.ancestors().find_map(FnDef::cast));
513 let parent_fn = if let Some(p) = parent_fn {
514 p
515 } else {
516 return Ok(None);
517 };
518 let function = ctry!(source_binder::function_from_source( 500 let function = ctry!(source_binder::function_from_source(
519 &*self.db, file_id, parent_fn 501 &*self.db,
502 frange.file_id,
503 parent_fn
520 )?); 504 )?);
521 let infer = function.infer(&*self.db)?; 505 let infer = function.infer(&*self.db)?;
522 Ok(infer.type_of_node(node).map(|t| t.to_string())) 506 Ok(infer.type_of_node(node).map(|t| t.to_string()))
diff --git a/crates/ra_analysis/src/lib.rs b/crates/ra_analysis/src/lib.rs
index 476d1b438..9f5e9f358 100644
--- a/crates/ra_analysis/src/lib.rs
+++ b/crates/ra_analysis/src/lib.rs
@@ -16,6 +16,10 @@ mod completion;
16mod symbol_index; 16mod symbol_index;
17pub mod mock_analysis; 17pub mod mock_analysis;
18 18
19mod extend_selection;
20mod syntax_highlighting;
21mod macros;
22
19use std::{fmt, sync::Arc}; 23use std::{fmt, sync::Arc};
20 24
21use rustc_hash::FxHashMap; 25use rustc_hash::FxHashMap;
@@ -37,7 +41,7 @@ pub use ra_editor::{
37pub use hir::FnSignatureInfo; 41pub use hir::FnSignatureInfo;
38 42
39pub use ra_db::{ 43pub use ra_db::{
40 Canceled, Cancelable, FilePosition, 44 Canceled, Cancelable, FilePosition, FileRange,
41 CrateGraph, CrateId, SourceRootId, FileId 45 CrateGraph, CrateId, SourceRootId, FileId
42}; 46};
43 47
@@ -270,14 +274,17 @@ pub struct Analysis {
270} 274}
271 275
272impl Analysis { 276impl Analysis {
277 pub fn file_text(&self, file_id: FileId) -> Arc<String> {
278 self.imp.file_text(file_id)
279 }
273 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode { 280 pub fn file_syntax(&self, file_id: FileId) -> SourceFileNode {
274 self.imp.file_syntax(file_id).clone() 281 self.imp.file_syntax(file_id).clone()
275 } 282 }
276 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> { 283 pub fn file_line_index(&self, file_id: FileId) -> Arc<LineIndex> {
277 self.imp.file_line_index(file_id) 284 self.imp.file_line_index(file_id)
278 } 285 }
279 pub fn extend_selection(&self, file: &SourceFileNode, range: TextRange) -> TextRange { 286 pub fn extend_selection(&self, frange: FileRange) -> TextRange {
280 ra_editor::extend_selection(file, range).unwrap_or(range) 287 extend_selection::extend_selection(&self.imp.db, frange)
281 } 288 }
282 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> { 289 pub fn matching_brace(&self, file: &SourceFileNode, offset: TextUnit) -> Option<TextUnit> {
283 ra_editor::matching_brace(file, offset) 290 ra_editor::matching_brace(file, offset)
@@ -286,9 +293,9 @@ impl Analysis {
286 let file = self.imp.file_syntax(file_id); 293 let file = self.imp.file_syntax(file_id);
287 ra_editor::syntax_tree(&file) 294 ra_editor::syntax_tree(&file)
288 } 295 }
289 pub fn join_lines(&self, file_id: FileId, range: TextRange) -> SourceChange { 296 pub fn join_lines(&self, frange: FileRange) -> SourceChange {
290 let file = self.imp.file_syntax(file_id); 297 let file = self.imp.file_syntax(frange.file_id);
291 SourceChange::from_local_edit(file_id, ra_editor::join_lines(&file, range)) 298 SourceChange::from_local_edit(frange.file_id, ra_editor::join_lines(&file, frange.range))
292 } 299 }
293 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> { 300 pub fn on_enter(&self, position: FilePosition) -> Option<SourceChange> {
294 let file = self.imp.file_syntax(position.file_id); 301 let file = self.imp.file_syntax(position.file_id);
@@ -323,13 +330,6 @@ impl Analysis {
323 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> { 330 pub fn find_all_refs(&self, position: FilePosition) -> Cancelable<Vec<(FileId, TextRange)>> {
324 self.imp.find_all_refs(position) 331 self.imp.find_all_refs(position)
325 } 332 }
326 pub fn doc_comment_for(
327 &self,
328 file_id: FileId,
329 symbol: FileSymbol,
330 ) -> Cancelable<Option<String>> {
331 self.imp.doc_comment_for(file_id, symbol)
332 }
333 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> { 333 pub fn doc_text_for(&self, file_id: FileId, symbol: FileSymbol) -> Cancelable<Option<String>> {
334 self.imp.doc_text_for(file_id, symbol) 334 self.imp.doc_text_for(file_id, symbol)
335 } 335 }
@@ -347,14 +347,13 @@ impl Analysis {
347 Ok(ra_editor::runnables(&file)) 347 Ok(ra_editor::runnables(&file))
348 } 348 }
349 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> { 349 pub fn highlight(&self, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
350 let file = self.imp.file_syntax(file_id); 350 syntax_highlighting::highlight(&*self.imp.db, file_id)
351 Ok(ra_editor::highlight(&file))
352 } 351 }
353 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> { 352 pub fn completions(&self, position: FilePosition) -> Cancelable<Option<Vec<CompletionItem>>> {
354 self.imp.completions(position) 353 self.imp.completions(position)
355 } 354 }
356 pub fn assists(&self, file_id: FileId, range: TextRange) -> Cancelable<Vec<SourceChange>> { 355 pub fn assists(&self, frange: FileRange) -> Cancelable<Vec<SourceChange>> {
357 Ok(self.imp.assists(file_id, range)) 356 Ok(self.imp.assists(frange))
358 } 357 }
359 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> { 358 pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
360 self.imp.diagnostics(file_id) 359 self.imp.diagnostics(file_id)
@@ -365,8 +364,8 @@ impl Analysis {
365 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> { 364 ) -> Cancelable<Option<(FnSignatureInfo, Option<usize>)>> {
366 self.imp.resolve_callable(position) 365 self.imp.resolve_callable(position)
367 } 366 }
368 pub fn type_of(&self, file_id: FileId, range: TextRange) -> Cancelable<Option<String>> { 367 pub fn type_of(&self, frange: FileRange) -> Cancelable<Option<String>> {
369 self.imp.type_of(file_id, range) 368 self.imp.type_of(frange)
370 } 369 }
371} 370}
372 371
diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs
new file mode 100644
index 000000000..b9feb7fad
--- /dev/null
+++ b/crates/ra_analysis/src/macros.rs
@@ -0,0 +1,75 @@
1/// Begining of macro expansion.
2///
3/// This code should be moved out of ra_analysis into hir (?) ideally.
4use ra_syntax::{ast, AstNode, SourceFileNode, TextRange};
5
6use crate::{db::RootDatabase, FileId};
7
8pub(crate) fn expand(
9 _db: &RootDatabase,
10 _file_id: FileId,
11 macro_call: ast::MacroCall,
12) -> Option<MacroExpansion> {
13 let path = macro_call.path()?;
14 if path.qualifier().is_some() {
15 return None;
16 }
17 let name_ref = path.segment()?.name_ref()?;
18 if name_ref.text() != "ctry" {
19 return None;
20 }
21
22 let arg = macro_call.token_tree()?;
23 let text = format!(
24 r"
25 fn dummy() {{
26 match {} {{
27 None => return Ok(None),
28 Some(it) => it,
29 }}
30 }}",
31 arg.syntax().text()
32 );
33 let file = SourceFileNode::parse(&text);
34 let match_expr = file.syntax().descendants().find_map(ast::MatchExpr::cast)?;
35 let match_arg = match_expr.expr()?;
36 let ranges_map = vec![(arg.syntax().range(), match_arg.syntax().range())];
37 let res = MacroExpansion {
38 source_file: file,
39 ranges_map,
40 };
41 Some(res)
42}
43
44pub(crate) struct MacroExpansion {
45 pub(crate) source_file: SourceFileNode,
46 pub(crate) ranges_map: Vec<(TextRange, TextRange)>,
47}
48
49impl MacroExpansion {
50 pub(crate) fn source_file(&self) -> &SourceFileNode {
51 &self.source_file
52 }
53 pub(crate) fn map_range_back(&self, tgt_range: TextRange) -> Option<TextRange> {
54 for (s_range, t_range) in self.ranges_map.iter() {
55 if tgt_range.is_subrange(&t_range) {
56 let tgt_at_zero_range = tgt_range - tgt_range.start();
57 let tgt_range_offset = tgt_range.start() - t_range.start();
58 let src_range = tgt_at_zero_range + tgt_range_offset + s_range.start();
59 return Some(src_range);
60 }
61 }
62 None
63 }
64 pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option<TextRange> {
65 for (s_range, t_range) in self.ranges_map.iter() {
66 if src_range.is_subrange(&s_range) {
67 let src_at_zero_range = src_range - src_range.start();
68 let src_range_offset = src_range.start() - s_range.start();
69 let src_range = src_at_zero_range + src_range_offset + t_range.start();
70 return Some(src_range);
71 }
72 }
73 None
74 }
75}
diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs
index 5ce2aa2b4..960529404 100644
--- a/crates/ra_analysis/src/mock_analysis.rs
+++ b/crates/ra_analysis/src/mock_analysis.rs
@@ -1,10 +1,10 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use relative_path::RelativePathBuf; 3use relative_path::RelativePathBuf;
4use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; 4use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
5use ra_db::mock::FileMap; 5use ra_db::mock::FileMap;
6 6
7use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, SourceRootId}; 7use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, FileRange, SourceRootId};
8 8
9/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis 9/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
10/// from a set of in-memory files. 10/// from a set of in-memory files.
@@ -66,6 +66,12 @@ impl MockAnalysis {
66 self.files.push((path.to_string(), text.to_string())); 66 self.files.push((path.to_string(), text.to_string()));
67 FilePosition { file_id, offset } 67 FilePosition { file_id, offset }
68 } 68 }
69 pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange {
70 let (range, text) = extract_range(text);
71 let file_id = FileId((self.files.len() + 1) as u32);
72 self.files.push((path.to_string(), text.to_string()));
73 FileRange { file_id, range }
74 }
69 pub fn id_of(&self, path: &str) -> FileId { 75 pub fn id_of(&self, path: &str) -> FileId {
70 let (idx, _) = self 76 let (idx, _) = self
71 .files 77 .files
@@ -115,3 +121,10 @@ pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) {
115 let pos = mock.add_file_with_position("/main.rs", code); 121 let pos = mock.add_file_with_position("/main.rs", code);
116 (mock.analysis(), pos) 122 (mock.analysis(), pos)
117} 123}
124
125/// Creates analysis for a single file, returns range marked with a pair of <|>.
126pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) {
127 let mut mock = MockAnalysis::new();
128 let pos = mock.add_file_with_range("/main.rs", code);
129 (mock.analysis(), pos)
130}
diff --git a/crates/ra_analysis/src/syntax_highlighting.rs b/crates/ra_analysis/src/syntax_highlighting.rs
new file mode 100644
index 000000000..38219da71
--- /dev/null
+++ b/crates/ra_analysis/src/syntax_highlighting.rs
@@ -0,0 +1,63 @@
1use ra_syntax::{ast, AstNode,};
2use ra_editor::HighlightedRange;
3use ra_db::SyntaxDatabase;
4
5use crate::{
6 db::RootDatabase,
7 FileId, Cancelable,
8};
9
10pub(crate) fn highlight(db: &RootDatabase, file_id: FileId) -> Cancelable<Vec<HighlightedRange>> {
11 let source_file = db.source_file(file_id);
12 let mut res = ra_editor::highlight(&source_file);
13 for macro_call in source_file
14 .syntax()
15 .descendants()
16 .filter_map(ast::MacroCall::cast)
17 {
18 if let Some(exp) = crate::macros::expand(db, file_id, macro_call) {
19 let mapped_ranges = ra_editor::highlight(exp.source_file())
20 .into_iter()
21 .filter_map(|r| {
22 let mapped_range = exp.map_range_back(r.range)?;
23 let res = HighlightedRange {
24 range: mapped_range,
25 tag: r.tag,
26 };
27 Some(res)
28 });
29 res.extend(mapped_ranges);
30 }
31 }
32 Ok(res)
33}
34
35#[cfg(test)]
36mod tests {
37 use crate::mock_analysis::single_file;
38 use test_utils::assert_eq_dbg;
39
40 #[test]
41 fn highlights_code_inside_macros() {
42 let (analysis, file_id) = single_file(
43 "
44 fn main() {
45 ctry!({ let x = 92; x});
46 }
47 ",
48 );
49 let highlights = analysis.highlight(file_id).unwrap();
50 assert_eq_dbg(
51 r#"[HighlightedRange { range: [13; 15), tag: "keyword" },
52 HighlightedRange { range: [16; 20), tag: "function" },
53 HighlightedRange { range: [41; 46), tag: "macro" },
54 HighlightedRange { range: [49; 52), tag: "keyword" },
55 HighlightedRange { range: [57; 59), tag: "literal" },
56 HighlightedRange { range: [49; 52), tag: "keyword" },
57 HighlightedRange { range: [53; 54), tag: "function" },
58 HighlightedRange { range: [57; 59), tag: "literal" },
59 HighlightedRange { range: [61; 62), tag: "text" }]"#,
60 &highlights,
61 )
62 }
63}
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml
index 4be32b5f3..75bcf2b39 100644
--- a/crates/ra_db/Cargo.toml
+++ b/crates/ra_db/Cargo.toml
@@ -7,9 +7,9 @@ authors = ["Aleksey Kladov <[email protected]>"]
7[dependencies] 7[dependencies]
8backtrace = "0.3.1" 8backtrace = "0.3.1"
9relative-path = "0.4.0" 9relative-path = "0.4.0"
10salsa = "0.8.0" 10salsa = "0.9.0"
11rustc-hash = "1.0" 11rustc-hash = "1.0"
12parking_lot = "0.6.4" 12parking_lot = "0.7.0"
13ra_syntax = { path = "../ra_syntax" } 13ra_syntax = { path = "../ra_syntax" }
14ra_editor = { path = "../ra_editor" } 14ra_editor = { path = "../ra_editor" }
15test_utils = { path = "../test_utils" } 15test_utils = { path = "../test_utils" }
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs
index 1f7c9187b..7181f2950 100644
--- a/crates/ra_db/src/lib.rs
+++ b/crates/ra_db/src/lib.rs
@@ -8,13 +8,13 @@ pub mod mock;
8use std::sync::Arc; 8use std::sync::Arc;
9 9
10use ra_editor::LineIndex; 10use ra_editor::LineIndex;
11use ra_syntax::{TextUnit, SourceFileNode}; 11use ra_syntax::{TextUnit, TextRange, SourceFileNode};
12 12
13pub use crate::{ 13pub use crate::{
14 cancelation::{Canceled, Cancelable}, 14 cancelation::{Canceled, Cancelable},
15 syntax_ptr::LocalSyntaxPtr, 15 syntax_ptr::LocalSyntaxPtr,
16 input::{ 16 input::{
17 FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, 17 FilesDatabase, FileId, CrateId, SourceRoot, SourceRootId, CrateGraph, Dependency,
18 FileTextQuery, FileSourceRootQuery, SourceRootQuery, LocalRootsQuery, LibraryRootsQuery, CrateGraphQuery, 18 FileTextQuery, FileSourceRootQuery, SourceRootQuery, LocalRootsQuery, LibraryRootsQuery, CrateGraphQuery,
19 FileRelativePathQuery 19 FileRelativePathQuery
20 }, 20 },
@@ -70,3 +70,9 @@ pub struct FilePosition {
70 pub file_id: FileId, 70 pub file_id: FileId,
71 pub offset: TextUnit, 71 pub offset: TextUnit,
72} 72}
73
74#[derive(Clone, Copy, Debug)]
75pub struct FileRange {
76 pub file_id: FileId,
77 pub range: TextRange,
78}
diff --git a/crates/ra_editor/Cargo.toml b/crates/ra_editor/Cargo.toml
index f39fe4af6..a97d2308f 100644
--- a/crates/ra_editor/Cargo.toml
+++ b/crates/ra_editor/Cargo.toml
@@ -6,7 +6,7 @@ authors = ["Aleksey Kladov <[email protected]>"]
6publish = false 6publish = false
7 7
8[dependencies] 8[dependencies]
9itertools = "0.7.8" 9itertools = "0.8.0"
10superslice = "0.1.0" 10superslice = "0.1.0"
11join_to_string = "0.1.1" 11join_to_string = "0.1.1"
12rustc-hash = "1.0" 12rustc-hash = "1.0"
diff --git a/crates/ra_editor/src/diagnostics.rs b/crates/ra_editor/src/diagnostics.rs
new file mode 100644
index 000000000..1b336cfe2
--- /dev/null
+++ b/crates/ra_editor/src/diagnostics.rs
@@ -0,0 +1,276 @@
1use itertools::Itertools;
2
3use ra_syntax::{
4 ast::{self, AstNode},
5 Location,
6 SourceFileNode,
7 SyntaxKind,
8 TextRange,
9};
10use ra_syntax::SyntaxNodeRef;
11use ra_text_edit::{
12 TextEdit,
13 TextEditBuilder,
14};
15
16use crate::{
17 Diagnostic,
18 LocalEdit,
19 Severity,
20};
21
22pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> {
23 fn location_to_range(location: Location) -> TextRange {
24 match location {
25 Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
26 Location::Range(range) => range,
27 }
28 }
29
30 let mut errors: Vec<Diagnostic> = file
31 .errors()
32 .into_iter()
33 .map(|err| Diagnostic {
34 range: location_to_range(err.location()),
35 msg: format!("Syntax Error: {}", err),
36 severity: Severity::Error,
37 fix: None,
38 })
39 .collect();
40
41 for node in file.syntax().descendants() {
42 check_unnecessary_braces_in_use_statement(&mut errors, node);
43 check_struct_shorthand_initialization(&mut errors, node);
44 }
45
46 errors
47}
48
49fn check_unnecessary_braces_in_use_statement(
50 acc: &mut Vec<Diagnostic>,
51 node: SyntaxNodeRef,
52) -> Option<()> {
53 let use_tree_list = ast::UseTreeList::cast(node)?;
54 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
55 let range = use_tree_list.syntax().range();
56 let edit =
57 text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(single_use_tree)
58 .unwrap_or_else(|| {
59 let to_replace = single_use_tree.syntax().text().to_string();
60 let mut edit_builder = TextEditBuilder::new();
61 edit_builder.delete(range);
62 edit_builder.insert(range.start(), to_replace);
63 edit_builder.finish()
64 });
65
66 acc.push(Diagnostic {
67 range,
68 msg: format!("Unnecessary braces in use statement"),
69 severity: Severity::WeakWarning,
70 fix: Some(LocalEdit {
71 label: "Remove unnecessary braces".to_string(),
72 edit,
73 cursor_position: None,
74 }),
75 });
76 }
77
78 Some(())
79}
80
81fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
82 single_use_tree: ast::UseTree,
83) -> Option<TextEdit> {
84 let use_tree_list_node = single_use_tree.syntax().parent()?;
85 if single_use_tree
86 .path()?
87 .segment()?
88 .syntax()
89 .first_child()?
90 .kind()
91 == SyntaxKind::SELF_KW
92 {
93 let start = use_tree_list_node.prev_sibling()?.range().start();
94 let end = use_tree_list_node.range().end();
95 let range = TextRange::from_to(start, end);
96 let mut edit_builder = TextEditBuilder::new();
97 edit_builder.delete(range);
98 return Some(edit_builder.finish());
99 }
100 None
101}
102
103fn check_struct_shorthand_initialization(
104 acc: &mut Vec<Diagnostic>,
105 node: SyntaxNodeRef,
106) -> Option<()> {
107 let struct_lit = ast::StructLit::cast(node)?;
108 let named_field_list = struct_lit.named_field_list()?;
109 for named_field in named_field_list.fields() {
110 if let (Some(name_ref), Some(expr)) = (named_field.name_ref(), named_field.expr()) {
111 let field_name = name_ref.syntax().text().to_string();
112 let field_expr = expr.syntax().text().to_string();
113 if field_name == field_expr {
114 let mut edit_builder = TextEditBuilder::new();
115 edit_builder.delete(named_field.syntax().range());
116 edit_builder.insert(named_field.syntax().range().start(), field_name);
117 let edit = edit_builder.finish();
118
119 acc.push(Diagnostic {
120 range: named_field.syntax().range(),
121 msg: format!("Shorthand struct initialization"),
122 severity: Severity::WeakWarning,
123 fix: Some(LocalEdit {
124 label: "use struct shorthand initialization".to_string(),
125 edit,
126 cursor_position: None,
127 }),
128 });
129 }
130 }
131 }
132 Some(())
133}
134
135#[cfg(test)]
136mod tests {
137 use crate::test_utils::assert_eq_text;
138
139 use super::*;
140
141 type DiagnosticChecker = fn(&mut Vec<Diagnostic>, SyntaxNodeRef) -> Option<()>;
142
143 fn check_not_applicable(code: &str, func: DiagnosticChecker) {
144 let file = SourceFileNode::parse(code);
145 let mut diagnostics = Vec::new();
146 for node in file.syntax().descendants() {
147 func(&mut diagnostics, node);
148 }
149 assert!(diagnostics.is_empty());
150 }
151
152 fn check_apply(before: &str, after: &str, func: DiagnosticChecker) {
153 let file = SourceFileNode::parse(before);
154 let mut diagnostics = Vec::new();
155 for node in file.syntax().descendants() {
156 func(&mut diagnostics, node);
157 }
158 let diagnostic = diagnostics
159 .pop()
160 .unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
161 let fix = diagnostic.fix.unwrap();
162 let actual = fix.edit.apply(&before);
163 assert_eq_text!(after, &actual);
164 }
165
166 #[test]
167 fn test_check_unnecessary_braces_in_use_statement() {
168 check_not_applicable(
169 "
170 use a;
171 use a::{c, d::e};
172 ",
173 check_unnecessary_braces_in_use_statement,
174 );
175 check_apply(
176 "use {b};",
177 "use b;",
178 check_unnecessary_braces_in_use_statement,
179 );
180 check_apply(
181 "use a::{c};",
182 "use a::c;",
183 check_unnecessary_braces_in_use_statement,
184 );
185 check_apply(
186 "use a::{self};",
187 "use a;",
188 check_unnecessary_braces_in_use_statement,
189 );
190 check_apply(
191 "use a::{c, d::{e}};",
192 "use a::{c, d::e};",
193 check_unnecessary_braces_in_use_statement,
194 );
195 }
196
197 #[test]
198 fn test_check_struct_shorthand_initialization() {
199 check_not_applicable(
200 r#"
201 struct A {
202 a: &'static str
203 }
204
205 fn main() {
206 A {
207 a: "hello"
208 }
209 }
210 "#,
211 check_struct_shorthand_initialization,
212 );
213
214 check_apply(
215 r#"
216struct A {
217 a: &'static str
218}
219
220fn main() {
221 let a = "haha";
222 A {
223 a: a
224 }
225}
226 "#,
227 r#"
228struct A {
229 a: &'static str
230}
231
232fn main() {
233 let a = "haha";
234 A {
235 a
236 }
237}
238 "#,
239 check_struct_shorthand_initialization,
240 );
241
242 check_apply(
243 r#"
244struct A {
245 a: &'static str,
246 b: &'static str
247}
248
249fn main() {
250 let a = "haha";
251 let b = "bb";
252 A {
253 a: a,
254 b
255 }
256}
257 "#,
258 r#"
259struct A {
260 a: &'static str,
261 b: &'static str
262}
263
264fn main() {
265 let a = "haha";
266 let b = "bb";
267 A {
268 a,
269 b
270 }
271}
272 "#,
273 check_struct_shorthand_initialization,
274 );
275 }
276}
diff --git a/crates/ra_editor/src/lib.rs b/crates/ra_editor/src/lib.rs
index d9b89155b..a65637d52 100644
--- a/crates/ra_editor/src/lib.rs
+++ b/crates/ra_editor/src/lib.rs
@@ -7,6 +7,7 @@ mod symbols;
7#[cfg(test)] 7#[cfg(test)]
8mod test_utils; 8mod test_utils;
9mod typing; 9mod typing;
10mod diagnostics;
10 11
11pub use self::{ 12pub use self::{
12 code_actions::{add_derive, add_impl, flip_comma, introduce_variable, make_pub_crate, LocalEdit}, 13 code_actions::{add_derive, add_impl, flip_comma, introduce_variable, make_pub_crate, LocalEdit},
@@ -16,17 +17,17 @@ pub use self::{
16 line_index_utils::translate_offset_with_edit, 17 line_index_utils::translate_offset_with_edit,
17 symbols::{file_structure, file_symbols, FileSymbol, StructureNode}, 18 symbols::{file_structure, file_symbols, FileSymbol, StructureNode},
18 typing::{join_lines, on_enter, on_eq_typed}, 19 typing::{join_lines, on_enter, on_eq_typed},
20 diagnostics::diagnostics
19}; 21};
20use ra_text_edit::{TextEdit, TextEditBuilder}; 22use ra_text_edit::{TextEdit, TextEditBuilder};
21use ra_syntax::{ 23use ra_syntax::{
22 algo::find_leaf_at_offset, 24 algo::find_leaf_at_offset,
23 ast::{self, AstNode, NameOwner}, 25 ast::{self, AstNode, NameOwner},
24 SourceFileNode, 26 SourceFileNode,
25 Location,
26 SyntaxKind::{self, *}, 27 SyntaxKind::{self, *},
27 SyntaxNodeRef, TextRange, TextUnit, 28 SyntaxNodeRef, TextRange, TextUnit, Direction,
28}; 29};
29use itertools::Itertools; 30use rustc_hash::FxHashSet;
30 31
31#[derive(Debug)] 32#[derive(Debug)]
32pub struct HighlightedRange { 33pub struct HighlightedRange {
@@ -79,8 +80,13 @@ pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUni
79} 80}
80 81
81pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> { 82pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> {
83 // Visited nodes to handle highlighting priorities
84 let mut highlighted = FxHashSet::default();
82 let mut res = Vec::new(); 85 let mut res = Vec::new();
83 for node in file.syntax().descendants() { 86 for node in file.syntax().descendants() {
87 if highlighted.contains(&node) {
88 continue;
89 }
84 let tag = match node.kind() { 90 let tag = match node.kind() {
85 COMMENT => "comment", 91 COMMENT => "comment",
86 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string", 92 STRING | RAW_STRING | RAW_BYTE_STRING | BYTE_STRING => "string",
@@ -90,7 +96,30 @@ pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> {
90 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal", 96 INT_NUMBER | FLOAT_NUMBER | CHAR | BYTE => "literal",
91 LIFETIME => "parameter", 97 LIFETIME => "parameter",
92 k if k.is_keyword() => "keyword", 98 k if k.is_keyword() => "keyword",
93 _ => continue, 99 _ => {
100 if let Some(macro_call) = ast::MacroCall::cast(node) {
101 if let Some(path) = macro_call.path() {
102 if let Some(segment) = path.segment() {
103 if let Some(name_ref) = segment.name_ref() {
104 highlighted.insert(name_ref.syntax());
105 let range_start = name_ref.syntax().range().start();
106 let mut range_end = name_ref.syntax().range().end();
107 for sibling in path.syntax().siblings(Direction::Next) {
108 match sibling.kind() {
109 EXCL | IDENT => range_end = sibling.range().end(),
110 _ => (),
111 }
112 }
113 res.push(HighlightedRange {
114 range: TextRange::from_to(range_start, range_end),
115 tag: "macro",
116 })
117 }
118 }
119 }
120 }
121 continue;
122 }
94 }; 123 };
95 res.push(HighlightedRange { 124 res.push(HighlightedRange {
96 range: node.range(), 125 range: node.range(),
@@ -100,87 +129,6 @@ pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> {
100 res 129 res
101} 130}
102 131
103pub fn diagnostics(file: &SourceFileNode) -> Vec<Diagnostic> {
104 fn location_to_range(location: Location) -> TextRange {
105 match location {
106 Location::Offset(offset) => TextRange::offset_len(offset, 1.into()),
107 Location::Range(range) => range,
108 }
109 }
110
111 let mut errors: Vec<Diagnostic> = file
112 .errors()
113 .into_iter()
114 .map(|err| Diagnostic {
115 range: location_to_range(err.location()),
116 msg: format!("Syntax Error: {}", err),
117 severity: Severity::Error,
118 fix: None,
119 })
120 .collect();
121
122 let warnings = check_unnecessary_braces_in_use_statement(file);
123
124 errors.extend(warnings);
125 errors
126}
127
128fn check_unnecessary_braces_in_use_statement(file: &SourceFileNode) -> Vec<Diagnostic> {
129 let mut diagnostics = Vec::new();
130 for node in file.syntax().descendants() {
131 if let Some(use_tree_list) = ast::UseTreeList::cast(node) {
132 if let Some((single_use_tree,)) = use_tree_list.use_trees().collect_tuple() {
133 let range = use_tree_list.syntax().range();
134 let edit = text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
135 single_use_tree,
136 )
137 .unwrap_or_else(|| {
138 let to_replace = single_use_tree.syntax().text().to_string();
139 let mut edit_builder = TextEditBuilder::new();
140 edit_builder.delete(range);
141 edit_builder.insert(range.start(), to_replace);
142 edit_builder.finish()
143 });
144
145 diagnostics.push(Diagnostic {
146 range: range,
147 msg: format!("Unnecessary braces in use statement"),
148 severity: Severity::WeakWarning,
149 fix: Some(LocalEdit {
150 label: "Remove unnecessary braces".to_string(),
151 edit: edit,
152 cursor_position: None,
153 }),
154 })
155 }
156 }
157 }
158
159 diagnostics
160}
161
162fn text_edit_for_remove_unnecessary_braces_with_self_in_use_statement(
163 single_use_tree: ast::UseTree,
164) -> Option<TextEdit> {
165 let use_tree_list_node = single_use_tree.syntax().parent()?;
166 if single_use_tree
167 .path()?
168 .segment()?
169 .syntax()
170 .first_child()?
171 .kind()
172 == SyntaxKind::SELF_KW
173 {
174 let start = use_tree_list_node.prev_sibling()?.range().start();
175 let end = use_tree_list_node.range().end();
176 let range = TextRange::from_to(start, end);
177 let mut edit_builder = TextEditBuilder::new();
178 edit_builder.delete(range);
179 return Some(edit_builder.finish());
180 }
181 None
182}
183
184pub fn syntax_tree(file: &SourceFileNode) -> String { 132pub fn syntax_tree(file: &SourceFileNode) -> String {
185 ::ra_syntax::utils::dump_tree(file.syntax()) 133 ::ra_syntax::utils::dump_tree(file.syntax())
186} 134}
@@ -235,7 +183,7 @@ fn main() {}
235 r#"[HighlightedRange { range: [1; 11), tag: "comment" }, 183 r#"[HighlightedRange { range: [1; 11), tag: "comment" },
236 HighlightedRange { range: [12; 14), tag: "keyword" }, 184 HighlightedRange { range: [12; 14), tag: "keyword" },
237 HighlightedRange { range: [15; 19), tag: "function" }, 185 HighlightedRange { range: [15; 19), tag: "function" },
238 HighlightedRange { range: [29; 36), tag: "text" }, 186 HighlightedRange { range: [29; 37), tag: "macro" },
239 HighlightedRange { range: [38; 50), tag: "string" }, 187 HighlightedRange { range: [38; 50), tag: "string" },
240 HighlightedRange { range: [52; 54), tag: "literal" }]"#, 188 HighlightedRange { range: [52; 54), tag: "literal" }]"#,
241 &hls, 189 &hls,
@@ -281,33 +229,4 @@ fn test_foo() {}
281 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }"); 229 do_check("struct Foo { a: i32, }<|>", "struct Foo <|>{ a: i32, }");
282 } 230 }
283 231
284 #[test]
285 fn test_check_unnecessary_braces_in_use_statement() {
286 fn check_not_applicable(code: &str) {
287 let file = SourceFileNode::parse(code);
288 let diagnostics = check_unnecessary_braces_in_use_statement(&file);
289 assert!(diagnostics.is_empty());
290 }
291
292 fn check_apply(before: &str, after: &str) {
293 let file = SourceFileNode::parse(before);
294 let diagnostic = check_unnecessary_braces_in_use_statement(&file)
295 .pop()
296 .unwrap_or_else(|| panic!("no diagnostics for:\n{}\n", before));
297 let fix = diagnostic.fix.unwrap();
298 let actual = fix.edit.apply(&before);
299 assert_eq_text!(after, &actual);
300 }
301
302 check_not_applicable(
303 "
304 use a;
305 use a::{c, d::e};
306 ",
307 );
308 check_apply("use {b};", "use b;");
309 check_apply("use a::{c};", "use a::c;");
310 check_apply("use a::{self};", "use a;");
311 check_apply("use a::{c, d::{e}};", "use a::{c, d::e};");
312 }
313} 232}
diff --git a/crates/ra_hir/Cargo.toml b/crates/ra_hir/Cargo.toml
index 594176337..c3fbd327d 100644
--- a/crates/ra_hir/Cargo.toml
+++ b/crates/ra_hir/Cargo.toml
@@ -5,13 +5,14 @@ version = "0.1.0"
5authors = ["Aleksey Kladov <[email protected]>"] 5authors = ["Aleksey Kladov <[email protected]>"]
6 6
7[dependencies] 7[dependencies]
8arrayvec = "0.4.9" 8arrayvec = "0.4.10"
9log = "0.4.5" 9log = "0.4.5"
10relative-path = "0.4.0" 10relative-path = "0.4.0"
11salsa = "0.8.0" 11salsa = "0.9.0"
12rustc-hash = "1.0" 12rustc-hash = "1.0"
13parking_lot = "0.6.4" 13parking_lot = "0.7.0"
14id-arena = "2.0" 14id-arena = "2.0"
15ena = "0.11"
15ra_syntax = { path = "../ra_syntax" } 16ra_syntax = { path = "../ra_syntax" }
16ra_editor = { path = "../ra_editor" } 17ra_editor = { path = "../ra_editor" }
17ra_db = { path = "../ra_db" } 18ra_db = { path = "../ra_db" }
diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs
index 65c461148..e839a5a90 100644
--- a/crates/ra_hir/src/adt.rs
+++ b/crates/ra_hir/src/adt.rs
@@ -1,10 +1,10 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::{SmolStr, ast::{self, NameOwner, StructFlavor}}; 3use ra_syntax::ast::{self, NameOwner, StructFlavor};
4 4
5use crate::{ 5use crate::{
6 DefId, Cancelable, 6 DefId, Cancelable, Name, AsName,
7 db::{HirDatabase}, 7 db::HirDatabase,
8 type_ref::TypeRef, 8 type_ref::TypeRef,
9}; 9};
10 10
@@ -29,26 +29,26 @@ impl Struct {
29 Ok(db.struct_data(self.def_id)?) 29 Ok(db.struct_data(self.def_id)?)
30 } 30 }
31 31
32 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<SmolStr>> { 32 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
33 Ok(db.struct_data(self.def_id)?.name.clone()) 33 Ok(db.struct_data(self.def_id)?.name.clone())
34 } 34 }
35} 35}
36 36
37#[derive(Debug, Clone, PartialEq, Eq)] 37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct StructData { 38pub struct StructData {
39 name: Option<SmolStr>, 39 name: Option<Name>,
40 variant_data: Arc<VariantData>, 40 variant_data: Arc<VariantData>,
41} 41}
42 42
43impl StructData { 43impl StructData {
44 pub(crate) fn new(struct_def: ast::StructDef) -> StructData { 44 pub(crate) fn new(struct_def: ast::StructDef) -> StructData {
45 let name = struct_def.name().map(|n| n.text()); 45 let name = struct_def.name().map(|n| n.as_name());
46 let variant_data = VariantData::new(struct_def.flavor()); 46 let variant_data = VariantData::new(struct_def.flavor());
47 let variant_data = Arc::new(variant_data); 47 let variant_data = Arc::new(variant_data);
48 StructData { name, variant_data } 48 StructData { name, variant_data }
49 } 49 }
50 50
51 pub fn name(&self) -> Option<&SmolStr> { 51 pub fn name(&self) -> Option<&Name> {
52 self.name.as_ref() 52 self.name.as_ref()
53 } 53 }
54 54
@@ -70,27 +70,29 @@ impl Enum {
70 self.def_id 70 self.def_id
71 } 71 }
72 72
73 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<SmolStr>> { 73 pub fn name(&self, db: &impl HirDatabase) -> Cancelable<Option<Name>> {
74 Ok(db.enum_data(self.def_id)?.name.clone()) 74 Ok(db.enum_data(self.def_id)?.name.clone())
75 } 75 }
76
77 pub fn variants(&self, db: &impl HirDatabase) -> Cancelable<Vec<(Name, Arc<VariantData>)>> {
78 Ok(db.enum_data(self.def_id)?.variants.clone())
79 }
76} 80}
77 81
78#[derive(Debug, Clone, PartialEq, Eq)] 82#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct EnumData { 83pub struct EnumData {
80 name: Option<SmolStr>, 84 name: Option<Name>,
81 variants: Vec<(SmolStr, Arc<VariantData>)>, 85 variants: Vec<(Name, Arc<VariantData>)>,
82} 86}
83 87
84impl EnumData { 88impl EnumData {
85 pub(crate) fn new(enum_def: ast::EnumDef) -> Self { 89 pub(crate) fn new(enum_def: ast::EnumDef) -> Self {
86 let name = enum_def.name().map(|n| n.text()); 90 let name = enum_def.name().map(|n| n.as_name());
87 let variants = if let Some(evl) = enum_def.variant_list() { 91 let variants = if let Some(evl) = enum_def.variant_list() {
88 evl.variants() 92 evl.variants()
89 .map(|v| { 93 .map(|v| {
90 ( 94 (
91 v.name() 95 v.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
92 .map(|n| n.text())
93 .unwrap_or_else(|| SmolStr::new("[error]")),
94 Arc::new(VariantData::new(v.flavor())), 96 Arc::new(VariantData::new(v.flavor())),
95 ) 97 )
96 }) 98 })
@@ -105,12 +107,12 @@ impl EnumData {
105/// A single field of an enum variant or struct 107/// A single field of an enum variant or struct
106#[derive(Debug, Clone, PartialEq, Eq)] 108#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct StructField { 109pub struct StructField {
108 name: SmolStr, 110 name: Name,
109 type_ref: TypeRef, 111 type_ref: TypeRef,
110} 112}
111 113
112impl StructField { 114impl StructField {
113 pub fn name(&self) -> SmolStr { 115 pub fn name(&self) -> Name {
114 self.name.clone() 116 self.name.clone()
115 } 117 }
116 pub fn type_ref(&self) -> &TypeRef { 118 pub fn type_ref(&self) -> &TypeRef {
@@ -134,7 +136,7 @@ impl VariantData {
134 .fields() 136 .fields()
135 .enumerate() 137 .enumerate()
136 .map(|(i, fd)| StructField { 138 .map(|(i, fd)| StructField {
137 name: SmolStr::new(i.to_string()), 139 name: Name::tuple_field_name(i),
138 type_ref: TypeRef::from_ast_opt(fd.type_ref()), 140 type_ref: TypeRef::from_ast_opt(fd.type_ref()),
139 }) 141 })
140 .collect(); 142 .collect();
@@ -144,10 +146,7 @@ impl VariantData {
144 let fields = fl 146 let fields = fl
145 .fields() 147 .fields()
146 .map(|fd| StructField { 148 .map(|fd| StructField {
147 name: fd 149 name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing),
148 .name()
149 .map(|n| n.text())
150 .unwrap_or_else(|| SmolStr::new("[error]")),
151 type_ref: TypeRef::from_ast_opt(fd.type_ref()), 150 type_ref: TypeRef::from_ast_opt(fd.type_ref()),
152 }) 151 })
153 .collect(); 152 .collect();
@@ -157,10 +156,10 @@ impl VariantData {
157 } 156 }
158 } 157 }
159 158
160 pub(crate) fn get_field_type_ref(&self, field_name: &str) -> Option<&TypeRef> { 159 pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
161 self.fields() 160 self.fields()
162 .iter() 161 .iter()
163 .find(|f| f.name == field_name) 162 .find(|f| f.name == *field_name)
164 .map(|f| &f.type_ref) 163 .map(|f| &f.type_ref)
165 } 164 }
166 165
diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs
index e7f9afa77..5a8ca3b47 100644
--- a/crates/ra_hir/src/db.rs
+++ b/crates/ra_hir/src/db.rs
@@ -1,18 +1,13 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::{ 3use ra_syntax::SyntaxNode;
4 SmolStr,
5 SyntaxNode,
6 ast::FnDefNode,
7};
8use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; 4use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable};
9 5
10use crate::{ 6use crate::{
11 DefLoc, DefId, 7 DefLoc, DefId, Name,
12 SourceFileItems, SourceItemId, 8 SourceFileItems, SourceItemId,
13 query_definitions, 9 query_definitions,
14 FnScopes, 10 FnScopes,
15 function::FnId,
16 module::{ModuleId, ModuleTree, ModuleSource, 11 module::{ModuleId, ModuleTree, ModuleSource,
17 nameres::{ItemMap, InputModuleItems}}, 12 nameres::{ItemMap, InputModuleItems}},
18 ty::{InferenceResult, Ty}, 13 ty::{InferenceResult, Ty},
@@ -24,14 +19,10 @@ salsa::query_group! {
24pub trait HirDatabase: SyntaxDatabase 19pub trait HirDatabase: SyntaxDatabase
25 + AsRef<LocationIntener<DefLoc, DefId>> 20 + AsRef<LocationIntener<DefLoc, DefId>>
26{ 21{
27 fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> { 22 fn fn_scopes(def_id: DefId) -> Arc<FnScopes> {
28 type FnScopesQuery; 23 type FnScopesQuery;
29 use fn query_definitions::fn_scopes; 24 use fn query_definitions::fn_scopes;
30 } 25 }
31 fn fn_syntax(fn_id: FnId) -> FnDefNode {
32 type FnSyntaxQuery;
33 use fn query_definitions::fn_syntax;
34 }
35 26
36 fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> { 27 fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> {
37 type StructDataQuery; 28 type StructDataQuery;
@@ -43,19 +34,19 @@ pub trait HirDatabase: SyntaxDatabase
43 use fn query_definitions::enum_data; 34 use fn query_definitions::enum_data;
44 } 35 }
45 36
46 fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> { 37 fn infer(def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
47 type InferQuery; 38 type InferQuery;
48 use fn query_definitions::infer; 39 use fn crate::ty::infer;
49 } 40 }
50 41
51 fn type_for_def(def_id: DefId) -> Cancelable<Ty> { 42 fn type_for_def(def_id: DefId) -> Cancelable<Ty> {
52 type TypeForDefQuery; 43 type TypeForDefQuery;
53 use fn query_definitions::type_for_def; 44 use fn crate::ty::type_for_def;
54 } 45 }
55 46
56 fn type_for_field(def_id: DefId, field: SmolStr) -> Cancelable<Ty> { 47 fn type_for_field(def_id: DefId, field: Name) -> Cancelable<Ty> {
57 type TypeForFieldQuery; 48 type TypeForFieldQuery;
58 use fn query_definitions::type_for_field; 49 use fn crate::ty::type_for_field;
59 } 50 }
60 51
61 fn file_items(file_id: FileId) -> Arc<SourceFileItems> { 52 fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
diff --git a/crates/ra_hir/src/function.rs b/crates/ra_hir/src/function.rs
index 01f0f3a66..5a44132fc 100644
--- a/crates/ra_hir/src/function.rs
+++ b/crates/ra_hir/src/function.rs
@@ -11,42 +11,42 @@ use ra_syntax::{
11 ast::{self, AstNode, DocCommentsOwner, NameOwner}, 11 ast::{self, AstNode, DocCommentsOwner, NameOwner},
12}; 12};
13 13
14use crate::{ DefId, HirDatabase, ty::InferenceResult, Module }; 14use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module};
15 15
16pub use self::scope::FnScopes; 16pub use self::scope::FnScopes;
17 17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] 18#[derive(Debug)]
19pub struct FnId(pub(crate) DefId);
20
21pub struct Function { 19pub struct Function {
22 pub(crate) fn_id: FnId, 20 def_id: DefId,
23} 21}
24 22
25impl Function { 23impl Function {
26 pub(crate) fn new(def_id: DefId) -> Function { 24 pub(crate) fn new(def_id: DefId) -> Function {
27 let fn_id = FnId(def_id); 25 Function { def_id }
28 Function { fn_id }
29 } 26 }
30 27
31 pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode { 28 pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode {
32 db.fn_syntax(self.fn_id) 29 let def_loc = self.def_id.loc(db);
30 assert!(def_loc.kind == DefKind::Function);
31 let syntax = db.file_item(def_loc.source_item_id);
32 ast::FnDef::cast(syntax.borrowed()).unwrap().owned()
33 } 33 }
34 34
35 pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> { 35 pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> {
36 db.fn_scopes(self.fn_id) 36 db.fn_scopes(self.def_id)
37 } 37 }
38 38
39 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> { 39 pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> {
40 let syntax = db.fn_syntax(self.fn_id); 40 let syntax = self.syntax(db);
41 FnSignatureInfo::new(syntax.borrowed()) 41 FnSignatureInfo::new(syntax.borrowed())
42 } 42 }
43 43
44 pub fn infer(&self, db: &impl HirDatabase) -> Cancelable<Arc<InferenceResult>> { 44 pub fn infer(&self, db: &impl HirDatabase) -> Cancelable<Arc<InferenceResult>> {
45 db.infer(self.fn_id) 45 db.infer(self.def_id)
46 } 46 }
47 47
48 pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> { 48 pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> {
49 self.fn_id.0.module(db) 49 self.def_id.module(db)
50 } 50 }
51} 51}
52 52
diff --git a/crates/ra_hir/src/function/scope.rs b/crates/ra_hir/src/function/scope.rs
index a1a580979..3e4cfad0c 100644
--- a/crates/ra_hir/src/function/scope.rs
+++ b/crates/ra_hir/src/function/scope.rs
@@ -1,7 +1,7 @@
1use rustc_hash::{FxHashMap, FxHashSet}; 1use rustc_hash::{FxHashMap, FxHashSet};
2 2
3use ra_syntax::{ 3use ra_syntax::{
4 AstNode, SmolStr, SyntaxNodeRef, TextUnit, TextRange, 4 AstNode, SyntaxNodeRef, TextUnit, TextRange,
5 algo::generate, 5 algo::generate,
6 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner}, 6 ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
7}; 7};
@@ -9,6 +9,7 @@ use ra_db::LocalSyntaxPtr;
9 9
10use crate::{ 10use crate::{
11 arena::{Arena, Id}, 11 arena::{Arena, Id},
12 Name, AsName,
12}; 13};
13 14
14pub(crate) type ScopeId = Id<ScopeData>; 15pub(crate) type ScopeId = Id<ScopeData>;
@@ -22,7 +23,7 @@ pub struct FnScopes {
22 23
23#[derive(Debug, PartialEq, Eq)] 24#[derive(Debug, PartialEq, Eq)]
24pub struct ScopeEntry { 25pub struct ScopeEntry {
25 name: SmolStr, 26 name: Name,
26 ptr: LocalSyntaxPtr, 27 ptr: LocalSyntaxPtr,
27} 28}
28 29
@@ -101,11 +102,12 @@ impl FnScopes {
101 102
102 pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> { 103 pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
103 let mut shadowed = FxHashSet::default(); 104 let mut shadowed = FxHashSet::default();
105 let name = name_ref.as_name();
104 let ret = self 106 let ret = self
105 .scope_chain(name_ref.syntax()) 107 .scope_chain(name_ref.syntax())
106 .flat_map(|scope| self.entries(scope).iter()) 108 .flat_map(|scope| self.entries(scope).iter())
107 .filter(|entry| shadowed.insert(entry.name())) 109 .filter(|entry| shadowed.insert(entry.name()))
108 .filter(|entry| entry.name() == &name_ref.text()) 110 .filter(|entry| entry.name() == &name)
109 .nth(0); 111 .nth(0);
110 ret 112 ret
111 } 113 }
@@ -170,14 +172,14 @@ impl FnScopes {
170 172
171impl ScopeEntry { 173impl ScopeEntry {
172 fn new(pat: ast::BindPat) -> Option<ScopeEntry> { 174 fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
173 let name = pat.name()?; 175 let name = pat.name()?.as_name();
174 let res = ScopeEntry { 176 let res = ScopeEntry {
175 name: name.text(), 177 name,
176 ptr: LocalSyntaxPtr::new(pat.syntax()), 178 ptr: LocalSyntaxPtr::new(pat.syntax()),
177 }; 179 };
178 Some(res) 180 Some(res)
179 } 181 }
180 pub fn name(&self) -> &SmolStr { 182 pub fn name(&self) -> &Name {
181 &self.name 183 &self.name
182 } 184 }
183 pub fn ptr(&self) -> LocalSyntaxPtr { 185 pub fn ptr(&self) -> LocalSyntaxPtr {
@@ -334,7 +336,7 @@ pub struct ReferenceDescriptor {
334mod tests { 336mod tests {
335 use ra_editor::find_node_at_offset; 337 use ra_editor::find_node_at_offset;
336 use ra_syntax::SourceFileNode; 338 use ra_syntax::SourceFileNode;
337 use test_utils::extract_offset; 339 use test_utils::{extract_offset, assert_eq_text};
338 340
339 use super::*; 341 use super::*;
340 342
@@ -355,9 +357,11 @@ mod tests {
355 let actual = scopes 357 let actual = scopes
356 .scope_chain(marker.syntax()) 358 .scope_chain(marker.syntax())
357 .flat_map(|scope| scopes.entries(scope)) 359 .flat_map(|scope| scopes.entries(scope))
358 .map(|it| it.name()) 360 .map(|it| it.name().to_string())
359 .collect::<Vec<_>>(); 361 .collect::<Vec<_>>()
360 assert_eq!(actual.as_slice(), expected); 362 .join("\n");
363 let expected = expected.join("\n");
364 assert_eq_text!(&actual, &expected);
361 } 365 }
362 366
363 #[test] 367 #[test]
diff --git a/crates/ra_hir/src/krate.rs b/crates/ra_hir/src/krate.rs
index 1196dcef1..89b1e639e 100644
--- a/crates/ra_hir/src/krate.rs
+++ b/crates/ra_hir/src/krate.rs
@@ -1,7 +1,6 @@
1use ra_syntax::SmolStr;
2pub use ra_db::CrateId; 1pub use ra_db::CrateId;
3 2
4use crate::{HirDatabase, Module, Cancelable}; 3use crate::{HirDatabase, Module, Cancelable, Name, AsName};
5 4
6/// hir::Crate describes a single crate. It's the main inteface with which 5/// hir::Crate describes a single crate. It's the main inteface with which
7/// crate's dependencies interact. Mostly, it should be just a proxy for the 6/// crate's dependencies interact. Mostly, it should be just a proxy for the
@@ -14,7 +13,7 @@ pub struct Crate {
14#[derive(Debug)] 13#[derive(Debug)]
15pub struct CrateDependency { 14pub struct CrateDependency {
16 pub krate: Crate, 15 pub krate: Crate,
17 pub name: SmolStr, 16 pub name: Name,
18} 17}
19 18
20impl Crate { 19impl Crate {
@@ -27,7 +26,7 @@ impl Crate {
27 .dependencies(self.crate_id) 26 .dependencies(self.crate_id)
28 .map(|dep| { 27 .map(|dep| {
29 let krate = Crate::new(dep.crate_id()); 28 let krate = Crate::new(dep.crate_id());
30 let name = dep.name.clone(); 29 let name = dep.as_name();
31 CrateDependency { krate, name } 30 CrateDependency { krate, name }
32 }) 31 })
33 .collect() 32 .collect()
diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs
index f1cc0ccd0..5bbb09c01 100644
--- a/crates/ra_hir/src/lib.rs
+++ b/crates/ra_hir/src/lib.rs
@@ -22,6 +22,7 @@ mod path;
22mod arena; 22mod arena;
23pub mod source_binder; 23pub mod source_binder;
24 24
25mod name;
25mod krate; 26mod krate;
26mod module; 27mod module;
27mod function; 28mod function;
@@ -37,10 +38,12 @@ use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
37use crate::{ 38use crate::{
38 db::HirDatabase, 39 db::HirDatabase,
39 arena::{Arena, Id}, 40 arena::{Arena, Id},
41 name::{AsName, KnownName},
40}; 42};
41 43
42pub use self::{ 44pub use self::{
43 path::{Path, PathKind}, 45 path::{Path, PathKind},
46 name::Name,
44 krate::Crate, 47 krate::Crate,
45 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution}, 48 module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution},
46 function::{Function, FnScopes}, 49 function::{Function, FnScopes},
diff --git a/crates/ra_hir/src/mock.rs b/crates/ra_hir/src/mock.rs
index f6882cb77..a2507c9b5 100644
--- a/crates/ra_hir/src/mock.rs
+++ b/crates/ra_hir/src/mock.rs
@@ -189,7 +189,6 @@ salsa::database_storage! {
189 fn file_item() for db::FileItemQuery; 189 fn file_item() for db::FileItemQuery;
190 fn input_module_items() for db::InputModuleItemsQuery; 190 fn input_module_items() for db::InputModuleItemsQuery;
191 fn item_map() for db::ItemMapQuery; 191 fn item_map() for db::ItemMapQuery;
192 fn fn_syntax() for db::FnSyntaxQuery;
193 fn submodules() for db::SubmodulesQuery; 192 fn submodules() for db::SubmodulesQuery;
194 fn infer() for db::InferQuery; 193 fn infer() for db::InferQuery;
195 fn type_for_def() for db::TypeForDefQuery; 194 fn type_for_def() for db::TypeForDefQuery;
diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs
index b9d36f01f..24c346984 100644
--- a/crates/ra_hir/src/module.rs
+++ b/crates/ra_hir/src/module.rs
@@ -7,13 +7,14 @@ use log;
7use ra_syntax::{ 7use ra_syntax::{
8 algo::generate, 8 algo::generate,
9 ast::{self, AstNode, NameOwner}, 9 ast::{self, AstNode, NameOwner},
10 SmolStr, SyntaxNode, 10 SyntaxNode,
11}; 11};
12use ra_db::{SourceRootId, FileId, Cancelable}; 12use ra_db::{SourceRootId, FileId, Cancelable};
13use relative_path::RelativePathBuf; 13use relative_path::RelativePathBuf;
14 14
15use crate::{ 15use crate::{
16 DefKind, DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, 16 Def, DefKind, DefLoc, DefId,
17 Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate,
17 arena::{Arena, Id}, 18 arena::{Arena, Id},
18}; 19};
19 20
@@ -84,7 +85,7 @@ impl Module {
84 } 85 }
85 86
86 /// `name` is `None` for the crate's root module 87 /// `name` is `None` for the crate's root module
87 pub fn name(&self) -> Option<SmolStr> { 88 pub fn name(&self) -> Option<&Name> {
88 let link = self.module_id.parent_link(&self.tree)?; 89 let link = self.module_id.parent_link(&self.tree)?;
89 Some(link.name(&self.tree)) 90 Some(link.name(&self.tree))
90 } 91 }
@@ -100,7 +101,7 @@ impl Module {
100 } 101 }
101 102
102 /// Finds a child module with the specified name. 103 /// Finds a child module with the specified name.
103 pub fn child(&self, name: &str) -> Option<Module> { 104 pub fn child(&self, name: &Name) -> Option<Module> {
104 let child_id = self.module_id.child(&self.tree, name)?; 105 let child_id = self.module_id.child(&self.tree, name)?;
105 Some(Module { 106 Some(Module {
106 module_id: child_id, 107 module_id: child_id,
@@ -138,13 +139,8 @@ impl Module {
138 } else { 139 } else {
139 return Ok(PerNs::none()); 140 return Ok(PerNs::none());
140 }; 141 };
141 let module = match curr.loc(db) { 142 let module = match curr.resolve(db)? {
142 DefLoc { 143 Def::Module(it) => it,
143 kind: DefKind::Module,
144 source_root_id,
145 module_id,
146 ..
147 } => Module::new(db, source_root_id, module_id)?,
148 // TODO here would be the place to handle enum variants... 144 // TODO here would be the place to handle enum variants...
149 _ => return Ok(PerNs::none()), 145 _ => return Ok(PerNs::none()),
150 }; 146 };
@@ -230,15 +226,15 @@ impl ModuleId {
230 .last() 226 .last()
231 .unwrap() 227 .unwrap()
232 } 228 }
233 fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> { 229 fn child(self, tree: &ModuleTree, name: &Name) -> Option<ModuleId> {
234 let link = tree.mods[self] 230 let link = tree.mods[self]
235 .children 231 .children
236 .iter() 232 .iter()
237 .map(|&it| &tree.links[it]) 233 .map(|&it| &tree.links[it])
238 .find(|it| it.name == name)?; 234 .find(|it| it.name == *name)?;
239 Some(*link.points_to.first()?) 235 Some(*link.points_to.first()?)
240 } 236 }
241 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (SmolStr, ModuleId)> + 'a { 237 fn children<'a>(self, tree: &'a ModuleTree) -> impl Iterator<Item = (Name, ModuleId)> + 'a {
242 tree.mods[self].children.iter().filter_map(move |&it| { 238 tree.mods[self].children.iter().filter_map(move |&it| {
243 let link = &tree.links[it]; 239 let link = &tree.links[it];
244 let module = *link.points_to.first()?; 240 let module = *link.points_to.first()?;
@@ -263,8 +259,8 @@ impl LinkId {
263 fn owner(self, tree: &ModuleTree) -> ModuleId { 259 fn owner(self, tree: &ModuleTree) -> ModuleId {
264 tree.links[self].owner 260 tree.links[self].owner
265 } 261 }
266 fn name(self, tree: &ModuleTree) -> SmolStr { 262 fn name(self, tree: &ModuleTree) -> &Name {
267 tree.links[self].name.clone() 263 &tree.links[self].name
268 } 264 }
269 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode { 265 fn bind_source<'a>(self, tree: &ModuleTree, db: &impl HirDatabase) -> ast::ModuleNode {
270 let owner = self.owner(tree); 266 let owner = self.owner(tree);
@@ -328,7 +324,7 @@ impl ModuleSource {
328#[derive(Hash, Debug, PartialEq, Eq)] 324#[derive(Hash, Debug, PartialEq, Eq)]
329struct LinkData { 325struct LinkData {
330 owner: ModuleId, 326 owner: ModuleId,
331 name: SmolStr, 327 name: Name,
332 points_to: Vec<ModuleId>, 328 points_to: Vec<ModuleId>,
333 problem: Option<Problem>, 329 problem: Option<Problem>,
334} 330}
diff --git a/crates/ra_hir/src/module/imp.rs b/crates/ra_hir/src/module/imp.rs
index 748fdb64e..eded85a63 100644
--- a/crates/ra_hir/src/module/imp.rs
+++ b/crates/ra_hir/src/module/imp.rs
@@ -1,16 +1,13 @@
1use std::sync::Arc; 1use std::sync::Arc;
2 2
3use ra_syntax::{ 3use ra_syntax::ast::{self, NameOwner};
4 ast::{self, NameOwner},
5 SmolStr,
6};
7use relative_path::RelativePathBuf; 4use relative_path::RelativePathBuf;
8use rustc_hash::{FxHashMap, FxHashSet}; 5use rustc_hash::{FxHashMap, FxHashSet};
9use arrayvec::ArrayVec; 6use arrayvec::ArrayVec;
10use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId}; 7use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId};
11 8
12use crate::{ 9use crate::{
13 HirDatabase, 10 HirDatabase, Name, AsName,
14}; 11};
15 12
16use super::{ 13use super::{
@@ -20,12 +17,12 @@ use super::{
20 17
21#[derive(Clone, Hash, PartialEq, Eq, Debug)] 18#[derive(Clone, Hash, PartialEq, Eq, Debug)]
22pub enum Submodule { 19pub enum Submodule {
23 Declaration(SmolStr), 20 Declaration(Name),
24 Definition(SmolStr, ModuleSource), 21 Definition(Name, ModuleSource),
25} 22}
26 23
27impl Submodule { 24impl Submodule {
28 fn name(&self) -> &SmolStr { 25 fn name(&self) -> &Name {
29 match self { 26 match self {
30 Submodule::Declaration(name) => name, 27 Submodule::Declaration(name) => name,
31 Submodule::Definition(name, _) => name, 28 Submodule::Definition(name, _) => name,
@@ -35,14 +32,14 @@ impl Submodule {
35 32
36pub(crate) fn modules<'a>( 33pub(crate) fn modules<'a>(
37 root: impl ast::ModuleItemOwner<'a>, 34 root: impl ast::ModuleItemOwner<'a>,
38) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> { 35) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
39 root.items() 36 root.items()
40 .filter_map(|item| match item { 37 .filter_map(|item| match item {
41 ast::ModuleItem::Module(m) => Some(m), 38 ast::ModuleItem::Module(m) => Some(m),
42 _ => None, 39 _ => None,
43 }) 40 })
44 .filter_map(|module| { 41 .filter_map(|module| {
45 let name = module.name()?.text(); 42 let name = module.name()?.as_name();
46 Some((name, module)) 43 Some((name, module))
47 }) 44 })
48} 45}
@@ -155,7 +152,7 @@ fn build_subtree(
155fn resolve_submodule( 152fn resolve_submodule(
156 db: &impl HirDatabase, 153 db: &impl HirDatabase,
157 source: ModuleSource, 154 source: ModuleSource,
158 name: &SmolStr, 155 name: &Name,
159) -> (Vec<FileId>, Option<Problem>) { 156) -> (Vec<FileId>, Option<Problem>) {
160 // FIXME: handle submodules of inline modules properly 157 // FIXME: handle submodules of inline modules properly
161 let file_id = source.file_id(); 158 let file_id = source.file_id();
diff --git a/crates/ra_hir/src/module/nameres.rs b/crates/ra_hir/src/module/nameres.rs
index 98cd225dd..68eb02a98 100644
--- a/crates/ra_hir/src/module/nameres.rs
+++ b/crates/ra_hir/src/module/nameres.rs
@@ -14,14 +14,12 @@
14//! modifications (that is, typing inside a function shold not change IMIs), 14//! modifications (that is, typing inside a function shold not change IMIs),
15//! such that the results of name resolution can be preserved unless the module 15//! such that the results of name resolution can be preserved unless the module
16//! structure itself is modified. 16//! structure itself is modified.
17use std::{ 17use std::sync::Arc;
18 sync::Arc,
19};
20 18
21use rustc_hash::FxHashMap; 19use rustc_hash::FxHashMap;
22use ra_syntax::{ 20use ra_syntax::{
23 TextRange, 21 TextRange,
24 SmolStr, SyntaxKind::{self, *}, 22 SyntaxKind::{self, *},
25 ast::{self, AstNode} 23 ast::{self, AstNode}
26}; 24};
27use ra_db::SourceRootId; 25use ra_db::SourceRootId;
@@ -32,6 +30,7 @@ use crate::{
32 SourceItemId, SourceFileItemId, SourceFileItems, 30 SourceItemId, SourceFileItemId, SourceFileItems,
33 Path, PathKind, 31 Path, PathKind,
34 HirDatabase, Crate, 32 HirDatabase, Crate,
33 Name, AsName,
35 module::{Module, ModuleId, ModuleTree}, 34 module::{Module, ModuleId, ModuleTree},
36}; 35};
37 36
@@ -45,14 +44,14 @@ pub struct ItemMap {
45 44
46#[derive(Debug, Default, PartialEq, Eq, Clone)] 45#[derive(Debug, Default, PartialEq, Eq, Clone)]
47pub struct ModuleScope { 46pub struct ModuleScope {
48 items: FxHashMap<SmolStr, Resolution>, 47 items: FxHashMap<Name, Resolution>,
49} 48}
50 49
51impl ModuleScope { 50impl ModuleScope {
52 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a SmolStr, &'a Resolution)> + 'a { 51 pub fn entries<'a>(&'a self) -> impl Iterator<Item = (&'a Name, &'a Resolution)> + 'a {
53 self.items.iter() 52 self.items.iter()
54 } 53 }
55 pub fn get(&self, name: &SmolStr) -> Option<&Resolution> { 54 pub fn get(&self, name: &Name) -> Option<&Resolution> {
56 self.items.get(name) 55 self.items.get(name)
57 } 56 }
58} 57}
@@ -72,7 +71,7 @@ pub struct InputModuleItems {
72#[derive(Debug, PartialEq, Eq)] 71#[derive(Debug, PartialEq, Eq)]
73struct ModuleItem { 72struct ModuleItem {
74 id: SourceFileItemId, 73 id: SourceFileItemId,
75 name: SmolStr, 74 name: Name,
76 kind: SyntaxKind, 75 kind: SyntaxKind,
77 vis: Vis, 76 vis: Vis,
78} 77}
@@ -260,7 +259,7 @@ impl InputModuleItems {
260 259
261impl ModuleItem { 260impl ModuleItem {
262 fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> { 261 fn new<'a>(file_items: &SourceFileItems, item: impl ast::NameOwner<'a>) -> Option<ModuleItem> {
263 let name = item.name()?.text(); 262 let name = item.name()?.as_name();
264 let kind = item.syntax().kind(); 263 let kind = item.syntax().kind();
265 let vis = Vis::Other; 264 let vis = Vis::Other;
266 let id = file_items.id_of_unchecked(item.syntax()); 265 let id = file_items.id_of_unchecked(item.syntax());
@@ -328,7 +327,11 @@ where
328 for dep in krate.dependencies(self.db) { 327 for dep in krate.dependencies(self.db) {
329 if let Some(module) = dep.krate.root_module(self.db)? { 328 if let Some(module) = dep.krate.root_module(self.db)? {
330 let def_id = module.def_id(self.db); 329 let def_id = module.def_id(self.db);
331 self.add_module_item(&mut module_items, dep.name, PerNs::types(def_id)); 330 self.add_module_item(
331 &mut module_items,
332 dep.name.clone(),
333 PerNs::types(def_id),
334 );
332 } 335 }
333 } 336 }
334 }; 337 };
@@ -389,7 +392,7 @@ where
389 Ok(()) 392 Ok(())
390 } 393 }
391 394
392 fn add_module_item(&self, module_items: &mut ModuleScope, name: SmolStr, def_id: PerNs<DefId>) { 395 fn add_module_item(&self, module_items: &mut ModuleScope, name: Name, def_id: PerNs<DefId>) {
393 let resolution = Resolution { 396 let resolution = Resolution {
394 def_id, 397 def_id,
395 import: None, 398 import: None,
diff --git a/crates/ra_hir/src/module/nameres/tests.rs b/crates/ra_hir/src/module/nameres/tests.rs
index 03ea5c1d6..ca20f064f 100644
--- a/crates/ra_hir/src/module/nameres/tests.rs
+++ b/crates/ra_hir/src/module/nameres/tests.rs
@@ -2,8 +2,8 @@ use std::sync::Arc;
2 2
3use salsa::Database; 3use salsa::Database;
4use ra_db::{FilesDatabase, CrateGraph}; 4use ra_db::{FilesDatabase, CrateGraph};
5use ra_syntax::SmolStr;
6use relative_path::RelativePath; 5use relative_path::RelativePath;
6use test_utils::assert_eq_text;
7 7
8use crate::{ 8use crate::{
9 self as hir, 9 self as hir,
@@ -21,6 +21,35 @@ fn item_map(fixture: &str) -> (Arc<hir::ItemMap>, hir::ModuleId) {
21 (db.item_map(source_root).unwrap(), module_id) 21 (db.item_map(source_root).unwrap(), module_id)
22} 22}
23 23
24fn check_module_item_map(map: &hir::ItemMap, module_id: hir::ModuleId, expected: &str) {
25 let mut lines = map.per_module[&module_id]
26 .items
27 .iter()
28 .map(|(name, res)| format!("{}: {}", name, dump_resolution(res)))
29 .collect::<Vec<_>>();
30 lines.sort();
31 let actual = lines.join("\n");
32 let expected = expected
33 .trim()
34 .lines()
35 .map(|it| it.trim())
36 .collect::<Vec<_>>()
37 .join("\n");
38 assert_eq_text!(&actual, &expected);
39
40 fn dump_resolution(resolution: &hir::Resolution) -> &'static str {
41 match (
42 resolution.def_id.types.is_some(),
43 resolution.def_id.values.is_some(),
44 ) {
45 (true, true) => "t v",
46 (true, false) => "t",
47 (false, true) => "v",
48 (false, false) => "_",
49 }
50 }
51}
52
24#[test] 53#[test]
25fn item_map_smoke_test() { 54fn item_map_smoke_test() {
26 let (item_map, module_id) = item_map( 55 let (item_map, module_id) = item_map(
@@ -38,13 +67,18 @@ fn item_map_smoke_test() {
38 pub struct Baz; 67 pub struct Baz;
39 ", 68 ",
40 ); 69 );
41 let name = SmolStr::from("Baz"); 70 check_module_item_map(
42 let resolution = &item_map.per_module[&module_id].items[&name]; 71 &item_map,
43 assert!(resolution.def_id.take_types().is_some()); 72 module_id,
73 "
74 Baz: t v
75 foo: t
76 ",
77 );
44} 78}
45 79
46#[test] 80#[test]
47fn test_self() { 81fn item_map_using_self() {
48 let (item_map, module_id) = item_map( 82 let (item_map, module_id) = item_map(
49 " 83 "
50 //- /lib.rs 84 //- /lib.rs
@@ -57,9 +91,14 @@ fn test_self() {
57 pub struct Baz; 91 pub struct Baz;
58 ", 92 ",
59 ); 93 );
60 let name = SmolStr::from("Baz"); 94 check_module_item_map(
61 let resolution = &item_map.per_module[&module_id].items[&name]; 95 &item_map,
62 assert!(resolution.def_id.take_types().is_some()); 96 module_id,
97 "
98 Baz: t v
99 foo: t
100 ",
101 );
63} 102}
64 103
65#[test] 104#[test]
@@ -90,9 +129,14 @@ fn item_map_across_crates() {
90 let module_id = module.module_id; 129 let module_id = module.module_id;
91 let item_map = db.item_map(source_root).unwrap(); 130 let item_map = db.item_map(source_root).unwrap();
92 131
93 let name = SmolStr::from("Baz"); 132 check_module_item_map(
94 let resolution = &item_map.per_module[&module_id].items[&name]; 133 &item_map,
95 assert!(resolution.def_id.take_types().is_some()); 134 module_id,
135 "
136 Baz: t v
137 test_crate: t
138 ",
139 );
96} 140}
97 141
98#[test] 142#[test]
diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs
new file mode 100644
index 000000000..51e8b3da8
--- /dev/null
+++ b/crates/ra_hir/src/name.rs
@@ -0,0 +1,105 @@
1use std::fmt;
2
3use ra_syntax::{ast, SmolStr};
4
5/// `Name` is a wrapper around string, which is used in hir for both references
6/// and declarations. In theory, names should also carry hygene info, but we are
7/// not there yet!
8#[derive(Clone, PartialEq, Eq, Hash)]
9pub struct Name {
10 text: SmolStr,
11}
12
13impl fmt::Display for Name {
14 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
15 fmt::Display::fmt(&self.text, f)
16 }
17}
18
19impl fmt::Debug for Name {
20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 fmt::Debug::fmt(&self.text, f)
22 }
23}
24
25impl Name {
26 fn new(text: SmolStr) -> Name {
27 Name { text }
28 }
29
30 pub(crate) fn missing() -> Name {
31 Name::new("[missing name]".into())
32 }
33
34 pub(crate) fn tuple_field_name(idx: usize) -> Name {
35 Name::new(idx.to_string().into())
36 }
37
38 pub(crate) fn as_known_name(&self) -> Option<KnownName> {
39 let name = match self.text.as_str() {
40 "isize" => KnownName::Isize,
41 "i8" => KnownName::I8,
42 "i16" => KnownName::I16,
43 "i32" => KnownName::I32,
44 "i64" => KnownName::I64,
45 "i128" => KnownName::I128,
46 "usize" => KnownName::Usize,
47 "u8" => KnownName::U8,
48 "u16" => KnownName::U16,
49 "u32" => KnownName::U32,
50 "u64" => KnownName::U64,
51 "u128" => KnownName::U128,
52 "f32" => KnownName::F32,
53 "f64" => KnownName::F64,
54 _ => return None,
55 };
56 Some(name)
57 }
58}
59
60pub(crate) trait AsName {
61 fn as_name(&self) -> Name;
62}
63
64impl AsName for ast::NameRef<'_> {
65 fn as_name(&self) -> Name {
66 Name::new(self.text())
67 }
68}
69
70impl AsName for ast::Name<'_> {
71 fn as_name(&self) -> Name {
72 Name::new(self.text())
73 }
74}
75
76impl AsName for ra_db::Dependency {
77 fn as_name(&self) -> Name {
78 Name::new(self.name.clone())
79 }
80}
81
82// Ideally, should be replaced with
83// ```
84// const ISIZE: Name = Name::new("isize")
85// ```
86// but const-fn is not that powerful yet.
87#[derive(Debug)]
88pub(crate) enum KnownName {
89 Isize,
90 I8,
91 I16,
92 I32,
93 I64,
94 I128,
95
96 Usize,
97 U8,
98 U16,
99 U32,
100 U64,
101 U128,
102
103 F32,
104 F64,
105}
diff --git a/crates/ra_hir/src/path.rs b/crates/ra_hir/src/path.rs
index 0b260072c..93f7203fe 100644
--- a/crates/ra_hir/src/path.rs
+++ b/crates/ra_hir/src/path.rs
@@ -1,9 +1,11 @@
1use ra_syntax::{SmolStr, ast, AstNode, TextRange}; 1use ra_syntax::{ast, AstNode, TextRange};
2
3use crate::{Name, AsName};
2 4
3#[derive(Debug, Clone, PartialEq, Eq, Hash)] 5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
4pub struct Path { 6pub struct Path {
5 pub kind: PathKind, 7 pub kind: PathKind,
6 pub segments: Vec<SmolStr>, 8 pub segments: Vec<Name>,
7} 9}
8 10
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -29,7 +31,7 @@ impl Path {
29 loop { 31 loop {
30 let segment = path.segment()?; 32 let segment = path.segment()?;
31 match segment.kind()? { 33 match segment.kind()? {
32 ast::PathSegmentKind::Name(name) => segments.push(name.text()), 34 ast::PathSegmentKind::Name(name) => segments.push(name.as_name()),
33 ast::PathSegmentKind::CrateKw => { 35 ast::PathSegmentKind::CrateKw => {
34 kind = PathKind::Crate; 36 kind = PathKind::Crate;
35 break; 37 break;
@@ -67,6 +69,14 @@ impl Path {
67 pub fn is_ident(&self) -> bool { 69 pub fn is_ident(&self) -> bool {
68 self.kind == PathKind::Plain && self.segments.len() == 1 70 self.kind == PathKind::Plain && self.segments.len() == 1
69 } 71 }
72
73 /// If this path is a single identifier, like `foo`, return its name.
74 pub fn as_ident(&self) -> Option<&Name> {
75 if self.kind != PathKind::Plain || self.segments.len() > 1 {
76 return None;
77 }
78 self.segments.first()
79 }
70} 80}
71 81
72fn expand_use_tree( 82fn expand_use_tree(
@@ -130,7 +140,7 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
130 kind: PathKind::Plain, 140 kind: PathKind::Plain,
131 segments: Vec::with_capacity(1), 141 segments: Vec::with_capacity(1),
132 }); 142 });
133 res.segments.push(name.text()); 143 res.segments.push(name.as_name());
134 res 144 res
135 } 145 }
136 ast::PathSegmentKind::CrateKw => { 146 ast::PathSegmentKind::CrateKw => {
diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs
index 4a7958a12..721bd4195 100644
--- a/crates/ra_hir/src/query_definitions.rs
+++ b/crates/ra_hir/src/query_definitions.rs
@@ -5,55 +5,30 @@ use std::{
5 5
6use rustc_hash::FxHashMap; 6use rustc_hash::FxHashMap;
7use ra_syntax::{ 7use ra_syntax::{
8 AstNode, SyntaxNode, SmolStr, 8 AstNode, SyntaxNode,
9 ast::{self, FnDef, FnDefNode, NameOwner, ModuleItemOwner} 9 ast::{self, NameOwner, ModuleItemOwner}
10}; 10};
11use ra_db::{SourceRootId, FileId, Cancelable,}; 11use ra_db::{SourceRootId, FileId, Cancelable,};
12 12
13use crate::{ 13use crate::{
14 SourceFileItems, SourceItemId, DefKind, Function, DefId, 14 SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName,
15 db::HirDatabase, 15 db::HirDatabase,
16 function::{FnScopes, FnId}, 16 function::FnScopes,
17 module::{ 17 module::{
18 ModuleSource, ModuleSourceNode, ModuleId, 18 ModuleSource, ModuleSourceNode, ModuleId,
19 imp::Submodule, 19 imp::Submodule,
20 nameres::{InputModuleItems, ItemMap, Resolver}, 20 nameres::{InputModuleItems, ItemMap, Resolver},
21 }, 21 },
22 ty::{self, InferenceResult, Ty},
23 adt::{StructData, EnumData}, 22 adt::{StructData, EnumData},
24}; 23};
25 24
26/// Resolve `FnId` to the corresponding `SyntaxNode` 25pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Arc<FnScopes> {
27pub(super) fn fn_syntax(db: &impl HirDatabase, fn_id: FnId) -> FnDefNode { 26 let function = Function::new(def_id);
28 let def_loc = fn_id.0.loc(db); 27 let syntax = function.syntax(db);
29 assert!(def_loc.kind == DefKind::Function);
30 let syntax = db.file_item(def_loc.source_item_id);
31 FnDef::cast(syntax.borrowed()).unwrap().owned()
32}
33
34pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> {
35 let syntax = db.fn_syntax(fn_id);
36 let res = FnScopes::new(syntax.borrowed()); 28 let res = FnScopes::new(syntax.borrowed());
37 Arc::new(res) 29 Arc::new(res)
38} 30}
39 31
40pub(super) fn infer(db: &impl HirDatabase, fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
41 let function = Function { fn_id };
42 ty::infer(db, function).map(Arc::new)
43}
44
45pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
46 ty::type_for_def(db, def_id)
47}
48
49pub(super) fn type_for_field(
50 db: &impl HirDatabase,
51 def_id: DefId,
52 field: SmolStr,
53) -> Cancelable<Ty> {
54 ty::type_for_field(db, def_id, field)
55}
56
57pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { 32pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {
58 let def_loc = def_id.loc(db); 33 let def_loc = def_id.loc(db);
59 assert!(def_loc.kind == DefKind::Struct); 34 assert!(def_loc.kind == DefKind::Struct);
@@ -130,14 +105,14 @@ pub(crate) fn submodules(
130 105
131pub(crate) fn modules<'a>( 106pub(crate) fn modules<'a>(
132 root: impl ast::ModuleItemOwner<'a>, 107 root: impl ast::ModuleItemOwner<'a>,
133) -> impl Iterator<Item = (SmolStr, ast::Module<'a>)> { 108) -> impl Iterator<Item = (Name, ast::Module<'a>)> {
134 root.items() 109 root.items()
135 .filter_map(|item| match item { 110 .filter_map(|item| match item {
136 ast::ModuleItem::Module(m) => Some(m), 111 ast::ModuleItem::Module(m) => Some(m),
137 _ => None, 112 _ => None,
138 }) 113 })
139 .filter_map(|module| { 114 .filter_map(|module| {
140 let name = module.name()?.text(); 115 let name = module.name()?.as_name();
141 Some((name, module)) 116 Some((name, module))
142 }) 117 })
143} 118}
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index a0165aef2..a0d1daf71 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -8,14 +8,14 @@
8use ra_db::{FileId, FilePosition, Cancelable}; 8use ra_db::{FileId, FilePosition, Cancelable};
9use ra_editor::find_node_at_offset; 9use ra_editor::find_node_at_offset;
10use ra_syntax::{ 10use ra_syntax::{
11 ast::{self, AstNode}, 11 ast::{self, AstNode, NameOwner},
12 SyntaxNodeRef, 12 SyntaxNodeRef,
13}; 13};
14 14
15use crate::{ 15use crate::{
16 HirDatabase, Module, Function, SourceItemId, 16 HirDatabase, Module, Function, SourceItemId,
17 module::ModuleSource, 17 module::ModuleSource,
18 DefKind, DefLoc 18 DefKind, DefLoc, AsName,
19}; 19};
20 20
21/// Locates the module by `FileId`. Picks topmost module in the file. 21/// Locates the module by `FileId`. Picks topmost module in the file.
@@ -24,6 +24,25 @@ pub fn module_from_file_id(db: &impl HirDatabase, file_id: FileId) -> Cancelable
24 module_from_source(db, module_source) 24 module_from_source(db, module_source)
25} 25}
26 26
27/// Locates the child module by `mod child;` declaration.
28pub fn module_from_declaration(
29 db: &impl HirDatabase,
30 file_id: FileId,
31 decl: ast::Module,
32) -> Cancelable<Option<Module>> {
33 let parent_module = module_from_file_id(db, file_id)?;
34 let child_name = decl.name();
35 match (parent_module, child_name) {
36 (Some(parent_module), Some(child_name)) => {
37 if let Some(child) = parent_module.child(&child_name.as_name()) {
38 return Ok(Some(child));
39 }
40 }
41 _ => (),
42 }
43 Ok(None)
44}
45
27/// Locates the module by position in the source code. 46/// Locates the module by position in the source code.
28pub fn module_from_position( 47pub fn module_from_position(
29 db: &impl HirDatabase, 48 db: &impl HirDatabase,
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 67b523c2c..719b3f7cd 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -1,27 +1,133 @@
1//! The type system. We currently use this to infer types for completion.
2//!
3//! For type inference, compare the implementations in rustc (the various
4//! check_* methods in librustc_typeck/check/mod.rs are a good entry point) and
5//! IntelliJ-Rust (org.rust.lang.core.types.infer). Our entry point for
6//! inference here is the `infer` function, which infers the types of all
7//! expressions in a given function.
8//!
9//! The central struct here is `Ty`, which represents a type. During inference,
10//! it can contain type 'variables' which represent currently unknown types; as
11//! we walk through the expressions, we might determine that certain variables
12//! need to be equal to each other, or to certain types. To record this, we use
13//! the union-find implementation from the `ena` crate, which is extracted from
14//! rustc.
15
1mod primitive; 16mod primitive;
2#[cfg(test)] 17#[cfg(test)]
3mod tests; 18mod tests;
4 19
5use std::sync::Arc; 20use std::sync::Arc;
6use std::fmt; 21use std::{fmt, mem};
7 22
8use log; 23use log;
9use rustc_hash::{FxHashMap}; 24use rustc_hash::FxHashMap;
25use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError};
10 26
11use ra_db::{LocalSyntaxPtr, Cancelable}; 27use ra_db::{LocalSyntaxPtr, Cancelable};
12use ra_syntax::{ 28use ra_syntax::{
13 SmolStr,
14 ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp}, 29 ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp},
15 SyntaxNodeRef 30 SyntaxNodeRef
16}; 31};
17 32
18use crate::{ 33use crate::{
19 Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, 34 Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName,
20 db::HirDatabase, 35 db::HirDatabase,
21 adt::VariantData,
22 type_ref::{TypeRef, Mutability}, 36 type_ref::{TypeRef, Mutability},
23}; 37};
24 38
39/// The ID of a type variable.
40#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
41pub struct TypeVarId(u32);
42
43impl UnifyKey for TypeVarId {
44 type Value = TypeVarValue;
45
46 fn index(&self) -> u32 {
47 self.0
48 }
49
50 fn from_index(i: u32) -> Self {
51 TypeVarId(i)
52 }
53
54 fn tag() -> &'static str {
55 "TypeVarId"
56 }
57}
58
59/// The value of a type variable: either we already know the type, or we don't
60/// know it yet.
61#[derive(Clone, PartialEq, Eq, Debug)]
62pub enum TypeVarValue {
63 Known(Ty),
64 Unknown,
65}
66
67impl TypeVarValue {
68 fn known(&self) -> Option<&Ty> {
69 match self {
70 TypeVarValue::Known(ty) => Some(ty),
71 TypeVarValue::Unknown => None,
72 }
73 }
74}
75
76impl UnifyValue for TypeVarValue {
77 type Error = NoError;
78
79 fn unify_values(value1: &Self, value2: &Self) -> Result<Self, NoError> {
80 match (value1, value2) {
81 // We should never equate two type variables, both of which have
82 // known types. Instead, we recursively equate those types.
83 (TypeVarValue::Known(..), TypeVarValue::Known(..)) => {
84 panic!("equating two type variables, both of which have known types")
85 }
86
87 // If one side is known, prefer that one.
88 (TypeVarValue::Known(..), TypeVarValue::Unknown) => Ok(value1.clone()),
89 (TypeVarValue::Unknown, TypeVarValue::Known(..)) => Ok(value2.clone()),
90
91 (TypeVarValue::Unknown, TypeVarValue::Unknown) => Ok(TypeVarValue::Unknown),
92 }
93 }
94}
95
96/// The kinds of placeholders we need during type inference. Currently, we only
97/// have type variables; in the future, we will probably also need int and float
98/// variables, for inference of literal values (e.g. `100` could be one of
99/// several integer types).
100#[derive(Clone, PartialEq, Eq, Hash, Debug)]
101pub enum InferTy {
102 TypeVar(TypeVarId),
103}
104
105/// When inferring an expression, we propagate downward whatever type hint we
106/// are able in the form of an `Expectation`.
107#[derive(Clone, PartialEq, Eq, Debug)]
108struct Expectation {
109 ty: Ty,
110 // TODO: In some cases, we need to be aware whether the expectation is that
111 // the type match exactly what we passed, or whether it just needs to be
112 // coercible to the expected type. See Expectation::rvalue_hint in rustc.
113}
114
115impl Expectation {
116 /// The expectation that the type of the expression needs to equal the given
117 /// type.
118 fn has_type(ty: Ty) -> Self {
119 Expectation { ty }
120 }
121
122 /// This expresses no expectation on the type.
123 fn none() -> Self {
124 Expectation { ty: Ty::Unknown }
125 }
126}
127
128/// A type. This is based on the `TyKind` enum in rustc (librustc/ty/sty.rs).
129///
130/// This should be cheap to clone.
25#[derive(Clone, PartialEq, Eq, Hash, Debug)] 131#[derive(Clone, PartialEq, Eq, Hash, Debug)]
26pub enum Ty { 132pub enum Ty {
27 /// The primitive boolean type. Written as `bool`. 133 /// The primitive boolean type. Written as `bool`.
@@ -45,7 +151,7 @@ pub enum Ty {
45 /// The DefId of the struct/enum. 151 /// The DefId of the struct/enum.
46 def_id: DefId, 152 def_id: DefId,
47 /// The name, for displaying. 153 /// The name, for displaying.
48 name: SmolStr, 154 name: Name,
49 // later we'll need generic substitutions here 155 // later we'll need generic substitutions here
50 }, 156 },
51 157
@@ -55,14 +161,14 @@ pub enum Ty {
55 // An array with the given length. Written as `[T; n]`. 161 // An array with the given length. Written as `[T; n]`.
56 // Array(Ty, ty::Const), 162 // Array(Ty, ty::Const),
57 /// The pointee of an array slice. Written as `[T]`. 163 /// The pointee of an array slice. Written as `[T]`.
58 Slice(TyRef), 164 Slice(Arc<Ty>),
59 165
60 /// A raw pointer. Written as `*mut T` or `*const T` 166 /// A raw pointer. Written as `*mut T` or `*const T`
61 RawPtr(TyRef, Mutability), 167 RawPtr(Arc<Ty>, Mutability),
62 168
63 /// A reference; a pointer with an associated lifetime. Written as 169 /// A reference; a pointer with an associated lifetime. Written as
64 /// `&'a mut T` or `&'a T`. 170 /// `&'a mut T` or `&'a T`.
65 Ref(TyRef, Mutability), 171 Ref(Arc<Ty>, Mutability),
66 172
67 /// A pointer to a function. Written as `fn() -> i32`. 173 /// A pointer to a function. Written as `fn() -> i32`.
68 /// 174 ///
@@ -74,52 +180,51 @@ pub enum Ty {
74 /// ``` 180 /// ```
75 FnPtr(Arc<FnSig>), 181 FnPtr(Arc<FnSig>),
76 182
183 // rustc has a separate type for each function, which just coerces to the
184 // above function pointer type. Once we implement generics, we will probably
185 // need this as well.
186
77 // A trait, defined with `dyn trait`. 187 // A trait, defined with `dyn trait`.
78 // Dynamic(), 188 // Dynamic(),
79 /// The anonymous type of a closure. Used to represent the type of 189 // The anonymous type of a closure. Used to represent the type of
80 /// `|a| a`. 190 // `|a| a`.
81 // Closure(DefId, ClosureSubsts<'tcx>), 191 // Closure(DefId, ClosureSubsts<'tcx>),
82 192
83 /// The anonymous type of a generator. Used to represent the type of 193 // The anonymous type of a generator. Used to represent the type of
84 /// `|a| yield a`. 194 // `|a| yield a`.
85 // Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability), 195 // Generator(DefId, GeneratorSubsts<'tcx>, hir::GeneratorMovability),
86 196
87 /// A type representin the types stored inside a generator. 197 // A type representin the types stored inside a generator.
88 /// This should only appear in GeneratorInteriors. 198 // This should only appear in GeneratorInteriors.
89 // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>), 199 // GeneratorWitness(Binder<&'tcx List<Ty<'tcx>>>),
90 200 /// The never type `!`.
91 /// The never type `!`
92 Never, 201 Never,
93 202
94 /// A tuple type. For example, `(i32, bool)`. 203 /// A tuple type. For example, `(i32, bool)`.
95 Tuple(Vec<Ty>), 204 Tuple(Arc<[Ty]>),
96 205
97 // The projection of an associated type. For example, 206 // The projection of an associated type. For example,
98 // `<T as Trait<..>>::N`. 207 // `<T as Trait<..>>::N`.pub
99 // Projection(ProjectionTy), 208 // Projection(ProjectionTy),
100 209
101 // Opaque (`impl Trait`) type found in a return type. 210 // Opaque (`impl Trait`) type found in a return type.
102 // The `DefId` comes either from
103 // * the `impl Trait` ast::Ty node,
104 // * or the `existential type` declaration
105 // The substitutions are for the generics of the function in question.
106 // Opaque(DefId, Substs), 211 // Opaque(DefId, Substs),
107 212
108 // A type parameter; for example, `T` in `fn f<T>(x: T) {} 213 // A type parameter; for example, `T` in `fn f<T>(x: T) {}
109 // Param(ParamTy), 214 // Param(ParamTy),
110 215 /// A type variable used during type checking. Not to be confused with a
111 // A placeholder type - universally quantified higher-ranked type. 216 /// type parameter.
112 // Placeholder(ty::PlaceholderType), 217 Infer(InferTy),
113 218
114 // A type variable used during type checking. 219 /// A placeholder for a type which could not be computed; this is propagated
115 // Infer(InferTy), 220 /// to avoid useless error messages. Doubles as a placeholder where type
116 /// A placeholder for a type which could not be computed; this is 221 /// variables are inserted before type checking, since we want to try to
117 /// propagated to avoid useless error messages. 222 /// infer a better type here anyway -- for the IDE use case, we want to try
223 /// to infer as much as possible even in the presence of type errors.
118 Unknown, 224 Unknown,
119} 225}
120 226
121type TyRef = Arc<Ty>; 227/// A function signature.
122
123#[derive(Clone, PartialEq, Eq, Hash, Debug)] 228#[derive(Clone, PartialEq, Eq, Hash, Debug)]
124pub struct FnSig { 229pub struct FnSig {
125 input: Vec<Ty>, 230 input: Vec<Ty>,
@@ -138,8 +243,8 @@ impl Ty {
138 let inner_tys = inner 243 let inner_tys = inner
139 .iter() 244 .iter()
140 .map(|tr| Ty::from_hir(db, module, tr)) 245 .map(|tr| Ty::from_hir(db, module, tr))
141 .collect::<Cancelable<_>>()?; 246 .collect::<Cancelable<Vec<_>>>()?;
142 Ty::Tuple(inner_tys) 247 Ty::Tuple(inner_tys.into())
143 } 248 }
144 TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?, 249 TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?,
145 TypeRef::RawPtr(inner, mutability) => { 250 TypeRef::RawPtr(inner, mutability) => {
@@ -155,7 +260,7 @@ impl Ty {
155 let inner_ty = Ty::from_hir(db, module, inner)?; 260 let inner_ty = Ty::from_hir(db, module, inner)?;
156 Ty::Ref(Arc::new(inner_ty), *mutability) 261 Ty::Ref(Arc::new(inner_ty), *mutability)
157 } 262 }
158 TypeRef::Placeholder => Ty::Unknown, // TODO 263 TypeRef::Placeholder => Ty::Unknown,
159 TypeRef::Fn(params) => { 264 TypeRef::Fn(params) => {
160 let mut inner_tys = params 265 let mut inner_tys = params
161 .iter() 266 .iter()
@@ -179,13 +284,12 @@ impl Ty {
179 module: &Module, 284 module: &Module,
180 path: &Path, 285 path: &Path,
181 ) -> Cancelable<Self> { 286 ) -> Cancelable<Self> {
182 if path.is_ident() { 287 if let Some(name) = path.as_ident() {
183 let name = &path.segments[0]; 288 if let Some(int_ty) = primitive::IntTy::from_name(name) {
184 if let Some(int_ty) = primitive::IntTy::from_string(&name) {
185 return Ok(Ty::Int(int_ty)); 289 return Ok(Ty::Int(int_ty));
186 } else if let Some(uint_ty) = primitive::UintTy::from_string(&name) { 290 } else if let Some(uint_ty) = primitive::UintTy::from_name(name) {
187 return Ok(Ty::Uint(uint_ty)); 291 return Ok(Ty::Uint(uint_ty));
188 } else if let Some(float_ty) = primitive::FloatTy::from_string(&name) { 292 } else if let Some(float_ty) = primitive::FloatTy::from_name(name) {
189 return Ok(Ty::Float(float_ty)); 293 return Ok(Ty::Float(float_ty));
190 } 294 }
191 } 295 }
@@ -219,7 +323,41 @@ impl Ty {
219 } 323 }
220 324
221 pub fn unit() -> Self { 325 pub fn unit() -> Self {
222 Ty::Tuple(Vec::new()) 326 Ty::Tuple(Arc::new([]))
327 }
328
329 fn walk_mut(&mut self, f: &mut impl FnMut(&mut Ty)) {
330 f(self);
331 match self {
332 Ty::Slice(t) => Arc::make_mut(t).walk_mut(f),
333 Ty::RawPtr(t, _) => Arc::make_mut(t).walk_mut(f),
334 Ty::Ref(t, _) => Arc::make_mut(t).walk_mut(f),
335 Ty::Tuple(ts) => {
336 // Without an Arc::make_mut_slice, we can't avoid the clone here:
337 let mut v: Vec<_> = ts.iter().cloned().collect();
338 for t in &mut v {
339 t.walk_mut(f);
340 }
341 *ts = v.into();
342 }
343 Ty::FnPtr(sig) => {
344 let sig_mut = Arc::make_mut(sig);
345 for input in &mut sig_mut.input {
346 input.walk_mut(f);
347 }
348 sig_mut.output.walk_mut(f);
349 }
350 Ty::Adt { .. } => {} // need to walk type parameters later
351 _ => {}
352 }
353 }
354
355 fn fold(mut self, f: &mut impl FnMut(Ty) -> Ty) -> Ty {
356 self.walk_mut(&mut |ty_mut| {
357 let ty = mem::replace(ty_mut, Ty::Unknown);
358 *ty_mut = f(ty);
359 });
360 self
223 } 361 }
224} 362}
225 363
@@ -238,7 +376,7 @@ impl fmt::Display for Ty {
238 Ty::Never => write!(f, "!"), 376 Ty::Never => write!(f, "!"),
239 Ty::Tuple(ts) => { 377 Ty::Tuple(ts) => {
240 write!(f, "(")?; 378 write!(f, "(")?;
241 for t in ts { 379 for t in ts.iter() {
242 write!(f, "{},", t)?; 380 write!(f, "{},", t)?;
243 } 381 }
244 write!(f, ")") 382 write!(f, ")")
@@ -252,11 +390,16 @@ impl fmt::Display for Ty {
252 } 390 }
253 Ty::Adt { name, .. } => write!(f, "{}", name), 391 Ty::Adt { name, .. } => write!(f, "{}", name),
254 Ty::Unknown => write!(f, "[unknown]"), 392 Ty::Unknown => write!(f, "[unknown]"),
393 Ty::Infer(..) => write!(f, "_"),
255 } 394 }
256 } 395 }
257} 396}
258 397
259pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> { 398// Functions returning declared types for items
399
400/// Compute the declared type of a function. This should not need to look at the
401/// function body (but currently uses the function AST, so does anyway - TODO).
402fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
260 let syntax = f.syntax(db); 403 let syntax = f.syntax(db);
261 let module = f.module(db)?; 404 let module = f.module(db)?;
262 let node = syntax.borrowed(); 405 let node = syntax.borrowed();
@@ -269,30 +412,30 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
269 .collect() 412 .collect()
270 }) 413 })
271 .unwrap_or_else(|| Ok(Vec::new()))?; 414 .unwrap_or_else(|| Ok(Vec::new()))?;
272 let output = Ty::from_ast_opt(db, &module, node.ret_type().and_then(|rt| rt.type_ref()))?; 415 let output = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) {
416 Ty::from_ast(db, &module, type_ref)?
417 } else {
418 Ty::unit()
419 };
273 let sig = FnSig { input, output }; 420 let sig = FnSig { input, output };
274 Ok(Ty::FnPtr(Arc::new(sig))) 421 Ok(Ty::FnPtr(Arc::new(sig)))
275} 422}
276 423
277pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> { 424fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> {
278 Ok(Ty::Adt { 425 Ok(Ty::Adt {
279 def_id: s.def_id(), 426 def_id: s.def_id(),
280 name: s 427 name: s.name(db)?.unwrap_or_else(Name::missing),
281 .name(db)?
282 .unwrap_or_else(|| SmolStr::new("[unnamed struct]")),
283 }) 428 })
284} 429}
285 430
286pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> { 431pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> {
287 Ok(Ty::Adt { 432 Ok(Ty::Adt {
288 def_id: s.def_id(), 433 def_id: s.def_id(),
289 name: s 434 name: s.name(db)?.unwrap_or_else(Name::missing),
290 .name(db)?
291 .unwrap_or_else(|| SmolStr::new("[unnamed enum]")),
292 }) 435 })
293} 436}
294 437
295pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { 438pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
296 let def = def_id.resolve(db)?; 439 let def = def_id.resolve(db)?;
297 match def { 440 match def {
298 Def::Module(..) => { 441 Def::Module(..) => {
@@ -309,11 +452,7 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
309 } 452 }
310} 453}
311 454
312pub(super) fn type_for_field( 455pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Cancelable<Ty> {
313 db: &impl HirDatabase,
314 def_id: DefId,
315 field: SmolStr,
316) -> Cancelable<Ty> {
317 let def = def_id.resolve(db)?; 456 let def = def_id.resolve(db)?;
318 let variant_data = match def { 457 let variant_data = match def {
319 Def::Struct(s) => { 458 Def::Struct(s) => {
@@ -336,23 +475,29 @@ pub(super) fn type_for_field(
336 Ty::from_hir(db, &module, &type_ref) 475 Ty::from_hir(db, &module, &type_ref)
337} 476}
338 477
478/// The result of type inference: A mapping from expressions and patterns to types.
339#[derive(Clone, PartialEq, Eq, Debug)] 479#[derive(Clone, PartialEq, Eq, Debug)]
340pub struct InferenceResult { 480pub struct InferenceResult {
341 type_of: FxHashMap<LocalSyntaxPtr, Ty>, 481 type_of: FxHashMap<LocalSyntaxPtr, Ty>,
342} 482}
343 483
344impl InferenceResult { 484impl InferenceResult {
485 /// Returns the type of the given syntax node, if it was inferred. Will
486 /// return `None` for syntax nodes not in the inferred function or not
487 /// pointing to an expression/pattern, `Some(Ty::Unknown)` for
488 /// expressions/patterns that could not be inferred.
345 pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option<Ty> { 489 pub fn type_of_node(&self, node: SyntaxNodeRef) -> Option<Ty> {
346 self.type_of.get(&LocalSyntaxPtr::new(node)).cloned() 490 self.type_of.get(&LocalSyntaxPtr::new(node)).cloned()
347 } 491 }
348} 492}
349 493
494/// The inference context contains all information needed during type inference.
350#[derive(Clone, Debug)] 495#[derive(Clone, Debug)]
351pub struct InferenceContext<'a, D: HirDatabase> { 496struct InferenceContext<'a, D: HirDatabase> {
352 db: &'a D, 497 db: &'a D,
353 scopes: Arc<FnScopes>, 498 scopes: Arc<FnScopes>,
354 module: Module, 499 module: Module,
355 // TODO unification tables... 500 var_unification_table: InPlaceUnificationTable<TypeVarId>,
356 type_of: FxHashMap<LocalSyntaxPtr, Ty>, 501 type_of: FxHashMap<LocalSyntaxPtr, Ty>,
357} 502}
358 503
@@ -360,33 +505,116 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
360 fn new(db: &'a D, scopes: Arc<FnScopes>, module: Module) -> Self { 505 fn new(db: &'a D, scopes: Arc<FnScopes>, module: Module) -> Self {
361 InferenceContext { 506 InferenceContext {
362 type_of: FxHashMap::default(), 507 type_of: FxHashMap::default(),
508 var_unification_table: InPlaceUnificationTable::new(),
363 db, 509 db,
364 scopes, 510 scopes,
365 module, 511 module,
366 } 512 }
367 } 513 }
368 514
515 fn resolve_all(mut self) -> InferenceResult {
516 let mut types = mem::replace(&mut self.type_of, FxHashMap::default());
517 for ty in types.values_mut() {
518 let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown));
519 *ty = resolved;
520 }
521 InferenceResult { type_of: types }
522 }
523
369 fn write_ty(&mut self, node: SyntaxNodeRef, ty: Ty) { 524 fn write_ty(&mut self, node: SyntaxNodeRef, ty: Ty) {
370 self.type_of.insert(LocalSyntaxPtr::new(node), ty); 525 self.type_of.insert(LocalSyntaxPtr::new(node), ty);
371 } 526 }
372 527
373 fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> Option<Ty> { 528 fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
374 if *ty1 == Ty::Unknown { 529 match (ty1, ty2) {
375 return Some(ty2.clone()); 530 (Ty::Unknown, ..) => true,
376 } 531 (.., Ty::Unknown) => true,
377 if *ty2 == Ty::Unknown { 532 (Ty::Bool, _)
378 return Some(ty1.clone()); 533 | (Ty::Str, _)
534 | (Ty::Never, _)
535 | (Ty::Char, _)
536 | (Ty::Int(..), Ty::Int(..))
537 | (Ty::Uint(..), Ty::Uint(..))
538 | (Ty::Float(..), Ty::Float(..)) => ty1 == ty2,
539 (
540 Ty::Adt {
541 def_id: def_id1, ..
542 },
543 Ty::Adt {
544 def_id: def_id2, ..
545 },
546 ) if def_id1 == def_id2 => true,
547 (Ty::Slice(t1), Ty::Slice(t2)) => self.unify(t1, t2),
548 (Ty::RawPtr(t1, m1), Ty::RawPtr(t2, m2)) if m1 == m2 => self.unify(t1, t2),
549 (Ty::Ref(t1, m1), Ty::Ref(t2, m2)) if m1 == m2 => self.unify(t1, t2),
550 (Ty::FnPtr(sig1), Ty::FnPtr(sig2)) if sig1 == sig2 => true,
551 (Ty::Tuple(ts1), Ty::Tuple(ts2)) if ts1.len() == ts2.len() => ts1
552 .iter()
553 .zip(ts2.iter())
554 .all(|(t1, t2)| self.unify(t1, t2)),
555 (Ty::Infer(InferTy::TypeVar(tv1)), Ty::Infer(InferTy::TypeVar(tv2))) => {
556 self.var_unification_table.union(*tv1, *tv2);
557 true
558 }
559 (Ty::Infer(InferTy::TypeVar(tv)), other) | (other, Ty::Infer(InferTy::TypeVar(tv))) => {
560 self.var_unification_table
561 .union_value(*tv, TypeVarValue::Known(other.clone()));
562 true
563 }
564 _ => false,
379 } 565 }
380 if ty1 == ty2 { 566 }
381 return Some(ty1.clone()); 567
568 fn new_type_var(&mut self) -> Ty {
569 Ty::Infer(InferTy::TypeVar(
570 self.var_unification_table.new_key(TypeVarValue::Unknown),
571 ))
572 }
573
574 /// Replaces Ty::Unknown by a new type var, so we can maybe still infer it.
575 fn insert_type_vars_shallow(&mut self, ty: Ty) -> Ty {
576 match ty {
577 Ty::Unknown => self.new_type_var(),
578 _ => ty,
382 } 579 }
383 // TODO implement actual unification
384 return None;
385 } 580 }
386 581
387 fn unify_with_coercion(&mut self, ty1: &Ty, ty2: &Ty) -> Option<Ty> { 582 fn insert_type_vars(&mut self, ty: Ty) -> Ty {
388 // TODO implement coercion 583 ty.fold(&mut |ty| self.insert_type_vars_shallow(ty))
389 self.unify(ty1, ty2) 584 }
585
586 /// Resolves the type as far as currently possible, replacing type variables
587 /// by their known types. All types returned by the infer_* functions should
588 /// be resolved as far as possible, i.e. contain no type variables with
589 /// known type.
590 fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
591 ty.fold(&mut |ty| match ty {
592 Ty::Infer(InferTy::TypeVar(tv)) => {
593 if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() {
594 // known_ty may contain other variables that are known by now
595 self.resolve_ty_as_possible(known_ty.clone())
596 } else {
597 Ty::Infer(InferTy::TypeVar(tv))
598 }
599 }
600 _ => ty,
601 })
602 }
603
604 /// Resolves the type completely; type variables without known type are
605 /// replaced by Ty::Unknown.
606 fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
607 ty.fold(&mut |ty| match ty {
608 Ty::Infer(InferTy::TypeVar(tv)) => {
609 if let Some(known_ty) = self.var_unification_table.probe_value(tv).known() {
610 // known_ty may contain other variables that are known by now
611 self.resolve_ty_completely(known_ty.clone())
612 } else {
613 Ty::Unknown
614 }
615 }
616 _ => ty,
617 })
390 } 618 }
391 619
392 fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> { 620 fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> {
@@ -397,21 +625,19 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
397 let name = ctry!(ast_path.segment().and_then(|s| s.name_ref())); 625 let name = ctry!(ast_path.segment().and_then(|s| s.name_ref()));
398 if let Some(scope_entry) = self.scopes.resolve_local_name(name) { 626 if let Some(scope_entry) = self.scopes.resolve_local_name(name) {
399 let ty = ctry!(self.type_of.get(&scope_entry.ptr())); 627 let ty = ctry!(self.type_of.get(&scope_entry.ptr()));
400 return Ok(Some(ty.clone())); 628 let ty = self.resolve_ty_as_possible(ty.clone());
629 return Ok(Some(ty));
401 }; 630 };
402 }; 631 };
403 632
404 // resolve in module 633 // resolve in module
405 let resolved = ctry!(self.module.resolve_path(self.db, &path)?.take_values()); 634 let resolved = ctry!(self.module.resolve_path(self.db, &path)?.take_values());
406 let ty = self.db.type_for_def(resolved)?; 635 let ty = self.db.type_for_def(resolved)?;
407 // TODO we will need to add type variables for type parameters etc. here 636 let ty = self.insert_type_vars(ty);
408 Ok(Some(ty)) 637 Ok(Some(ty))
409 } 638 }
410 639
411 fn resolve_variant( 640 fn resolve_variant(&self, path: Option<ast::Path>) -> Cancelable<(Ty, Option<DefId>)> {
412 &self,
413 path: Option<ast::Path>,
414 ) -> Cancelable<(Ty, Option<Arc<VariantData>>)> {
415 let path = if let Some(path) = path.and_then(Path::from_ast) { 641 let path = if let Some(path) = path.and_then(Path::from_ast) {
416 path 642 path
417 } else { 643 } else {
@@ -424,102 +650,116 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
424 }; 650 };
425 Ok(match def_id.resolve(self.db)? { 651 Ok(match def_id.resolve(self.db)? {
426 Def::Struct(s) => { 652 Def::Struct(s) => {
427 let struct_data = self.db.struct_data(def_id)?;
428 let ty = type_for_struct(self.db, s)?; 653 let ty = type_for_struct(self.db, s)?;
429 (ty, Some(struct_data.variant_data().clone())) 654 (ty, Some(def_id))
430 } 655 }
431 _ => (Ty::Unknown, None), 656 _ => (Ty::Unknown, None),
432 }) 657 })
433 } 658 }
434 659
435 fn infer_expr_opt(&mut self, expr: Option<ast::Expr>) -> Cancelable<Ty> { 660 fn infer_expr_opt(
661 &mut self,
662 expr: Option<ast::Expr>,
663 expected: &Expectation,
664 ) -> Cancelable<Ty> {
436 if let Some(e) = expr { 665 if let Some(e) = expr {
437 self.infer_expr(e) 666 self.infer_expr(e, expected)
438 } else { 667 } else {
439 Ok(Ty::Unknown) 668 Ok(Ty::Unknown)
440 } 669 }
441 } 670 }
442 671
443 fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> { 672 fn infer_expr(&mut self, expr: ast::Expr, expected: &Expectation) -> Cancelable<Ty> {
444 let ty = match expr { 673 let ty = match expr {
445 ast::Expr::IfExpr(e) => { 674 ast::Expr::IfExpr(e) => {
446 if let Some(condition) = e.condition() { 675 if let Some(condition) = e.condition() {
447 // TODO if no pat, this should be bool 676 let expected = if condition.pat().is_none() {
448 self.infer_expr_opt(condition.expr())?; 677 Expectation::has_type(Ty::Bool)
678 } else {
679 Expectation::none()
680 };
681 self.infer_expr_opt(condition.expr(), &expected)?;
449 // TODO write type for pat 682 // TODO write type for pat
450 }; 683 };
451 let if_ty = self.infer_block_opt(e.then_branch())?; 684 let if_ty = self.infer_block_opt(e.then_branch(), expected)?;
452 let else_ty = self.infer_block_opt(e.else_branch())?; 685 if let Some(else_branch) = e.else_branch() {
453 if let Some(ty) = self.unify(&if_ty, &else_ty) { 686 self.infer_block(else_branch, expected)?;
454 ty
455 } else { 687 } else {
456 // TODO report diagnostic 688 // no else branch -> unit
457 Ty::Unknown 689 self.unify(&expected.ty, &Ty::unit()); // actually coerce
458 } 690 }
691 if_ty
459 } 692 }
460 ast::Expr::BlockExpr(e) => self.infer_block_opt(e.block())?, 693 ast::Expr::BlockExpr(e) => self.infer_block_opt(e.block(), expected)?,
461 ast::Expr::LoopExpr(e) => { 694 ast::Expr::LoopExpr(e) => {
462 self.infer_block_opt(e.loop_body())?; 695 self.infer_block_opt(e.loop_body(), &Expectation::has_type(Ty::unit()))?;
463 // TODO never, or the type of the break param 696 // TODO never, or the type of the break param
464 Ty::Unknown 697 Ty::Unknown
465 } 698 }
466 ast::Expr::WhileExpr(e) => { 699 ast::Expr::WhileExpr(e) => {
467 if let Some(condition) = e.condition() { 700 if let Some(condition) = e.condition() {
468 // TODO if no pat, this should be bool 701 let expected = if condition.pat().is_none() {
469 self.infer_expr_opt(condition.expr())?; 702 Expectation::has_type(Ty::Bool)
703 } else {
704 Expectation::none()
705 };
706 self.infer_expr_opt(condition.expr(), &expected)?;
470 // TODO write type for pat 707 // TODO write type for pat
471 }; 708 };
472 self.infer_block_opt(e.loop_body())?; 709 self.infer_block_opt(e.loop_body(), &Expectation::has_type(Ty::unit()))?;
473 // TODO always unit? 710 // TODO always unit?
474 Ty::Unknown 711 Ty::unit()
475 } 712 }
476 ast::Expr::ForExpr(e) => { 713 ast::Expr::ForExpr(e) => {
477 let _iterable_ty = self.infer_expr_opt(e.iterable()); 714 let _iterable_ty = self.infer_expr_opt(e.iterable(), &Expectation::none());
478 if let Some(_pat) = e.pat() { 715 if let Some(_pat) = e.pat() {
479 // TODO write type for pat 716 // TODO write type for pat
480 } 717 }
481 self.infer_block_opt(e.loop_body())?; 718 self.infer_block_opt(e.loop_body(), &Expectation::has_type(Ty::unit()))?;
482 // TODO always unit? 719 // TODO always unit?
483 Ty::Unknown 720 Ty::unit()
484 } 721 }
485 ast::Expr::LambdaExpr(e) => { 722 ast::Expr::LambdaExpr(e) => {
486 let _body_ty = self.infer_expr_opt(e.body())?; 723 let _body_ty = self.infer_expr_opt(e.body(), &Expectation::none())?;
487 Ty::Unknown 724 Ty::Unknown
488 } 725 }
489 ast::Expr::CallExpr(e) => { 726 ast::Expr::CallExpr(e) => {
490 let callee_ty = self.infer_expr_opt(e.expr())?; 727 let callee_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
491 if let Some(arg_list) = e.arg_list() { 728 let (arg_tys, ret_ty) = match &callee_ty {
492 for arg in arg_list.args() { 729 Ty::FnPtr(sig) => (&sig.input[..], sig.output.clone()),
493 // TODO unify / expect argument type
494 self.infer_expr(arg)?;
495 }
496 }
497 match callee_ty {
498 Ty::FnPtr(sig) => sig.output.clone(),
499 _ => { 730 _ => {
500 // not callable 731 // not callable
501 // TODO report an error? 732 // TODO report an error?
502 Ty::Unknown 733 (&[][..], Ty::Unknown)
734 }
735 };
736 if let Some(arg_list) = e.arg_list() {
737 for (i, arg) in arg_list.args().enumerate() {
738 self.infer_expr(
739 arg,
740 &Expectation::has_type(arg_tys.get(i).cloned().unwrap_or(Ty::Unknown)),
741 )?;
503 } 742 }
504 } 743 }
744 ret_ty
505 } 745 }
506 ast::Expr::MethodCallExpr(e) => { 746 ast::Expr::MethodCallExpr(e) => {
507 let _receiver_ty = self.infer_expr_opt(e.expr())?; 747 let _receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
508 if let Some(arg_list) = e.arg_list() { 748 if let Some(arg_list) = e.arg_list() {
509 for arg in arg_list.args() { 749 for arg in arg_list.args() {
510 // TODO unify / expect argument type 750 // TODO unify / expect argument type
511 self.infer_expr(arg)?; 751 self.infer_expr(arg, &Expectation::none())?;
512 } 752 }
513 } 753 }
514 Ty::Unknown 754 Ty::Unknown
515 } 755 }
516 ast::Expr::MatchExpr(e) => { 756 ast::Expr::MatchExpr(e) => {
517 let _ty = self.infer_expr_opt(e.expr())?; 757 let _ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
518 if let Some(match_arm_list) = e.match_arm_list() { 758 if let Some(match_arm_list) = e.match_arm_list() {
519 for arm in match_arm_list.arms() { 759 for arm in match_arm_list.arms() {
520 // TODO type the bindings in pat 760 // TODO type the bindings in pat
521 // TODO type the guard 761 // TODO type the guard
522 let _ty = self.infer_expr_opt(arm.expr())?; 762 let _ty = self.infer_expr_opt(arm.expr(), &Expectation::none())?;
523 } 763 }
524 // TODO unify all the match arm types 764 // TODO unify all the match arm types
525 Ty::Unknown 765 Ty::Unknown
@@ -532,10 +772,11 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
532 ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown), 772 ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown),
533 ast::Expr::ContinueExpr(_e) => Ty::Never, 773 ast::Expr::ContinueExpr(_e) => Ty::Never,
534 ast::Expr::BreakExpr(_e) => Ty::Never, 774 ast::Expr::BreakExpr(_e) => Ty::Never,
535 ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr())?, 775 ast::Expr::ParenExpr(e) => self.infer_expr_opt(e.expr(), expected)?,
536 ast::Expr::Label(_e) => Ty::Unknown, 776 ast::Expr::Label(_e) => Ty::Unknown,
537 ast::Expr::ReturnExpr(e) => { 777 ast::Expr::ReturnExpr(e) => {
538 self.infer_expr_opt(e.expr())?; 778 // TODO expect return type of function
779 self.infer_expr_opt(e.expr(), &Expectation::none())?;
539 Ty::Never 780 Ty::Never
540 } 781 }
541 ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => { 782 ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => {
@@ -543,11 +784,16 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
543 Ty::Unknown 784 Ty::Unknown
544 } 785 }
545 ast::Expr::StructLit(e) => { 786 ast::Expr::StructLit(e) => {
546 let (ty, _variant_data) = self.resolve_variant(e.path())?; 787 let (ty, def_id) = self.resolve_variant(e.path())?;
547 if let Some(nfl) = e.named_field_list() { 788 if let Some(nfl) = e.named_field_list() {
548 for field in nfl.fields() { 789 for field in nfl.fields() {
549 // TODO unify with / expect field type 790 let field_ty = if let (Some(def_id), Some(nr)) = (def_id, field.name_ref())
550 self.infer_expr_opt(field.expr())?; 791 {
792 self.db.type_for_field(def_id, nr.as_name())?
793 } else {
794 Ty::Unknown
795 };
796 self.infer_expr_opt(field.expr(), &Expectation::has_type(field_ty))?;
551 } 797 }
552 } 798 }
553 ty 799 ty
@@ -558,40 +804,42 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
558 } 804 }
559 ast::Expr::IndexExpr(_e) => Ty::Unknown, 805 ast::Expr::IndexExpr(_e) => Ty::Unknown,
560 ast::Expr::FieldExpr(e) => { 806 ast::Expr::FieldExpr(e) => {
561 let receiver_ty = self.infer_expr_opt(e.expr())?; 807 let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
562 if let Some(nr) = e.name_ref() { 808 if let Some(nr) = e.name_ref() {
563 let text = nr.text(); 809 let ty = match receiver_ty {
564 match receiver_ty {
565 Ty::Tuple(fields) => { 810 Ty::Tuple(fields) => {
566 let i = text.parse::<usize>().ok(); 811 let i = nr.text().parse::<usize>().ok();
567 i.and_then(|i| fields.get(i).cloned()) 812 i.and_then(|i| fields.get(i).cloned())
568 .unwrap_or(Ty::Unknown) 813 .unwrap_or(Ty::Unknown)
569 } 814 }
570 Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, text)?, 815 Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, nr.as_name())?,
571 _ => Ty::Unknown, 816 _ => Ty::Unknown,
572 } 817 };
818 self.insert_type_vars(ty)
573 } else { 819 } else {
574 Ty::Unknown 820 Ty::Unknown
575 } 821 }
576 } 822 }
577 ast::Expr::TryExpr(e) => { 823 ast::Expr::TryExpr(e) => {
578 let _inner_ty = self.infer_expr_opt(e.expr())?; 824 let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
579 Ty::Unknown 825 Ty::Unknown
580 } 826 }
581 ast::Expr::CastExpr(e) => { 827 ast::Expr::CastExpr(e) => {
582 let _inner_ty = self.infer_expr_opt(e.expr())?; 828 let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
583 let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?; 829 let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?;
830 let cast_ty = self.insert_type_vars(cast_ty);
584 // TODO do the coercion... 831 // TODO do the coercion...
585 cast_ty 832 cast_ty
586 } 833 }
587 ast::Expr::RefExpr(e) => { 834 ast::Expr::RefExpr(e) => {
588 let inner_ty = self.infer_expr_opt(e.expr())?; 835 // TODO pass the expectation down
836 let inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
589 let m = Mutability::from_mutable(e.is_mut()); 837 let m = Mutability::from_mutable(e.is_mut());
590 // TODO reference coercions etc. 838 // TODO reference coercions etc.
591 Ty::Ref(Arc::new(inner_ty), m) 839 Ty::Ref(Arc::new(inner_ty), m)
592 } 840 }
593 ast::Expr::PrefixExpr(e) => { 841 ast::Expr::PrefixExpr(e) => {
594 let inner_ty = self.infer_expr_opt(e.expr())?; 842 let inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
595 match e.op() { 843 match e.op() {
596 Some(PrefixOp::Deref) => { 844 Some(PrefixOp::Deref) => {
597 match inner_ty { 845 match inner_ty {
@@ -609,28 +857,34 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
609 ast::Expr::BinExpr(_e) => Ty::Unknown, 857 ast::Expr::BinExpr(_e) => Ty::Unknown,
610 ast::Expr::Literal(_e) => Ty::Unknown, 858 ast::Expr::Literal(_e) => Ty::Unknown,
611 }; 859 };
860 // use a new type variable if we got Ty::Unknown here
861 let ty = self.insert_type_vars_shallow(ty);
862 self.unify(&ty, &expected.ty);
612 self.write_ty(expr.syntax(), ty.clone()); 863 self.write_ty(expr.syntax(), ty.clone());
613 Ok(ty) 864 Ok(ty)
614 } 865 }
615 866
616 fn infer_block_opt(&mut self, node: Option<ast::Block>) -> Cancelable<Ty> { 867 fn infer_block_opt(
868 &mut self,
869 node: Option<ast::Block>,
870 expected: &Expectation,
871 ) -> Cancelable<Ty> {
617 if let Some(b) = node { 872 if let Some(b) = node {
618 self.infer_block(b) 873 self.infer_block(b, expected)
619 } else { 874 } else {
620 Ok(Ty::Unknown) 875 Ok(Ty::Unknown)
621 } 876 }
622 } 877 }
623 878
624 fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> { 879 fn infer_block(&mut self, node: ast::Block, expected: &Expectation) -> Cancelable<Ty> {
625 for stmt in node.statements() { 880 for stmt in node.statements() {
626 match stmt { 881 match stmt {
627 ast::Stmt::LetStmt(stmt) => { 882 ast::Stmt::LetStmt(stmt) => {
628 let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?; 883 let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?;
884 let decl_ty = self.insert_type_vars(decl_ty);
629 let ty = if let Some(expr) = stmt.initializer() { 885 let ty = if let Some(expr) = stmt.initializer() {
630 // TODO pass expectation 886 let expr_ty = self.infer_expr(expr, &Expectation::has_type(decl_ty))?;
631 let expr_ty = self.infer_expr(expr)?; 887 expr_ty
632 self.unify_with_coercion(&expr_ty, &decl_ty)
633 .unwrap_or(decl_ty)
634 } else { 888 } else {
635 decl_ty 889 decl_ty
636 }; 890 };
@@ -640,12 +894,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
640 }; 894 };
641 } 895 }
642 ast::Stmt::ExprStmt(expr_stmt) => { 896 ast::Stmt::ExprStmt(expr_stmt) => {
643 self.infer_expr_opt(expr_stmt.expr())?; 897 self.infer_expr_opt(expr_stmt.expr(), &Expectation::none())?;
644 } 898 }
645 } 899 }
646 } 900 }
647 let ty = if let Some(expr) = node.expr() { 901 let ty = if let Some(expr) = node.expr() {
648 self.infer_expr(expr)? 902 self.infer_expr(expr, expected)?
649 } else { 903 } else {
650 Ty::unit() 904 Ty::unit()
651 }; 905 };
@@ -654,7 +908,8 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
654 } 908 }
655} 909}
656 910
657pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceResult> { 911pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
912 let function = Function::new(def_id); // TODO: consts also need inference
658 let scopes = function.scopes(db); 913 let scopes = function.scopes(db);
659 let module = function.module(db)?; 914 let module = function.module(db)?;
660 let mut ctx = InferenceContext::new(db, scopes, module); 915 let mut ctx = InferenceContext::new(db, scopes, module);
@@ -671,25 +926,27 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR
671 }; 926 };
672 if let Some(type_ref) = param.type_ref() { 927 if let Some(type_ref) = param.type_ref() {
673 let ty = Ty::from_ast(db, &ctx.module, type_ref)?; 928 let ty = Ty::from_ast(db, &ctx.module, type_ref)?;
929 let ty = ctx.insert_type_vars(ty);
674 ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty); 930 ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
675 } else { 931 } else {
676 // TODO self param 932 // TODO self param
933 let type_var = ctx.new_type_var();
677 ctx.type_of 934 ctx.type_of
678 .insert(LocalSyntaxPtr::new(pat.syntax()), Ty::Unknown); 935 .insert(LocalSyntaxPtr::new(pat.syntax()), type_var);
679 }; 936 };
680 } 937 }
681 } 938 }
682 939
683 // TODO get Ty for node.ret_type() and pass that to infer_block as expectation 940 let ret_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) {
684 // (see Expectation in rustc_typeck) 941 let ty = Ty::from_ast(db, &ctx.module, type_ref)?;
942 ctx.insert_type_vars(ty)
943 } else {
944 Ty::unit()
945 };
685 946
686 if let Some(block) = node.body() { 947 if let Some(block) = node.body() {
687 ctx.infer_block(block)?; 948 ctx.infer_block(block, &Expectation::has_type(ret_ty))?;
688 } 949 }
689 950
690 // TODO 'resolve' the types: replace inference variables by their inferred results 951 Ok(Arc::new(ctx.resolve_all()))
691
692 Ok(InferenceResult {
693 type_of: ctx.type_of,
694 })
695} 952}
diff --git a/crates/ra_hir/src/ty/primitive.rs b/crates/ra_hir/src/ty/primitive.rs
index ad79b17e4..498d42d52 100644
--- a/crates/ra_hir/src/ty/primitive.rs
+++ b/crates/ra_hir/src/ty/primitive.rs
@@ -1,5 +1,7 @@
1use std::fmt; 1use std::fmt;
2 2
3use crate::{Name, KnownName};
4
3#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] 5#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
4pub enum IntTy { 6pub enum IntTy {
5 Isize, 7 Isize,
@@ -34,14 +36,14 @@ impl IntTy {
34 } 36 }
35 } 37 }
36 38
37 pub fn from_string(s: &str) -> Option<IntTy> { 39 pub fn from_name(name: &Name) -> Option<IntTy> {
38 match s { 40 match name.as_known_name()? {
39 "isize" => Some(IntTy::Isize), 41 KnownName::Isize => Some(IntTy::Isize),
40 "i8" => Some(IntTy::I8), 42 KnownName::I8 => Some(IntTy::I8),
41 "i16" => Some(IntTy::I16), 43 KnownName::I16 => Some(IntTy::I16),
42 "i32" => Some(IntTy::I32), 44 KnownName::I32 => Some(IntTy::I32),
43 "i64" => Some(IntTy::I64), 45 KnownName::I64 => Some(IntTy::I64),
44 "i128" => Some(IntTy::I128), 46 KnownName::I128 => Some(IntTy::I128),
45 _ => None, 47 _ => None,
46 } 48 }
47 } 49 }
@@ -69,14 +71,14 @@ impl UintTy {
69 } 71 }
70 } 72 }
71 73
72 pub fn from_string(s: &str) -> Option<UintTy> { 74 pub fn from_name(name: &Name) -> Option<UintTy> {
73 match s { 75 match name.as_known_name()? {
74 "usize" => Some(UintTy::Usize), 76 KnownName::Usize => Some(UintTy::Usize),
75 "u8" => Some(UintTy::U8), 77 KnownName::U8 => Some(UintTy::U8),
76 "u16" => Some(UintTy::U16), 78 KnownName::U16 => Some(UintTy::U16),
77 "u32" => Some(UintTy::U32), 79 KnownName::U32 => Some(UintTy::U32),
78 "u64" => Some(UintTy::U64), 80 KnownName::U64 => Some(UintTy::U64),
79 "u128" => Some(UintTy::U128), 81 KnownName::U128 => Some(UintTy::U128),
80 _ => None, 82 _ => None,
81 } 83 }
82 } 84 }
@@ -120,10 +122,10 @@ impl FloatTy {
120 } 122 }
121 } 123 }
122 124
123 pub fn from_string(s: &str) -> Option<FloatTy> { 125 pub fn from_name(name: &Name) -> Option<FloatTy> {
124 match s { 126 match name.as_known_name()? {
125 "f32" => Some(FloatTy::F32), 127 KnownName::F32 => Some(FloatTy::F32),
126 "f64" => Some(FloatTy::F64), 128 KnownName::F64 => Some(FloatTy::F64),
127 _ => None, 129 _ => None,
128 } 130 }
129 } 131 }
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index a76925b58..93bf431c4 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -113,6 +113,27 @@ fn test(a: &u32, b: &mut u32, c: *const u32, d: *mut u32) {
113 ); 113 );
114} 114}
115 115
116#[test]
117fn infer_backwards() {
118 check_inference(
119 r#"
120fn takes_u32(x: u32) {}
121
122struct S { i32_field: i32 }
123
124fn test() -> &mut &f64 {
125 let a = unknown_function();
126 takes_u32(a);
127 let b = unknown_function();
128 S { i32_field: b };
129 let c = unknown_function();
130 &mut &c
131}
132"#,
133 "0006_backwards.txt",
134 );
135}
136
116fn infer(content: &str) -> String { 137fn infer(content: &str) -> String {
117 let (db, _, file_id) = MockDatabase::with_single_file(content); 138 let (db, _, file_id) = MockDatabase::with_single_file(content);
118 let source_file = db.source_file(file_id); 139 let source_file = db.source_file(file_id);
diff --git a/crates/ra_hir/src/ty/tests/data/0002_let.txt b/crates/ra_hir/src/ty/tests/data/0002_let.txt
index 2d0d1f57b..916ca25a1 100644
--- a/crates/ra_hir/src/ty/tests/data/0002_let.txt
+++ b/crates/ra_hir/src/ty/tests/data/0002_let.txt
@@ -1,5 +1,5 @@
1[21; 22) 'a': [unknown] 1[21; 22) 'a': [unknown]
2[52; 53) '1': [unknown] 2[52; 53) '1': usize
3[11; 71) '{ ...= b; }': () 3[11; 71) '{ ...= b; }': ()
4[63; 64) 'c': usize 4[63; 64) 'c': usize
5[25; 31) '1isize': [unknown] 5[25; 31) '1isize': [unknown]
diff --git a/crates/ra_hir/src/ty/tests/data/0003_paths.txt b/crates/ra_hir/src/ty/tests/data/0003_paths.txt
index dcb5456ae..2a12d264f 100644
--- a/crates/ra_hir/src/ty/tests/data/0003_paths.txt
+++ b/crates/ra_hir/src/ty/tests/data/0003_paths.txt
@@ -1,7 +1,7 @@
1[15; 20) '{ 1 }': [unknown] 1[15; 20) '{ 1 }': u32
2[17; 18) '1': [unknown] 2[17; 18) '1': u32
3[50; 51) '1': [unknown] 3[50; 51) '1': u32
4[48; 53) '{ 1 }': [unknown] 4[48; 53) '{ 1 }': u32
5[82; 88) 'b::c()': u32 5[82; 88) 'b::c()': u32
6[67; 91) '{ ...c(); }': () 6[67; 91) '{ ...c(); }': ()
7[73; 74) 'a': fn() -> u32 7[73; 74) 'a': fn() -> u32
diff --git a/crates/ra_hir/src/ty/tests/data/0004_struct.txt b/crates/ra_hir/src/ty/tests/data/0004_struct.txt
index cc8f3665b..b4af18b87 100644
--- a/crates/ra_hir/src/ty/tests/data/0004_struct.txt
+++ b/crates/ra_hir/src/ty/tests/data/0004_struct.txt
@@ -1,5 +1,5 @@
1[86; 90) 'C(1)': [unknown] 1[86; 90) 'C(1)': [unknown]
2[121; 122) 'B': [unknown] 2[121; 122) 'B': B
3[86; 87) 'C': [unknown] 3[86; 87) 'C': [unknown]
4[129; 130) '1': [unknown] 4[129; 130) '1': [unknown]
5[107; 108) 'a': A 5[107; 108) 'a': A
@@ -13,4 +13,4 @@
13[96; 97) 'B': [unknown] 13[96; 97) 'B': [unknown]
14[88; 89) '1': [unknown] 14[88; 89) '1': [unknown]
15[82; 83) 'c': [unknown] 15[82; 83) 'c': [unknown]
16[127; 131) 'C(1)': [unknown] 16[127; 131) 'C(1)': C
diff --git a/crates/ra_hir/src/ty/tests/data/0006_backwards.txt b/crates/ra_hir/src/ty/tests/data/0006_backwards.txt
new file mode 100644
index 000000000..120069401
--- /dev/null
+++ b/crates/ra_hir/src/ty/tests/data/0006_backwards.txt
@@ -0,0 +1,20 @@
1[22; 24) '{}': ()
2[14; 15) 'x': u32
3[142; 158) 'unknow...nction': [unknown]
4[126; 127) 'a': u32
5[198; 216) 'unknow...tion()': f64
6[228; 229) 'c': f64
7[198; 214) 'unknow...nction': [unknown]
8[166; 184) 'S { i3...d: b }': S
9[222; 229) '&mut &c': &mut &f64
10[194; 195) 'c': f64
11[92; 110) 'unknow...tion()': u32
12[142; 160) 'unknow...tion()': i32
13[92; 108) 'unknow...nction': [unknown]
14[116; 128) 'takes_u32(a)': ()
15[78; 231) '{ ...t &c }': &mut &f64
16[227; 229) '&c': &f64
17[88; 89) 'a': u32
18[181; 182) 'b': i32
19[116; 125) 'takes_u32': fn(u32,) -> ()
20[138; 139) 'b': i32
diff --git a/crates/ra_lsp_server/Cargo.toml b/crates/ra_lsp_server/Cargo.toml
index 3c8c240cd..646df2497 100644
--- a/crates/ra_lsp_server/Cargo.toml
+++ b/crates/ra_lsp_server/Cargo.toml
@@ -11,13 +11,13 @@ relative-path = "0.4.0"
11failure = "0.1.2" 11failure = "0.1.2"
12failure_derive = "0.1.2" 12failure_derive = "0.1.2"
13serde_json = "1.0.24" 13serde_json = "1.0.24"
14serde = "1.0.71" 14serde = "1.0.83"
15drop_bomb = "0.1.0" 15drop_bomb = "0.1.0"
16crossbeam-channel = "0.2.4" 16crossbeam-channel = "0.2.4"
17flexi_logger = "0.10.0" 17flexi_logger = "0.10.0"
18log = "0.4.3" 18log = "0.4.3"
19url_serde = "0.2.0" 19url_serde = "0.2.0"
20languageserver-types = "0.53.0" 20languageserver-types = "0.53.1"
21walkdir = "2.2.7" 21walkdir = "2.2.7"
22im = "12.0.0" 22im = "12.0.0"
23cargo_metadata = "0.6.0" 23cargo_metadata = "0.6.0"
@@ -37,3 +37,4 @@ ra_vfs = { path = "../ra_vfs" }
37[dev-dependencies] 37[dev-dependencies]
38tempdir = "0.3.7" 38tempdir = "0.3.7"
39test_utils = { path = "../test_utils" } 39test_utils = { path = "../test_utils" }
40tools = { path = "../tools" }
diff --git a/crates/ra_lsp_server/src/caps.rs b/crates/ra_lsp_server/src/caps.rs
index 5f7038f63..8d508a3ba 100644
--- a/crates/ra_lsp_server/src/caps.rs
+++ b/crates/ra_lsp_server/src/caps.rs
@@ -33,7 +33,7 @@ pub fn server_capabilities() -> ServerCapabilities {
33 workspace_symbol_provider: Some(true), 33 workspace_symbol_provider: Some(true),
34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)), 34 code_action_provider: Some(CodeActionProviderCapability::Simple(true)),
35 code_lens_provider: None, 35 code_lens_provider: None,
36 document_formatting_provider: None, 36 document_formatting_provider: Some(true),
37 document_range_formatting_provider: None, 37 document_range_formatting_provider: None,
38 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions { 38 document_on_type_formatting_provider: Some(DocumentOnTypeFormattingOptions {
39 first_trigger_character: "=".to_string(), 39 first_trigger_character: "=".to_string(),
diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs
index d3670104e..0d6e62727 100644
--- a/crates/ra_lsp_server/src/conv.rs
+++ b/crates/ra_lsp_server/src/conv.rs
@@ -2,7 +2,7 @@ use languageserver_types::{
2 self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier, 2 self, Location, Position, Range, SymbolKind, TextDocumentEdit, TextDocumentIdentifier,
3 TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat, 3 TextDocumentItem, TextDocumentPositionParams, Url, VersionedTextDocumentIdentifier, InsertTextFormat,
4}; 4};
5use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition, CompletionItem, CompletionItemKind, InsertText}; 5use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText};
6use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; 6use ra_editor::{LineCol, LineIndex, translate_offset_with_edit};
7use ra_text_edit::{AtomTextEdit, TextEdit}; 7use ra_text_edit::{AtomTextEdit, TextEdit};
8use ra_syntax::{SyntaxKind, TextRange, TextUnit}; 8use ra_syntax::{SyntaxKind, TextRange, TextUnit};
@@ -57,6 +57,7 @@ impl Conv for CompletionItemKind {
57 CompletionItemKind::Function => Function, 57 CompletionItemKind::Function => Function,
58 CompletionItemKind::Struct => Struct, 58 CompletionItemKind::Struct => Struct,
59 CompletionItemKind::Enum => Enum, 59 CompletionItemKind::Enum => Enum,
60 CompletionItemKind::EnumVariant => EnumMember,
60 CompletionItemKind::Binding => Variable, 61 CompletionItemKind::Binding => Variable,
61 CompletionItemKind::Field => Field, 62 CompletionItemKind::Field => Field,
62 } 63 }
@@ -218,6 +219,17 @@ impl<'a> TryConvWith for &'a TextDocumentPositionParams {
218 } 219 }
219} 220}
220 221
222impl<'a> TryConvWith for (&'a TextDocumentIdentifier, Range) {
223 type Ctx = ServerWorld;
224 type Output = FileRange;
225 fn try_conv_with(self, world: &ServerWorld) -> Result<FileRange> {
226 let file_id = self.0.try_conv_with(world)?;
227 let line_index = world.analysis().file_line_index(file_id);
228 let range = self.1.conv_with(&line_index);
229 Ok(FileRange { file_id, range })
230 }
231}
232
221impl<T: TryConvWith> TryConvWith for Vec<T> { 233impl<T: TryConvWith> TryConvWith for Vec<T> {
222 type Ctx = <T as TryConvWith>::Ctx; 234 type Ctx = <T as TryConvWith>::Ctx;
223 type Output = Vec<<T as TryConvWith>::Output>; 235 type Output = Vec<<T as TryConvWith>::Output>;
diff --git a/crates/ra_lsp_server/src/main_loop.rs b/crates/ra_lsp_server/src/main_loop.rs
index 1edb9fae4..97c1be778 100644
--- a/crates/ra_lsp_server/src/main_loop.rs
+++ b/crates/ra_lsp_server/src/main_loop.rs
@@ -295,6 +295,7 @@ fn on_request(
295 .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)? 295 .on::<req::PrepareRenameRequest>(handlers::handle_prepare_rename)?
296 .on::<req::Rename>(handlers::handle_rename)? 296 .on::<req::Rename>(handlers::handle_rename)?
297 .on::<req::References>(handlers::handle_references)? 297 .on::<req::References>(handlers::handle_references)?
298 .on::<req::Formatting>(handlers::handle_formatting)?
298 .finish(); 299 .finish();
299 match req { 300 match req {
300 Ok(id) => { 301 Ok(id) => {
diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs
index 0e9a66a8a..a2c12a4c1 100644
--- a/crates/ra_lsp_server/src/main_loop/handlers.rs
+++ b/crates/ra_lsp_server/src/main_loop/handlers.rs
@@ -6,13 +6,16 @@ use languageserver_types::{
6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind, 6 DiagnosticSeverity, DocumentSymbol, Documentation, FoldingRange, FoldingRangeKind,
7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position, 7 FoldingRangeParams, Location, MarkupContent, MarkupKind, MarkedString, Position,
8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit, 8 PrepareRenameResponse, RenameParams, SymbolInformation, TextDocumentIdentifier, TextEdit,
9 Range,
9 WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents, 10 WorkspaceEdit, ParameterInformation, ParameterLabel, SignatureInformation, Hover, HoverContents,
11 DocumentFormattingParams,
10}; 12};
11use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition, Severity}; 13use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity};
12use ra_syntax::{TextUnit, text_utils::intersect}; 14use ra_syntax::{TextUnit, text_utils::intersect};
13use ra_text_edit::text_utils::contains_offset_nonstrict; 15use ra_text_edit::text_utils::contains_offset_nonstrict;
14use rustc_hash::FxHashMap; 16use rustc_hash::FxHashMap;
15use serde_json::to_value; 17use serde_json::to_value;
18use std::io::Write;
16 19
17use crate::{ 20use crate::{
18 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith}, 21 conv::{to_location, Conv, ConvWith, MapConvWith, TryConvWith},
@@ -33,13 +36,13 @@ pub fn handle_extend_selection(
33 params: req::ExtendSelectionParams, 36 params: req::ExtendSelectionParams,
34) -> Result<req::ExtendSelectionResult> { 37) -> Result<req::ExtendSelectionResult> {
35 let file_id = params.text_document.try_conv_with(&world)?; 38 let file_id = params.text_document.try_conv_with(&world)?;
36 let file = world.analysis().file_syntax(file_id);
37 let line_index = world.analysis().file_line_index(file_id); 39 let line_index = world.analysis().file_line_index(file_id);
38 let selections = params 40 let selections = params
39 .selections 41 .selections
40 .into_iter() 42 .into_iter()
41 .map_conv_with(&line_index) 43 .map_conv_with(&line_index)
42 .map(|r| world.analysis().extend_selection(&file, r)) 44 .map(|range| FileRange { file_id, range })
45 .map(|frange| world.analysis().extend_selection(frange))
43 .map_conv_with(&line_index) 46 .map_conv_with(&line_index)
44 .collect(); 47 .collect();
45 Ok(req::ExtendSelectionResult { selections }) 48 Ok(req::ExtendSelectionResult { selections })
@@ -71,13 +74,8 @@ pub fn handle_join_lines(
71 world: ServerWorld, 74 world: ServerWorld,
72 params: req::JoinLinesParams, 75 params: req::JoinLinesParams,
73) -> Result<req::SourceChange> { 76) -> Result<req::SourceChange> {
74 let file_id = params.text_document.try_conv_with(&world)?; 77 let frange = (&params.text_document, params.range).try_conv_with(&world)?;
75 let line_index = world.analysis().file_line_index(file_id); 78 world.analysis().join_lines(frange).try_conv_with(&world)
76 let range = params.range.conv_with(&line_index);
77 world
78 .analysis()
79 .join_lines(file_id, range)
80 .try_conv_with(&world)
81} 79}
82 80
83pub fn handle_on_enter( 81pub fn handle_on_enter(
@@ -606,6 +604,40 @@ pub fn handle_references(
606 )) 604 ))
607} 605}
608 606
607pub fn handle_formatting(
608 world: ServerWorld,
609 params: DocumentFormattingParams,
610) -> Result<Option<Vec<TextEdit>>> {
611 let file_id = params.text_document.try_conv_with(&world)?;
612 let file = world.analysis().file_text(file_id);
613
614 let file_line_index = world.analysis().file_line_index(file_id);
615 let end_position = TextUnit::of_str(&file).conv_with(&file_line_index);
616
617 use std::process;
618 let mut rustfmt = process::Command::new("rustfmt")
619 .stdin(process::Stdio::piped())
620 .stdout(process::Stdio::piped())
621 .spawn()?;
622
623 rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?;
624
625 let output = rustfmt.wait_with_output()?;
626 let captured_stdout = String::from_utf8(output.stdout)?;
627 if !output.status.success() {
628 failure::bail!(
629 "rustfmt exited with error code {}: {}.",
630 output.status,
631 captured_stdout,
632 );
633 }
634
635 Ok(Some(vec![TextEdit {
636 range: Range::new(Position::new(0, 0), end_position),
637 new_text: captured_stdout,
638 }]))
639}
640
609pub fn handle_code_action( 641pub fn handle_code_action(
610 world: ServerWorld, 642 world: ServerWorld,
611 params: req::CodeActionParams, 643 params: req::CodeActionParams,
@@ -614,7 +646,10 @@ pub fn handle_code_action(
614 let line_index = world.analysis().file_line_index(file_id); 646 let line_index = world.analysis().file_line_index(file_id);
615 let range = params.range.conv_with(&line_index); 647 let range = params.range.conv_with(&line_index);
616 648
617 let assists = world.analysis().assists(file_id, range)?.into_iter(); 649 let assists = world
650 .analysis()
651 .assists(FileRange { file_id, range })?
652 .into_iter();
618 let fixes = world 653 let fixes = world
619 .analysis() 654 .analysis()
620 .diagnostics(file_id)? 655 .diagnostics(file_id)?
diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs
index 1f5cc5e8b..b0e1e65b6 100644
--- a/crates/ra_lsp_server/tests/heavy_tests/main.rs
+++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs
@@ -1,8 +1,8 @@
1mod support; 1mod support;
2 2
3use serde_json::json; 3use serde_json::json;
4use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams}; 4use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams, Formatting};
5use languageserver_types::{Position, Range, CodeActionContext}; 5use languageserver_types::{Position, Range, CodeActionContext, DocumentFormattingParams, FormattingOptions};
6 6
7use crate::support::project; 7use crate::support::project;
8 8
@@ -118,6 +118,60 @@ fn test_eggs() {}
118 ); 118 );
119} 119}
120 120
121use std::collections::HashMap;
122#[test]
123fn test_format_document() {
124 tools::install_rustfmt().unwrap();
125
126 let server = project(
127 r#"
128[package]
129name = "foo"
130version = "0.0.0"
131
132//- src/lib.rs
133mod bar;
134
135fn main() {
136}
137
138pub use std::collections::HashMap;
139"#,
140 );
141 server.wait_for_feedback("workspace loaded");
142
143 server.request::<Formatting>(
144 DocumentFormattingParams {
145 text_document: server.doc_id("src/lib.rs"),
146 options: FormattingOptions {
147 tab_size: 4,
148 insert_spaces: false,
149 properties: HashMap::new(),
150 },
151 },
152 json!([
153 {
154 "newText": r#"mod bar;
155
156fn main() {}
157
158pub use std::collections::HashMap;
159"#,
160 "range": {
161 "end": {
162 "character": 0,
163 "line": 6
164 },
165 "start": {
166 "character": 0,
167 "line": 0
168 }
169 }
170 }
171 ]),
172 );
173}
174
121#[test] 175#[test]
122fn test_missing_module_code_action() { 176fn test_missing_module_code_action() {
123 let server = project( 177 let server = project(
diff --git a/crates/ra_syntax/Cargo.toml b/crates/ra_syntax/Cargo.toml
index 8c9a7e238..fd6966d4a 100644
--- a/crates/ra_syntax/Cargo.toml
+++ b/crates/ra_syntax/Cargo.toml
@@ -8,11 +8,11 @@ description = "Comment and whitespace preserving parser for the Rust langauge"
8repository = "https://github.com/rust-analyzer/rust-analyzer" 8repository = "https://github.com/rust-analyzer/rust-analyzer"
9 9
10[dependencies] 10[dependencies]
11arrayvec = "0.4.7" 11arrayvec = "0.4.10"
12unicode-xid = "0.1.0" 12unicode-xid = "0.1.0"
13itertools = "0.7.8" 13itertools = "0.8.0"
14drop_bomb = "0.1.4" 14drop_bomb = "0.1.4"
15parking_lot = "0.6.0" 15parking_lot = "0.7.0"
16rowan = "0.1.2" 16rowan = "0.1.2"
17text_unit = "0.1.5" 17text_unit = "0.1.5"
18ra_text_edit = { path = "../ra_text_edit" } 18ra_text_edit = { path = "../ra_text_edit" }
diff --git a/crates/ra_syntax/fuzz/Cargo.lock b/crates/ra_syntax/fuzz/Cargo.lock
new file mode 100644
index 000000000..e68fe7f61
--- /dev/null
+++ b/crates/ra_syntax/fuzz/Cargo.lock
@@ -0,0 +1,530 @@
1[[package]]
2name = "arbitrary"
3version = "0.1.1"
4source = "registry+https://github.com/rust-lang/crates.io-index"
5
6[[package]]
7name = "arrayvec"
8version = "0.4.10"
9source = "registry+https://github.com/rust-lang/crates.io-index"
10dependencies = [
11 "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
12]
13
14[[package]]
15name = "bit-set"
16version = "0.5.0"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18dependencies = [
19 "bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
20]
21
22[[package]]
23name = "bit-vec"
24version = "0.5.0"
25source = "registry+https://github.com/rust-lang/crates.io-index"
26
27[[package]]
28name = "bitflags"
29version = "1.0.4"
30source = "registry+https://github.com/rust-lang/crates.io-index"
31
32[[package]]
33name = "byteorder"
34version = "1.2.7"
35source = "registry+https://github.com/rust-lang/crates.io-index"
36
37[[package]]
38name = "cc"
39version = "1.0.28"
40source = "registry+https://github.com/rust-lang/crates.io-index"
41
42[[package]]
43name = "cfg-if"
44version = "0.1.6"
45source = "registry+https://github.com/rust-lang/crates.io-index"
46
47[[package]]
48name = "cloudabi"
49version = "0.0.3"
50source = "registry+https://github.com/rust-lang/crates.io-index"
51dependencies = [
52 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
53]
54
55[[package]]
56name = "drop_bomb"
57version = "0.1.4"
58source = "registry+https://github.com/rust-lang/crates.io-index"
59
60[[package]]
61name = "either"
62version = "1.5.0"
63source = "registry+https://github.com/rust-lang/crates.io-index"
64
65[[package]]
66name = "fnv"
67version = "1.0.6"
68source = "registry+https://github.com/rust-lang/crates.io-index"
69
70[[package]]
71name = "fuchsia-zircon"
72version = "0.3.3"
73source = "registry+https://github.com/rust-lang/crates.io-index"
74dependencies = [
75 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
76 "fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
77]
78
79[[package]]
80name = "fuchsia-zircon-sys"
81version = "0.3.3"
82source = "registry+https://github.com/rust-lang/crates.io-index"
83
84[[package]]
85name = "itertools"
86version = "0.8.0"
87source = "registry+https://github.com/rust-lang/crates.io-index"
88dependencies = [
89 "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
90]
91
92[[package]]
93name = "lazy_static"
94version = "1.2.0"
95source = "registry+https://github.com/rust-lang/crates.io-index"
96
97[[package]]
98name = "libc"
99version = "0.2.45"
100source = "registry+https://github.com/rust-lang/crates.io-index"
101
102[[package]]
103name = "libfuzzer-sys"
104version = "0.1.0"
105source = "git+https://github.com/rust-fuzz/libfuzzer-sys.git#4a413199b5cb1bbed6a1d157b2342b925f8464ac"
106dependencies = [
107 "arbitrary 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
108 "cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)",
109]
110
111[[package]]
112name = "lock_api"
113version = "0.1.5"
114source = "registry+https://github.com/rust-lang/crates.io-index"
115dependencies = [
116 "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
117 "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
118]
119
120[[package]]
121name = "nodrop"
122version = "0.1.13"
123source = "registry+https://github.com/rust-lang/crates.io-index"
124
125[[package]]
126name = "num-traits"
127version = "0.2.6"
128source = "registry+https://github.com/rust-lang/crates.io-index"
129
130[[package]]
131name = "owning_ref"
132version = "0.4.0"
133source = "registry+https://github.com/rust-lang/crates.io-index"
134dependencies = [
135 "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
136]
137
138[[package]]
139name = "parking_lot"
140version = "0.6.4"
141source = "registry+https://github.com/rust-lang/crates.io-index"
142dependencies = [
143 "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
144 "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
145]
146
147[[package]]
148name = "parking_lot"
149version = "0.7.0"
150source = "registry+https://github.com/rust-lang/crates.io-index"
151dependencies = [
152 "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
153 "parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
154]
155
156[[package]]
157name = "parking_lot_core"
158version = "0.3.1"
159source = "registry+https://github.com/rust-lang/crates.io-index"
160dependencies = [
161 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
162 "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
163 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
164 "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
165 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
166]
167
168[[package]]
169name = "parking_lot_core"
170version = "0.4.0"
171source = "registry+https://github.com/rust-lang/crates.io-index"
172dependencies = [
173 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
174 "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
175 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
176 "smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
177 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
178]
179
180[[package]]
181name = "proptest"
182version = "0.8.7"
183source = "registry+https://github.com/rust-lang/crates.io-index"
184dependencies = [
185 "bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
186 "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
187 "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)",
188 "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
189 "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
190 "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
191 "rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)",
192 "regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
193 "rusty-fork 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
194 "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
195]
196
197[[package]]
198name = "quick-error"
199version = "1.2.2"
200source = "registry+https://github.com/rust-lang/crates.io-index"
201
202[[package]]
203name = "ra_syntax"
204version = "0.1.0"
205dependencies = [
206 "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
207 "drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
208 "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
209 "parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
210 "ra_text_edit 0.1.0",
211 "rowan 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
212 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
213 "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
214]
215
216[[package]]
217name = "ra_syntax-fuzz"
218version = "0.0.1"
219dependencies = [
220 "libfuzzer-sys 0.1.0 (git+https://github.com/rust-fuzz/libfuzzer-sys.git)",
221 "ra_syntax 0.1.0",
222]
223
224[[package]]
225name = "ra_text_edit"
226version = "0.1.0"
227dependencies = [
228 "proptest 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)",
229 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
230]
231
232[[package]]
233name = "rand"
234version = "0.5.5"
235source = "registry+https://github.com/rust-lang/crates.io-index"
236dependencies = [
237 "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
238 "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
239 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
240 "rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
241 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
242]
243
244[[package]]
245name = "rand"
246version = "0.6.1"
247source = "registry+https://github.com/rust-lang/crates.io-index"
248dependencies = [
249 "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
250 "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
251 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
252 "rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
253 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
254 "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
255 "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
256 "rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
257 "rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
258 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
259 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
260]
261
262[[package]]
263name = "rand_chacha"
264version = "0.1.0"
265source = "registry+https://github.com/rust-lang/crates.io-index"
266dependencies = [
267 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
268 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
269]
270
271[[package]]
272name = "rand_core"
273version = "0.2.2"
274source = "registry+https://github.com/rust-lang/crates.io-index"
275dependencies = [
276 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
277]
278
279[[package]]
280name = "rand_core"
281version = "0.3.0"
282source = "registry+https://github.com/rust-lang/crates.io-index"
283
284[[package]]
285name = "rand_hc"
286version = "0.1.0"
287source = "registry+https://github.com/rust-lang/crates.io-index"
288dependencies = [
289 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
290]
291
292[[package]]
293name = "rand_isaac"
294version = "0.1.1"
295source = "registry+https://github.com/rust-lang/crates.io-index"
296dependencies = [
297 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
298]
299
300[[package]]
301name = "rand_pcg"
302version = "0.1.1"
303source = "registry+https://github.com/rust-lang/crates.io-index"
304dependencies = [
305 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
306 "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
307]
308
309[[package]]
310name = "rand_xorshift"
311version = "0.1.0"
312source = "registry+https://github.com/rust-lang/crates.io-index"
313dependencies = [
314 "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
315]
316
317[[package]]
318name = "redox_syscall"
319version = "0.1.49"
320source = "registry+https://github.com/rust-lang/crates.io-index"
321
322[[package]]
323name = "regex-syntax"
324version = "0.6.4"
325source = "registry+https://github.com/rust-lang/crates.io-index"
326dependencies = [
327 "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
328]
329
330[[package]]
331name = "remove_dir_all"
332version = "0.5.1"
333source = "registry+https://github.com/rust-lang/crates.io-index"
334dependencies = [
335 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
336]
337
338[[package]]
339name = "rowan"
340version = "0.1.3"
341source = "registry+https://github.com/rust-lang/crates.io-index"
342dependencies = [
343 "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
344 "smol_str 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
345 "text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
346]
347
348[[package]]
349name = "rustc_version"
350version = "0.2.3"
351source = "registry+https://github.com/rust-lang/crates.io-index"
352dependencies = [
353 "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
354]
355
356[[package]]
357name = "rusty-fork"
358version = "0.2.1"
359source = "registry+https://github.com/rust-lang/crates.io-index"
360dependencies = [
361 "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
362 "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
363 "tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
364 "wait-timeout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
365]
366
367[[package]]
368name = "scopeguard"
369version = "0.3.3"
370source = "registry+https://github.com/rust-lang/crates.io-index"
371
372[[package]]
373name = "semver"
374version = "0.9.0"
375source = "registry+https://github.com/rust-lang/crates.io-index"
376dependencies = [
377 "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
378]
379
380[[package]]
381name = "semver-parser"
382version = "0.7.0"
383source = "registry+https://github.com/rust-lang/crates.io-index"
384
385[[package]]
386name = "smallvec"
387version = "0.6.7"
388source = "registry+https://github.com/rust-lang/crates.io-index"
389dependencies = [
390 "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
391]
392
393[[package]]
394name = "smol_str"
395version = "0.1.8"
396source = "registry+https://github.com/rust-lang/crates.io-index"
397
398[[package]]
399name = "stable_deref_trait"
400version = "1.1.1"
401source = "registry+https://github.com/rust-lang/crates.io-index"
402
403[[package]]
404name = "tempfile"
405version = "3.0.5"
406source = "registry+https://github.com/rust-lang/crates.io-index"
407dependencies = [
408 "cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
409 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
410 "rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
411 "redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)",
412 "remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
413 "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
414]
415
416[[package]]
417name = "text_unit"
418version = "0.1.5"
419source = "registry+https://github.com/rust-lang/crates.io-index"
420
421[[package]]
422name = "ucd-util"
423version = "0.1.3"
424source = "registry+https://github.com/rust-lang/crates.io-index"
425
426[[package]]
427name = "unicode-xid"
428version = "0.1.0"
429source = "registry+https://github.com/rust-lang/crates.io-index"
430
431[[package]]
432name = "unreachable"
433version = "1.0.0"
434source = "registry+https://github.com/rust-lang/crates.io-index"
435dependencies = [
436 "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
437]
438
439[[package]]
440name = "void"
441version = "1.0.2"
442source = "registry+https://github.com/rust-lang/crates.io-index"
443
444[[package]]
445name = "wait-timeout"
446version = "0.1.5"
447source = "registry+https://github.com/rust-lang/crates.io-index"
448dependencies = [
449 "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)",
450]
451
452[[package]]
453name = "winapi"
454version = "0.3.6"
455source = "registry+https://github.com/rust-lang/crates.io-index"
456dependencies = [
457 "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
458 "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
459]
460
461[[package]]
462name = "winapi-i686-pc-windows-gnu"
463version = "0.4.0"
464source = "registry+https://github.com/rust-lang/crates.io-index"
465
466[[package]]
467name = "winapi-x86_64-pc-windows-gnu"
468version = "0.4.0"
469source = "registry+https://github.com/rust-lang/crates.io-index"
470
471[metadata]
472"checksum arbitrary 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c7d1523aa3a127adf8b27af2404c03c12825b4c4d0698f01648d63fa9df62ee"
473"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
474"checksum bit-set 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6f1efcc46c18245a69c38fcc5cc650f16d3a59d034f3106e9ed63748f695730a"
475"checksum bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4440d5cb623bb7390ae27fec0bb6c61111969860f8e3ae198bfa0663645e67cf"
476"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
477"checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d"
478"checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749"
479"checksum cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "082bb9b28e00d3c9d39cc03e64ce4cea0f1bb9b3fde493f0cbc008472d22bdf4"
480"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
481"checksum drop_bomb 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "69b26e475fd29098530e709294e94e661974c851aed42512793f120fed4e199f"
482"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
483"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3"
484"checksum fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
485"checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
486"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
487"checksum lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a374c89b9db55895453a74c1e38861d9deec0b01b405a82516e9d5de4820dea1"
488"checksum libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)" = "2d2857ec59fadc0773853c664d2d18e7198e83883e7060b63c924cb077bd5c74"
489"checksum libfuzzer-sys 0.1.0 (git+https://github.com/rust-fuzz/libfuzzer-sys.git)" = "<none>"
490"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c"
491"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
492"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1"
493"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13"
494"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5"
495"checksum parking_lot 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9723236a9525c757d9725b993511e3fc941e33f27751942232f0058298297edf"
496"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c"
497"checksum parking_lot_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "94c8c7923936b28d546dfd14d4472eaf34c99b14e1c973a32b3e6d4eb04298c9"
498"checksum proptest 0.8.7 (registry+https://github.com/rust-lang/crates.io-index)" = "926d0604475349f463fe44130aae73f2294b5309ab2ca0310b998bd334ef191f"
499"checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0"
500"checksum rand 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e464cd887e869cddcae8792a4ee31d23c7edd516700695608f5b98c67ee0131c"
501"checksum rand 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae9d223d52ae411a33cf7e54ec6034ec165df296ccd23533d671a28252b6f66a"
502"checksum rand_chacha 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "771b009e3a508cb67e8823dda454aaa5368c7bc1c16829fb77d3e980440dd34a"
503"checksum rand_core 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1961a422c4d189dfb50ffa9320bf1f2a9bd54ecb92792fb9477f99a1045f3372"
504"checksum rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0905b6b7079ec73b314d4c748701f6931eb79fd97c668caa3f1899b22b32c6db"
505"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
506"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
507"checksum rand_pcg 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "086bd09a33c7044e56bb44d5bdde5a60e7f119a9e95b0775f545de759a32fe05"
508"checksum rand_xorshift 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effa3fcaa47e18db002bdde6060944b6d2f9cfd8db471c30e873448ad9187be3"
509"checksum redox_syscall 0.1.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f22c50afdcf3f0a31ebb6b47697f6a7c5e5a24967e842858118bce0615f0afad"
510"checksum regex-syntax 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4e47a2ed29da7a9e1960e1639e7a982e6edc6d49be308a3b02daf511504a16d1"
511"checksum remove_dir_all 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3488ba1b9a2084d38645c4c08276a1752dcbf2c7130d74f1569681ad5d2799c5"
512"checksum rowan 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a9ccca91953e9c549cac18e8f41daa5d49dad1c9a4c9bb977ac42718bb34e1bf"
513"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
514"checksum rusty-fork 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9591f190d2852720b679c21f66ad929f9f1d7bb09d1193c26167586029d8489c"
515"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
516"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
517"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
518"checksum smallvec 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b73ea3738b47563803ef814925e69be00799a8c07420be8b996f8e98fb2336db"
519"checksum smol_str 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "486a74e9b9fc53373808f7a17e10fc728adcb1fbe272292271d8bea61175e181"
520"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
521"checksum tempfile 3.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "7e91405c14320e5c79b3d148e1c86f40749a36e490642202a31689cb1a3452b2"
522"checksum text_unit 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8009d7bdbd896a7e09b595f8f9325a19047fc708653e60d0895202b82135048f"
523"checksum ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
524"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
525"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56"
526"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
527"checksum wait-timeout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b9f3bf741a801531993db6478b95682117471f76916f5e690dd8d45395b09349"
528"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
529"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
530"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index c22e026cf..c5ac90a62 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -1838,6 +1838,51 @@ impl<R: TreeRoot<RaTypes>> LoopExprNode<R> {
1838impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {} 1838impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {}
1839impl<'a> LoopExpr<'a> {} 1839impl<'a> LoopExpr<'a> {}
1840 1840
1841// MacroCall
1842#[derive(Debug, Clone, Copy,)]
1843pub struct MacroCallNode<R: TreeRoot<RaTypes> = OwnedRoot> {
1844 pub(crate) syntax: SyntaxNode<R>,
1845}
1846pub type MacroCall<'a> = MacroCallNode<RefRoot<'a>>;
1847
1848impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<MacroCallNode<R1>> for MacroCallNode<R2> {
1849 fn eq(&self, other: &MacroCallNode<R1>) -> bool { self.syntax == other.syntax }
1850}
1851impl<R: TreeRoot<RaTypes>> Eq for MacroCallNode<R> {}
1852impl<R: TreeRoot<RaTypes>> Hash for MacroCallNode<R> {
1853 fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
1854}
1855
1856impl<'a> AstNode<'a> for MacroCall<'a> {
1857 fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
1858 match syntax.kind() {
1859 MACRO_CALL => Some(MacroCall { syntax }),
1860 _ => None,
1861 }
1862 }
1863 fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
1864}
1865
1866impl<R: TreeRoot<RaTypes>> MacroCallNode<R> {
1867 pub fn borrowed(&self) -> MacroCall {
1868 MacroCallNode { syntax: self.syntax.borrowed() }
1869 }
1870 pub fn owned(&self) -> MacroCallNode {
1871 MacroCallNode { syntax: self.syntax.owned() }
1872 }
1873}
1874
1875
1876impl<'a> MacroCall<'a> {
1877 pub fn token_tree(self) -> Option<TokenTree<'a>> {
1878 super::child_opt(self)
1879 }
1880
1881 pub fn path(self) -> Option<Path<'a>> {
1882 super::child_opt(self)
1883 }
1884}
1885
1841// MatchArm 1886// MatchArm
1842#[derive(Debug, Clone, Copy,)] 1887#[derive(Debug, Clone, Copy,)]
1843pub struct MatchArmNode<R: TreeRoot<RaTypes> = OwnedRoot> { 1888pub struct MatchArmNode<R: TreeRoot<RaTypes> = OwnedRoot> {
diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron
index 4bcff4e14..aab4839a9 100644
--- a/crates/ra_syntax/src/grammar.ron
+++ b/crates/ra_syntax/src/grammar.ron
@@ -484,6 +484,7 @@ Grammar(
484 484
485 "Name": (), 485 "Name": (),
486 "NameRef": (), 486 "NameRef": (),
487 "MacroCall": ( options: [ "TokenTree", "Path" ] ),
487 "Attr": ( options: [ ["value", "TokenTree"] ] ), 488 "Attr": ( options: [ ["value", "TokenTree"] ] ),
488 "TokenTree": (), 489 "TokenTree": (),
489 "TypeParamList": ( 490 "TypeParamList": (
diff --git a/crates/ra_syntax/src/string_lexing/parser.rs b/crates/ra_syntax/src/string_lexing/parser.rs
index 14c6015c2..e835382fc 100644
--- a/crates/ra_syntax/src/string_lexing/parser.rs
+++ b/crates/ra_syntax/src/string_lexing/parser.rs
@@ -82,7 +82,7 @@ impl<'a> Parser<'a> {
82 82
83 fn parse_escape(&mut self, start: TextUnit) -> StringComponent { 83 fn parse_escape(&mut self, start: TextUnit) -> StringComponent {
84 if self.peek().is_none() { 84 if self.peek().is_none() {
85 return StringComponent::new(TextRange::from_to(start, start), AsciiEscape); 85 return StringComponent::new(TextRange::from_to(start, self.get_pos()), AsciiEscape);
86 } 86 }
87 87
88 let next = self.advance(); 88 let next = self.advance();
diff --git a/crates/ra_syntax/src/validation/char.rs b/crates/ra_syntax/src/validation/char.rs
index 19cd3830f..10d3d1dec 100644
--- a/crates/ra_syntax/src/validation/char.rs
+++ b/crates/ra_syntax/src/validation/char.rs
@@ -70,7 +70,7 @@ pub(super) fn validate_char_component(
70 70
71fn validate_ascii_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) { 71fn validate_ascii_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) {
72 if text.len() == 1 { 72 if text.len() == 1 {
73 // Escape sequence consists only of leading `\` 73 // Escape sequence consists only of leading `\` (only occurs at EOF, otherwise e.g. '\' is treated as an unclosed char containing a single quote `'`)
74 errors.push(SyntaxError::new(EmptyAsciiEscape, range)); 74 errors.push(SyntaxError::new(EmptyAsciiEscape, range));
75 } else { 75 } else {
76 let escape_code = text.chars().skip(1).next().unwrap(); 76 let escape_code = text.chars().skip(1).next().unwrap();
diff --git a/crates/ra_syntax/tests/data/parser/fuzz-failures/0002.rs b/crates/ra_syntax/tests/data/parser/fuzz-failures/0002.rs
new file mode 100644
index 000000000..f35dc7289
--- /dev/null
+++ b/crates/ra_syntax/tests/data/parser/fuzz-failures/0002.rs
@@ -0,0 +1 @@
!('\ \ No newline at end of file
diff --git a/crates/ra_vfs/src/io.rs b/crates/ra_vfs/src/io.rs
index be400bae9..4cfdb83da 100644
--- a/crates/ra_vfs/src/io.rs
+++ b/crates/ra_vfs/src/io.rs
@@ -8,7 +8,7 @@ use walkdir::{DirEntry, WalkDir};
8use thread_worker::{WorkerHandle}; 8use thread_worker::{WorkerHandle};
9use relative_path::RelativePathBuf; 9use relative_path::RelativePathBuf;
10 10
11use crate::VfsRoot; 11use crate::{VfsRoot, has_rs_extension};
12 12
13pub(crate) struct Task { 13pub(crate) struct Task {
14 pub(crate) root: VfsRoot, 14 pub(crate) root: VfsRoot,
@@ -59,7 +59,7 @@ fn load_root(root: &Path, filter: &dyn Fn(&DirEntry) -> bool) -> Vec<(RelativePa
59 continue; 59 continue;
60 } 60 }
61 let path = entry.path(); 61 let path = entry.path();
62 if path.extension().and_then(|os| os.to_str()) != Some("rs") { 62 if !has_rs_extension(path) {
63 continue; 63 continue;
64 } 64 }
65 let text = match fs::read_to_string(path) { 65 let text = match fs::read_to_string(path) {
diff --git a/crates/ra_vfs/src/lib.rs b/crates/ra_vfs/src/lib.rs
index 4de07b093..90d5e21f4 100644
--- a/crates/ra_vfs/src/lib.rs
+++ b/crates/ra_vfs/src/lib.rs
@@ -2,11 +2,13 @@
2//! 2//!
3//! When doing analysis, we don't want to do any IO, we want to keep all source 3//! When doing analysis, we don't want to do any IO, we want to keep all source
4//! code in memory. However, the actual source code is stored on disk, so you 4//! code in memory. However, the actual source code is stored on disk, so you
5//! component which does this.
6//! need to get it into the memory in the first place somehow. VFS is the 5//! need to get it into the memory in the first place somehow. VFS is the
6//! component which does this.
7//! 7//!
8//! It also is responsible for watching the disk for changes, and for merging 8//! It is also responsible for watching the disk for changes, and for merging
9//! editor state (modified, unsaved files) with disk state. 9//! editor state (modified, unsaved files) with disk state.
10//! TODO: Some LSP clients support watching the disk, so this crate should
11//! to support custom watcher events (related to https://github.com/rust-analyzer/rust-analyzer/issues/131)
10//! 12//!
11//! VFS is based on a concept of roots: a set of directories on the file system 13//! VFS is based on a concept of roots: a set of directories on the file system
12//! whihc are watched for changes. Typically, there will be a root for each 14//! whihc are watched for changes. Typically, there will be a root for each
@@ -29,7 +31,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
29use relative_path::RelativePathBuf; 31use relative_path::RelativePathBuf;
30use crossbeam_channel::Receiver; 32use crossbeam_channel::Receiver;
31use walkdir::DirEntry; 33use walkdir::DirEntry;
32use thread_worker::{WorkerHandle}; 34use thread_worker::WorkerHandle;
33 35
34use crate::{ 36use crate::{
35 arena::{ArenaId, Arena}, 37 arena::{ArenaId, Arena},
@@ -57,12 +59,8 @@ impl RootFilter {
57 if !(self.file_filter)(path) { 59 if !(self.file_filter)(path) {
58 return None; 60 return None;
59 } 61 }
60 if !(path.starts_with(&self.root)) { 62 let path = path.strip_prefix(&self.root).ok()?;
61 return None; 63 RelativePathBuf::from_path(path).ok()
62 }
63 let path = path.strip_prefix(&self.root).unwrap();
64 let path = RelativePathBuf::from_path(path).unwrap();
65 Some(path)
66 } 64 }
67} 65}
68 66
diff --git a/crates/test_utils/Cargo.toml b/crates/test_utils/Cargo.toml
index 8c8fcd7ae..02727993e 100644
--- a/crates/test_utils/Cargo.toml
+++ b/crates/test_utils/Cargo.toml
@@ -6,6 +6,6 @@ authors = ["Aleksey Kladov <[email protected]>"]
6 6
7[dependencies] 7[dependencies]
8difference = "2.0.0" 8difference = "2.0.0"
9itertools = "0.7.8" 9itertools = "0.8.0"
10text_unit = "0.1.2" 10text_unit = "0.1.2"
11serde_json = "1.0.24" 11serde_json = "1.0.24"
diff --git a/crates/tools/Cargo.toml b/crates/tools/Cargo.toml
index 41b19c5c6..4795b1387 100644
--- a/crates/tools/Cargo.toml
+++ b/crates/tools/Cargo.toml
@@ -8,6 +8,6 @@ publish = false
8[dependencies] 8[dependencies]
9teraron = "0.0.1" 9teraron = "0.0.1"
10walkdir = "2.1.3" 10walkdir = "2.1.3"
11itertools = "0.7.8" 11itertools = "0.8.0"
12clap = "2.32.0" 12clap = "2.32.0"
13failure = "0.1.1" 13failure = "0.1.1"
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs
index 6f96b8120..e5b32c25c 100644
--- a/crates/tools/src/lib.rs
+++ b/crates/tools/src/lib.rs
@@ -117,7 +117,7 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
117 Ok(()) 117 Ok(())
118} 118}
119 119
120fn install_rustfmt() -> Result<()> { 120pub fn install_rustfmt() -> Result<()> {
121 run(&format!("rustup install {}", TOOLCHAIN), ".")?; 121 run(&format!("rustup install {}", TOOLCHAIN), ".")?;
122 run( 122 run(
123 &format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), 123 &format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN),