diff options
Diffstat (limited to 'crates')
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" | |||
8 | description = "Generic LSP server scaffold." | 8 | description = "Generic LSP server scaffold." |
9 | 9 | ||
10 | [dependencies] | 10 | [dependencies] |
11 | languageserver-types = "0.53.0" | 11 | languageserver-types = "0.53.1" |
12 | log = "0.4.3" | 12 | log = "0.4.3" |
13 | failure = "0.1.2" | 13 | failure = "0.1.2" |
14 | serde_json = "1.0.24" | 14 | serde_json = "1.0.24" |
15 | serde = { version = "1.0.71", features = ["derive"] } | 15 | serde = { version = "1.0.83", features = ["derive"] } |
16 | crossbeam-channel = "0.2.4" | 16 | crossbeam-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" | |||
9 | relative-path = "0.4.0" | 9 | relative-path = "0.4.0" |
10 | rayon = "1.0.2" | 10 | rayon = "1.0.2" |
11 | fst = "0.3.1" | 11 | fst = "0.3.1" |
12 | salsa = "0.8.0" | 12 | salsa = "0.9.0" |
13 | rustc-hash = "1.0" | 13 | rustc-hash = "1.0" |
14 | parking_lot = "0.6.4" | 14 | parking_lot = "0.7.0" |
15 | ra_syntax = { path = "../ra_syntax" } | 15 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_editor = { path = "../ra_editor" } | 16 | ra_editor = { path = "../ra_editor" } |
17 | ra_text_edit = { path = "../ra_text_edit" } | 17 | ra_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). |
8 | pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { | 8 | pub(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 | ||
41 | fn is_in_loop_body(leaf: SyntaxNodeRef) -> bool { | 46 | fn 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 | ||
60 | fn complete_return(fn_def: ast::FnDef, is_stmt: bool) -> Option<CompletionItem> { | 65 | fn 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 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | Cancelable, | 2 | Cancelable, |
3 | completion::{CompletionItem, Completions, CompletionKind, CompletionContext}, | 3 | completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}, |
4 | }; | 4 | }; |
5 | 5 | ||
6 | pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { | 6 | pub(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 | ||
9 | pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &CompletionContext) { | 9 | pub(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 @@ | |||
1 | use crate::db; | ||
2 | |||
3 | use hir::PerNs; | 1 | use hir::PerNs; |
4 | 2 | ||
3 | use 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 | ||
150 | impl Into<CompletionItem> for Builder { | 166 | impl 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 @@ | |||
1 | use ra_db::SyntaxDatabase; | ||
2 | use ra_syntax::{ | ||
3 | SyntaxNodeRef, AstNode, | ||
4 | ast, algo::find_covering_node, | ||
5 | }; | ||
6 | |||
7 | use crate::{ | ||
8 | TextRange, FileRange, | ||
9 | db::RootDatabase, | ||
10 | }; | ||
11 | |||
12 | pub(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 | |||
28 | fn 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)] | ||
35 | mod 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::*; | |||
7 | use salsa::{Database, ParallelDatabase}; | 7 | use salsa::{Database, ParallelDatabase}; |
8 | 8 | ||
9 | use hir::{ | 9 | use hir::{ |
10 | self, | 10 | self, FnSignatureInfo, Problem, source_binder, |
11 | FnSignatureInfo, | ||
12 | Problem, | ||
13 | source_binder, | ||
14 | }; | 11 | }; |
15 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; | 12 | use ra_db::{FilesDatabase, SourceRoot, SourceRootId, SyntaxDatabase}; |
16 | use ra_editor::{self, FileSymbol, find_node_at_offset, LineIndex, LocalEdit, Severity}; | 13 | use 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 | ||
148 | impl AnalysisImpl { | 142 | impl 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; | |||
16 | mod symbol_index; | 16 | mod symbol_index; |
17 | pub mod mock_analysis; | 17 | pub mod mock_analysis; |
18 | 18 | ||
19 | mod extend_selection; | ||
20 | mod syntax_highlighting; | ||
21 | mod macros; | ||
22 | |||
19 | use std::{fmt, sync::Arc}; | 23 | use std::{fmt, sync::Arc}; |
20 | 24 | ||
21 | use rustc_hash::FxHashMap; | 25 | use rustc_hash::FxHashMap; |
@@ -37,7 +41,7 @@ pub use ra_editor::{ | |||
37 | pub use hir::FnSignatureInfo; | 41 | pub use hir::FnSignatureInfo; |
38 | 42 | ||
39 | pub use ra_db::{ | 43 | pub 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 | ||
272 | impl Analysis { | 276 | impl 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. | ||
4 | use ra_syntax::{ast, AstNode, SourceFileNode, TextRange}; | ||
5 | |||
6 | use crate::{db::RootDatabase, FileId}; | ||
7 | |||
8 | pub(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 | |||
44 | pub(crate) struct MacroExpansion { | ||
45 | pub(crate) source_file: SourceFileNode, | ||
46 | pub(crate) ranges_map: Vec<(TextRange, TextRange)>, | ||
47 | } | ||
48 | |||
49 | impl 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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use relative_path::RelativePathBuf; | 3 | use relative_path::RelativePathBuf; |
4 | use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; | 4 | use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; |
5 | use ra_db::mock::FileMap; | 5 | use ra_db::mock::FileMap; |
6 | 6 | ||
7 | use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, SourceRootId}; | 7 | use 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 <|>. | ||
126 | pub 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 @@ | |||
1 | use ra_syntax::{ast, AstNode,}; | ||
2 | use ra_editor::HighlightedRange; | ||
3 | use ra_db::SyntaxDatabase; | ||
4 | |||
5 | use crate::{ | ||
6 | db::RootDatabase, | ||
7 | FileId, Cancelable, | ||
8 | }; | ||
9 | |||
10 | pub(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)] | ||
36 | mod 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] |
8 | backtrace = "0.3.1" | 8 | backtrace = "0.3.1" |
9 | relative-path = "0.4.0" | 9 | relative-path = "0.4.0" |
10 | salsa = "0.8.0" | 10 | salsa = "0.9.0" |
11 | rustc-hash = "1.0" | 11 | rustc-hash = "1.0" |
12 | parking_lot = "0.6.4" | 12 | parking_lot = "0.7.0" |
13 | ra_syntax = { path = "../ra_syntax" } | 13 | ra_syntax = { path = "../ra_syntax" } |
14 | ra_editor = { path = "../ra_editor" } | 14 | ra_editor = { path = "../ra_editor" } |
15 | test_utils = { path = "../test_utils" } | 15 | test_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; | |||
8 | use std::sync::Arc; | 8 | use std::sync::Arc; |
9 | 9 | ||
10 | use ra_editor::LineIndex; | 10 | use ra_editor::LineIndex; |
11 | use ra_syntax::{TextUnit, SourceFileNode}; | 11 | use ra_syntax::{TextUnit, TextRange, SourceFileNode}; |
12 | 12 | ||
13 | pub use crate::{ | 13 | pub 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)] | ||
75 | pub 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]>"] | |||
6 | publish = false | 6 | publish = false |
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | itertools = "0.7.8" | 9 | itertools = "0.8.0" |
10 | superslice = "0.1.0" | 10 | superslice = "0.1.0" |
11 | join_to_string = "0.1.1" | 11 | join_to_string = "0.1.1" |
12 | rustc-hash = "1.0" | 12 | rustc-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 @@ | |||
1 | use itertools::Itertools; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | ast::{self, AstNode}, | ||
5 | Location, | ||
6 | SourceFileNode, | ||
7 | SyntaxKind, | ||
8 | TextRange, | ||
9 | }; | ||
10 | use ra_syntax::SyntaxNodeRef; | ||
11 | use ra_text_edit::{ | ||
12 | TextEdit, | ||
13 | TextEditBuilder, | ||
14 | }; | ||
15 | |||
16 | use crate::{ | ||
17 | Diagnostic, | ||
18 | LocalEdit, | ||
19 | Severity, | ||
20 | }; | ||
21 | |||
22 | pub 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 | |||
49 | fn 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 | |||
81 | fn 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 | |||
103 | fn 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)] | ||
136 | mod 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#" | ||
216 | struct A { | ||
217 | a: &'static str | ||
218 | } | ||
219 | |||
220 | fn main() { | ||
221 | let a = "haha"; | ||
222 | A { | ||
223 | a: a | ||
224 | } | ||
225 | } | ||
226 | "#, | ||
227 | r#" | ||
228 | struct A { | ||
229 | a: &'static str | ||
230 | } | ||
231 | |||
232 | fn main() { | ||
233 | let a = "haha"; | ||
234 | A { | ||
235 | a | ||
236 | } | ||
237 | } | ||
238 | "#, | ||
239 | check_struct_shorthand_initialization, | ||
240 | ); | ||
241 | |||
242 | check_apply( | ||
243 | r#" | ||
244 | struct A { | ||
245 | a: &'static str, | ||
246 | b: &'static str | ||
247 | } | ||
248 | |||
249 | fn main() { | ||
250 | let a = "haha"; | ||
251 | let b = "bb"; | ||
252 | A { | ||
253 | a: a, | ||
254 | b | ||
255 | } | ||
256 | } | ||
257 | "#, | ||
258 | r#" | ||
259 | struct A { | ||
260 | a: &'static str, | ||
261 | b: &'static str | ||
262 | } | ||
263 | |||
264 | fn 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)] |
8 | mod test_utils; | 8 | mod test_utils; |
9 | mod typing; | 9 | mod typing; |
10 | mod diagnostics; | ||
10 | 11 | ||
11 | pub use self::{ | 12 | pub 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 | }; |
20 | use ra_text_edit::{TextEdit, TextEditBuilder}; | 22 | use ra_text_edit::{TextEdit, TextEditBuilder}; |
21 | use ra_syntax::{ | 23 | use 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 | }; |
29 | use itertools::Itertools; | 30 | use rustc_hash::FxHashSet; |
30 | 31 | ||
31 | #[derive(Debug)] | 32 | #[derive(Debug)] |
32 | pub struct HighlightedRange { | 33 | pub struct HighlightedRange { |
@@ -79,8 +80,13 @@ pub fn matching_brace(file: &SourceFileNode, offset: TextUnit) -> Option<TextUni | |||
79 | } | 80 | } |
80 | 81 | ||
81 | pub fn highlight(file: &SourceFileNode) -> Vec<HighlightedRange> { | 82 | pub 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 | ||
103 | pub 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 | |||
128 | fn 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 | |||
162 | fn 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 | |||
184 | pub fn syntax_tree(file: &SourceFileNode) -> String { | 132 | pub 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" | |||
5 | authors = ["Aleksey Kladov <[email protected]>"] | 5 | authors = ["Aleksey Kladov <[email protected]>"] |
6 | 6 | ||
7 | [dependencies] | 7 | [dependencies] |
8 | arrayvec = "0.4.9" | 8 | arrayvec = "0.4.10" |
9 | log = "0.4.5" | 9 | log = "0.4.5" |
10 | relative-path = "0.4.0" | 10 | relative-path = "0.4.0" |
11 | salsa = "0.8.0" | 11 | salsa = "0.9.0" |
12 | rustc-hash = "1.0" | 12 | rustc-hash = "1.0" |
13 | parking_lot = "0.6.4" | 13 | parking_lot = "0.7.0" |
14 | id-arena = "2.0" | 14 | id-arena = "2.0" |
15 | ena = "0.11" | ||
15 | ra_syntax = { path = "../ra_syntax" } | 16 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_editor = { path = "../ra_editor" } | 17 | ra_editor = { path = "../ra_editor" } |
17 | ra_db = { path = "../ra_db" } | 18 | ra_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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{SmolStr, ast::{self, NameOwner, StructFlavor}}; | 3 | use ra_syntax::ast::{self, NameOwner, StructFlavor}; |
4 | 4 | ||
5 | use crate::{ | 5 | use 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)] |
38 | pub struct StructData { | 38 | pub struct StructData { |
39 | name: Option<SmolStr>, | 39 | name: Option<Name>, |
40 | variant_data: Arc<VariantData>, | 40 | variant_data: Arc<VariantData>, |
41 | } | 41 | } |
42 | 42 | ||
43 | impl StructData { | 43 | impl 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)] |
79 | pub struct EnumData { | 83 | pub 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 | ||
84 | impl EnumData { | 88 | impl 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)] |
107 | pub struct StructField { | 109 | pub struct StructField { |
108 | name: SmolStr, | 110 | name: Name, |
109 | type_ref: TypeRef, | 111 | type_ref: TypeRef, |
110 | } | 112 | } |
111 | 113 | ||
112 | impl StructField { | 114 | impl 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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::SyntaxNode; |
4 | SmolStr, | ||
5 | SyntaxNode, | ||
6 | ast::FnDefNode, | ||
7 | }; | ||
8 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; | 4 | use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; |
9 | 5 | ||
10 | use crate::{ | 6 | use 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! { | |||
24 | pub trait HirDatabase: SyntaxDatabase | 19 | pub 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 | ||
14 | use crate::{ DefId, HirDatabase, ty::InferenceResult, Module }; | 14 | use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module}; |
15 | 15 | ||
16 | pub use self::scope::FnScopes; | 16 | pub use self::scope::FnScopes; |
17 | 17 | ||
18 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | 18 | #[derive(Debug)] |
19 | pub struct FnId(pub(crate) DefId); | ||
20 | |||
21 | pub struct Function { | 19 | pub struct Function { |
22 | pub(crate) fn_id: FnId, | 20 | def_id: DefId, |
23 | } | 21 | } |
24 | 22 | ||
25 | impl Function { | 23 | impl 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 @@ | |||
1 | use rustc_hash::{FxHashMap, FxHashSet}; | 1 | use rustc_hash::{FxHashMap, FxHashSet}; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use 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 | ||
10 | use crate::{ | 10 | use crate::{ |
11 | arena::{Arena, Id}, | 11 | arena::{Arena, Id}, |
12 | Name, AsName, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | pub(crate) type ScopeId = Id<ScopeData>; | 15 | pub(crate) type ScopeId = Id<ScopeData>; |
@@ -22,7 +23,7 @@ pub struct FnScopes { | |||
22 | 23 | ||
23 | #[derive(Debug, PartialEq, Eq)] | 24 | #[derive(Debug, PartialEq, Eq)] |
24 | pub struct ScopeEntry { | 25 | pub 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 | ||
171 | impl ScopeEntry { | 173 | impl 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 { | |||
334 | mod tests { | 336 | mod 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 @@ | |||
1 | use ra_syntax::SmolStr; | ||
2 | pub use ra_db::CrateId; | 1 | pub use ra_db::CrateId; |
3 | 2 | ||
4 | use crate::{HirDatabase, Module, Cancelable}; | 3 | use 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)] |
15 | pub struct CrateDependency { | 14 | pub struct CrateDependency { |
16 | pub krate: Crate, | 15 | pub krate: Crate, |
17 | pub name: SmolStr, | 16 | pub name: Name, |
18 | } | 17 | } |
19 | 18 | ||
20 | impl Crate { | 19 | impl 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; | |||
22 | mod arena; | 22 | mod arena; |
23 | pub mod source_binder; | 23 | pub mod source_binder; |
24 | 24 | ||
25 | mod name; | ||
25 | mod krate; | 26 | mod krate; |
26 | mod module; | 27 | mod module; |
27 | mod function; | 28 | mod function; |
@@ -37,10 +38,12 @@ use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable}; | |||
37 | use crate::{ | 38 | use crate::{ |
38 | db::HirDatabase, | 39 | db::HirDatabase, |
39 | arena::{Arena, Id}, | 40 | arena::{Arena, Id}, |
41 | name::{AsName, KnownName}, | ||
40 | }; | 42 | }; |
41 | 43 | ||
42 | pub use self::{ | 44 | pub 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; | |||
7 | use ra_syntax::{ | 7 | use 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 | }; |
12 | use ra_db::{SourceRootId, FileId, Cancelable}; | 12 | use ra_db::{SourceRootId, FileId, Cancelable}; |
13 | use relative_path::RelativePathBuf; | 13 | use relative_path::RelativePathBuf; |
14 | 14 | ||
15 | use crate::{ | 15 | use 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)] |
329 | struct LinkData { | 325 | struct 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 @@ | |||
1 | use std::sync::Arc; | 1 | use std::sync::Arc; |
2 | 2 | ||
3 | use ra_syntax::{ | 3 | use ra_syntax::ast::{self, NameOwner}; |
4 | ast::{self, NameOwner}, | ||
5 | SmolStr, | ||
6 | }; | ||
7 | use relative_path::RelativePathBuf; | 4 | use relative_path::RelativePathBuf; |
8 | use rustc_hash::{FxHashMap, FxHashSet}; | 5 | use rustc_hash::{FxHashMap, FxHashSet}; |
9 | use arrayvec::ArrayVec; | 6 | use arrayvec::ArrayVec; |
10 | use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId}; | 7 | use ra_db::{SourceRoot, SourceRootId, Cancelable, FileId}; |
11 | 8 | ||
12 | use crate::{ | 9 | use crate::{ |
13 | HirDatabase, | 10 | HirDatabase, Name, AsName, |
14 | }; | 11 | }; |
15 | 12 | ||
16 | use super::{ | 13 | use super::{ |
@@ -20,12 +17,12 @@ use super::{ | |||
20 | 17 | ||
21 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] | 18 | #[derive(Clone, Hash, PartialEq, Eq, Debug)] |
22 | pub enum Submodule { | 19 | pub enum Submodule { |
23 | Declaration(SmolStr), | 20 | Declaration(Name), |
24 | Definition(SmolStr, ModuleSource), | 21 | Definition(Name, ModuleSource), |
25 | } | 22 | } |
26 | 23 | ||
27 | impl Submodule { | 24 | impl 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 | ||
36 | pub(crate) fn modules<'a>( | 33 | pub(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( | |||
155 | fn resolve_submodule( | 152 | fn 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. |
17 | use std::{ | 17 | use std::sync::Arc; |
18 | sync::Arc, | ||
19 | }; | ||
20 | 18 | ||
21 | use rustc_hash::FxHashMap; | 19 | use rustc_hash::FxHashMap; |
22 | use ra_syntax::{ | 20 | use ra_syntax::{ |
23 | TextRange, | 21 | TextRange, |
24 | SmolStr, SyntaxKind::{self, *}, | 22 | SyntaxKind::{self, *}, |
25 | ast::{self, AstNode} | 23 | ast::{self, AstNode} |
26 | }; | 24 | }; |
27 | use ra_db::SourceRootId; | 25 | use 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)] |
47 | pub struct ModuleScope { | 46 | pub struct ModuleScope { |
48 | items: FxHashMap<SmolStr, Resolution>, | 47 | items: FxHashMap<Name, Resolution>, |
49 | } | 48 | } |
50 | 49 | ||
51 | impl ModuleScope { | 50 | impl 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)] |
73 | struct ModuleItem { | 72 | struct 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 | ||
261 | impl ModuleItem { | 260 | impl 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 | ||
3 | use salsa::Database; | 3 | use salsa::Database; |
4 | use ra_db::{FilesDatabase, CrateGraph}; | 4 | use ra_db::{FilesDatabase, CrateGraph}; |
5 | use ra_syntax::SmolStr; | ||
6 | use relative_path::RelativePath; | 5 | use relative_path::RelativePath; |
6 | use test_utils::assert_eq_text; | ||
7 | 7 | ||
8 | use crate::{ | 8 | use 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 | ||
24 | fn 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] |
25 | fn item_map_smoke_test() { | 54 | fn 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] |
47 | fn test_self() { | 81 | fn 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 @@ | |||
1 | use std::fmt; | ||
2 | |||
3 | use 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)] | ||
9 | pub struct Name { | ||
10 | text: SmolStr, | ||
11 | } | ||
12 | |||
13 | impl fmt::Display for Name { | ||
14 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
15 | fmt::Display::fmt(&self.text, f) | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl fmt::Debug for Name { | ||
20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
21 | fmt::Debug::fmt(&self.text, f) | ||
22 | } | ||
23 | } | ||
24 | |||
25 | impl 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 | |||
60 | pub(crate) trait AsName { | ||
61 | fn as_name(&self) -> Name; | ||
62 | } | ||
63 | |||
64 | impl AsName for ast::NameRef<'_> { | ||
65 | fn as_name(&self) -> Name { | ||
66 | Name::new(self.text()) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | impl AsName for ast::Name<'_> { | ||
71 | fn as_name(&self) -> Name { | ||
72 | Name::new(self.text()) | ||
73 | } | ||
74 | } | ||
75 | |||
76 | impl 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)] | ||
88 | pub(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 @@ | |||
1 | use ra_syntax::{SmolStr, ast, AstNode, TextRange}; | 1 | use ra_syntax::{ast, AstNode, TextRange}; |
2 | |||
3 | use crate::{Name, AsName}; | ||
2 | 4 | ||
3 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 5 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
4 | pub struct Path { | 6 | pub 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 | ||
72 | fn expand_use_tree( | 82 | fn 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 | ||
6 | use rustc_hash::FxHashMap; | 6 | use rustc_hash::FxHashMap; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | AstNode, SyntaxNode, SmolStr, | 8 | AstNode, SyntaxNode, |
9 | ast::{self, FnDef, FnDefNode, NameOwner, ModuleItemOwner} | 9 | ast::{self, NameOwner, ModuleItemOwner} |
10 | }; | 10 | }; |
11 | use ra_db::{SourceRootId, FileId, Cancelable,}; | 11 | use ra_db::{SourceRootId, FileId, Cancelable,}; |
12 | 12 | ||
13 | use crate::{ | 13 | use 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` | 25 | pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Arc<FnScopes> { |
27 | pub(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 | |||
34 | pub(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 | ||
40 | pub(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 | |||
45 | pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | ||
46 | ty::type_for_def(db, def_id) | ||
47 | } | ||
48 | |||
49 | pub(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 | |||
57 | pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> { | 32 | pub(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 | ||
131 | pub(crate) fn modules<'a>( | 106 | pub(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 @@ | |||
8 | use ra_db::{FileId, FilePosition, Cancelable}; | 8 | use ra_db::{FileId, FilePosition, Cancelable}; |
9 | use ra_editor::find_node_at_offset; | 9 | use ra_editor::find_node_at_offset; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | ast::{self, AstNode}, | 11 | ast::{self, AstNode, NameOwner}, |
12 | SyntaxNodeRef, | 12 | SyntaxNodeRef, |
13 | }; | 13 | }; |
14 | 14 | ||
15 | use crate::{ | 15 | use 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. | ||
28 | pub 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. |
28 | pub fn module_from_position( | 47 | pub 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 | |||
1 | mod primitive; | 16 | mod primitive; |
2 | #[cfg(test)] | 17 | #[cfg(test)] |
3 | mod tests; | 18 | mod tests; |
4 | 19 | ||
5 | use std::sync::Arc; | 20 | use std::sync::Arc; |
6 | use std::fmt; | 21 | use std::{fmt, mem}; |
7 | 22 | ||
8 | use log; | 23 | use log; |
9 | use rustc_hash::{FxHashMap}; | 24 | use rustc_hash::FxHashMap; |
25 | use ena::unify::{InPlaceUnificationTable, UnifyKey, UnifyValue, NoError}; | ||
10 | 26 | ||
11 | use ra_db::{LocalSyntaxPtr, Cancelable}; | 27 | use ra_db::{LocalSyntaxPtr, Cancelable}; |
12 | use ra_syntax::{ | 28 | use 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 | ||
18 | use crate::{ | 33 | use 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)] | ||
41 | pub struct TypeVarId(u32); | ||
42 | |||
43 | impl 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)] | ||
62 | pub enum TypeVarValue { | ||
63 | Known(Ty), | ||
64 | Unknown, | ||
65 | } | ||
66 | |||
67 | impl TypeVarValue { | ||
68 | fn known(&self) -> Option<&Ty> { | ||
69 | match self { | ||
70 | TypeVarValue::Known(ty) => Some(ty), | ||
71 | TypeVarValue::Unknown => None, | ||
72 | } | ||
73 | } | ||
74 | } | ||
75 | |||
76 | impl 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)] | ||
101 | pub 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)] | ||
108 | struct 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 | |||
115 | impl 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)] |
26 | pub enum Ty { | 132 | pub 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 | ||
121 | type TyRef = Arc<Ty>; | 227 | /// A function signature. |
122 | |||
123 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | 228 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
124 | pub struct FnSig { | 229 | pub 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 | ||
259 | pub 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). | ||
402 | fn 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 | ||
277 | pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable<Ty> { | 424 | fn 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 | ||
286 | pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable<Ty> { | 431 | pub 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 | ||
295 | pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> { | 438 | pub(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 | ||
312 | pub(super) fn type_for_field( | 455 | pub(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)] |
340 | pub struct InferenceResult { | 480 | pub struct InferenceResult { |
341 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, | 481 | type_of: FxHashMap<LocalSyntaxPtr, Ty>, |
342 | } | 482 | } |
343 | 483 | ||
344 | impl InferenceResult { | 484 | impl 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)] |
351 | pub struct InferenceContext<'a, D: HirDatabase> { | 496 | struct 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 | ||
657 | pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceResult> { | 911 | pub 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 @@ | |||
1 | use std::fmt; | 1 | use std::fmt; |
2 | 2 | ||
3 | use crate::{Name, KnownName}; | ||
4 | |||
3 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] | 5 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] |
4 | pub enum IntTy { | 6 | pub 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] | ||
117 | fn infer_backwards() { | ||
118 | check_inference( | ||
119 | r#" | ||
120 | fn takes_u32(x: u32) {} | ||
121 | |||
122 | struct S { i32_field: i32 } | ||
123 | |||
124 | fn 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 | |||
116 | fn infer(content: &str) -> String { | 137 | fn 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" | |||
11 | failure = "0.1.2" | 11 | failure = "0.1.2" |
12 | failure_derive = "0.1.2" | 12 | failure_derive = "0.1.2" |
13 | serde_json = "1.0.24" | 13 | serde_json = "1.0.24" |
14 | serde = "1.0.71" | 14 | serde = "1.0.83" |
15 | drop_bomb = "0.1.0" | 15 | drop_bomb = "0.1.0" |
16 | crossbeam-channel = "0.2.4" | 16 | crossbeam-channel = "0.2.4" |
17 | flexi_logger = "0.10.0" | 17 | flexi_logger = "0.10.0" |
18 | log = "0.4.3" | 18 | log = "0.4.3" |
19 | url_serde = "0.2.0" | 19 | url_serde = "0.2.0" |
20 | languageserver-types = "0.53.0" | 20 | languageserver-types = "0.53.1" |
21 | walkdir = "2.2.7" | 21 | walkdir = "2.2.7" |
22 | im = "12.0.0" | 22 | im = "12.0.0" |
23 | cargo_metadata = "0.6.0" | 23 | cargo_metadata = "0.6.0" |
@@ -37,3 +37,4 @@ ra_vfs = { path = "../ra_vfs" } | |||
37 | [dev-dependencies] | 37 | [dev-dependencies] |
38 | tempdir = "0.3.7" | 38 | tempdir = "0.3.7" |
39 | test_utils = { path = "../test_utils" } | 39 | test_utils = { path = "../test_utils" } |
40 | tools = { 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 | }; |
5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition, CompletionItem, CompletionItemKind, InsertText}; | 5 | use ra_analysis::{FileId, FileSystemEdit, SourceChange, SourceFileEdit, FilePosition,FileRange, CompletionItem, CompletionItemKind, InsertText}; |
6 | use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; | 6 | use ra_editor::{LineCol, LineIndex, translate_offset_with_edit}; |
7 | use ra_text_edit::{AtomTextEdit, TextEdit}; | 7 | use ra_text_edit::{AtomTextEdit, TextEdit}; |
8 | use ra_syntax::{SyntaxKind, TextRange, TextUnit}; | 8 | use 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 | ||
222 | impl<'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 | |||
221 | impl<T: TryConvWith> TryConvWith for Vec<T> { | 233 | impl<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 | }; |
11 | use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FilePosition, Severity}; | 13 | use ra_analysis::{FileId, FoldKind, Query, RunnableKind, FileRange, FilePosition, Severity}; |
12 | use ra_syntax::{TextUnit, text_utils::intersect}; | 14 | use ra_syntax::{TextUnit, text_utils::intersect}; |
13 | use ra_text_edit::text_utils::contains_offset_nonstrict; | 15 | use ra_text_edit::text_utils::contains_offset_nonstrict; |
14 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
15 | use serde_json::to_value; | 17 | use serde_json::to_value; |
18 | use std::io::Write; | ||
16 | 19 | ||
17 | use crate::{ | 20 | use 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 = (¶ms.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 | ||
83 | pub fn handle_on_enter( | 81 | pub fn handle_on_enter( |
@@ -606,6 +604,40 @@ pub fn handle_references( | |||
606 | )) | 604 | )) |
607 | } | 605 | } |
608 | 606 | ||
607 | pub 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 | |||
609 | pub fn handle_code_action( | 641 | pub 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 @@ | |||
1 | mod support; | 1 | mod support; |
2 | 2 | ||
3 | use serde_json::json; | 3 | use serde_json::json; |
4 | use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams}; | 4 | use ra_lsp_server::req::{Runnables, RunnablesParams, CodeActionRequest, CodeActionParams, Formatting}; |
5 | use languageserver_types::{Position, Range, CodeActionContext}; | 5 | use languageserver_types::{Position, Range, CodeActionContext, DocumentFormattingParams, FormattingOptions}; |
6 | 6 | ||
7 | use crate::support::project; | 7 | use crate::support::project; |
8 | 8 | ||
@@ -118,6 +118,60 @@ fn test_eggs() {} | |||
118 | ); | 118 | ); |
119 | } | 119 | } |
120 | 120 | ||
121 | use std::collections::HashMap; | ||
122 | #[test] | ||
123 | fn test_format_document() { | ||
124 | tools::install_rustfmt().unwrap(); | ||
125 | |||
126 | let server = project( | ||
127 | r#" | ||
128 | [package] | ||
129 | name = "foo" | ||
130 | version = "0.0.0" | ||
131 | |||
132 | //- src/lib.rs | ||
133 | mod bar; | ||
134 | |||
135 | fn main() { | ||
136 | } | ||
137 | |||
138 | pub 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 | |||
156 | fn main() {} | ||
157 | |||
158 | pub 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] |
122 | fn test_missing_module_code_action() { | 176 | fn 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" | |||
8 | repository = "https://github.com/rust-analyzer/rust-analyzer" | 8 | repository = "https://github.com/rust-analyzer/rust-analyzer" |
9 | 9 | ||
10 | [dependencies] | 10 | [dependencies] |
11 | arrayvec = "0.4.7" | 11 | arrayvec = "0.4.10" |
12 | unicode-xid = "0.1.0" | 12 | unicode-xid = "0.1.0" |
13 | itertools = "0.7.8" | 13 | itertools = "0.8.0" |
14 | drop_bomb = "0.1.4" | 14 | drop_bomb = "0.1.4" |
15 | parking_lot = "0.6.0" | 15 | parking_lot = "0.7.0" |
16 | rowan = "0.1.2" | 16 | rowan = "0.1.2" |
17 | text_unit = "0.1.5" | 17 | text_unit = "0.1.5" |
18 | ra_text_edit = { path = "../ra_text_edit" } | 18 | ra_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]] | ||
2 | name = "arbitrary" | ||
3 | version = "0.1.1" | ||
4 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
5 | |||
6 | [[package]] | ||
7 | name = "arrayvec" | ||
8 | version = "0.4.10" | ||
9 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
10 | dependencies = [ | ||
11 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", | ||
12 | ] | ||
13 | |||
14 | [[package]] | ||
15 | name = "bit-set" | ||
16 | version = "0.5.0" | ||
17 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
18 | dependencies = [ | ||
19 | "bit-vec 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
20 | ] | ||
21 | |||
22 | [[package]] | ||
23 | name = "bit-vec" | ||
24 | version = "0.5.0" | ||
25 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
26 | |||
27 | [[package]] | ||
28 | name = "bitflags" | ||
29 | version = "1.0.4" | ||
30 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
31 | |||
32 | [[package]] | ||
33 | name = "byteorder" | ||
34 | version = "1.2.7" | ||
35 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
36 | |||
37 | [[package]] | ||
38 | name = "cc" | ||
39 | version = "1.0.28" | ||
40 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
41 | |||
42 | [[package]] | ||
43 | name = "cfg-if" | ||
44 | version = "0.1.6" | ||
45 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
46 | |||
47 | [[package]] | ||
48 | name = "cloudabi" | ||
49 | version = "0.0.3" | ||
50 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
51 | dependencies = [ | ||
52 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", | ||
53 | ] | ||
54 | |||
55 | [[package]] | ||
56 | name = "drop_bomb" | ||
57 | version = "0.1.4" | ||
58 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
59 | |||
60 | [[package]] | ||
61 | name = "either" | ||
62 | version = "1.5.0" | ||
63 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
64 | |||
65 | [[package]] | ||
66 | name = "fnv" | ||
67 | version = "1.0.6" | ||
68 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
69 | |||
70 | [[package]] | ||
71 | name = "fuchsia-zircon" | ||
72 | version = "0.3.3" | ||
73 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
74 | dependencies = [ | ||
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]] | ||
80 | name = "fuchsia-zircon-sys" | ||
81 | version = "0.3.3" | ||
82 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
83 | |||
84 | [[package]] | ||
85 | name = "itertools" | ||
86 | version = "0.8.0" | ||
87 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
88 | dependencies = [ | ||
89 | "either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
90 | ] | ||
91 | |||
92 | [[package]] | ||
93 | name = "lazy_static" | ||
94 | version = "1.2.0" | ||
95 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
96 | |||
97 | [[package]] | ||
98 | name = "libc" | ||
99 | version = "0.2.45" | ||
100 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
101 | |||
102 | [[package]] | ||
103 | name = "libfuzzer-sys" | ||
104 | version = "0.1.0" | ||
105 | source = "git+https://github.com/rust-fuzz/libfuzzer-sys.git#4a413199b5cb1bbed6a1d157b2342b925f8464ac" | ||
106 | dependencies = [ | ||
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]] | ||
112 | name = "lock_api" | ||
113 | version = "0.1.5" | ||
114 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
115 | dependencies = [ | ||
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]] | ||
121 | name = "nodrop" | ||
122 | version = "0.1.13" | ||
123 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
124 | |||
125 | [[package]] | ||
126 | name = "num-traits" | ||
127 | version = "0.2.6" | ||
128 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
129 | |||
130 | [[package]] | ||
131 | name = "owning_ref" | ||
132 | version = "0.4.0" | ||
133 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
134 | dependencies = [ | ||
135 | "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", | ||
136 | ] | ||
137 | |||
138 | [[package]] | ||
139 | name = "parking_lot" | ||
140 | version = "0.6.4" | ||
141 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
142 | dependencies = [ | ||
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]] | ||
148 | name = "parking_lot" | ||
149 | version = "0.7.0" | ||
150 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
151 | dependencies = [ | ||
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]] | ||
157 | name = "parking_lot_core" | ||
158 | version = "0.3.1" | ||
159 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
160 | dependencies = [ | ||
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]] | ||
169 | name = "parking_lot_core" | ||
170 | version = "0.4.0" | ||
171 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
172 | dependencies = [ | ||
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]] | ||
181 | name = "proptest" | ||
182 | version = "0.8.7" | ||
183 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
184 | dependencies = [ | ||
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]] | ||
198 | name = "quick-error" | ||
199 | version = "1.2.2" | ||
200 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
201 | |||
202 | [[package]] | ||
203 | name = "ra_syntax" | ||
204 | version = "0.1.0" | ||
205 | dependencies = [ | ||
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]] | ||
217 | name = "ra_syntax-fuzz" | ||
218 | version = "0.0.1" | ||
219 | dependencies = [ | ||
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]] | ||
225 | name = "ra_text_edit" | ||
226 | version = "0.1.0" | ||
227 | dependencies = [ | ||
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]] | ||
233 | name = "rand" | ||
234 | version = "0.5.5" | ||
235 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
236 | dependencies = [ | ||
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]] | ||
245 | name = "rand" | ||
246 | version = "0.6.1" | ||
247 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
248 | dependencies = [ | ||
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]] | ||
263 | name = "rand_chacha" | ||
264 | version = "0.1.0" | ||
265 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
266 | dependencies = [ | ||
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]] | ||
272 | name = "rand_core" | ||
273 | version = "0.2.2" | ||
274 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
275 | dependencies = [ | ||
276 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
277 | ] | ||
278 | |||
279 | [[package]] | ||
280 | name = "rand_core" | ||
281 | version = "0.3.0" | ||
282 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
283 | |||
284 | [[package]] | ||
285 | name = "rand_hc" | ||
286 | version = "0.1.0" | ||
287 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
288 | dependencies = [ | ||
289 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
290 | ] | ||
291 | |||
292 | [[package]] | ||
293 | name = "rand_isaac" | ||
294 | version = "0.1.1" | ||
295 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
296 | dependencies = [ | ||
297 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
298 | ] | ||
299 | |||
300 | [[package]] | ||
301 | name = "rand_pcg" | ||
302 | version = "0.1.1" | ||
303 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
304 | dependencies = [ | ||
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]] | ||
310 | name = "rand_xorshift" | ||
311 | version = "0.1.0" | ||
312 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
313 | dependencies = [ | ||
314 | "rand_core 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
315 | ] | ||
316 | |||
317 | [[package]] | ||
318 | name = "redox_syscall" | ||
319 | version = "0.1.49" | ||
320 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
321 | |||
322 | [[package]] | ||
323 | name = "regex-syntax" | ||
324 | version = "0.6.4" | ||
325 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
326 | dependencies = [ | ||
327 | "ucd-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", | ||
328 | ] | ||
329 | |||
330 | [[package]] | ||
331 | name = "remove_dir_all" | ||
332 | version = "0.5.1" | ||
333 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
334 | dependencies = [ | ||
335 | "winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||
336 | ] | ||
337 | |||
338 | [[package]] | ||
339 | name = "rowan" | ||
340 | version = "0.1.3" | ||
341 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
342 | dependencies = [ | ||
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]] | ||
349 | name = "rustc_version" | ||
350 | version = "0.2.3" | ||
351 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
352 | dependencies = [ | ||
353 | "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
354 | ] | ||
355 | |||
356 | [[package]] | ||
357 | name = "rusty-fork" | ||
358 | version = "0.2.1" | ||
359 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
360 | dependencies = [ | ||
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]] | ||
368 | name = "scopeguard" | ||
369 | version = "0.3.3" | ||
370 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
371 | |||
372 | [[package]] | ||
373 | name = "semver" | ||
374 | version = "0.9.0" | ||
375 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
376 | dependencies = [ | ||
377 | "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
378 | ] | ||
379 | |||
380 | [[package]] | ||
381 | name = "semver-parser" | ||
382 | version = "0.7.0" | ||
383 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
384 | |||
385 | [[package]] | ||
386 | name = "smallvec" | ||
387 | version = "0.6.7" | ||
388 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
389 | dependencies = [ | ||
390 | "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", | ||
391 | ] | ||
392 | |||
393 | [[package]] | ||
394 | name = "smol_str" | ||
395 | version = "0.1.8" | ||
396 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
397 | |||
398 | [[package]] | ||
399 | name = "stable_deref_trait" | ||
400 | version = "1.1.1" | ||
401 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
402 | |||
403 | [[package]] | ||
404 | name = "tempfile" | ||
405 | version = "3.0.5" | ||
406 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
407 | dependencies = [ | ||
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]] | ||
417 | name = "text_unit" | ||
418 | version = "0.1.5" | ||
419 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
420 | |||
421 | [[package]] | ||
422 | name = "ucd-util" | ||
423 | version = "0.1.3" | ||
424 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
425 | |||
426 | [[package]] | ||
427 | name = "unicode-xid" | ||
428 | version = "0.1.0" | ||
429 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
430 | |||
431 | [[package]] | ||
432 | name = "unreachable" | ||
433 | version = "1.0.0" | ||
434 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
435 | dependencies = [ | ||
436 | "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", | ||
437 | ] | ||
438 | |||
439 | [[package]] | ||
440 | name = "void" | ||
441 | version = "1.0.2" | ||
442 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
443 | |||
444 | [[package]] | ||
445 | name = "wait-timeout" | ||
446 | version = "0.1.5" | ||
447 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
448 | dependencies = [ | ||
449 | "libc 0.2.45 (registry+https://github.com/rust-lang/crates.io-index)", | ||
450 | ] | ||
451 | |||
452 | [[package]] | ||
453 | name = "winapi" | ||
454 | version = "0.3.6" | ||
455 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
456 | dependencies = [ | ||
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]] | ||
462 | name = "winapi-i686-pc-windows-gnu" | ||
463 | version = "0.4.0" | ||
464 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
465 | |||
466 | [[package]] | ||
467 | name = "winapi-x86_64-pc-windows-gnu" | ||
468 | version = "0.4.0" | ||
469 | source = "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> { | |||
1838 | impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {} | 1838 | impl<'a> ast::LoopBodyOwner<'a> for LoopExpr<'a> {} |
1839 | impl<'a> LoopExpr<'a> {} | 1839 | impl<'a> LoopExpr<'a> {} |
1840 | 1840 | ||
1841 | // MacroCall | ||
1842 | #[derive(Debug, Clone, Copy,)] | ||
1843 | pub struct MacroCallNode<R: TreeRoot<RaTypes> = OwnedRoot> { | ||
1844 | pub(crate) syntax: SyntaxNode<R>, | ||
1845 | } | ||
1846 | pub type MacroCall<'a> = MacroCallNode<RefRoot<'a>>; | ||
1847 | |||
1848 | impl<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 | } | ||
1851 | impl<R: TreeRoot<RaTypes>> Eq for MacroCallNode<R> {} | ||
1852 | impl<R: TreeRoot<RaTypes>> Hash for MacroCallNode<R> { | ||
1853 | fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) } | ||
1854 | } | ||
1855 | |||
1856 | impl<'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 | |||
1866 | impl<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 | |||
1876 | impl<'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,)] |
1843 | pub struct MatchArmNode<R: TreeRoot<RaTypes> = OwnedRoot> { | 1888 | pub 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 | ||
71 | fn validate_ascii_escape(text: &str, range: TextRange, errors: &mut Vec<SyntaxError>) { | 71 | fn 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}; | |||
8 | use thread_worker::{WorkerHandle}; | 8 | use thread_worker::{WorkerHandle}; |
9 | use relative_path::RelativePathBuf; | 9 | use relative_path::RelativePathBuf; |
10 | 10 | ||
11 | use crate::VfsRoot; | 11 | use crate::{VfsRoot, has_rs_extension}; |
12 | 12 | ||
13 | pub(crate) struct Task { | 13 | pub(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}; | |||
29 | use relative_path::RelativePathBuf; | 31 | use relative_path::RelativePathBuf; |
30 | use crossbeam_channel::Receiver; | 32 | use crossbeam_channel::Receiver; |
31 | use walkdir::DirEntry; | 33 | use walkdir::DirEntry; |
32 | use thread_worker::{WorkerHandle}; | 34 | use thread_worker::WorkerHandle; |
33 | 35 | ||
34 | use crate::{ | 36 | use 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] |
8 | difference = "2.0.0" | 8 | difference = "2.0.0" |
9 | itertools = "0.7.8" | 9 | itertools = "0.8.0" |
10 | text_unit = "0.1.2" | 10 | text_unit = "0.1.2" |
11 | serde_json = "1.0.24" | 11 | serde_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] |
9 | teraron = "0.0.1" | 9 | teraron = "0.0.1" |
10 | walkdir = "2.1.3" | 10 | walkdir = "2.1.3" |
11 | itertools = "0.7.8" | 11 | itertools = "0.8.0" |
12 | clap = "2.32.0" | 12 | clap = "2.32.0" |
13 | failure = "0.1.1" | 13 | failure = "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 | ||
120 | fn install_rustfmt() -> Result<()> { | 120 | pub 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), |