diff options
23 files changed, 1549 insertions, 149 deletions
diff --git a/Cargo.lock b/Cargo.lock index 8dcea6ea4..f1847e769 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -168,9 +168,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" | |||
168 | 168 | ||
169 | [[package]] | 169 | [[package]] |
170 | name = "chalk-derive" | 170 | name = "chalk-derive" |
171 | version = "0.37.0" | 171 | version = "0.43.0" |
172 | source = "registry+https://github.com/rust-lang/crates.io-index" | 172 | source = "registry+https://github.com/rust-lang/crates.io-index" |
173 | checksum = "564b529b0d620da43dc7ea46fa95b5c602e783e1870aeb07e8cbb6d7ff71bee6" | 173 | checksum = "e2d9e0c8adcced1ab0fea5cb8a38647922893d5b495e363e1814299fd380469b" |
174 | dependencies = [ | 174 | dependencies = [ |
175 | "proc-macro2", | 175 | "proc-macro2", |
176 | "quote", | 176 | "quote", |
@@ -180,9 +180,9 @@ dependencies = [ | |||
180 | 180 | ||
181 | [[package]] | 181 | [[package]] |
182 | name = "chalk-ir" | 182 | name = "chalk-ir" |
183 | version = "0.37.0" | 183 | version = "0.43.0" |
184 | source = "registry+https://github.com/rust-lang/crates.io-index" | 184 | source = "registry+https://github.com/rust-lang/crates.io-index" |
185 | checksum = "e67d29482387f4cbed6d8f1b1f7d24f00ff10612c700c65fe4af220df11e4d24" | 185 | checksum = "c5218266a5709bc4943de997e64d3fab41c9e9f68efd54a898de53135e987bd3" |
186 | dependencies = [ | 186 | dependencies = [ |
187 | "chalk-derive", | 187 | "chalk-derive", |
188 | "lazy_static", | 188 | "lazy_static", |
@@ -190,9 +190,9 @@ dependencies = [ | |||
190 | 190 | ||
191 | [[package]] | 191 | [[package]] |
192 | name = "chalk-recursive" | 192 | name = "chalk-recursive" |
193 | version = "0.37.0" | 193 | version = "0.43.0" |
194 | source = "registry+https://github.com/rust-lang/crates.io-index" | 194 | source = "registry+https://github.com/rust-lang/crates.io-index" |
195 | checksum = "5c52032be6fbdf91b6a7df3cafba3a6683fdabeff88e7ab73eea96e28657d973" | 195 | checksum = "ed8f34f13fd4f30251f9f6f1dc56f80363201390ecbcac2fdfc8e33036cd9c4a" |
196 | dependencies = [ | 196 | dependencies = [ |
197 | "chalk-derive", | 197 | "chalk-derive", |
198 | "chalk-ir", | 198 | "chalk-ir", |
@@ -203,9 +203,9 @@ dependencies = [ | |||
203 | 203 | ||
204 | [[package]] | 204 | [[package]] |
205 | name = "chalk-solve" | 205 | name = "chalk-solve" |
206 | version = "0.37.0" | 206 | version = "0.43.0" |
207 | source = "registry+https://github.com/rust-lang/crates.io-index" | 207 | source = "registry+https://github.com/rust-lang/crates.io-index" |
208 | checksum = "e0378bdfe1547b6fd545f518373b08c1e0c14920f7555a62d049021250a2b89b" | 208 | checksum = "379c9f584488346044709d4c638c38d61a06fe593d4de2ac5f15fd2b0ba4cd9d" |
209 | dependencies = [ | 209 | dependencies = [ |
210 | "chalk-derive", | 210 | "chalk-derive", |
211 | "chalk-ir", | 211 | "chalk-ir", |
diff --git a/crates/assists/src/handlers/early_return.rs b/crates/assists/src/handlers/early_return.rs index 7fd78e9d4..7bcc318a9 100644 --- a/crates/assists/src/handlers/early_return.rs +++ b/crates/assists/src/handlers/early_return.rs | |||
@@ -112,7 +112,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) | |||
112 | let then_branch = | 112 | let then_branch = |
113 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); | 113 | make::block_expr(once(make::expr_stmt(early_expression).into()), None); |
114 | let cond = invert_boolean_expression(cond_expr); | 114 | let cond = invert_boolean_expression(cond_expr); |
115 | make::expr_if(make::condition(cond, None), then_branch) | 115 | make::expr_if(make::condition(cond, None), then_branch, None) |
116 | .indent(if_indent_level) | 116 | .indent(if_indent_level) |
117 | }; | 117 | }; |
118 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) | 118 | replace(new_expr.syntax(), &then_block, &parent_block, &if_expr) |
diff --git a/crates/assists/src/handlers/move_guard.rs b/crates/assists/src/handlers/move_guard.rs index e1855b63d..eaffd80ce 100644 --- a/crates/assists/src/handlers/move_guard.rs +++ b/crates/assists/src/handlers/move_guard.rs | |||
@@ -42,6 +42,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext) -> | |||
42 | let if_expr = make::expr_if( | 42 | let if_expr = make::expr_if( |
43 | make::condition(guard_condition, None), | 43 | make::condition(guard_condition, None), |
44 | make::block_expr(None, Some(arm_expr.clone())), | 44 | make::block_expr(None, Some(arm_expr.clone())), |
45 | None, | ||
45 | ) | 46 | ) |
46 | .indent(arm_expr.indent_level()); | 47 | .indent(arm_expr.indent_level()); |
47 | 48 | ||
diff --git a/crates/assists/src/handlers/replace_if_let_with_match.rs b/crates/assists/src/handlers/replace_if_let_with_match.rs index 9a49c48c1..4a355c66f 100644 --- a/crates/assists/src/handlers/replace_if_let_with_match.rs +++ b/crates/assists/src/handlers/replace_if_let_with_match.rs | |||
@@ -1,3 +1,6 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use ide_db::{ty_filter::TryEnum, RootDatabase}; | ||
1 | use syntax::{ | 4 | use syntax::{ |
2 | ast::{ | 5 | ast::{ |
3 | self, | 6 | self, |
@@ -8,7 +11,6 @@ use syntax::{ | |||
8 | }; | 11 | }; |
9 | 12 | ||
10 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; | 13 | use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists}; |
11 | use ide_db::ty_filter::TryEnum; | ||
12 | 14 | ||
13 | // Assist: replace_if_let_with_match | 15 | // Assist: replace_if_let_with_match |
14 | // | 16 | // |
@@ -79,6 +81,91 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext) | |||
79 | ) | 81 | ) |
80 | } | 82 | } |
81 | 83 | ||
84 | // Assist: replace_match_with_if_let | ||
85 | // | ||
86 | // Replaces a binary `match` with a wildcard pattern and no guards with an `if let` expression. | ||
87 | // | ||
88 | // ``` | ||
89 | // enum Action { Move { distance: u32 }, Stop } | ||
90 | // | ||
91 | // fn handle(action: Action) { | ||
92 | // <|>match action { | ||
93 | // Action::Move { distance } => foo(distance), | ||
94 | // _ => bar(), | ||
95 | // } | ||
96 | // } | ||
97 | // ``` | ||
98 | // -> | ||
99 | // ``` | ||
100 | // enum Action { Move { distance: u32 }, Stop } | ||
101 | // | ||
102 | // fn handle(action: Action) { | ||
103 | // if let Action::Move { distance } = action { | ||
104 | // foo(distance) | ||
105 | // } else { | ||
106 | // bar() | ||
107 | // } | ||
108 | // } | ||
109 | // ``` | ||
110 | pub(crate) fn replace_match_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
111 | let match_expr: ast::MatchExpr = ctx.find_node_at_offset()?; | ||
112 | let mut arms = match_expr.match_arm_list()?.arms(); | ||
113 | let first_arm = arms.next()?; | ||
114 | let second_arm = arms.next()?; | ||
115 | if arms.next().is_some() || first_arm.guard().is_some() || second_arm.guard().is_some() { | ||
116 | return None; | ||
117 | } | ||
118 | let condition_expr = match_expr.expr()?; | ||
119 | let (if_let_pat, then_expr, else_expr) = if is_pat_wildcard_or_sad(&ctx.sema, &first_arm.pat()?) | ||
120 | { | ||
121 | (second_arm.pat()?, second_arm.expr()?, first_arm.expr()?) | ||
122 | } else if is_pat_wildcard_or_sad(&ctx.sema, &second_arm.pat()?) { | ||
123 | (first_arm.pat()?, first_arm.expr()?, second_arm.expr()?) | ||
124 | } else { | ||
125 | return None; | ||
126 | }; | ||
127 | |||
128 | let target = match_expr.syntax().text_range(); | ||
129 | acc.add( | ||
130 | AssistId("replace_match_with_if_let", AssistKind::RefactorRewrite), | ||
131 | "Replace with if let", | ||
132 | target, | ||
133 | move |edit| { | ||
134 | let condition = make::condition(condition_expr, Some(if_let_pat)); | ||
135 | let then_block = match then_expr.reset_indent() { | ||
136 | ast::Expr::BlockExpr(block) => block, | ||
137 | expr => make::block_expr(iter::empty(), Some(expr)), | ||
138 | }; | ||
139 | let else_expr = match else_expr { | ||
140 | ast::Expr::BlockExpr(block) | ||
141 | if block.statements().count() == 0 && block.expr().is_none() => | ||
142 | { | ||
143 | None | ||
144 | } | ||
145 | ast::Expr::TupleExpr(tuple) if tuple.fields().count() == 0 => None, | ||
146 | expr => Some(expr), | ||
147 | }; | ||
148 | let if_let_expr = make::expr_if( | ||
149 | condition, | ||
150 | then_block, | ||
151 | else_expr.map(|else_expr| { | ||
152 | ast::ElseBranch::Block(make::block_expr(iter::empty(), Some(else_expr))) | ||
153 | }), | ||
154 | ) | ||
155 | .indent(IndentLevel::from_node(match_expr.syntax())); | ||
156 | |||
157 | edit.replace_ast::<ast::Expr>(match_expr.into(), if_let_expr); | ||
158 | }, | ||
159 | ) | ||
160 | } | ||
161 | |||
162 | fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -> bool { | ||
163 | sema.type_of_pat(&pat) | ||
164 | .and_then(|ty| TryEnum::from_ty(sema, &ty)) | ||
165 | .map(|it| it.sad_pattern().syntax().text() == pat.syntax().text()) | ||
166 | .unwrap_or_else(|| matches!(pat, ast::Pat::WildcardPat(_))) | ||
167 | } | ||
168 | |||
82 | #[cfg(test)] | 169 | #[cfg(test)] |
83 | mod tests { | 170 | mod tests { |
84 | use super::*; | 171 | use super::*; |
@@ -252,4 +339,192 @@ fn main() { | |||
252 | "#, | 339 | "#, |
253 | ) | 340 | ) |
254 | } | 341 | } |
342 | |||
343 | #[test] | ||
344 | fn test_replace_match_with_if_let_unwraps_simple_expressions() { | ||
345 | check_assist( | ||
346 | replace_match_with_if_let, | ||
347 | r#" | ||
348 | impl VariantData { | ||
349 | pub fn is_struct(&self) -> bool { | ||
350 | <|>match *self { | ||
351 | VariantData::Struct(..) => true, | ||
352 | _ => false, | ||
353 | } | ||
354 | } | ||
355 | } "#, | ||
356 | r#" | ||
357 | impl VariantData { | ||
358 | pub fn is_struct(&self) -> bool { | ||
359 | if let VariantData::Struct(..) = *self { | ||
360 | true | ||
361 | } else { | ||
362 | false | ||
363 | } | ||
364 | } | ||
365 | } "#, | ||
366 | ) | ||
367 | } | ||
368 | |||
369 | #[test] | ||
370 | fn test_replace_match_with_if_let_doesnt_unwrap_multiline_expressions() { | ||
371 | check_assist( | ||
372 | replace_match_with_if_let, | ||
373 | r#" | ||
374 | fn foo() { | ||
375 | <|>match a { | ||
376 | VariantData::Struct(..) => { | ||
377 | bar( | ||
378 | 123 | ||
379 | ) | ||
380 | } | ||
381 | _ => false, | ||
382 | } | ||
383 | } "#, | ||
384 | r#" | ||
385 | fn foo() { | ||
386 | if let VariantData::Struct(..) = a { | ||
387 | bar( | ||
388 | 123 | ||
389 | ) | ||
390 | } else { | ||
391 | false | ||
392 | } | ||
393 | } "#, | ||
394 | ) | ||
395 | } | ||
396 | |||
397 | #[test] | ||
398 | fn replace_match_with_if_let_target() { | ||
399 | check_assist_target( | ||
400 | replace_match_with_if_let, | ||
401 | r#" | ||
402 | impl VariantData { | ||
403 | pub fn is_struct(&self) -> bool { | ||
404 | <|>match *self { | ||
405 | VariantData::Struct(..) => true, | ||
406 | _ => false, | ||
407 | } | ||
408 | } | ||
409 | } "#, | ||
410 | r#"match *self { | ||
411 | VariantData::Struct(..) => true, | ||
412 | _ => false, | ||
413 | }"#, | ||
414 | ); | ||
415 | } | ||
416 | |||
417 | #[test] | ||
418 | fn special_case_option_match_to_if_let() { | ||
419 | check_assist( | ||
420 | replace_match_with_if_let, | ||
421 | r#" | ||
422 | enum Option<T> { Some(T), None } | ||
423 | use Option::*; | ||
424 | |||
425 | fn foo(x: Option<i32>) { | ||
426 | <|>match x { | ||
427 | Some(x) => println!("{}", x), | ||
428 | None => println!("none"), | ||
429 | } | ||
430 | } | ||
431 | "#, | ||
432 | r#" | ||
433 | enum Option<T> { Some(T), None } | ||
434 | use Option::*; | ||
435 | |||
436 | fn foo(x: Option<i32>) { | ||
437 | if let Some(x) = x { | ||
438 | println!("{}", x) | ||
439 | } else { | ||
440 | println!("none") | ||
441 | } | ||
442 | } | ||
443 | "#, | ||
444 | ); | ||
445 | } | ||
446 | |||
447 | #[test] | ||
448 | fn special_case_result_match_to_if_let() { | ||
449 | check_assist( | ||
450 | replace_match_with_if_let, | ||
451 | r#" | ||
452 | enum Result<T, E> { Ok(T), Err(E) } | ||
453 | use Result::*; | ||
454 | |||
455 | fn foo(x: Result<i32, ()>) { | ||
456 | <|>match x { | ||
457 | Ok(x) => println!("{}", x), | ||
458 | Err(_) => println!("none"), | ||
459 | } | ||
460 | } | ||
461 | "#, | ||
462 | r#" | ||
463 | enum Result<T, E> { Ok(T), Err(E) } | ||
464 | use Result::*; | ||
465 | |||
466 | fn foo(x: Result<i32, ()>) { | ||
467 | if let Ok(x) = x { | ||
468 | println!("{}", x) | ||
469 | } else { | ||
470 | println!("none") | ||
471 | } | ||
472 | } | ||
473 | "#, | ||
474 | ); | ||
475 | } | ||
476 | |||
477 | #[test] | ||
478 | fn nested_indent_match_to_if_let() { | ||
479 | check_assist( | ||
480 | replace_match_with_if_let, | ||
481 | r#" | ||
482 | fn main() { | ||
483 | if true { | ||
484 | <|>match path.strip_prefix(root_path) { | ||
485 | Ok(rel_path) => { | ||
486 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
487 | Some((*id, rel_path)) | ||
488 | } | ||
489 | _ => None, | ||
490 | } | ||
491 | } | ||
492 | } | ||
493 | "#, | ||
494 | r#" | ||
495 | fn main() { | ||
496 | if true { | ||
497 | if let Ok(rel_path) = path.strip_prefix(root_path) { | ||
498 | let rel_path = RelativePathBuf::from_path(rel_path).ok()?; | ||
499 | Some((*id, rel_path)) | ||
500 | } else { | ||
501 | None | ||
502 | } | ||
503 | } | ||
504 | } | ||
505 | "#, | ||
506 | ) | ||
507 | } | ||
508 | |||
509 | #[test] | ||
510 | fn replace_match_with_if_let_empty_wildcard_expr() { | ||
511 | check_assist( | ||
512 | replace_match_with_if_let, | ||
513 | r#" | ||
514 | fn main() { | ||
515 | <|>match path.strip_prefix(root_path) { | ||
516 | Ok(rel_path) => println!("{}", rel_path), | ||
517 | _ => (), | ||
518 | } | ||
519 | } | ||
520 | "#, | ||
521 | r#" | ||
522 | fn main() { | ||
523 | if let Ok(rel_path) = path.strip_prefix(root_path) { | ||
524 | println!("{}", rel_path) | ||
525 | } | ||
526 | } | ||
527 | "#, | ||
528 | ) | ||
529 | } | ||
255 | } | 530 | } |
diff --git a/crates/assists/src/handlers/replace_let_with_if_let.rs b/crates/assists/src/handlers/replace_let_with_if_let.rs index 69d3b08d3..5970e283c 100644 --- a/crates/assists/src/handlers/replace_let_with_if_let.rs +++ b/crates/assists/src/handlers/replace_let_with_if_let.rs | |||
@@ -60,7 +60,7 @@ pub(crate) fn replace_let_with_if_let(acc: &mut Assists, ctx: &AssistContext) -> | |||
60 | }; | 60 | }; |
61 | let block = | 61 | let block = |
62 | make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); | 62 | make::block_expr(None, None).indent(IndentLevel::from_node(let_stmt.syntax())); |
63 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); | 63 | let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block, None); |
64 | let stmt = make::expr_stmt(if_); | 64 | let stmt = make::expr_stmt(if_); |
65 | 65 | ||
66 | let placeholder = stmt.syntax().descendants().find_map(ast::WildcardPat::cast).unwrap(); | 66 | let placeholder = stmt.syntax().descendants().find_map(ast::WildcardPat::cast).unwrap(); |
diff --git a/crates/assists/src/lib.rs b/crates/assists/src/lib.rs index dfe6c2729..b8ce7418d 100644 --- a/crates/assists/src/lib.rs +++ b/crates/assists/src/lib.rs | |||
@@ -209,6 +209,7 @@ mod handlers { | |||
209 | reorder_fields::reorder_fields, | 209 | reorder_fields::reorder_fields, |
210 | replace_derive_with_manual_impl::replace_derive_with_manual_impl, | 210 | replace_derive_with_manual_impl::replace_derive_with_manual_impl, |
211 | replace_if_let_with_match::replace_if_let_with_match, | 211 | replace_if_let_with_match::replace_if_let_with_match, |
212 | replace_if_let_with_match::replace_match_with_if_let, | ||
212 | replace_impl_trait_with_generic::replace_impl_trait_with_generic, | 213 | replace_impl_trait_with_generic::replace_impl_trait_with_generic, |
213 | replace_let_with_if_let::replace_let_with_if_let, | 214 | replace_let_with_if_let::replace_let_with_if_let, |
214 | replace_qualified_name_with_use::replace_qualified_name_with_use, | 215 | replace_qualified_name_with_use::replace_qualified_name_with_use, |
diff --git a/crates/assists/src/tests/generated.rs b/crates/assists/src/tests/generated.rs index 8d50c8791..853bde09c 100644 --- a/crates/assists/src/tests/generated.rs +++ b/crates/assists/src/tests/generated.rs | |||
@@ -890,6 +890,34 @@ fn compute() -> Option<i32> { None } | |||
890 | } | 890 | } |
891 | 891 | ||
892 | #[test] | 892 | #[test] |
893 | fn doctest_replace_match_with_if_let() { | ||
894 | check_doc_test( | ||
895 | "replace_match_with_if_let", | ||
896 | r#####" | ||
897 | enum Action { Move { distance: u32 }, Stop } | ||
898 | |||
899 | fn handle(action: Action) { | ||
900 | <|>match action { | ||
901 | Action::Move { distance } => foo(distance), | ||
902 | _ => bar(), | ||
903 | } | ||
904 | } | ||
905 | "#####, | ||
906 | r#####" | ||
907 | enum Action { Move { distance: u32 }, Stop } | ||
908 | |||
909 | fn handle(action: Action) { | ||
910 | if let Action::Move { distance } = action { | ||
911 | foo(distance) | ||
912 | } else { | ||
913 | bar() | ||
914 | } | ||
915 | } | ||
916 | "#####, | ||
917 | ) | ||
918 | } | ||
919 | |||
920 | #[test] | ||
893 | fn doctest_replace_qualified_name_with_use() { | 921 | fn doctest_replace_qualified_name_with_use() { |
894 | check_doc_test( | 922 | check_doc_test( |
895 | "replace_qualified_name_with_use", | 923 | "replace_qualified_name_with_use", |
diff --git a/crates/hir_ty/Cargo.toml b/crates/hir_ty/Cargo.toml index cf5c38a23..289e812fe 100644 --- a/crates/hir_ty/Cargo.toml +++ b/crates/hir_ty/Cargo.toml | |||
@@ -17,9 +17,9 @@ ena = "0.14.0" | |||
17 | log = "0.4.8" | 17 | log = "0.4.8" |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | scoped-tls = "1" | 19 | scoped-tls = "1" |
20 | chalk-solve = { version = "0.37", default-features = false } | 20 | chalk-solve = { version = "0.43", default-features = false } |
21 | chalk-ir = "0.37" | 21 | chalk-ir = "0.43" |
22 | chalk-recursive = "0.37" | 22 | chalk-recursive = "0.43" |
23 | 23 | ||
24 | stdx = { path = "../stdx", version = "0.0.0" } | 24 | stdx = { path = "../stdx", version = "0.0.0" } |
25 | hir_def = { path = "../hir_def", version = "0.0.0" } | 25 | hir_def = { path = "../hir_def", version = "0.0.0" } |
diff --git a/crates/hir_ty/src/db.rs b/crates/hir_ty/src/db.rs index 25cf9eb7f..66bdb8e88 100644 --- a/crates/hir_ty/src/db.rs +++ b/crates/hir_ty/src/db.rs | |||
@@ -99,6 +99,12 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> { | |||
99 | #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] | 99 | #[salsa::invoke(crate::traits::chalk::fn_def_datum_query)] |
100 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>; | 100 | fn fn_def_datum(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> Arc<chalk::FnDefDatum>; |
101 | 101 | ||
102 | #[salsa::invoke(crate::traits::chalk::fn_def_variance_query)] | ||
103 | fn fn_def_variance(&self, krate: CrateId, fn_def_id: chalk::FnDefId) -> chalk::Variances; | ||
104 | |||
105 | #[salsa::invoke(crate::traits::chalk::adt_variance_query)] | ||
106 | fn adt_variance(&self, krate: CrateId, adt_id: chalk::AdtId) -> chalk::Variances; | ||
107 | |||
102 | #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] | 108 | #[salsa::invoke(crate::traits::chalk::associated_ty_value_query)] |
103 | fn associated_ty_value( | 109 | fn associated_ty_value( |
104 | &self, | 110 | &self, |
diff --git a/crates/hir_ty/src/tests/regression.rs b/crates/hir_ty/src/tests/regression.rs index 94d86b0d1..8cf4e7012 100644 --- a/crates/hir_ty/src/tests/regression.rs +++ b/crates/hir_ty/src/tests/regression.rs | |||
@@ -840,3 +840,46 @@ fn issue_4966() { | |||
840 | "#]], | 840 | "#]], |
841 | ); | 841 | ); |
842 | } | 842 | } |
843 | |||
844 | #[test] | ||
845 | fn issue_6628() { | ||
846 | check_infer( | ||
847 | r#" | ||
848 | #[lang = "fn_once"] | ||
849 | pub trait FnOnce<Args> { | ||
850 | type Output; | ||
851 | } | ||
852 | |||
853 | struct S<T>(); | ||
854 | impl<T> S<T> { | ||
855 | fn f(&self, _t: T) {} | ||
856 | fn g<F: FnOnce(&T)>(&self, _f: F) {} | ||
857 | } | ||
858 | fn main() { | ||
859 | let s = S(); | ||
860 | s.g(|_x| {}); | ||
861 | s.f(10); | ||
862 | } | ||
863 | "#, | ||
864 | expect![[r#" | ||
865 | 105..109 'self': &S<T> | ||
866 | 111..113 '_t': T | ||
867 | 118..120 '{}': () | ||
868 | 146..150 'self': &S<T> | ||
869 | 152..154 '_f': F | ||
870 | 159..161 '{}': () | ||
871 | 174..225 '{ ...10); }': () | ||
872 | 184..185 's': S<i32> | ||
873 | 188..189 'S': S<i32>() -> S<i32> | ||
874 | 188..191 'S()': S<i32> | ||
875 | 197..198 's': S<i32> | ||
876 | 197..209 's.g(|_x| {})': () | ||
877 | 201..208 '|_x| {}': |&i32| -> () | ||
878 | 202..204 '_x': &i32 | ||
879 | 206..208 '{}': () | ||
880 | 215..216 's': S<i32> | ||
881 | 215..222 's.f(10)': () | ||
882 | 219..221 '10': i32 | ||
883 | "#]], | ||
884 | ); | ||
885 | } | ||
diff --git a/crates/hir_ty/src/traits.rs b/crates/hir_ty/src/traits.rs index ce1174cbe..dfa51896b 100644 --- a/crates/hir_ty/src/traits.rs +++ b/crates/hir_ty/src/traits.rs | |||
@@ -1,4 +1,5 @@ | |||
1 | //! Trait solving using Chalk. | 1 | //! Trait solving using Chalk. |
2 | use std::env::var; | ||
2 | use std::sync::Arc; | 3 | use std::sync::Arc; |
3 | 4 | ||
4 | use base_db::CrateId; | 5 | use base_db::CrateId; |
@@ -15,12 +16,6 @@ use self::chalk::{from_chalk, Interner, ToChalk}; | |||
15 | 16 | ||
16 | pub(crate) mod chalk; | 17 | pub(crate) mod chalk; |
17 | 18 | ||
18 | // This controls the maximum size of types Chalk considers. If we set this too | ||
19 | // high, we can run into slow edge cases; if we set it too low, Chalk won't | ||
20 | // find some solutions. | ||
21 | // FIXME this is currently hardcoded in the recursive solver | ||
22 | // const CHALK_SOLVER_MAX_SIZE: usize = 10; | ||
23 | |||
24 | /// This controls how much 'time' we give the Chalk solver before giving up. | 19 | /// This controls how much 'time' we give the Chalk solver before giving up. |
25 | const CHALK_SOLVER_FUEL: i32 = 100; | 20 | const CHALK_SOLVER_FUEL: i32 = 100; |
26 | 21 | ||
@@ -31,9 +26,11 @@ struct ChalkContext<'a> { | |||
31 | } | 26 | } |
32 | 27 | ||
33 | fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { | 28 | fn create_chalk_solver() -> chalk_recursive::RecursiveSolver<Interner> { |
34 | let overflow_depth = 100; | 29 | let overflow_depth = |
30 | var("CHALK_OVERFLOW_DEPTH").ok().and_then(|s| s.parse().ok()).unwrap_or(100); | ||
35 | let caching_enabled = true; | 31 | let caching_enabled = true; |
36 | chalk_recursive::RecursiveSolver::new(overflow_depth, caching_enabled) | 32 | let max_size = var("CHALK_SOLVER_MAX_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(30); |
33 | chalk_recursive::RecursiveSolver::new(overflow_depth, max_size, caching_enabled) | ||
37 | } | 34 | } |
38 | 35 | ||
39 | /// A set of clauses that we assume to be true. E.g. if we are inside this function: | 36 | /// A set of clauses that we assume to be true. E.g. if we are inside this function: |
diff --git a/crates/hir_ty/src/traits/chalk.rs b/crates/hir_ty/src/traits/chalk.rs index 55e2c3a3e..69eae6f79 100644 --- a/crates/hir_ty/src/traits/chalk.rs +++ b/crates/hir_ty/src/traits/chalk.rs | |||
@@ -104,7 +104,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
104 | }; | 104 | }; |
105 | 105 | ||
106 | // Note: Since we're using impls_for_trait, only impls where the trait | 106 | // Note: Since we're using impls_for_trait, only impls where the trait |
107 | // can be resolved should ever reach Chalk. `impl_datum` relies on that | 107 | // can be resolved should ever reach Chalk. Symbol’s value as variable is void: impl_datum relies on that |
108 | // and will panic if the trait can't be resolved. | 108 | // and will panic if the trait can't be resolved. |
109 | let in_deps = self.db.trait_impls_in_deps(self.krate); | 109 | let in_deps = self.db.trait_impls_in_deps(self.krate); |
110 | let in_self = self.db.trait_impls_in_crate(self.krate); | 110 | let in_self = self.db.trait_impls_in_crate(self.krate); |
@@ -206,7 +206,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
206 | Some((trait_, alias)) | 206 | Some((trait_, alias)) |
207 | }) | 207 | }) |
208 | { | 208 | { |
209 | // Making up `AsyncBlock<T>: Future<Output = T>` | 209 | // Making up Symbol’s value as variable is void: AsyncBlock<T>: |
210 | // | 210 | // |
211 | // |--------------------OpaqueTyDatum-------------------| | 211 | // |--------------------OpaqueTyDatum-------------------| |
212 | // |-------------OpaqueTyDatumBound--------------| | 212 | // |-------------OpaqueTyDatumBound--------------| |
@@ -242,7 +242,7 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
242 | // The opaque type has 1 parameter. | 242 | // The opaque type has 1 parameter. |
243 | make_binders(bound, 1) | 243 | make_binders(bound, 1) |
244 | } else { | 244 | } else { |
245 | // If failed to find `Future::Output`, return empty bounds as fallback. | 245 | // If failed to find Symbol’s value as variable is void: Future::Output, return empty bounds as fallback. |
246 | let bound = OpaqueTyDatumBound { | 246 | let bound = OpaqueTyDatumBound { |
247 | bounds: make_binders(vec![], 0), | 247 | bounds: make_binders(vec![], 0), |
248 | where_clauses: make_binders(vec![], 0), | 248 | where_clauses: make_binders(vec![], 0), |
@@ -343,6 +343,23 @@ impl<'a> chalk_solve::RustIrDatabase<Interner> for ChalkContext<'a> { | |||
343 | // FIXME | 343 | // FIXME |
344 | unimplemented!() | 344 | unimplemented!() |
345 | } | 345 | } |
346 | |||
347 | fn unification_database(&self) -> &dyn chalk_ir::UnificationDatabase<Interner> { | ||
348 | self | ||
349 | } | ||
350 | } | ||
351 | |||
352 | impl<'a> chalk_ir::UnificationDatabase<Interner> for ChalkContext<'a> { | ||
353 | fn fn_def_variance( | ||
354 | &self, | ||
355 | fn_def_id: chalk_ir::FnDefId<Interner>, | ||
356 | ) -> chalk_ir::Variances<Interner> { | ||
357 | self.db.fn_def_variance(self.krate, fn_def_id) | ||
358 | } | ||
359 | |||
360 | fn adt_variance(&self, adt_id: chalk_ir::AdtId<Interner>) -> chalk_ir::Variances<Interner> { | ||
361 | self.db.adt_variance(self.krate, adt_id) | ||
362 | } | ||
346 | } | 363 | } |
347 | 364 | ||
348 | pub(crate) fn program_clauses_for_chalk_env_query( | 365 | pub(crate) fn program_clauses_for_chalk_env_query( |
@@ -644,6 +661,32 @@ pub(crate) fn fn_def_datum_query( | |||
644 | Arc::new(datum) | 661 | Arc::new(datum) |
645 | } | 662 | } |
646 | 663 | ||
664 | pub(crate) fn fn_def_variance_query( | ||
665 | db: &dyn HirDatabase, | ||
666 | _krate: CrateId, | ||
667 | fn_def_id: FnDefId, | ||
668 | ) -> Variances { | ||
669 | let callable_def: CallableDefId = from_chalk(db, fn_def_id); | ||
670 | let generic_params = generics(db.upcast(), callable_def.into()); | ||
671 | Variances::from( | ||
672 | &Interner, | ||
673 | std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), | ||
674 | ) | ||
675 | } | ||
676 | |||
677 | pub(crate) fn adt_variance_query( | ||
678 | db: &dyn HirDatabase, | ||
679 | _krate: CrateId, | ||
680 | adt_id: AdtId, | ||
681 | ) -> Variances { | ||
682 | let adt: crate::AdtId = from_chalk(db, adt_id); | ||
683 | let generic_params = generics(db.upcast(), adt.into()); | ||
684 | Variances::from( | ||
685 | &Interner, | ||
686 | std::iter::repeat(chalk_ir::Variance::Invariant).take(generic_params.len()), | ||
687 | ) | ||
688 | } | ||
689 | |||
647 | impl From<FnDefId> for crate::db::InternedCallableDefId { | 690 | impl From<FnDefId> for crate::db::InternedCallableDefId { |
648 | fn from(fn_def_id: FnDefId) -> Self { | 691 | fn from(fn_def_id: FnDefId) -> Self { |
649 | InternKey::from_intern_id(fn_def_id.0) | 692 | InternKey::from_intern_id(fn_def_id.0) |
diff --git a/crates/hir_ty/src/traits/chalk/interner.rs b/crates/hir_ty/src/traits/chalk/interner.rs index 39569e690..6a4aa8333 100644 --- a/crates/hir_ty/src/traits/chalk/interner.rs +++ b/crates/hir_ty/src/traits/chalk/interner.rs | |||
@@ -25,6 +25,7 @@ pub(crate) type FnDefId = chalk_ir::FnDefId<Interner>; | |||
25 | pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; | 25 | pub(crate) type FnDefDatum = chalk_solve::rust_ir::FnDefDatum<Interner>; |
26 | pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; | 26 | pub(crate) type OpaqueTyId = chalk_ir::OpaqueTyId<Interner>; |
27 | pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; | 27 | pub(crate) type OpaqueTyDatum = chalk_solve::rust_ir::OpaqueTyDatum<Interner>; |
28 | pub(crate) type Variances = chalk_ir::Variances<Interner>; | ||
28 | 29 | ||
29 | impl chalk_ir::interner::Interner for Interner { | 30 | impl chalk_ir::interner::Interner for Interner { |
30 | type InternedType = Arc<chalk_ir::TyData<Self>>; | 31 | type InternedType = Arc<chalk_ir::TyData<Self>>; |
@@ -41,6 +42,7 @@ impl chalk_ir::interner::Interner for Interner { | |||
41 | type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>; | 42 | type InternedVariableKinds = Vec<chalk_ir::VariableKind<Self>>; |
42 | type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>; | 43 | type InternedCanonicalVarKinds = Vec<chalk_ir::CanonicalVarKind<Self>>; |
43 | type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>; | 44 | type InternedConstraints = Vec<chalk_ir::InEnvironment<chalk_ir::Constraint<Self>>>; |
45 | type InternedVariances = Arc<[chalk_ir::Variance]>; | ||
44 | type DefId = InternId; | 46 | type DefId = InternId; |
45 | type InternedAdtId = hir_def::AdtId; | 47 | type InternedAdtId = hir_def::AdtId; |
46 | type Identifier = TypeAliasId; | 48 | type Identifier = TypeAliasId; |
@@ -370,6 +372,20 @@ impl chalk_ir::interner::Interner for Interner { | |||
370 | ) -> Option<fmt::Result> { | 372 | ) -> Option<fmt::Result> { |
371 | None | 373 | None |
372 | } | 374 | } |
375 | |||
376 | fn intern_variances<E>( | ||
377 | &self, | ||
378 | data: impl IntoIterator<Item = Result<chalk_ir::Variance, E>>, | ||
379 | ) -> Result<Self::InternedVariances, E> { | ||
380 | data.into_iter().collect() | ||
381 | } | ||
382 | |||
383 | fn variances_data<'a>( | ||
384 | &self, | ||
385 | variances: &'a Self::InternedVariances, | ||
386 | ) -> &'a [chalk_ir::Variance] { | ||
387 | &variances | ||
388 | } | ||
373 | } | 389 | } |
374 | 390 | ||
375 | impl chalk_ir::interner::HasInterner for Interner { | 391 | impl chalk_ir::interner::HasInterner for Interner { |
diff --git a/crates/hir_ty/src/traits/chalk/mapping.rs b/crates/hir_ty/src/traits/chalk/mapping.rs index 86cbc4c7e..8700d664e 100644 --- a/crates/hir_ty/src/traits/chalk/mapping.rs +++ b/crates/hir_ty/src/traits/chalk/mapping.rs | |||
@@ -31,7 +31,8 @@ impl ToChalk for Ty { | |||
31 | TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), | 31 | TypeCtor::Ref(m) => ref_to_chalk(db, m, apply_ty.parameters), |
32 | TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), | 32 | TypeCtor::Array => array_to_chalk(db, apply_ty.parameters), |
33 | TypeCtor::FnPtr { num_args: _, is_varargs } => { | 33 | TypeCtor::FnPtr { num_args: _, is_varargs } => { |
34 | let substitution = apply_ty.parameters.to_chalk(db).shifted_in(&Interner); | 34 | let substitution = |
35 | chalk_ir::FnSubst(apply_ty.parameters.to_chalk(db).shifted_in(&Interner)); | ||
35 | chalk_ir::TyKind::Function(chalk_ir::FnPointer { | 36 | chalk_ir::TyKind::Function(chalk_ir::FnPointer { |
36 | num_binders: 0, | 37 | num_binders: 0, |
37 | sig: chalk_ir::FnSig { | 38 | sig: chalk_ir::FnSig { |
@@ -183,7 +184,7 @@ impl ToChalk for Ty { | |||
183 | assert_eq!(num_binders, 0); | 184 | assert_eq!(num_binders, 0); |
184 | let parameters: Substs = from_chalk( | 185 | let parameters: Substs = from_chalk( |
185 | db, | 186 | db, |
186 | substitution.shifted_out(&Interner).expect("fn ptr should have no binders"), | 187 | substitution.0.shifted_out(&Interner).expect("fn ptr should have no binders"), |
187 | ); | 188 | ); |
188 | Ty::Apply(ApplicationTy { | 189 | Ty::Apply(ApplicationTy { |
189 | ctor: TypeCtor::FnPtr { | 190 | ctor: TypeCtor::FnPtr { |
@@ -536,6 +537,7 @@ impl ToChalk for GenericPredicate { | |||
536 | // we don't produce any where clauses with binders and can't currently deal with them | 537 | // we don't produce any where clauses with binders and can't currently deal with them |
537 | match where_clause | 538 | match where_clause |
538 | .skip_binders() | 539 | .skip_binders() |
540 | .clone() | ||
539 | .shifted_out(&Interner) | 541 | .shifted_out(&Interner) |
540 | .expect("unexpected bound vars in where clause") | 542 | .expect("unexpected bound vars in where clause") |
541 | { | 543 | { |
@@ -661,7 +663,12 @@ where | |||
661 | chalk_ir::TyVariableKind::Integer => TyKind::Integer, | 663 | chalk_ir::TyVariableKind::Integer => TyKind::Integer, |
662 | chalk_ir::TyVariableKind::Float => TyKind::Float, | 664 | chalk_ir::TyVariableKind::Float => TyKind::Float, |
663 | }, | 665 | }, |
664 | chalk_ir::VariableKind::Lifetime => panic!("unexpected lifetime from Chalk"), | 666 | // HACK: Chalk can sometimes return new lifetime variables. We |
667 | // want to just skip them, but to not mess up the indices of | ||
668 | // other variables, we'll just create a new type variable in | ||
669 | // their place instead. This should not matter (we never see the | ||
670 | // actual *uses* of the lifetime variable). | ||
671 | chalk_ir::VariableKind::Lifetime => TyKind::General, | ||
665 | chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"), | 672 | chalk_ir::VariableKind::Const(_) => panic!("unexpected const from Chalk"), |
666 | }) | 673 | }) |
667 | .collect(); | 674 | .collect(); |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index 72f6c5725..7576097b3 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt | |||
@@ -104,4 +104,168 @@ | |||
104 | }, | 104 | }, |
105 | fixes: [], | 105 | fixes: [], |
106 | }, | 106 | }, |
107 | MappedRustDiagnostic { | ||
108 | url: Url { | ||
109 | scheme: "file", | ||
110 | host: None, | ||
111 | port: None, | ||
112 | path: "/test/compiler/lib.rs", | ||
113 | query: None, | ||
114 | fragment: None, | ||
115 | }, | ||
116 | diagnostic: Diagnostic { | ||
117 | range: Range { | ||
118 | start: Position { | ||
119 | line: 0, | ||
120 | character: 8, | ||
121 | }, | ||
122 | end: Position { | ||
123 | line: 0, | ||
124 | character: 19, | ||
125 | }, | ||
126 | }, | ||
127 | severity: Some( | ||
128 | Hint, | ||
129 | ), | ||
130 | code: Some( | ||
131 | String( | ||
132 | "trivially_copy_pass_by_ref", | ||
133 | ), | ||
134 | ), | ||
135 | code_description: Some( | ||
136 | CodeDescription { | ||
137 | href: Url { | ||
138 | scheme: "https", | ||
139 | host: Some( | ||
140 | Domain( | ||
141 | "rust-lang.github.io", | ||
142 | ), | ||
143 | ), | ||
144 | port: None, | ||
145 | path: "/rust-clippy/master/index.html", | ||
146 | query: None, | ||
147 | fragment: Some( | ||
148 | "trivially_copy_pass_by_ref", | ||
149 | ), | ||
150 | }, | ||
151 | }, | ||
152 | ), | ||
153 | source: Some( | ||
154 | "clippy", | ||
155 | ), | ||
156 | message: "lint level defined here", | ||
157 | related_information: Some( | ||
158 | [ | ||
159 | DiagnosticRelatedInformation { | ||
160 | location: Location { | ||
161 | uri: Url { | ||
162 | scheme: "file", | ||
163 | host: None, | ||
164 | port: None, | ||
165 | path: "/test/compiler/mir/tagset.rs", | ||
166 | query: None, | ||
167 | fragment: None, | ||
168 | }, | ||
169 | range: Range { | ||
170 | start: Position { | ||
171 | line: 41, | ||
172 | character: 23, | ||
173 | }, | ||
174 | end: Position { | ||
175 | line: 41, | ||
176 | character: 28, | ||
177 | }, | ||
178 | }, | ||
179 | }, | ||
180 | message: "original diagnostic", | ||
181 | }, | ||
182 | ], | ||
183 | ), | ||
184 | tags: None, | ||
185 | data: None, | ||
186 | }, | ||
187 | fixes: [], | ||
188 | }, | ||
189 | MappedRustDiagnostic { | ||
190 | url: Url { | ||
191 | scheme: "file", | ||
192 | host: None, | ||
193 | port: None, | ||
194 | path: "/test/compiler/mir/tagset.rs", | ||
195 | query: None, | ||
196 | fragment: None, | ||
197 | }, | ||
198 | diagnostic: Diagnostic { | ||
199 | range: Range { | ||
200 | start: Position { | ||
201 | line: 41, | ||
202 | character: 23, | ||
203 | }, | ||
204 | end: Position { | ||
205 | line: 41, | ||
206 | character: 28, | ||
207 | }, | ||
208 | }, | ||
209 | severity: Some( | ||
210 | Hint, | ||
211 | ), | ||
212 | code: Some( | ||
213 | String( | ||
214 | "trivially_copy_pass_by_ref", | ||
215 | ), | ||
216 | ), | ||
217 | code_description: Some( | ||
218 | CodeDescription { | ||
219 | href: Url { | ||
220 | scheme: "https", | ||
221 | host: Some( | ||
222 | Domain( | ||
223 | "rust-lang.github.io", | ||
224 | ), | ||
225 | ), | ||
226 | port: None, | ||
227 | path: "/rust-clippy/master/index.html", | ||
228 | query: None, | ||
229 | fragment: Some( | ||
230 | "trivially_copy_pass_by_ref", | ||
231 | ), | ||
232 | }, | ||
233 | }, | ||
234 | ), | ||
235 | source: Some( | ||
236 | "clippy", | ||
237 | ), | ||
238 | message: "consider passing by value instead", | ||
239 | related_information: Some( | ||
240 | [ | ||
241 | DiagnosticRelatedInformation { | ||
242 | location: Location { | ||
243 | uri: Url { | ||
244 | scheme: "file", | ||
245 | host: None, | ||
246 | port: None, | ||
247 | path: "/test/compiler/mir/tagset.rs", | ||
248 | query: None, | ||
249 | fragment: None, | ||
250 | }, | ||
251 | range: Range { | ||
252 | start: Position { | ||
253 | line: 41, | ||
254 | character: 23, | ||
255 | }, | ||
256 | end: Position { | ||
257 | line: 41, | ||
258 | character: 28, | ||
259 | }, | ||
260 | }, | ||
261 | }, | ||
262 | message: "original diagnostic", | ||
263 | }, | ||
264 | ], | ||
265 | ), | ||
266 | tags: None, | ||
267 | data: None, | ||
268 | }, | ||
269 | fixes: [], | ||
270 | }, | ||
107 | ] | 271 | ] |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt index bbec6a796..bdcf2a38f 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/macro_compiler_error.txt | |||
@@ -4,19 +4,19 @@ | |||
4 | scheme: "file", | 4 | scheme: "file", |
5 | host: None, | 5 | host: None, |
6 | port: None, | 6 | port: None, |
7 | path: "/test/crates/hir_def/src/data.rs", | 7 | path: "/test/crates/hir_def/src/path.rs", |
8 | query: None, | 8 | query: None, |
9 | fragment: None, | 9 | fragment: None, |
10 | }, | 10 | }, |
11 | diagnostic: Diagnostic { | 11 | diagnostic: Diagnostic { |
12 | range: Range { | 12 | range: Range { |
13 | start: Position { | 13 | start: Position { |
14 | line: 79, | 14 | line: 264, |
15 | character: 15, | 15 | character: 8, |
16 | }, | 16 | }, |
17 | end: Position { | 17 | end: Position { |
18 | line: 79, | 18 | line: 264, |
19 | character: 41, | 19 | character: 76, |
20 | }, | 20 | }, |
21 | }, | 21 | }, |
22 | severity: Some( | 22 | severity: Some( |
@@ -36,22 +36,22 @@ | |||
36 | scheme: "file", | 36 | scheme: "file", |
37 | host: None, | 37 | host: None, |
38 | port: None, | 38 | port: None, |
39 | path: "/test/crates/hir_def/src/path.rs", | 39 | path: "/test/crates/hir_def/src/data.rs", |
40 | query: None, | 40 | query: None, |
41 | fragment: None, | 41 | fragment: None, |
42 | }, | 42 | }, |
43 | range: Range { | 43 | range: Range { |
44 | start: Position { | 44 | start: Position { |
45 | line: 264, | 45 | line: 79, |
46 | character: 8, | 46 | character: 15, |
47 | }, | 47 | }, |
48 | end: Position { | 48 | end: Position { |
49 | line: 264, | 49 | line: 79, |
50 | character: 76, | 50 | character: 41, |
51 | }, | 51 | }, |
52 | }, | 52 | }, |
53 | }, | 53 | }, |
54 | message: "Error originated from macro here", | 54 | message: "Exact error occurred here", |
55 | }, | 55 | }, |
56 | ], | 56 | ], |
57 | ), | 57 | ), |
@@ -65,19 +65,19 @@ | |||
65 | scheme: "file", | 65 | scheme: "file", |
66 | host: None, | 66 | host: None, |
67 | port: None, | 67 | port: None, |
68 | path: "/test/crates/hir_def/src/path.rs", | 68 | path: "/test/crates/hir_def/src/data.rs", |
69 | query: None, | 69 | query: None, |
70 | fragment: None, | 70 | fragment: None, |
71 | }, | 71 | }, |
72 | diagnostic: Diagnostic { | 72 | diagnostic: Diagnostic { |
73 | range: Range { | 73 | range: Range { |
74 | start: Position { | 74 | start: Position { |
75 | line: 264, | 75 | line: 79, |
76 | character: 8, | 76 | character: 15, |
77 | }, | 77 | }, |
78 | end: Position { | 78 | end: Position { |
79 | line: 264, | 79 | line: 79, |
80 | character: 76, | 80 | character: 41, |
81 | }, | 81 | }, |
82 | }, | 82 | }, |
83 | severity: Some( | 83 | severity: Some( |
@@ -89,33 +89,7 @@ | |||
89 | "rustc", | 89 | "rustc", |
90 | ), | 90 | ), |
91 | message: "Please register your known path in the path module", | 91 | message: "Please register your known path in the path module", |
92 | related_information: Some( | 92 | related_information: None, |
93 | [ | ||
94 | DiagnosticRelatedInformation { | ||
95 | location: Location { | ||
96 | uri: Url { | ||
97 | scheme: "file", | ||
98 | host: None, | ||
99 | port: None, | ||
100 | path: "/test/crates/hir_def/src/data.rs", | ||
101 | query: None, | ||
102 | fragment: None, | ||
103 | }, | ||
104 | range: Range { | ||
105 | start: Position { | ||
106 | line: 79, | ||
107 | character: 15, | ||
108 | }, | ||
109 | end: Position { | ||
110 | line: 79, | ||
111 | character: 41, | ||
112 | }, | ||
113 | }, | ||
114 | }, | ||
115 | message: "Exact error occured here", | ||
116 | }, | ||
117 | ], | ||
118 | ), | ||
119 | tags: None, | 93 | tags: None, |
120 | data: None, | 94 | data: None, |
121 | }, | 95 | }, |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt index c709de95f..23d42b4d0 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt | |||
@@ -32,7 +32,33 @@ | |||
32 | "rustc", | 32 | "rustc", |
33 | ), | 33 | ), |
34 | message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", | 34 | message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", |
35 | related_information: None, | 35 | related_information: Some( |
36 | [ | ||
37 | DiagnosticRelatedInformation { | ||
38 | location: Location { | ||
39 | uri: Url { | ||
40 | scheme: "file", | ||
41 | host: None, | ||
42 | port: None, | ||
43 | path: "/test/driver/subcommand/repl.rs", | ||
44 | query: None, | ||
45 | fragment: None, | ||
46 | }, | ||
47 | range: Range { | ||
48 | start: Position { | ||
49 | line: 290, | ||
50 | character: 8, | ||
51 | }, | ||
52 | end: Position { | ||
53 | line: 290, | ||
54 | character: 11, | ||
55 | }, | ||
56 | }, | ||
57 | }, | ||
58 | message: "consider prefixing with an underscore", | ||
59 | }, | ||
60 | ], | ||
61 | ), | ||
36 | tags: Some( | 62 | tags: Some( |
37 | [ | 63 | [ |
38 | Unnecessary, | 64 | Unnecessary, |
@@ -87,4 +113,114 @@ | |||
87 | }, | 113 | }, |
88 | ], | 114 | ], |
89 | }, | 115 | }, |
116 | MappedRustDiagnostic { | ||
117 | url: Url { | ||
118 | scheme: "file", | ||
119 | host: None, | ||
120 | port: None, | ||
121 | path: "/test/driver/subcommand/repl.rs", | ||
122 | query: None, | ||
123 | fragment: None, | ||
124 | }, | ||
125 | diagnostic: Diagnostic { | ||
126 | range: Range { | ||
127 | start: Position { | ||
128 | line: 290, | ||
129 | character: 8, | ||
130 | }, | ||
131 | end: Position { | ||
132 | line: 290, | ||
133 | character: 11, | ||
134 | }, | ||
135 | }, | ||
136 | severity: Some( | ||
137 | Hint, | ||
138 | ), | ||
139 | code: Some( | ||
140 | String( | ||
141 | "unused_variables", | ||
142 | ), | ||
143 | ), | ||
144 | code_description: None, | ||
145 | source: Some( | ||
146 | "rustc", | ||
147 | ), | ||
148 | message: "consider prefixing with an underscore", | ||
149 | related_information: Some( | ||
150 | [ | ||
151 | DiagnosticRelatedInformation { | ||
152 | location: Location { | ||
153 | uri: Url { | ||
154 | scheme: "file", | ||
155 | host: None, | ||
156 | port: None, | ||
157 | path: "/test/driver/subcommand/repl.rs", | ||
158 | query: None, | ||
159 | fragment: None, | ||
160 | }, | ||
161 | range: Range { | ||
162 | start: Position { | ||
163 | line: 290, | ||
164 | character: 8, | ||
165 | }, | ||
166 | end: Position { | ||
167 | line: 290, | ||
168 | character: 11, | ||
169 | }, | ||
170 | }, | ||
171 | }, | ||
172 | message: "original diagnostic", | ||
173 | }, | ||
174 | ], | ||
175 | ), | ||
176 | tags: None, | ||
177 | data: None, | ||
178 | }, | ||
179 | fixes: [ | ||
180 | CodeAction { | ||
181 | title: "consider prefixing with an underscore", | ||
182 | group: None, | ||
183 | kind: Some( | ||
184 | CodeActionKind( | ||
185 | "quickfix", | ||
186 | ), | ||
187 | ), | ||
188 | edit: Some( | ||
189 | SnippetWorkspaceEdit { | ||
190 | changes: Some( | ||
191 | { | ||
192 | Url { | ||
193 | scheme: "file", | ||
194 | host: None, | ||
195 | port: None, | ||
196 | path: "/test/driver/subcommand/repl.rs", | ||
197 | query: None, | ||
198 | fragment: None, | ||
199 | }: [ | ||
200 | TextEdit { | ||
201 | range: Range { | ||
202 | start: Position { | ||
203 | line: 290, | ||
204 | character: 8, | ||
205 | }, | ||
206 | end: Position { | ||
207 | line: 290, | ||
208 | character: 11, | ||
209 | }, | ||
210 | }, | ||
211 | new_text: "_foo", | ||
212 | }, | ||
213 | ], | ||
214 | }, | ||
215 | ), | ||
216 | document_changes: None, | ||
217 | }, | ||
218 | ), | ||
219 | is_preferred: Some( | ||
220 | true, | ||
221 | ), | ||
222 | data: None, | ||
223 | }, | ||
224 | ], | ||
225 | }, | ||
90 | ] | 226 | ] |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt index 632f438d7..4e428bedc 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt | |||
@@ -32,7 +32,33 @@ | |||
32 | "rustc", | 32 | "rustc", |
33 | ), | 33 | ), |
34 | message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", | 34 | message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", |
35 | related_information: None, | 35 | related_information: Some( |
36 | [ | ||
37 | DiagnosticRelatedInformation { | ||
38 | location: Location { | ||
39 | uri: Url { | ||
40 | scheme: "file", | ||
41 | host: None, | ||
42 | port: None, | ||
43 | path: "/test/driver/subcommand/repl.rs", | ||
44 | query: None, | ||
45 | fragment: None, | ||
46 | }, | ||
47 | range: Range { | ||
48 | start: Position { | ||
49 | line: 290, | ||
50 | character: 8, | ||
51 | }, | ||
52 | end: Position { | ||
53 | line: 290, | ||
54 | character: 11, | ||
55 | }, | ||
56 | }, | ||
57 | }, | ||
58 | message: "consider prefixing with an underscore", | ||
59 | }, | ||
60 | ], | ||
61 | ), | ||
36 | tags: Some( | 62 | tags: Some( |
37 | [ | 63 | [ |
38 | Unnecessary, | 64 | Unnecessary, |
@@ -87,4 +113,114 @@ | |||
87 | }, | 113 | }, |
88 | ], | 114 | ], |
89 | }, | 115 | }, |
116 | MappedRustDiagnostic { | ||
117 | url: Url { | ||
118 | scheme: "file", | ||
119 | host: None, | ||
120 | port: None, | ||
121 | path: "/test/driver/subcommand/repl.rs", | ||
122 | query: None, | ||
123 | fragment: None, | ||
124 | }, | ||
125 | diagnostic: Diagnostic { | ||
126 | range: Range { | ||
127 | start: Position { | ||
128 | line: 290, | ||
129 | character: 8, | ||
130 | }, | ||
131 | end: Position { | ||
132 | line: 290, | ||
133 | character: 11, | ||
134 | }, | ||
135 | }, | ||
136 | severity: Some( | ||
137 | Hint, | ||
138 | ), | ||
139 | code: Some( | ||
140 | String( | ||
141 | "unused_variables", | ||
142 | ), | ||
143 | ), | ||
144 | code_description: None, | ||
145 | source: Some( | ||
146 | "rustc", | ||
147 | ), | ||
148 | message: "consider prefixing with an underscore", | ||
149 | related_information: Some( | ||
150 | [ | ||
151 | DiagnosticRelatedInformation { | ||
152 | location: Location { | ||
153 | uri: Url { | ||
154 | scheme: "file", | ||
155 | host: None, | ||
156 | port: None, | ||
157 | path: "/test/driver/subcommand/repl.rs", | ||
158 | query: None, | ||
159 | fragment: None, | ||
160 | }, | ||
161 | range: Range { | ||
162 | start: Position { | ||
163 | line: 290, | ||
164 | character: 8, | ||
165 | }, | ||
166 | end: Position { | ||
167 | line: 290, | ||
168 | character: 11, | ||
169 | }, | ||
170 | }, | ||
171 | }, | ||
172 | message: "original diagnostic", | ||
173 | }, | ||
174 | ], | ||
175 | ), | ||
176 | tags: None, | ||
177 | data: None, | ||
178 | }, | ||
179 | fixes: [ | ||
180 | CodeAction { | ||
181 | title: "consider prefixing with an underscore", | ||
182 | group: None, | ||
183 | kind: Some( | ||
184 | CodeActionKind( | ||
185 | "quickfix", | ||
186 | ), | ||
187 | ), | ||
188 | edit: Some( | ||
189 | SnippetWorkspaceEdit { | ||
190 | changes: Some( | ||
191 | { | ||
192 | Url { | ||
193 | scheme: "file", | ||
194 | host: None, | ||
195 | port: None, | ||
196 | path: "/test/driver/subcommand/repl.rs", | ||
197 | query: None, | ||
198 | fragment: None, | ||
199 | }: [ | ||
200 | TextEdit { | ||
201 | range: Range { | ||
202 | start: Position { | ||
203 | line: 290, | ||
204 | character: 8, | ||
205 | }, | ||
206 | end: Position { | ||
207 | line: 290, | ||
208 | character: 11, | ||
209 | }, | ||
210 | }, | ||
211 | new_text: "_foo", | ||
212 | }, | ||
213 | ], | ||
214 | }, | ||
215 | ), | ||
216 | document_changes: None, | ||
217 | }, | ||
218 | ), | ||
219 | is_preferred: Some( | ||
220 | true, | ||
221 | ), | ||
222 | data: None, | ||
223 | }, | ||
224 | ], | ||
225 | }, | ||
90 | ] | 226 | ] |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt index c0b79428d..4ddd7efae 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt | |||
@@ -32,7 +32,33 @@ | |||
32 | "rustc", | 32 | "rustc", |
33 | ), | 33 | ), |
34 | message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", | 34 | message: "unused variable: `foo`\n#[warn(unused_variables)] on by default", |
35 | related_information: None, | 35 | related_information: Some( |
36 | [ | ||
37 | DiagnosticRelatedInformation { | ||
38 | location: Location { | ||
39 | uri: Url { | ||
40 | scheme: "file", | ||
41 | host: None, | ||
42 | port: None, | ||
43 | path: "/test/driver/subcommand/repl.rs", | ||
44 | query: None, | ||
45 | fragment: None, | ||
46 | }, | ||
47 | range: Range { | ||
48 | start: Position { | ||
49 | line: 290, | ||
50 | character: 8, | ||
51 | }, | ||
52 | end: Position { | ||
53 | line: 290, | ||
54 | character: 11, | ||
55 | }, | ||
56 | }, | ||
57 | }, | ||
58 | message: "consider prefixing with an underscore", | ||
59 | }, | ||
60 | ], | ||
61 | ), | ||
36 | tags: Some( | 62 | tags: Some( |
37 | [ | 63 | [ |
38 | Unnecessary, | 64 | Unnecessary, |
@@ -87,4 +113,114 @@ | |||
87 | }, | 113 | }, |
88 | ], | 114 | ], |
89 | }, | 115 | }, |
116 | MappedRustDiagnostic { | ||
117 | url: Url { | ||
118 | scheme: "file", | ||
119 | host: None, | ||
120 | port: None, | ||
121 | path: "/test/driver/subcommand/repl.rs", | ||
122 | query: None, | ||
123 | fragment: None, | ||
124 | }, | ||
125 | diagnostic: Diagnostic { | ||
126 | range: Range { | ||
127 | start: Position { | ||
128 | line: 290, | ||
129 | character: 8, | ||
130 | }, | ||
131 | end: Position { | ||
132 | line: 290, | ||
133 | character: 11, | ||
134 | }, | ||
135 | }, | ||
136 | severity: Some( | ||
137 | Hint, | ||
138 | ), | ||
139 | code: Some( | ||
140 | String( | ||
141 | "unused_variables", | ||
142 | ), | ||
143 | ), | ||
144 | code_description: None, | ||
145 | source: Some( | ||
146 | "rustc", | ||
147 | ), | ||
148 | message: "consider prefixing with an underscore", | ||
149 | related_information: Some( | ||
150 | [ | ||
151 | DiagnosticRelatedInformation { | ||
152 | location: Location { | ||
153 | uri: Url { | ||
154 | scheme: "file", | ||
155 | host: None, | ||
156 | port: None, | ||
157 | path: "/test/driver/subcommand/repl.rs", | ||
158 | query: None, | ||
159 | fragment: None, | ||
160 | }, | ||
161 | range: Range { | ||
162 | start: Position { | ||
163 | line: 290, | ||
164 | character: 8, | ||
165 | }, | ||
166 | end: Position { | ||
167 | line: 290, | ||
168 | character: 11, | ||
169 | }, | ||
170 | }, | ||
171 | }, | ||
172 | message: "original diagnostic", | ||
173 | }, | ||
174 | ], | ||
175 | ), | ||
176 | tags: None, | ||
177 | data: None, | ||
178 | }, | ||
179 | fixes: [ | ||
180 | CodeAction { | ||
181 | title: "consider prefixing with an underscore", | ||
182 | group: None, | ||
183 | kind: Some( | ||
184 | CodeActionKind( | ||
185 | "quickfix", | ||
186 | ), | ||
187 | ), | ||
188 | edit: Some( | ||
189 | SnippetWorkspaceEdit { | ||
190 | changes: Some( | ||
191 | { | ||
192 | Url { | ||
193 | scheme: "file", | ||
194 | host: None, | ||
195 | port: None, | ||
196 | path: "/test/driver/subcommand/repl.rs", | ||
197 | query: None, | ||
198 | fragment: None, | ||
199 | }: [ | ||
200 | TextEdit { | ||
201 | range: Range { | ||
202 | start: Position { | ||
203 | line: 290, | ||
204 | character: 8, | ||
205 | }, | ||
206 | end: Position { | ||
207 | line: 290, | ||
208 | character: 11, | ||
209 | }, | ||
210 | }, | ||
211 | new_text: "_foo", | ||
212 | }, | ||
213 | ], | ||
214 | }, | ||
215 | ), | ||
216 | document_changes: None, | ||
217 | }, | ||
218 | ), | ||
219 | is_preferred: Some( | ||
220 | true, | ||
221 | ), | ||
222 | data: None, | ||
223 | }, | ||
224 | ], | ||
225 | }, | ||
90 | ] | 226 | ] |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt index b9650f3e4..f455cf25e 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_wrong_number_of_parameters.txt | |||
@@ -81,4 +81,86 @@ | |||
81 | }, | 81 | }, |
82 | fixes: [], | 82 | fixes: [], |
83 | }, | 83 | }, |
84 | MappedRustDiagnostic { | ||
85 | url: Url { | ||
86 | scheme: "file", | ||
87 | host: None, | ||
88 | port: None, | ||
89 | path: "/test/compiler/ty/select.rs", | ||
90 | query: None, | ||
91 | fragment: None, | ||
92 | }, | ||
93 | diagnostic: Diagnostic { | ||
94 | range: Range { | ||
95 | start: Position { | ||
96 | line: 218, | ||
97 | character: 4, | ||
98 | }, | ||
99 | end: Position { | ||
100 | line: 230, | ||
101 | character: 5, | ||
102 | }, | ||
103 | }, | ||
104 | severity: Some( | ||
105 | Hint, | ||
106 | ), | ||
107 | code: Some( | ||
108 | String( | ||
109 | "E0061", | ||
110 | ), | ||
111 | ), | ||
112 | code_description: Some( | ||
113 | CodeDescription { | ||
114 | href: Url { | ||
115 | scheme: "https", | ||
116 | host: Some( | ||
117 | Domain( | ||
118 | "doc.rust-lang.org", | ||
119 | ), | ||
120 | ), | ||
121 | port: None, | ||
122 | path: "/error-index.html", | ||
123 | query: None, | ||
124 | fragment: Some( | ||
125 | "E0061", | ||
126 | ), | ||
127 | }, | ||
128 | }, | ||
129 | ), | ||
130 | source: Some( | ||
131 | "rustc", | ||
132 | ), | ||
133 | message: "defined here", | ||
134 | related_information: Some( | ||
135 | [ | ||
136 | DiagnosticRelatedInformation { | ||
137 | location: Location { | ||
138 | uri: Url { | ||
139 | scheme: "file", | ||
140 | host: None, | ||
141 | port: None, | ||
142 | path: "/test/compiler/ty/select.rs", | ||
143 | query: None, | ||
144 | fragment: None, | ||
145 | }, | ||
146 | range: Range { | ||
147 | start: Position { | ||
148 | line: 103, | ||
149 | character: 17, | ||
150 | }, | ||
151 | end: Position { | ||
152 | line: 103, | ||
153 | character: 29, | ||
154 | }, | ||
155 | }, | ||
156 | }, | ||
157 | message: "original diagnostic", | ||
158 | }, | ||
159 | ], | ||
160 | ), | ||
161 | tags: None, | ||
162 | data: None, | ||
163 | }, | ||
164 | fixes: [], | ||
165 | }, | ||
84 | ] | 166 | ] |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index c45f68a91..4cbdb3b92 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt | |||
@@ -74,6 +74,309 @@ | |||
74 | }, | 74 | }, |
75 | message: "unnecessary let binding", | 75 | message: "unnecessary let binding", |
76 | }, | 76 | }, |
77 | DiagnosticRelatedInformation { | ||
78 | location: Location { | ||
79 | uri: Url { | ||
80 | scheme: "file", | ||
81 | host: None, | ||
82 | port: None, | ||
83 | path: "/test/src/main.rs", | ||
84 | query: None, | ||
85 | fragment: None, | ||
86 | }, | ||
87 | range: Range { | ||
88 | start: Position { | ||
89 | line: 2, | ||
90 | character: 4, | ||
91 | }, | ||
92 | end: Position { | ||
93 | line: 2, | ||
94 | character: 30, | ||
95 | }, | ||
96 | }, | ||
97 | }, | ||
98 | message: "return the expression directly", | ||
99 | }, | ||
100 | ], | ||
101 | ), | ||
102 | tags: None, | ||
103 | data: None, | ||
104 | }, | ||
105 | fixes: [ | ||
106 | CodeAction { | ||
107 | title: "return the expression directly", | ||
108 | group: None, | ||
109 | kind: Some( | ||
110 | CodeActionKind( | ||
111 | "quickfix", | ||
112 | ), | ||
113 | ), | ||
114 | edit: Some( | ||
115 | SnippetWorkspaceEdit { | ||
116 | changes: Some( | ||
117 | { | ||
118 | Url { | ||
119 | scheme: "file", | ||
120 | host: None, | ||
121 | port: None, | ||
122 | path: "/test/src/main.rs", | ||
123 | query: None, | ||
124 | fragment: None, | ||
125 | }: [ | ||
126 | TextEdit { | ||
127 | range: Range { | ||
128 | start: Position { | ||
129 | line: 2, | ||
130 | character: 4, | ||
131 | }, | ||
132 | end: Position { | ||
133 | line: 2, | ||
134 | character: 30, | ||
135 | }, | ||
136 | }, | ||
137 | new_text: "", | ||
138 | }, | ||
139 | TextEdit { | ||
140 | range: Range { | ||
141 | start: Position { | ||
142 | line: 3, | ||
143 | character: 4, | ||
144 | }, | ||
145 | end: Position { | ||
146 | line: 3, | ||
147 | character: 5, | ||
148 | }, | ||
149 | }, | ||
150 | new_text: "(0..10).collect()", | ||
151 | }, | ||
152 | ], | ||
153 | }, | ||
154 | ), | ||
155 | document_changes: None, | ||
156 | }, | ||
157 | ), | ||
158 | is_preferred: Some( | ||
159 | true, | ||
160 | ), | ||
161 | data: None, | ||
162 | }, | ||
163 | ], | ||
164 | }, | ||
165 | MappedRustDiagnostic { | ||
166 | url: Url { | ||
167 | scheme: "file", | ||
168 | host: None, | ||
169 | port: None, | ||
170 | path: "/test/src/main.rs", | ||
171 | query: None, | ||
172 | fragment: None, | ||
173 | }, | ||
174 | diagnostic: Diagnostic { | ||
175 | range: Range { | ||
176 | start: Position { | ||
177 | line: 2, | ||
178 | character: 4, | ||
179 | }, | ||
180 | end: Position { | ||
181 | line: 2, | ||
182 | character: 30, | ||
183 | }, | ||
184 | }, | ||
185 | severity: Some( | ||
186 | Hint, | ||
187 | ), | ||
188 | code: Some( | ||
189 | String( | ||
190 | "let_and_return", | ||
191 | ), | ||
192 | ), | ||
193 | code_description: Some( | ||
194 | CodeDescription { | ||
195 | href: Url { | ||
196 | scheme: "https", | ||
197 | host: Some( | ||
198 | Domain( | ||
199 | "rust-lang.github.io", | ||
200 | ), | ||
201 | ), | ||
202 | port: None, | ||
203 | path: "/rust-clippy/master/index.html", | ||
204 | query: None, | ||
205 | fragment: Some( | ||
206 | "let_and_return", | ||
207 | ), | ||
208 | }, | ||
209 | }, | ||
210 | ), | ||
211 | source: Some( | ||
212 | "clippy", | ||
213 | ), | ||
214 | message: "unnecessary let binding", | ||
215 | related_information: Some( | ||
216 | [ | ||
217 | DiagnosticRelatedInformation { | ||
218 | location: Location { | ||
219 | uri: Url { | ||
220 | scheme: "file", | ||
221 | host: None, | ||
222 | port: None, | ||
223 | path: "/test/src/main.rs", | ||
224 | query: None, | ||
225 | fragment: None, | ||
226 | }, | ||
227 | range: Range { | ||
228 | start: Position { | ||
229 | line: 3, | ||
230 | character: 4, | ||
231 | }, | ||
232 | end: Position { | ||
233 | line: 3, | ||
234 | character: 5, | ||
235 | }, | ||
236 | }, | ||
237 | }, | ||
238 | message: "original diagnostic", | ||
239 | }, | ||
240 | ], | ||
241 | ), | ||
242 | tags: None, | ||
243 | data: None, | ||
244 | }, | ||
245 | fixes: [ | ||
246 | CodeAction { | ||
247 | title: "return the expression directly", | ||
248 | group: None, | ||
249 | kind: Some( | ||
250 | CodeActionKind( | ||
251 | "quickfix", | ||
252 | ), | ||
253 | ), | ||
254 | edit: Some( | ||
255 | SnippetWorkspaceEdit { | ||
256 | changes: Some( | ||
257 | { | ||
258 | Url { | ||
259 | scheme: "file", | ||
260 | host: None, | ||
261 | port: None, | ||
262 | path: "/test/src/main.rs", | ||
263 | query: None, | ||
264 | fragment: None, | ||
265 | }: [ | ||
266 | TextEdit { | ||
267 | range: Range { | ||
268 | start: Position { | ||
269 | line: 2, | ||
270 | character: 4, | ||
271 | }, | ||
272 | end: Position { | ||
273 | line: 2, | ||
274 | character: 30, | ||
275 | }, | ||
276 | }, | ||
277 | new_text: "", | ||
278 | }, | ||
279 | TextEdit { | ||
280 | range: Range { | ||
281 | start: Position { | ||
282 | line: 3, | ||
283 | character: 4, | ||
284 | }, | ||
285 | end: Position { | ||
286 | line: 3, | ||
287 | character: 5, | ||
288 | }, | ||
289 | }, | ||
290 | new_text: "(0..10).collect()", | ||
291 | }, | ||
292 | ], | ||
293 | }, | ||
294 | ), | ||
295 | document_changes: None, | ||
296 | }, | ||
297 | ), | ||
298 | is_preferred: Some( | ||
299 | true, | ||
300 | ), | ||
301 | data: None, | ||
302 | }, | ||
303 | ], | ||
304 | }, | ||
305 | MappedRustDiagnostic { | ||
306 | url: Url { | ||
307 | scheme: "file", | ||
308 | host: None, | ||
309 | port: None, | ||
310 | path: "/test/src/main.rs", | ||
311 | query: None, | ||
312 | fragment: None, | ||
313 | }, | ||
314 | diagnostic: Diagnostic { | ||
315 | range: Range { | ||
316 | start: Position { | ||
317 | line: 2, | ||
318 | character: 4, | ||
319 | }, | ||
320 | end: Position { | ||
321 | line: 2, | ||
322 | character: 30, | ||
323 | }, | ||
324 | }, | ||
325 | severity: Some( | ||
326 | Hint, | ||
327 | ), | ||
328 | code: Some( | ||
329 | String( | ||
330 | "let_and_return", | ||
331 | ), | ||
332 | ), | ||
333 | code_description: Some( | ||
334 | CodeDescription { | ||
335 | href: Url { | ||
336 | scheme: "https", | ||
337 | host: Some( | ||
338 | Domain( | ||
339 | "rust-lang.github.io", | ||
340 | ), | ||
341 | ), | ||
342 | port: None, | ||
343 | path: "/rust-clippy/master/index.html", | ||
344 | query: None, | ||
345 | fragment: Some( | ||
346 | "let_and_return", | ||
347 | ), | ||
348 | }, | ||
349 | }, | ||
350 | ), | ||
351 | source: Some( | ||
352 | "clippy", | ||
353 | ), | ||
354 | message: "return the expression directly", | ||
355 | related_information: Some( | ||
356 | [ | ||
357 | DiagnosticRelatedInformation { | ||
358 | location: Location { | ||
359 | uri: Url { | ||
360 | scheme: "file", | ||
361 | host: None, | ||
362 | port: None, | ||
363 | path: "/test/src/main.rs", | ||
364 | query: None, | ||
365 | fragment: None, | ||
366 | }, | ||
367 | range: Range { | ||
368 | start: Position { | ||
369 | line: 3, | ||
370 | character: 4, | ||
371 | }, | ||
372 | end: Position { | ||
373 | line: 3, | ||
374 | character: 5, | ||
375 | }, | ||
376 | }, | ||
377 | }, | ||
378 | message: "original diagnostic", | ||
379 | }, | ||
77 | ], | 380 | ], |
78 | ), | 381 | ), |
79 | tags: None, | 382 | tags: None, |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 324019614..f16f97131 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -75,8 +75,10 @@ fn diagnostic_related_information( | |||
75 | } | 75 | } |
76 | 76 | ||
77 | enum MappedRustChildDiagnostic { | 77 | enum MappedRustChildDiagnostic { |
78 | Related(lsp_types::DiagnosticRelatedInformation), | 78 | Related { |
79 | SuggestedFix(lsp_ext::CodeAction), | 79 | related: lsp_types::DiagnosticRelatedInformation, |
80 | suggested_fix: Option<lsp_ext::CodeAction>, | ||
81 | }, | ||
80 | MessageLine(String), | 82 | MessageLine(String), |
81 | } | 83 | } |
82 | 84 | ||
@@ -103,23 +105,32 @@ fn map_rust_child_diagnostic( | |||
103 | } | 105 | } |
104 | 106 | ||
105 | if edit_map.is_empty() { | 107 | if edit_map.is_empty() { |
106 | MappedRustChildDiagnostic::Related(lsp_types::DiagnosticRelatedInformation { | 108 | MappedRustChildDiagnostic::Related { |
107 | location: location(workspace_root, spans[0]), | 109 | related: lsp_types::DiagnosticRelatedInformation { |
108 | message: rd.message.clone(), | 110 | location: location(workspace_root, spans[0]), |
109 | }) | 111 | message: rd.message.clone(), |
112 | }, | ||
113 | suggested_fix: None, | ||
114 | } | ||
110 | } else { | 115 | } else { |
111 | MappedRustChildDiagnostic::SuggestedFix(lsp_ext::CodeAction { | 116 | MappedRustChildDiagnostic::Related { |
112 | title: rd.message.clone(), | 117 | related: lsp_types::DiagnosticRelatedInformation { |
113 | group: None, | 118 | location: location(workspace_root, spans[0]), |
114 | kind: Some(lsp_types::CodeActionKind::QUICKFIX), | 119 | message: rd.message.clone(), |
115 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | 120 | }, |
116 | // FIXME: there's no good reason to use edit_map here.... | 121 | suggested_fix: Some(lsp_ext::CodeAction { |
117 | changes: Some(edit_map), | 122 | title: rd.message.clone(), |
118 | document_changes: None, | 123 | group: None, |
124 | kind: Some(lsp_types::CodeActionKind::QUICKFIX), | ||
125 | edit: Some(lsp_ext::SnippetWorkspaceEdit { | ||
126 | // FIXME: there's no good reason to use edit_map here.... | ||
127 | changes: Some(edit_map), | ||
128 | document_changes: None, | ||
129 | }), | ||
130 | is_preferred: Some(true), | ||
131 | data: None, | ||
119 | }), | 132 | }), |
120 | is_preferred: Some(true), | 133 | } |
121 | data: None, | ||
122 | }) | ||
123 | } | 134 | } |
124 | } | 135 | } |
125 | 136 | ||
@@ -179,8 +190,12 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
179 | for child in &rd.children { | 190 | for child in &rd.children { |
180 | let child = map_rust_child_diagnostic(workspace_root, &child); | 191 | let child = map_rust_child_diagnostic(workspace_root, &child); |
181 | match child { | 192 | match child { |
182 | MappedRustChildDiagnostic::Related(related) => related_information.push(related), | 193 | MappedRustChildDiagnostic::Related { related, suggested_fix } => { |
183 | MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action), | 194 | related_information.push(related); |
195 | if let Some(code_action) = suggested_fix { | ||
196 | fixes.push(code_action); | ||
197 | } | ||
198 | } | ||
184 | MappedRustChildDiagnostic::MessageLine(message_line) => { | 199 | MappedRustChildDiagnostic::MessageLine(message_line) => { |
185 | format_to!(message, "\n{}", message_line); | 200 | format_to!(message, "\n{}", message_line); |
186 | 201 | ||
@@ -219,7 +234,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
219 | 234 | ||
220 | primary_spans | 235 | primary_spans |
221 | .iter() | 236 | .iter() |
222 | .map(|primary_span| { | 237 | .flat_map(|primary_span| { |
223 | let location = location(workspace_root, &primary_span); | 238 | let location = location(workspace_root, &primary_span); |
224 | 239 | ||
225 | let mut message = message.clone(); | 240 | let mut message = message.clone(); |
@@ -229,72 +244,100 @@ pub(crate) fn map_rust_diagnostic_to_lsp( | |||
229 | } | 244 | } |
230 | } | 245 | } |
231 | 246 | ||
247 | // Each primary diagnostic span may result in multiple LSP diagnostics. | ||
248 | let mut diagnostics = Vec::new(); | ||
249 | |||
250 | let mut related_macro_info = None; | ||
251 | |||
232 | // If error occurs from macro expansion, add related info pointing to | 252 | // If error occurs from macro expansion, add related info pointing to |
233 | // where the error originated | 253 | // where the error originated |
234 | // Also, we would generate an additional diagnostic, so that exact place of macro | 254 | // Also, we would generate an additional diagnostic, so that exact place of macro |
235 | // will be highlighted in the error origin place. | 255 | // will be highlighted in the error origin place. |
236 | let additional_diagnostic = | 256 | if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { |
237 | if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() { | 257 | let in_macro_location = location_naive(workspace_root, &primary_span); |
238 | let in_macro_location = location_naive(workspace_root, &primary_span); | ||
239 | 258 | ||
240 | // Add related information for the main disagnostic. | 259 | // Add related information for the main disagnostic. |
241 | related_information.push(lsp_types::DiagnosticRelatedInformation { | 260 | related_macro_info = Some(lsp_types::DiagnosticRelatedInformation { |
242 | location: in_macro_location.clone(), | 261 | location: in_macro_location.clone(), |
243 | message: "Error originated from macro here".to_string(), | 262 | message: "Error originated from macro here".to_string(), |
244 | }); | 263 | }); |
245 | 264 | ||
246 | // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. | 265 | // For the additional in-macro diagnostic we add the inverse message pointing to the error location in code. |
247 | let information_for_additional_diagnostic = | 266 | let information_for_additional_diagnostic = |
248 | vec![lsp_types::DiagnosticRelatedInformation { | 267 | vec![lsp_types::DiagnosticRelatedInformation { |
249 | location: location.clone(), | 268 | location: location.clone(), |
250 | message: "Exact error occured here".to_string(), | 269 | message: "Exact error occurred here".to_string(), |
251 | }]; | 270 | }]; |
252 | 271 | ||
253 | let diagnostic = lsp_types::Diagnostic { | 272 | let diagnostic = lsp_types::Diagnostic { |
254 | range: in_macro_location.range, | 273 | range: in_macro_location.range, |
255 | severity, | 274 | severity, |
256 | code: code.clone().map(lsp_types::NumberOrString::String), | 275 | code: code.clone().map(lsp_types::NumberOrString::String), |
257 | code_description: code_description.clone(), | 276 | code_description: code_description.clone(), |
258 | source: Some(source.clone()), | 277 | source: Some(source.clone()), |
259 | message: message.clone(), | 278 | message: message.clone(), |
260 | related_information: Some(information_for_additional_diagnostic), | 279 | related_information: Some(information_for_additional_diagnostic), |
261 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | 280 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, |
262 | data: None, | 281 | data: None, |
263 | }; | ||
264 | |||
265 | Some(MappedRustDiagnostic { | ||
266 | url: in_macro_location.uri, | ||
267 | diagnostic, | ||
268 | fixes: fixes.clone(), | ||
269 | }) | ||
270 | } else { | ||
271 | None | ||
272 | }; | 282 | }; |
273 | 283 | ||
274 | let diagnostic = lsp_types::Diagnostic { | 284 | diagnostics.push(MappedRustDiagnostic { |
275 | range: location.range, | 285 | url: in_macro_location.uri, |
276 | severity, | 286 | diagnostic, |
277 | code: code.clone().map(lsp_types::NumberOrString::String), | 287 | fixes: fixes.clone(), |
278 | code_description: code_description.clone(), | 288 | }); |
279 | source: Some(source.clone()), | 289 | } |
280 | message, | 290 | |
281 | related_information: if related_information.is_empty() { | 291 | // Emit the primary diagnostic. |
282 | None | 292 | diagnostics.push(MappedRustDiagnostic { |
283 | } else { | 293 | url: location.uri.clone(), |
284 | Some(related_information.clone()) | 294 | diagnostic: lsp_types::Diagnostic { |
295 | range: location.range, | ||
296 | severity, | ||
297 | code: code.clone().map(lsp_types::NumberOrString::String), | ||
298 | code_description: code_description.clone(), | ||
299 | source: Some(source.clone()), | ||
300 | message, | ||
301 | related_information: if related_information.is_empty() { | ||
302 | None | ||
303 | } else { | ||
304 | let mut related = related_information.clone(); | ||
305 | related.extend(related_macro_info); | ||
306 | Some(related) | ||
307 | }, | ||
308 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | ||
309 | data: None, | ||
285 | }, | 310 | }, |
286 | tags: if tags.is_empty() { None } else { Some(tags.clone()) }, | 311 | fixes: fixes.clone(), |
287 | data: None, | 312 | }); |
288 | }; | ||
289 | 313 | ||
290 | let main_diagnostic = | 314 | // Emit hint-level diagnostics for all `related_information` entries such as "help"s. |
291 | MappedRustDiagnostic { url: location.uri, diagnostic, fixes: fixes.clone() }; | 315 | // This is useful because they will show up in the user's editor, unlike |
292 | match additional_diagnostic { | 316 | // `related_information`, which just produces hard-to-read links, at least in VS Code. |
293 | None => vec![main_diagnostic], | 317 | let back_ref = lsp_types::DiagnosticRelatedInformation { |
294 | Some(additional_diagnostic) => vec![main_diagnostic, additional_diagnostic], | 318 | location, |
319 | message: "original diagnostic".to_string(), | ||
320 | }; | ||
321 | for info in &related_information { | ||
322 | diagnostics.push(MappedRustDiagnostic { | ||
323 | url: info.location.uri.clone(), | ||
324 | fixes: fixes.clone(), // share fixes to make them easier to apply | ||
325 | diagnostic: lsp_types::Diagnostic { | ||
326 | range: info.location.range, | ||
327 | severity: Some(lsp_types::DiagnosticSeverity::Hint), | ||
328 | code: code.clone().map(lsp_types::NumberOrString::String), | ||
329 | code_description: code_description.clone(), | ||
330 | source: Some(source.clone()), | ||
331 | message: info.message.clone(), | ||
332 | related_information: Some(vec![back_ref.clone()]), | ||
333 | tags: None, // don't apply modifiers again | ||
334 | data: None, | ||
335 | }, | ||
336 | }); | ||
295 | } | 337 | } |
338 | |||
339 | diagnostics | ||
296 | }) | 340 | }) |
297 | .flatten() | ||
298 | .collect() | 341 | .collect() |
299 | } | 342 | } |
300 | 343 | ||
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 876659a2b..cc09b77a5 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -171,8 +171,17 @@ pub fn expr_return() -> ast::Expr { | |||
171 | pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { | 171 | pub fn expr_match(expr: ast::Expr, match_arm_list: ast::MatchArmList) -> ast::Expr { |
172 | expr_from_text(&format!("match {} {}", expr, match_arm_list)) | 172 | expr_from_text(&format!("match {} {}", expr, match_arm_list)) |
173 | } | 173 | } |
174 | pub fn expr_if(condition: ast::Condition, then_branch: ast::BlockExpr) -> ast::Expr { | 174 | pub fn expr_if( |
175 | expr_from_text(&format!("if {} {}", condition, then_branch)) | 175 | condition: ast::Condition, |
176 | then_branch: ast::BlockExpr, | ||
177 | else_branch: Option<ast::ElseBranch>, | ||
178 | ) -> ast::Expr { | ||
179 | let else_branch = match else_branch { | ||
180 | Some(ast::ElseBranch::Block(block)) => format!("else {}", block), | ||
181 | Some(ast::ElseBranch::IfExpr(if_expr)) => format!("else {}", if_expr), | ||
182 | None => String::new(), | ||
183 | }; | ||
184 | expr_from_text(&format!("if {} {} {}", condition, then_branch, else_branch)) | ||
176 | } | 185 | } |
177 | pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { | 186 | pub fn expr_prefix(op: SyntaxKind, expr: ast::Expr) -> ast::Expr { |
178 | let token = token(op); | 187 | let token = token(op); |