aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock3
-rw-r--r--crates/ra_assists/src/ast_transform.rs24
-rw-r--r--crates/ra_assists/src/handlers/add_missing_impl_members.rs50
-rw-r--r--crates/ra_assists/src/handlers/change_visibility.rs516
-rw-r--r--crates/ra_db/src/fixture.rs20
-rw-r--r--crates/ra_flycheck/Cargo.toml4
-rw-r--r--crates/ra_flycheck/src/conv.rs341
-rw-r--r--crates/ra_flycheck/src/conv/test.rs1072
-rw-r--r--crates/ra_flycheck/src/lib.rs88
-rw-r--r--crates/ra_hir/src/code_model.rs11
-rw-r--r--crates/ra_hir_ty/src/lib.rs5
-rw-r--r--crates/ra_ide/src/completion/completion_context.rs2
-rw-r--r--crates/ra_ide/src/completion/presentation.rs58
-rw-r--r--crates/ra_ide/src/lib.rs8
-rw-r--r--crates/ra_project_model/src/lib.rs2
-rw-r--r--crates/ra_syntax/src/ast/make.rs4
-rw-r--r--crates/ra_text_edit/src/lib.rs2
-rw-r--r--crates/rust-analyzer/Cargo.toml1
-rw-r--r--crates/rust-analyzer/src/cli.rs1
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs2
-rw-r--r--crates/rust-analyzer/src/diagnostics.rs1
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap (renamed from crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap)2
-rw-r--r--crates/rust-analyzer/src/diagnostics/to_proto.rs1397
-rw-r--r--crates/rust-analyzer/src/main_loop.rs67
-rw-r--r--crates/rust-analyzer/src/to_proto.rs22
-rw-r--r--crates/rust-analyzer/src/world.rs6
-rw-r--r--editors/code/src/commands/ssr.ts2
34 files changed, 2187 insertions, 1538 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3a34978b1..f94cea814 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -975,10 +975,8 @@ version = "0.1.0"
975dependencies = [ 975dependencies = [
976 "cargo_metadata", 976 "cargo_metadata",
977 "crossbeam-channel", 977 "crossbeam-channel",
978 "insta",
979 "jod-thread", 978 "jod-thread",
980 "log", 979 "log",
981 "lsp-types",
982 "ra_toolchain", 980 "ra_toolchain",
983 "serde_json", 981 "serde_json",
984] 982]
@@ -1381,6 +1379,7 @@ dependencies = [
1381 "crossbeam-channel", 1379 "crossbeam-channel",
1382 "env_logger", 1380 "env_logger",
1383 "globset", 1381 "globset",
1382 "insta",
1384 "itertools", 1383 "itertools",
1385 "jod-thread", 1384 "jod-thread",
1386 "log", 1385 "log",
diff --git a/crates/ra_assists/src/ast_transform.rs b/crates/ra_assists/src/ast_transform.rs
index 9ac65ab39..3079a02a2 100644
--- a/crates/ra_assists/src/ast_transform.rs
+++ b/crates/ra_assists/src/ast_transform.rs
@@ -1,7 +1,7 @@
1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined. 1//! `AstTransformer`s are functions that replace nodes in an AST and can be easily combined.
2use rustc_hash::FxHashMap; 2use rustc_hash::FxHashMap;
3 3
4use hir::{PathResolution, SemanticsScope}; 4use hir::{HirDisplay, PathResolution, SemanticsScope};
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_syntax::{ 6use ra_syntax::{
7 algo::SyntaxRewriter, 7 algo::SyntaxRewriter,
@@ -51,7 +51,27 @@ impl<'a> SubstituteTypeParams<'a> {
51 .into_iter() 51 .into_iter()
52 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky 52 // this is a trait impl, so we need to skip the first type parameter -- this is a bit hacky
53 .skip(1) 53 .skip(1)
54 .zip(substs.into_iter()) 54 // The actual list of trait type parameters may be longer than the one
55 // used in the `impl` block due to trailing default type parametrs.
56 // For that case we extend the `substs` with an empty iterator so we
57 // can still hit those trailing values and check if they actually have
58 // a default type. If they do, go for that type from `hir` to `ast` so
59 // the resulting change can be applied correctly.
60 .zip(substs.into_iter().map(Some).chain(std::iter::repeat(None)))
61 .filter_map(|(k, v)| match v {
62 Some(v) => Some((k, v)),
63 None => {
64 let default = k.default(source_scope.db)?;
65 Some((
66 k,
67 ast::make::type_ref(
68 &default
69 .display_source_code(source_scope.db, source_scope.module()?.into())
70 .ok()?,
71 ),
72 ))
73 }
74 })
55 .collect(); 75 .collect();
56 return SubstituteTypeParams { 76 return SubstituteTypeParams {
57 source_scope, 77 source_scope,
diff --git a/crates/ra_assists/src/handlers/add_missing_impl_members.rs b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
index c1ce87914..22e1156d2 100644
--- a/crates/ra_assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ra_assists/src/handlers/add_missing_impl_members.rs
@@ -618,4 +618,54 @@ impl Foo for S {
618}"#, 618}"#,
619 ) 619 )
620 } 620 }
621
622 #[test]
623 fn test_generic_single_default_parameter() {
624 check_assist(
625 add_missing_impl_members,
626 r#"
627trait Foo<T = Self> {
628 fn bar(&self, other: &T);
629}
630
631struct S;
632impl Foo for S { <|> }"#,
633 r#"
634trait Foo<T = Self> {
635 fn bar(&self, other: &T);
636}
637
638struct S;
639impl Foo for S {
640 <|>fn bar(&self, other: &Self) {
641 todo!()
642 }
643}"#,
644 )
645 }
646
647 #[test]
648 fn test_generic_default_parameter_is_second() {
649 check_assist(
650 add_missing_impl_members,
651 r#"
652trait Foo<T1, T2 = Self> {
653 fn bar(&self, this: &T1, that: &T2);
654}
655
656struct S<T>;
657impl Foo<T> for S<T> { <|> }"#,
658 r#"
659trait Foo<T1, T2 = Self> {
660 fn bar(&self, this: &T1, that: &T2);
661}
662
663struct S<T>;
664impl Foo<T> for S<T> {
665 <|>fn bar(&self, this: &T, that: &Self) {
666 todo!()
667 }
668}"#,
669 )
670 }
621} 671}
diff --git a/crates/ra_assists/src/handlers/change_visibility.rs b/crates/ra_assists/src/handlers/change_visibility.rs
index e631766ef..40cf4b422 100644
--- a/crates/ra_assists/src/handlers/change_visibility.rs
+++ b/crates/ra_assists/src/handlers/change_visibility.rs
@@ -5,11 +5,14 @@ use ra_syntax::{
5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY, 5 ATTR, COMMENT, CONST_DEF, ENUM_DEF, FN_DEF, MODULE, STRUCT_DEF, TRAIT_DEF, VISIBILITY,
6 WHITESPACE, 6 WHITESPACE,
7 }, 7 },
8 SyntaxNode, TextSize, T, 8 SyntaxNode, TextRange, TextSize, T,
9}; 9};
10
11use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution};
10use test_utils::tested_by; 12use test_utils::tested_by;
11 13
12use crate::{AssistContext, AssistId, Assists}; 14use crate::{AssistContext, AssistId, Assists};
15use ra_db::FileId;
13 16
14// Assist: change_visibility 17// Assist: change_visibility
15// 18//
@@ -27,6 +30,8 @@ pub(crate) fn change_visibility(acc: &mut Assists, ctx: &AssistContext) -> Optio
27 return change_vis(acc, vis); 30 return change_vis(acc, vis);
28 } 31 }
29 add_vis(acc, ctx) 32 add_vis(acc, ctx)
33 .or_else(|| add_vis_to_referenced_module_def(acc, ctx))
34 .or_else(|| add_vis_to_referenced_record_field(acc, ctx))
30} 35}
31 36
32fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 37fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
@@ -72,6 +77,143 @@ fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
72 }) 77 })
73} 78}
74 79
80fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
81 let path: ast::Path = ctx.find_node_at_offset()?;
82 let path_res = ctx.sema.resolve_path(&path)?;
83 let def = match path_res {
84 PathResolution::Def(def) => def,
85 _ => return None,
86 };
87
88 let current_module = ctx.sema.scope(&path.syntax()).module()?;
89 let target_module = def.module(ctx.db)?;
90
91 let vis = target_module.visibility_of(ctx.db, &def)?;
92 if vis.is_visible_from(ctx.db, current_module.into()) {
93 return None;
94 };
95
96 let (offset, target, target_file, target_name) = target_data_for_def(ctx.db, def)?;
97
98 let missing_visibility =
99 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
100
101 let assist_label = match target_name {
102 None => format!("Change visibility to {}", missing_visibility),
103 Some(name) => format!("Change visibility of {} to {}", name, missing_visibility),
104 };
105
106 acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
107 edit.set_file(target_file);
108 edit.insert(offset, format!("{} ", missing_visibility));
109 edit.set_cursor(offset);
110 })
111}
112
113fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
114 let record_field: ast::RecordField = ctx.find_node_at_offset()?;
115 let (record_field_def, _) = ctx.sema.resolve_record_field(&record_field)?;
116
117 let current_module = ctx.sema.scope(record_field.syntax()).module()?;
118 let visibility = record_field_def.visibility(ctx.db);
119 if visibility.is_visible_from(ctx.db, current_module.into()) {
120 return None;
121 }
122
123 let parent = record_field_def.parent_def(ctx.db);
124 let parent_name = parent.name(ctx.db);
125 let target_module = parent.module(ctx.db);
126
127 let in_file_source = record_field_def.source(ctx.db);
128 let (offset, target) = match in_file_source.value {
129 hir::FieldSource::Named(it) => {
130 let s = it.syntax();
131 (vis_offset(s), s.text_range())
132 }
133 hir::FieldSource::Pos(it) => {
134 let s = it.syntax();
135 (vis_offset(s), s.text_range())
136 }
137 };
138
139 let missing_visibility =
140 if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" };
141 let target_file = in_file_source.file_id.original_file(ctx.db);
142
143 let target_name = record_field_def.name(ctx.db);
144 let assist_label =
145 format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility);
146
147 acc.add(AssistId("change_visibility"), assist_label, target, |edit| {
148 edit.set_file(target_file);
149 edit.insert(offset, format!("{} ", missing_visibility));
150 edit.set_cursor(offset)
151 })
152}
153
154fn target_data_for_def(
155 db: &dyn HirDatabase,
156 def: hir::ModuleDef,
157) -> Option<(TextSize, TextRange, FileId, Option<hir::Name>)> {
158 fn offset_target_and_file_id<S, Ast>(
159 db: &dyn HirDatabase,
160 x: S,
161 ) -> (TextSize, TextRange, FileId)
162 where
163 S: HasSource<Ast = Ast>,
164 Ast: AstNode,
165 {
166 let source = x.source(db);
167 let in_file_syntax = source.syntax();
168 let file_id = in_file_syntax.file_id;
169 let syntax = in_file_syntax.value;
170 (vis_offset(syntax), syntax.text_range(), file_id.original_file(db.upcast()))
171 }
172
173 let target_name;
174 let (offset, target, target_file) = match def {
175 hir::ModuleDef::Function(f) => {
176 target_name = Some(f.name(db));
177 offset_target_and_file_id(db, f)
178 }
179 hir::ModuleDef::Adt(adt) => {
180 target_name = Some(adt.name(db));
181 match adt {
182 hir::Adt::Struct(s) => offset_target_and_file_id(db, s),
183 hir::Adt::Union(u) => offset_target_and_file_id(db, u),
184 hir::Adt::Enum(e) => offset_target_and_file_id(db, e),
185 }
186 }
187 hir::ModuleDef::Const(c) => {
188 target_name = c.name(db);
189 offset_target_and_file_id(db, c)
190 }
191 hir::ModuleDef::Static(s) => {
192 target_name = s.name(db);
193 offset_target_and_file_id(db, s)
194 }
195 hir::ModuleDef::Trait(t) => {
196 target_name = Some(t.name(db));
197 offset_target_and_file_id(db, t)
198 }
199 hir::ModuleDef::TypeAlias(t) => {
200 target_name = Some(t.name(db));
201 offset_target_and_file_id(db, t)
202 }
203 hir::ModuleDef::Module(m) => {
204 target_name = m.name(db);
205 let in_file_source = m.declaration_source(db)?;
206 let file_id = in_file_source.file_id.original_file(db.upcast());
207 let syntax = in_file_source.value.syntax();
208 (vis_offset(syntax), syntax.text_range(), file_id)
209 }
210 // Enum variants can't be private, we can't modify builtin types
211 hir::ModuleDef::EnumVariant(_) | hir::ModuleDef::BuiltinType(_) => return None,
212 };
213
214 Some((offset, target, target_file, target_name))
215}
216
75fn vis_offset(node: &SyntaxNode) -> TextSize { 217fn vis_offset(node: &SyntaxNode) -> TextSize {
76 node.children_with_tokens() 218 node.children_with_tokens()
77 .skip_while(|it| match it.kind() { 219 .skip_while(|it| match it.kind() {
@@ -192,6 +334,378 @@ mod tests {
192 } 334 }
193 335
194 #[test] 336 #[test]
337 fn change_visibility_of_fn_via_path() {
338 check_assist(
339 change_visibility,
340 r"mod foo { fn foo() {} }
341 fn main() { foo::foo<|>() } ",
342 r"mod foo { <|>pub(crate) fn foo() {} }
343 fn main() { foo::foo() } ",
344 );
345 check_assist_not_applicable(
346 change_visibility,
347 r"mod foo { pub fn foo() {} }
348 fn main() { foo::foo<|>() } ",
349 )
350 }
351
352 #[test]
353 fn change_visibility_of_adt_in_submodule_via_path() {
354 check_assist(
355 change_visibility,
356 r"mod foo { struct Foo; }
357 fn main() { foo::Foo<|> } ",
358 r"mod foo { <|>pub(crate) struct Foo; }
359 fn main() { foo::Foo } ",
360 );
361 check_assist_not_applicable(
362 change_visibility,
363 r"mod foo { pub struct Foo; }
364 fn main() { foo::Foo<|> } ",
365 );
366 check_assist(
367 change_visibility,
368 r"mod foo { enum Foo; }
369 fn main() { foo::Foo<|> } ",
370 r"mod foo { <|>pub(crate) enum Foo; }
371 fn main() { foo::Foo } ",
372 );
373 check_assist_not_applicable(
374 change_visibility,
375 r"mod foo { pub enum Foo; }
376 fn main() { foo::Foo<|> } ",
377 );
378 check_assist(
379 change_visibility,
380 r"mod foo { union Foo; }
381 fn main() { foo::Foo<|> } ",
382 r"mod foo { <|>pub(crate) union Foo; }
383 fn main() { foo::Foo } ",
384 );
385 check_assist_not_applicable(
386 change_visibility,
387 r"mod foo { pub union Foo; }
388 fn main() { foo::Foo<|> } ",
389 );
390 }
391
392 #[test]
393 fn change_visibility_of_adt_in_other_file_via_path() {
394 check_assist(
395 change_visibility,
396 r"
397 //- /main.rs
398 mod foo;
399 fn main() { foo::Foo<|> }
400
401 //- /foo.rs
402 struct Foo;
403 ",
404 r"<|>pub(crate) struct Foo;
405
406",
407 );
408 }
409
410 #[test]
411 fn change_visibility_of_struct_field_via_path() {
412 check_assist(
413 change_visibility,
414 r"mod foo { pub struct Foo { bar: (), } }
415 fn main() { foo::Foo { <|>bar: () }; } ",
416 r"mod foo { pub struct Foo { <|>pub(crate) bar: (), } }
417 fn main() { foo::Foo { bar: () }; } ",
418 );
419 check_assist(
420 change_visibility,
421 r"//- /lib.rs
422 mod foo;
423 fn main() { foo::Foo { <|>bar: () }; }
424 //- /foo.rs
425 pub struct Foo { bar: () }
426 ",
427 r"pub struct Foo { <|>pub(crate) bar: () }
428
429",
430 );
431 check_assist_not_applicable(
432 change_visibility,
433 r"mod foo { pub struct Foo { pub bar: (), } }
434 fn main() { foo::Foo { <|>bar: () }; } ",
435 );
436 check_assist_not_applicable(
437 change_visibility,
438 r"//- /lib.rs
439 mod foo;
440 fn main() { foo::Foo { <|>bar: () }; }
441 //- /foo.rs
442 pub struct Foo { pub bar: () }
443 ",
444 );
445 }
446
447 #[test]
448 fn change_visibility_of_enum_variant_field_via_path() {
449 check_assist(
450 change_visibility,
451 r"mod foo { pub enum Foo { Bar { bar: () } } }
452 fn main() { foo::Foo::Bar { <|>bar: () }; } ",
453 r"mod foo { pub enum Foo { Bar { <|>pub(crate) bar: () } } }
454 fn main() { foo::Foo::Bar { bar: () }; } ",
455 );
456 check_assist(
457 change_visibility,
458 r"//- /lib.rs
459 mod foo;
460 fn main() { foo::Foo::Bar { <|>bar: () }; }
461 //- /foo.rs
462 pub enum Foo { Bar { bar: () } }
463 ",
464 r"pub enum Foo { Bar { <|>pub(crate) bar: () } }
465
466",
467 );
468 check_assist_not_applicable(
469 change_visibility,
470 r"mod foo { pub struct Foo { pub bar: (), } }
471 fn main() { foo::Foo { <|>bar: () }; } ",
472 );
473 check_assist_not_applicable(
474 change_visibility,
475 r"//- /lib.rs
476 mod foo;
477 fn main() { foo::Foo { <|>bar: () }; }
478 //- /foo.rs
479 pub struct Foo { pub bar: () }
480 ",
481 );
482 }
483
484 #[test]
485 #[ignore]
486 // FIXME reenable this test when `Semantics::resolve_record_field` works with union fields
487 fn change_visibility_of_union_field_via_path() {
488 check_assist(
489 change_visibility,
490 r"mod foo { pub union Foo { bar: (), } }
491 fn main() { foo::Foo { <|>bar: () }; } ",
492 r"mod foo { pub union Foo { <|>pub(crate) bar: (), } }
493 fn main() { foo::Foo { bar: () }; } ",
494 );
495 check_assist(
496 change_visibility,
497 r"//- /lib.rs
498 mod foo;
499 fn main() { foo::Foo { <|>bar: () }; }
500 //- /foo.rs
501 pub union Foo { bar: () }
502 ",
503 r"pub union Foo { <|>pub(crate) bar: () }
504
505",
506 );
507 check_assist_not_applicable(
508 change_visibility,
509 r"mod foo { pub union Foo { pub bar: (), } }
510 fn main() { foo::Foo { <|>bar: () }; } ",
511 );
512 check_assist_not_applicable(
513 change_visibility,
514 r"//- /lib.rs
515 mod foo;
516 fn main() { foo::Foo { <|>bar: () }; }
517 //- /foo.rs
518 pub union Foo { pub bar: () }
519 ",
520 );
521 }
522
523 #[test]
524 fn not_applicable_for_enum_variants() {
525 check_assist_not_applicable(
526 change_visibility,
527 r"mod foo { pub enum Foo {Foo1} }
528 fn main() { foo::Foo::Foo1<|> } ",
529 );
530 }
531
532 #[test]
533 fn change_visibility_of_const_via_path() {
534 check_assist(
535 change_visibility,
536 r"mod foo { const FOO: () = (); }
537 fn main() { foo::FOO<|> } ",
538 r"mod foo { <|>pub(crate) const FOO: () = (); }
539 fn main() { foo::FOO } ",
540 );
541 check_assist_not_applicable(
542 change_visibility,
543 r"mod foo { pub const FOO: () = (); }
544 fn main() { foo::FOO<|> } ",
545 );
546 }
547
548 #[test]
549 fn change_visibility_of_static_via_path() {
550 check_assist(
551 change_visibility,
552 r"mod foo { static FOO: () = (); }
553 fn main() { foo::FOO<|> } ",
554 r"mod foo { <|>pub(crate) static FOO: () = (); }
555 fn main() { foo::FOO } ",
556 );
557 check_assist_not_applicable(
558 change_visibility,
559 r"mod foo { pub static FOO: () = (); }
560 fn main() { foo::FOO<|> } ",
561 );
562 }
563
564 #[test]
565 fn change_visibility_of_trait_via_path() {
566 check_assist(
567 change_visibility,
568 r"mod foo { trait Foo { fn foo(&self) {} } }
569 fn main() { let x: &dyn foo::<|>Foo; } ",
570 r"mod foo { <|>pub(crate) trait Foo { fn foo(&self) {} } }
571 fn main() { let x: &dyn foo::Foo; } ",
572 );
573 check_assist_not_applicable(
574 change_visibility,
575 r"mod foo { pub trait Foo { fn foo(&self) {} } }
576 fn main() { let x: &dyn foo::Foo<|>; } ",
577 );
578 }
579
580 #[test]
581 fn change_visibility_of_type_alias_via_path() {
582 check_assist(
583 change_visibility,
584 r"mod foo { type Foo = (); }
585 fn main() { let x: foo::Foo<|>; } ",
586 r"mod foo { <|>pub(crate) type Foo = (); }
587 fn main() { let x: foo::Foo; } ",
588 );
589 check_assist_not_applicable(
590 change_visibility,
591 r"mod foo { pub type Foo = (); }
592 fn main() { let x: foo::Foo<|>; } ",
593 );
594 }
595
596 #[test]
597 fn change_visibility_of_module_via_path() {
598 check_assist(
599 change_visibility,
600 r"mod foo { mod bar { fn bar() {} } }
601 fn main() { foo::bar<|>::bar(); } ",
602 r"mod foo { <|>pub(crate) mod bar { fn bar() {} } }
603 fn main() { foo::bar::bar(); } ",
604 );
605
606 check_assist(
607 change_visibility,
608 r"
609 //- /main.rs
610 mod foo;
611 fn main() { foo::bar<|>::baz(); }
612
613 //- /foo.rs
614 mod bar {
615 pub fn baz() {}
616 }
617 ",
618 r"<|>pub(crate) mod bar {
619 pub fn baz() {}
620}
621
622",
623 );
624
625 check_assist_not_applicable(
626 change_visibility,
627 r"mod foo { pub mod bar { pub fn bar() {} } }
628 fn main() { foo::bar<|>::bar(); } ",
629 );
630 }
631
632 #[test]
633 fn change_visibility_of_inline_module_in_other_file_via_path() {
634 check_assist(
635 change_visibility,
636 r"
637 //- /main.rs
638 mod foo;
639 fn main() { foo::bar<|>::baz(); }
640
641 //- /foo.rs
642 mod bar;
643
644 //- /foo/bar.rs
645 pub fn baz() {}
646 }
647 ",
648 r"<|>pub(crate) mod bar;
649",
650 );
651 }
652
653 #[test]
654 fn change_visibility_of_module_declaration_in_other_file_via_path() {
655 check_assist(
656 change_visibility,
657 r"//- /main.rs
658 mod foo;
659 fn main() { foo::bar<|>>::baz(); }
660
661 //- /foo.rs
662 mod bar {
663 pub fn baz() {}
664 }",
665 r"<|>pub(crate) mod bar {
666 pub fn baz() {}
667}
668",
669 );
670 }
671
672 #[test]
673 #[ignore]
674 // FIXME handle reexports properly
675 fn change_visibility_of_reexport() {
676 check_assist(
677 change_visibility,
678 r"
679 mod foo {
680 use bar::Baz;
681 mod bar { pub(super) struct Baz; }
682 }
683 foo::Baz<|>
684 ",
685 r"
686 mod foo {
687 <|>pub(crate) use bar::Baz;
688 mod bar { pub(super) struct Baz; }
689 }
690 foo::Baz
691 ",
692 )
693 }
694
695 #[test]
696 fn adds_pub_when_target_is_in_another_crate() {
697 check_assist(
698 change_visibility,
699 r"//- /main.rs crate:a deps:foo
700 foo::Bar<|>
701 //- /lib.rs crate:foo
702 struct Bar;",
703 r"<|>pub struct Bar;
704",
705 )
706 }
707
708 #[test]
195 fn change_visibility_target() { 709 fn change_visibility_target() {
196 check_assist_target(change_visibility, "<|>fn foo() {}", "fn"); 710 check_assist_target(change_visibility, "<|>fn foo() {}", "fn");
197 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)"); 711 check_assist_target(change_visibility, "pub(crate)<|> fn foo() {}", "pub(crate)");
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs
index 51d4c493e..f8f767091 100644
--- a/crates/ra_db/src/fixture.rs
+++ b/crates/ra_db/src/fixture.rs
@@ -2,7 +2,7 @@
2//! A fixture without metadata is parsed into a single source file. 2//! A fixture without metadata is parsed into a single source file.
3//! Use this to test functionality local to one file. 3//! Use this to test functionality local to one file.
4//! 4//!
5//! Example: 5//! Simple Example:
6//! ``` 6//! ```
7//! r#" 7//! r#"
8//! fn main() { 8//! fn main() {
@@ -15,7 +15,7 @@
15//! The basic form is specifying filenames, 15//! The basic form is specifying filenames,
16//! which is also how to define multiple files in a single test fixture 16//! which is also how to define multiple files in a single test fixture
17//! 17//!
18//! Example: 18//! Example using two files in the same crate:
19//! ``` 19//! ```
20//! " 20//! "
21//! //- /main.rs 21//! //- /main.rs
@@ -29,6 +29,20 @@
29//! " 29//! "
30//! ``` 30//! ```
31//! 31//!
32//! Example using two crates with one file each, with one crate depending on the other:
33//! ```
34//! r#"
35//! //- /main.rs crate:a deps:b
36//! fn main() {
37//! b::foo();
38//! }
39//! //- /lib.rs crate:b
40//! pub fn b() {
41//! println!("Hello World")
42//! }
43//! "#
44//! ```
45//!
32//! Metadata allows specifying all settings and variables 46//! Metadata allows specifying all settings and variables
33//! that are available in a real rust project: 47//! that are available in a real rust project:
34//! - crate names via `crate:cratename` 48//! - crate names via `crate:cratename`
@@ -36,7 +50,7 @@
36//! - configuration settings via `cfg:dbg=false,opt_level=2` 50//! - configuration settings via `cfg:dbg=false,opt_level=2`
37//! - environment variables via `env:PATH=/bin,RUST_LOG=debug` 51//! - environment variables via `env:PATH=/bin,RUST_LOG=debug`
38//! 52//!
39//! Example: 53//! Example using all available metadata:
40//! ``` 54//! ```
41//! " 55//! "
42//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo 56//! //- /lib.rs crate:foo deps:bar,baz cfg:foo=a,bar=b env:OUTDIR=path/to,OTHER=foo
diff --git a/crates/ra_flycheck/Cargo.toml b/crates/ra_flycheck/Cargo.toml
index eac502da5..1aa39bade 100644
--- a/crates/ra_flycheck/Cargo.toml
+++ b/crates/ra_flycheck/Cargo.toml
@@ -9,12 +9,8 @@ doctest = false
9 9
10[dependencies] 10[dependencies]
11crossbeam-channel = "0.4.0" 11crossbeam-channel = "0.4.0"
12lsp-types = { version = "0.74.0", features = ["proposed"] }
13log = "0.4.8" 12log = "0.4.8"
14cargo_metadata = "0.10.0" 13cargo_metadata = "0.10.0"
15serde_json = "1.0.48" 14serde_json = "1.0.48"
16jod-thread = "0.1.1" 15jod-thread = "0.1.1"
17ra_toolchain = { path = "../ra_toolchain" } 16ra_toolchain = { path = "../ra_toolchain" }
18
19[dev-dependencies]
20insta = "0.16.0"
diff --git a/crates/ra_flycheck/src/conv.rs b/crates/ra_flycheck/src/conv.rs
deleted file mode 100644
index 817543deb..000000000
--- a/crates/ra_flycheck/src/conv.rs
+++ /dev/null
@@ -1,341 +0,0 @@
1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format.
3use cargo_metadata::diagnostic::{
4 Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan,
5 DiagnosticSpanMacroExpansion,
6};
7use lsp_types::{
8 CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag,
9 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit,
10};
11use std::{
12 collections::HashMap,
13 fmt::Write,
14 path::{Component, Path, PathBuf, Prefix},
15 str::FromStr,
16};
17
18#[cfg(test)]
19mod test;
20
21/// Converts a Rust level string to a LSP severity
22fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
23 match val {
24 DiagnosticLevel::Ice => Some(DiagnosticSeverity::Error),
25 DiagnosticLevel::Error => Some(DiagnosticSeverity::Error),
26 DiagnosticLevel::Warning => Some(DiagnosticSeverity::Warning),
27 DiagnosticLevel::Note => Some(DiagnosticSeverity::Information),
28 DiagnosticLevel::Help => Some(DiagnosticSeverity::Hint),
29 DiagnosticLevel::Unknown => None,
30 }
31}
32
33/// Check whether a file name is from macro invocation
34fn is_from_macro(file_name: &str) -> bool {
35 file_name.starts_with('<') && file_name.ends_with('>')
36}
37
38/// Converts a Rust macro span to a LSP location recursively
39fn map_macro_span_to_location(
40 span_macro: &DiagnosticSpanMacroExpansion,
41 workspace_root: &PathBuf,
42) -> Option<Location> {
43 if !is_from_macro(&span_macro.span.file_name) {
44 return Some(map_span_to_location(&span_macro.span, workspace_root));
45 }
46
47 if let Some(expansion) = &span_macro.span.expansion {
48 return map_macro_span_to_location(&expansion, workspace_root);
49 }
50
51 None
52}
53
54/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
55fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
56 if span.expansion.is_some() {
57 let expansion = span.expansion.as_ref().unwrap();
58 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
59 return macro_range;
60 }
61 }
62
63 map_span_to_location_naive(span, workspace_root)
64}
65
66/// Converts a Rust span to a LSP location
67fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &PathBuf) -> Location {
68 let mut file_name = workspace_root.clone();
69 file_name.push(&span.file_name);
70 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap();
71
72 let range = Range::new(
73 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
74 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
75 );
76
77 Location { uri, range }
78}
79
80/// Converts a secondary Rust span to a LSP related information
81///
82/// If the span is unlabelled this will return `None`.
83fn map_secondary_span_to_related(
84 span: &DiagnosticSpan,
85 workspace_root: &PathBuf,
86) -> Option<DiagnosticRelatedInformation> {
87 if let Some(label) = &span.label {
88 let location = map_span_to_location(span, workspace_root);
89 Some(DiagnosticRelatedInformation { location, message: label.clone() })
90 } else {
91 // Nothing to label this with
92 None
93 }
94}
95
96/// Determines if diagnostic is related to unused code
97fn is_unused_or_unnecessary(rd: &RustDiagnostic) -> bool {
98 if let Some(code) = &rd.code {
99 match code.code.as_str() {
100 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
101 | "unused_imports" | "unused_macros" | "unused_variables" => true,
102 _ => false,
103 }
104 } else {
105 false
106 }
107}
108
109/// Determines if diagnostic is related to deprecated code
110fn is_deprecated(rd: &RustDiagnostic) -> bool {
111 if let Some(code) = &rd.code {
112 match code.code.as_str() {
113 "deprecated" => true,
114 _ => false,
115 }
116 } else {
117 false
118 }
119}
120
121enum MappedRustChildDiagnostic {
122 Related(DiagnosticRelatedInformation),
123 SuggestedFix(CodeAction),
124 MessageLine(String),
125}
126
127fn map_rust_child_diagnostic(
128 rd: &RustDiagnostic,
129 workspace_root: &PathBuf,
130) -> MappedRustChildDiagnostic {
131 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
132 if spans.is_empty() {
133 // `rustc` uses these spanless children as a way to print multi-line
134 // messages
135 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
136 }
137
138 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new();
139 for &span in &spans {
140 match (&span.suggestion_applicability, &span.suggested_replacement) {
141 (Some(Applicability::MachineApplicable), Some(suggested_replacement)) => {
142 let location = map_span_to_location(span, workspace_root);
143 let edit = TextEdit::new(location.range, suggested_replacement.clone());
144 edit_map.entry(location.uri).or_default().push(edit);
145 }
146 _ => {}
147 }
148 }
149
150 if !edit_map.is_empty() {
151 MappedRustChildDiagnostic::SuggestedFix(CodeAction {
152 title: rd.message.clone(),
153 kind: Some("quickfix".to_string()),
154 diagnostics: None,
155 edit: Some(WorkspaceEdit::new(edit_map)),
156 command: None,
157 is_preferred: None,
158 })
159 } else {
160 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
161 location: map_span_to_location(spans[0], workspace_root),
162 message: rd.message.clone(),
163 })
164 }
165}
166
167#[derive(Debug)]
168pub(crate) struct MappedRustDiagnostic {
169 pub location: Location,
170 pub diagnostic: Diagnostic,
171 pub fixes: Vec<CodeAction>,
172}
173
174/// Converts a Rust root diagnostic to LSP form
175///
176/// This flattens the Rust diagnostic by:
177///
178/// 1. Creating a LSP diagnostic with the root message and primary span.
179/// 2. Adding any labelled secondary spans to `relatedInformation`
180/// 3. Categorising child diagnostics as either `SuggestedFix`es,
181/// `relatedInformation` or additional message lines.
182///
183/// If the diagnostic has no primary span this will return `None`
184pub(crate) fn map_rust_diagnostic_to_lsp(
185 rd: &RustDiagnostic,
186 workspace_root: &PathBuf,
187) -> Vec<MappedRustDiagnostic> {
188 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
189 if primary_spans.is_empty() {
190 return vec![];
191 }
192
193 let severity = map_level_to_severity(rd.level);
194
195 let mut source = String::from("rustc");
196 let mut code = rd.code.as_ref().map(|c| c.code.clone());
197 if let Some(code_val) = &code {
198 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
199 let scoped_code: Vec<&str> = code_val.split("::").collect();
200 if scoped_code.len() == 2 {
201 source = String::from(scoped_code[0]);
202 code = Some(String::from(scoped_code[1]));
203 }
204 }
205
206 let mut needs_primary_span_label = true;
207 let mut related_information = vec![];
208 let mut tags = vec![];
209
210 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
211 let related = map_secondary_span_to_related(secondary_span, workspace_root);
212 if let Some(related) = related {
213 related_information.push(related);
214 }
215 }
216
217 let mut fixes = vec![];
218 let mut message = rd.message.clone();
219 for child in &rd.children {
220 let child = map_rust_child_diagnostic(&child, workspace_root);
221 match child {
222 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
223 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
224 MappedRustChildDiagnostic::MessageLine(message_line) => {
225 write!(&mut message, "\n{}", message_line).unwrap();
226
227 // These secondary messages usually duplicate the content of the
228 // primary span label.
229 needs_primary_span_label = false;
230 }
231 }
232 }
233
234 if is_unused_or_unnecessary(rd) {
235 tags.push(DiagnosticTag::Unnecessary);
236 }
237
238 if is_deprecated(rd) {
239 tags.push(DiagnosticTag::Deprecated);
240 }
241
242 primary_spans
243 .iter()
244 .map(|primary_span| {
245 let location = map_span_to_location(&primary_span, workspace_root);
246
247 let mut message = message.clone();
248 if needs_primary_span_label {
249 if let Some(primary_span_label) = &primary_span.label {
250 write!(&mut message, "\n{}", primary_span_label).unwrap();
251 }
252 }
253
254 // If error occurs from macro expansion, add related info pointing to
255 // where the error originated
256 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
257 let def_loc = map_span_to_location_naive(&primary_span, workspace_root);
258 related_information.push(DiagnosticRelatedInformation {
259 location: def_loc,
260 message: "Error originated from macro here".to_string(),
261 });
262 }
263
264 let diagnostic = Diagnostic {
265 range: location.range,
266 severity,
267 code: code.clone().map(NumberOrString::String),
268 source: Some(source.clone()),
269 message,
270 related_information: if !related_information.is_empty() {
271 Some(related_information.clone())
272 } else {
273 None
274 },
275 tags: if !tags.is_empty() { Some(tags.clone()) } else { None },
276 };
277
278 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() }
279 })
280 .collect()
281}
282
283/// Returns a `Url` object from a given path, will lowercase drive letters if present.
284/// This will only happen when processing windows paths.
285///
286/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
287pub fn url_from_path_with_drive_lowercasing(
288 path: impl AsRef<Path>,
289) -> Result<Url, Box<dyn std::error::Error + Send + Sync>> {
290 let component_has_windows_drive = path.as_ref().components().any(|comp| {
291 if let Component::Prefix(c) = comp {
292 match c.kind() {
293 Prefix::Disk(_) | Prefix::VerbatimDisk(_) => return true,
294 _ => return false,
295 }
296 }
297 false
298 });
299
300 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
301 if component_has_windows_drive {
302 let url_original = Url::from_file_path(&path)
303 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
304
305 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
306
307 // There is a drive partition, but we never found a colon.
308 // This should not happen, but in this case we just pass it through.
309 if drive_partition.len() == 1 {
310 return Ok(url_original);
311 }
312
313 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
314 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
315
316 Ok(url)
317 } else {
318 Ok(Url::from_file_path(&path)
319 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?)
320 }
321}
322
323// `Url` is not able to parse windows paths on unix machines.
324#[cfg(target_os = "windows")]
325#[cfg(test)]
326mod path_conversion_windows_tests {
327 use super::url_from_path_with_drive_lowercasing;
328 #[test]
329 fn test_lowercase_drive_letter_with_drive() {
330 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
331
332 assert_eq!(url.to_string(), "file:///c:/Test");
333 }
334
335 #[test]
336 fn test_drive_without_colon_passthrough() {
337 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
338
339 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
340 }
341}
diff --git a/crates/ra_flycheck/src/conv/test.rs b/crates/ra_flycheck/src/conv/test.rs
deleted file mode 100644
index 4e81455ca..000000000
--- a/crates/ra_flycheck/src/conv/test.rs
+++ /dev/null
@@ -1,1072 +0,0 @@
1//! This module contains the large and verbose snapshot tests for the
2//! conversions between `cargo check` json and LSP diagnostics.
3#[cfg(not(windows))]
4use crate::*;
5
6#[cfg(not(windows))]
7fn parse_diagnostic(val: &str) -> cargo_metadata::diagnostic::Diagnostic {
8 serde_json::from_str::<cargo_metadata::diagnostic::Diagnostic>(val).unwrap()
9}
10
11#[test]
12#[cfg(not(windows))]
13fn snap_rustc_incompatible_type_for_trait() {
14 let diag = parse_diagnostic(
15 r##"{
16 "message": "method `next` has an incompatible type for trait",
17 "code": {
18 "code": "E0053",
19 "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n"
20 },
21 "level": "error",
22 "spans": [
23 {
24 "file_name": "compiler/ty/list_iter.rs",
25 "byte_start": 1307,
26 "byte_end": 1350,
27 "line_start": 52,
28 "line_end": 52,
29 "column_start": 5,
30 "column_end": 48,
31 "is_primary": true,
32 "text": [
33 {
34 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
35 "highlight_start": 5,
36 "highlight_end": 48
37 }
38 ],
39 "label": "types differ in mutability",
40 "suggested_replacement": null,
41 "suggestion_applicability": null,
42 "expansion": null
43 }
44 ],
45 "children": [
46 {
47 "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`",
48 "code": null,
49 "level": "note",
50 "spans": [],
51 "children": [],
52 "rendered": null
53 }
54 ],
55 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
56 }
57 "##,
58 );
59
60 let workspace_root = PathBuf::from("/test/");
61 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
62 insta::assert_debug_snapshot!(diag);
63}
64
65#[test]
66#[cfg(not(windows))]
67fn snap_rustc_unused_variable() {
68 let diag = parse_diagnostic(
69 r##"{
70"message": "unused variable: `foo`",
71"code": {
72 "code": "unused_variables",
73 "explanation": null
74},
75"level": "warning",
76"spans": [
77 {
78 "file_name": "driver/subcommand/repl.rs",
79 "byte_start": 9228,
80 "byte_end": 9231,
81 "line_start": 291,
82 "line_end": 291,
83 "column_start": 9,
84 "column_end": 12,
85 "is_primary": true,
86 "text": [
87 {
88 "text": " let foo = 42;",
89 "highlight_start": 9,
90 "highlight_end": 12
91 }
92 ],
93 "label": null,
94 "suggested_replacement": null,
95 "suggestion_applicability": null,
96 "expansion": null
97 }
98],
99"children": [
100 {
101 "message": "#[warn(unused_variables)] on by default",
102 "code": null,
103 "level": "note",
104 "spans": [],
105 "children": [],
106 "rendered": null
107 },
108 {
109 "message": "consider prefixing with an underscore",
110 "code": null,
111 "level": "help",
112 "spans": [
113 {
114 "file_name": "driver/subcommand/repl.rs",
115 "byte_start": 9228,
116 "byte_end": 9231,
117 "line_start": 291,
118 "line_end": 291,
119 "column_start": 9,
120 "column_end": 12,
121 "is_primary": true,
122 "text": [
123 {
124 "text": " let foo = 42;",
125 "highlight_start": 9,
126 "highlight_end": 12
127 }
128 ],
129 "label": null,
130 "suggested_replacement": "_foo",
131 "suggestion_applicability": "MachineApplicable",
132 "expansion": null
133 }
134 ],
135 "children": [],
136 "rendered": null
137 }
138],
139"rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
140}"##,
141 );
142
143 let workspace_root = PathBuf::from("/test/");
144 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
145 insta::assert_debug_snapshot!(diag);
146}
147
148#[test]
149#[cfg(not(windows))]
150fn snap_rustc_wrong_number_of_parameters() {
151 let diag = parse_diagnostic(
152 r##"{
153"message": "this function takes 2 parameters but 3 parameters were supplied",
154"code": {
155 "code": "E0061",
156 "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n"
157},
158"level": "error",
159"spans": [
160 {
161 "file_name": "compiler/ty/select.rs",
162 "byte_start": 8787,
163 "byte_end": 9241,
164 "line_start": 219,
165 "line_end": 231,
166 "column_start": 5,
167 "column_end": 6,
168 "is_primary": false,
169 "text": [
170 {
171 "text": " pub fn add_evidence(",
172 "highlight_start": 5,
173 "highlight_end": 25
174 },
175 {
176 "text": " &mut self,",
177 "highlight_start": 1,
178 "highlight_end": 19
179 },
180 {
181 "text": " target_poly: &ty::Ref<ty::Poly>,",
182 "highlight_start": 1,
183 "highlight_end": 41
184 },
185 {
186 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
187 "highlight_start": 1,
188 "highlight_end": 43
189 },
190 {
191 "text": " ) {",
192 "highlight_start": 1,
193 "highlight_end": 8
194 },
195 {
196 "text": " match target_poly {",
197 "highlight_start": 1,
198 "highlight_end": 28
199 },
200 {
201 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
202 "highlight_start": 1,
203 "highlight_end": 81
204 },
205 {
206 "text": " ty::Ref::Fixed(target_ty) => {",
207 "highlight_start": 1,
208 "highlight_end": 43
209 },
210 {
211 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
212 "highlight_start": 1,
213 "highlight_end": 65
214 },
215 {
216 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
217 "highlight_start": 1,
218 "highlight_end": 76
219 },
220 {
221 "text": " }",
222 "highlight_start": 1,
223 "highlight_end": 14
224 },
225 {
226 "text": " }",
227 "highlight_start": 1,
228 "highlight_end": 10
229 },
230 {
231 "text": " }",
232 "highlight_start": 1,
233 "highlight_end": 6
234 }
235 ],
236 "label": "defined here",
237 "suggested_replacement": null,
238 "suggestion_applicability": null,
239 "expansion": null
240 },
241 {
242 "file_name": "compiler/ty/select.rs",
243 "byte_start": 4045,
244 "byte_end": 4057,
245 "line_start": 104,
246 "line_end": 104,
247 "column_start": 18,
248 "column_end": 30,
249 "is_primary": true,
250 "text": [
251 {
252 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
253 "highlight_start": 18,
254 "highlight_end": 30
255 }
256 ],
257 "label": "expected 2 parameters",
258 "suggested_replacement": null,
259 "suggestion_applicability": null,
260 "expansion": null
261 }
262],
263"children": [],
264"rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
265}"##,
266 );
267
268 let workspace_root = PathBuf::from("/test/");
269 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
270 insta::assert_debug_snapshot!(diag);
271}
272
273#[test]
274#[cfg(not(windows))]
275fn snap_clippy_pass_by_ref() {
276 let diag = parse_diagnostic(
277 r##"{
278"message": "this argument is passed by reference, but would be more efficient if passed by value",
279"code": {
280 "code": "clippy::trivially_copy_pass_by_ref",
281 "explanation": null
282},
283"level": "warning",
284"spans": [
285 {
286 "file_name": "compiler/mir/tagset.rs",
287 "byte_start": 941,
288 "byte_end": 946,
289 "line_start": 42,
290 "line_end": 42,
291 "column_start": 24,
292 "column_end": 29,
293 "is_primary": true,
294 "text": [
295 {
296 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
297 "highlight_start": 24,
298 "highlight_end": 29
299 }
300 ],
301 "label": null,
302 "suggested_replacement": null,
303 "suggestion_applicability": null,
304 "expansion": null
305 }
306],
307"children": [
308 {
309 "message": "lint level defined here",
310 "code": null,
311 "level": "note",
312 "spans": [
313 {
314 "file_name": "compiler/lib.rs",
315 "byte_start": 8,
316 "byte_end": 19,
317 "line_start": 1,
318 "line_end": 1,
319 "column_start": 9,
320 "column_end": 20,
321 "is_primary": true,
322 "text": [
323 {
324 "text": "#![warn(clippy::all)]",
325 "highlight_start": 9,
326 "highlight_end": 20
327 }
328 ],
329 "label": null,
330 "suggested_replacement": null,
331 "suggestion_applicability": null,
332 "expansion": null
333 }
334 ],
335 "children": [],
336 "rendered": null
337 },
338 {
339 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
340 "code": null,
341 "level": "note",
342 "spans": [],
343 "children": [],
344 "rendered": null
345 },
346 {
347 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
348 "code": null,
349 "level": "help",
350 "spans": [],
351 "children": [],
352 "rendered": null
353 },
354 {
355 "message": "consider passing by value instead",
356 "code": null,
357 "level": "help",
358 "spans": [
359 {
360 "file_name": "compiler/mir/tagset.rs",
361 "byte_start": 941,
362 "byte_end": 946,
363 "line_start": 42,
364 "line_end": 42,
365 "column_start": 24,
366 "column_end": 29,
367 "is_primary": true,
368 "text": [
369 {
370 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
371 "highlight_start": 24,
372 "highlight_end": 29
373 }
374 ],
375 "label": null,
376 "suggested_replacement": "self",
377 "suggestion_applicability": "Unspecified",
378 "expansion": null
379 }
380 ],
381 "children": [],
382 "rendered": null
383 }
384],
385"rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
386}"##,
387 );
388
389 let workspace_root = PathBuf::from("/test/");
390 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
391 insta::assert_debug_snapshot!(diag);
392}
393
394#[test]
395#[cfg(not(windows))]
396fn snap_rustc_mismatched_type() {
397 let diag = parse_diagnostic(
398 r##"{
399"message": "mismatched types",
400"code": {
401 "code": "E0308",
402 "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
403},
404"level": "error",
405"spans": [
406 {
407 "file_name": "runtime/compiler_support.rs",
408 "byte_start": 1589,
409 "byte_end": 1594,
410 "line_start": 48,
411 "line_end": 48,
412 "column_start": 65,
413 "column_end": 70,
414 "is_primary": true,
415 "text": [
416 {
417 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
418 "highlight_start": 65,
419 "highlight_end": 70
420 }
421 ],
422 "label": "expected usize, found u32",
423 "suggested_replacement": null,
424 "suggestion_applicability": null,
425 "expansion": null
426 }
427],
428"children": [],
429"rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
430}"##,
431 );
432
433 let workspace_root = PathBuf::from("/test/");
434 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
435 insta::assert_debug_snapshot!(diag);
436}
437
438#[test]
439#[cfg(not(windows))]
440fn snap_handles_macro_location() {
441 let diag = parse_diagnostic(
442 r##"{
443"rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
444"children": [
445 {
446 "children": [],
447 "code": null,
448 "level": "help",
449 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
450 "rendered": null,
451 "spans": []
452 }
453],
454"code": {
455 "code": "E0277",
456 "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
457},
458"level": "error",
459"message": "can't compare `{integer}` with `&str`",
460"spans": [
461 {
462 "byte_end": 155,
463 "byte_start": 153,
464 "column_end": 33,
465 "column_start": 31,
466 "expansion": {
467 "def_site_span": {
468 "byte_end": 940,
469 "byte_start": 0,
470 "column_end": 6,
471 "column_start": 1,
472 "expansion": null,
473 "file_name": "<::core::macros::assert_eq macros>",
474 "is_primary": false,
475 "label": null,
476 "line_end": 36,
477 "line_start": 1,
478 "suggested_replacement": null,
479 "suggestion_applicability": null,
480 "text": [
481 {
482 "highlight_end": 35,
483 "highlight_start": 1,
484 "text": "($ left : expr, $ right : expr) =>"
485 },
486 {
487 "highlight_end": 3,
488 "highlight_start": 1,
489 "text": "({"
490 },
491 {
492 "highlight_end": 33,
493 "highlight_start": 1,
494 "text": " match (& $ left, & $ right)"
495 },
496 {
497 "highlight_end": 7,
498 "highlight_start": 1,
499 "text": " {"
500 },
501 {
502 "highlight_end": 34,
503 "highlight_start": 1,
504 "text": " (left_val, right_val) =>"
505 },
506 {
507 "highlight_end": 11,
508 "highlight_start": 1,
509 "text": " {"
510 },
511 {
512 "highlight_end": 46,
513 "highlight_start": 1,
514 "text": " if ! (* left_val == * right_val)"
515 },
516 {
517 "highlight_end": 15,
518 "highlight_start": 1,
519 "text": " {"
520 },
521 {
522 "highlight_end": 25,
523 "highlight_start": 1,
524 "text": " panic !"
525 },
526 {
527 "highlight_end": 57,
528 "highlight_start": 1,
529 "text": " (r#\"assertion failed: `(left == right)`"
530 },
531 {
532 "highlight_end": 16,
533 "highlight_start": 1,
534 "text": " left: `{:?}`,"
535 },
536 {
537 "highlight_end": 18,
538 "highlight_start": 1,
539 "text": " right: `{:?}`\"#,"
540 },
541 {
542 "highlight_end": 47,
543 "highlight_start": 1,
544 "text": " & * left_val, & * right_val)"
545 },
546 {
547 "highlight_end": 15,
548 "highlight_start": 1,
549 "text": " }"
550 },
551 {
552 "highlight_end": 11,
553 "highlight_start": 1,
554 "text": " }"
555 },
556 {
557 "highlight_end": 7,
558 "highlight_start": 1,
559 "text": " }"
560 },
561 {
562 "highlight_end": 42,
563 "highlight_start": 1,
564 "text": " }) ; ($ left : expr, $ right : expr,) =>"
565 },
566 {
567 "highlight_end": 49,
568 "highlight_start": 1,
569 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
570 },
571 {
572 "highlight_end": 53,
573 "highlight_start": 1,
574 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
575 },
576 {
577 "highlight_end": 3,
578 "highlight_start": 1,
579 "text": "({"
580 },
581 {
582 "highlight_end": 37,
583 "highlight_start": 1,
584 "text": " match (& ($ left), & ($ right))"
585 },
586 {
587 "highlight_end": 7,
588 "highlight_start": 1,
589 "text": " {"
590 },
591 {
592 "highlight_end": 34,
593 "highlight_start": 1,
594 "text": " (left_val, right_val) =>"
595 },
596 {
597 "highlight_end": 11,
598 "highlight_start": 1,
599 "text": " {"
600 },
601 {
602 "highlight_end": 46,
603 "highlight_start": 1,
604 "text": " if ! (* left_val == * right_val)"
605 },
606 {
607 "highlight_end": 15,
608 "highlight_start": 1,
609 "text": " {"
610 },
611 {
612 "highlight_end": 25,
613 "highlight_start": 1,
614 "text": " panic !"
615 },
616 {
617 "highlight_end": 57,
618 "highlight_start": 1,
619 "text": " (r#\"assertion failed: `(left == right)`"
620 },
621 {
622 "highlight_end": 16,
623 "highlight_start": 1,
624 "text": " left: `{:?}`,"
625 },
626 {
627 "highlight_end": 22,
628 "highlight_start": 1,
629 "text": " right: `{:?}`: {}\"#,"
630 },
631 {
632 "highlight_end": 72,
633 "highlight_start": 1,
634 "text": " & * left_val, & * right_val, $ crate :: format_args !"
635 },
636 {
637 "highlight_end": 33,
638 "highlight_start": 1,
639 "text": " ($ ($ arg) +))"
640 },
641 {
642 "highlight_end": 15,
643 "highlight_start": 1,
644 "text": " }"
645 },
646 {
647 "highlight_end": 11,
648 "highlight_start": 1,
649 "text": " }"
650 },
651 {
652 "highlight_end": 7,
653 "highlight_start": 1,
654 "text": " }"
655 },
656 {
657 "highlight_end": 6,
658 "highlight_start": 1,
659 "text": " }) ;"
660 }
661 ]
662 },
663 "macro_decl_name": "assert_eq!",
664 "span": {
665 "byte_end": 38,
666 "byte_start": 16,
667 "column_end": 27,
668 "column_start": 5,
669 "expansion": null,
670 "file_name": "src/main.rs",
671 "is_primary": false,
672 "label": null,
673 "line_end": 2,
674 "line_start": 2,
675 "suggested_replacement": null,
676 "suggestion_applicability": null,
677 "text": [
678 {
679 "highlight_end": 27,
680 "highlight_start": 5,
681 "text": " assert_eq!(1, \"love\");"
682 }
683 ]
684 }
685 },
686 "file_name": "<::core::macros::assert_eq macros>",
687 "is_primary": true,
688 "label": "no implementation for `{integer} == &str`",
689 "line_end": 7,
690 "line_start": 7,
691 "suggested_replacement": null,
692 "suggestion_applicability": null,
693 "text": [
694 {
695 "highlight_end": 33,
696 "highlight_start": 31,
697 "text": " if ! (* left_val == * right_val)"
698 }
699 ]
700 }
701]
702}"##,
703 );
704
705 let workspace_root = PathBuf::from("/test/");
706 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
707 insta::assert_debug_snapshot!(diag);
708}
709
710#[test]
711#[cfg(not(windows))]
712fn snap_macro_compiler_error() {
713 let diag = parse_diagnostic(
714 r##"{
715 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n",
716 "children": [],
717 "code": null,
718 "level": "error",
719 "message": "Please register your known path in the path module",
720 "spans": [
721 {
722 "byte_end": 8285,
723 "byte_start": 8217,
724 "column_end": 77,
725 "column_start": 9,
726 "expansion": {
727 "def_site_span": {
728 "byte_end": 8294,
729 "byte_start": 7858,
730 "column_end": 2,
731 "column_start": 1,
732 "expansion": null,
733 "file_name": "crates/ra_hir_def/src/path.rs",
734 "is_primary": false,
735 "label": null,
736 "line_end": 267,
737 "line_start": 254,
738 "suggested_replacement": null,
739 "suggestion_applicability": null,
740 "text": [
741 {
742 "highlight_end": 28,
743 "highlight_start": 1,
744 "text": "macro_rules! __known_path {"
745 },
746 {
747 "highlight_end": 37,
748 "highlight_start": 1,
749 "text": " (std::iter::IntoIterator) => {};"
750 },
751 {
752 "highlight_end": 33,
753 "highlight_start": 1,
754 "text": " (std::result::Result) => {};"
755 },
756 {
757 "highlight_end": 29,
758 "highlight_start": 1,
759 "text": " (std::ops::Range) => {};"
760 },
761 {
762 "highlight_end": 33,
763 "highlight_start": 1,
764 "text": " (std::ops::RangeFrom) => {};"
765 },
766 {
767 "highlight_end": 33,
768 "highlight_start": 1,
769 "text": " (std::ops::RangeFull) => {};"
770 },
771 {
772 "highlight_end": 31,
773 "highlight_start": 1,
774 "text": " (std::ops::RangeTo) => {};"
775 },
776 {
777 "highlight_end": 40,
778 "highlight_start": 1,
779 "text": " (std::ops::RangeToInclusive) => {};"
780 },
781 {
782 "highlight_end": 38,
783 "highlight_start": 1,
784 "text": " (std::ops::RangeInclusive) => {};"
785 },
786 {
787 "highlight_end": 27,
788 "highlight_start": 1,
789 "text": " (std::ops::Try) => {};"
790 },
791 {
792 "highlight_end": 22,
793 "highlight_start": 1,
794 "text": " ($path:path) => {"
795 },
796 {
797 "highlight_end": 77,
798 "highlight_start": 1,
799 "text": " compile_error!(\"Please register your known path in the path module\")"
800 },
801 {
802 "highlight_end": 7,
803 "highlight_start": 1,
804 "text": " };"
805 },
806 {
807 "highlight_end": 2,
808 "highlight_start": 1,
809 "text": "}"
810 }
811 ]
812 },
813 "macro_decl_name": "$crate::__known_path!",
814 "span": {
815 "byte_end": 8427,
816 "byte_start": 8385,
817 "column_end": 51,
818 "column_start": 9,
819 "expansion": {
820 "def_site_span": {
821 "byte_end": 8611,
822 "byte_start": 8312,
823 "column_end": 2,
824 "column_start": 1,
825 "expansion": null,
826 "file_name": "crates/ra_hir_def/src/path.rs",
827 "is_primary": false,
828 "label": null,
829 "line_end": 277,
830 "line_start": 270,
831 "suggested_replacement": null,
832 "suggestion_applicability": null,
833 "text": [
834 {
835 "highlight_end": 22,
836 "highlight_start": 1,
837 "text": "macro_rules! __path {"
838 },
839 {
840 "highlight_end": 43,
841 "highlight_start": 1,
842 "text": " ($start:ident $(:: $seg:ident)*) => ({"
843 },
844 {
845 "highlight_end": 51,
846 "highlight_start": 1,
847 "text": " $crate::__known_path!($start $(:: $seg)*);"
848 },
849 {
850 "highlight_end": 87,
851 "highlight_start": 1,
852 "text": " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
853 },
854 {
855 "highlight_end": 76,
856 "highlight_start": 1,
857 "text": " $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
858 },
859 {
860 "highlight_end": 11,
861 "highlight_start": 1,
862 "text": " ])"
863 },
864 {
865 "highlight_end": 8,
866 "highlight_start": 1,
867 "text": " });"
868 },
869 {
870 "highlight_end": 2,
871 "highlight_start": 1,
872 "text": "}"
873 }
874 ]
875 },
876 "macro_decl_name": "path!",
877 "span": {
878 "byte_end": 2966,
879 "byte_start": 2940,
880 "column_end": 42,
881 "column_start": 16,
882 "expansion": null,
883 "file_name": "crates/ra_hir_def/src/data.rs",
884 "is_primary": false,
885 "label": null,
886 "line_end": 80,
887 "line_start": 80,
888 "suggested_replacement": null,
889 "suggestion_applicability": null,
890 "text": [
891 {
892 "highlight_end": 42,
893 "highlight_start": 16,
894 "text": " let path = path![std::future::Future];"
895 }
896 ]
897 }
898 },
899 "file_name": "crates/ra_hir_def/src/path.rs",
900 "is_primary": false,
901 "label": null,
902 "line_end": 272,
903 "line_start": 272,
904 "suggested_replacement": null,
905 "suggestion_applicability": null,
906 "text": [
907 {
908 "highlight_end": 51,
909 "highlight_start": 9,
910 "text": " $crate::__known_path!($start $(:: $seg)*);"
911 }
912 ]
913 }
914 },
915 "file_name": "crates/ra_hir_def/src/path.rs",
916 "is_primary": true,
917 "label": null,
918 "line_end": 265,
919 "line_start": 265,
920 "suggested_replacement": null,
921 "suggestion_applicability": null,
922 "text": [
923 {
924 "highlight_end": 77,
925 "highlight_start": 9,
926 "text": " compile_error!(\"Please register your known path in the path module\")"
927 }
928 ]
929 }
930 ]
931}
932 "##,
933 );
934
935 let workspace_root = PathBuf::from("/test/");
936 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
937 insta::assert_debug_snapshot!(diag);
938}
939
940#[test]
941#[cfg(not(windows))]
942fn snap_multi_line_fix() {
943 let diag = parse_diagnostic(
944 r##"{
945 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n",
946 "children": [
947 {
948 "children": [],
949 "code": null,
950 "level": "note",
951 "message": "`#[warn(clippy::let_and_return)]` on by default",
952 "rendered": null,
953 "spans": []
954 },
955 {
956 "children": [],
957 "code": null,
958 "level": "help",
959 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
960 "rendered": null,
961 "spans": []
962 },
963 {
964 "children": [],
965 "code": null,
966 "level": "help",
967 "message": "return the expression directly",
968 "rendered": null,
969 "spans": [
970 {
971 "byte_end": 55,
972 "byte_start": 29,
973 "column_end": 31,
974 "column_start": 5,
975 "expansion": null,
976 "file_name": "src/main.rs",
977 "is_primary": true,
978 "label": null,
979 "line_end": 3,
980 "line_start": 3,
981 "suggested_replacement": "",
982 "suggestion_applicability": "MachineApplicable",
983 "text": [
984 {
985 "highlight_end": 31,
986 "highlight_start": 5,
987 "text": " let a = (0..10).collect();"
988 }
989 ]
990 },
991 {
992 "byte_end": 61,
993 "byte_start": 60,
994 "column_end": 6,
995 "column_start": 5,
996 "expansion": null,
997 "file_name": "src/main.rs",
998 "is_primary": true,
999 "label": null,
1000 "line_end": 4,
1001 "line_start": 4,
1002 "suggested_replacement": "(0..10).collect()",
1003 "suggestion_applicability": "MachineApplicable",
1004 "text": [
1005 {
1006 "highlight_end": 6,
1007 "highlight_start": 5,
1008 "text": " a"
1009 }
1010 ]
1011 }
1012 ]
1013 }
1014 ],
1015 "code": {
1016 "code": "clippy::let_and_return",
1017 "explanation": null
1018 },
1019 "level": "warning",
1020 "message": "returning the result of a let binding from a block",
1021 "spans": [
1022 {
1023 "byte_end": 55,
1024 "byte_start": 29,
1025 "column_end": 31,
1026 "column_start": 5,
1027 "expansion": null,
1028 "file_name": "src/main.rs",
1029 "is_primary": false,
1030 "label": "unnecessary let binding",
1031 "line_end": 3,
1032 "line_start": 3,
1033 "suggested_replacement": null,
1034 "suggestion_applicability": null,
1035 "text": [
1036 {
1037 "highlight_end": 31,
1038 "highlight_start": 5,
1039 "text": " let a = (0..10).collect();"
1040 }
1041 ]
1042 },
1043 {
1044 "byte_end": 61,
1045 "byte_start": 60,
1046 "column_end": 6,
1047 "column_start": 5,
1048 "expansion": null,
1049 "file_name": "src/main.rs",
1050 "is_primary": true,
1051 "label": null,
1052 "line_end": 4,
1053 "line_start": 4,
1054 "suggested_replacement": null,
1055 "suggestion_applicability": null,
1056 "text": [
1057 {
1058 "highlight_end": 6,
1059 "highlight_start": 5,
1060 "text": " a"
1061 }
1062 ]
1063 }
1064 ]
1065 }
1066 "##,
1067 );
1068
1069 let workspace_root = PathBuf::from("/test/");
1070 let diag = map_rust_diagnostic_to_lsp(&diag, &workspace_root);
1071 insta::assert_debug_snapshot!(diag);
1072}
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs
index d5efb6ab3..041e38a9f 100644
--- a/crates/ra_flycheck/src/lib.rs
+++ b/crates/ra_flycheck/src/lib.rs
@@ -1,10 +1,9 @@
1//! cargo_check provides the functionality needed to run `cargo check` or 1//! cargo_check provides the functionality needed to run `cargo check` or
2//! another compatible command (f.x. clippy) in a background thread and provide 2//! another compatible command (f.x. clippy) in a background thread and provide
3//! LSP diagnostics based on the output of the command. 3//! LSP diagnostics based on the output of the command.
4mod conv;
5 4
6use std::{ 5use std::{
7 io::{self, BufRead, BufReader}, 6 io::{self, BufReader},
8 path::PathBuf, 7 path::PathBuf,
9 process::{Command, Stdio}, 8 process::{Command, Stdio},
10 time::Instant, 9 time::Instant,
@@ -12,14 +11,10 @@ use std::{
12 11
13use cargo_metadata::Message; 12use cargo_metadata::Message;
14use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; 13use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender};
15use lsp_types::{
16 CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin,
17 WorkDoneProgressEnd, WorkDoneProgressReport,
18};
19
20use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic};
21 14
22pub use crate::conv::url_from_path_with_drive_lowercasing; 15pub use cargo_metadata::diagnostic::{
16 Applicability, Diagnostic, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion,
17};
23 18
24#[derive(Clone, Debug, PartialEq, Eq)] 19#[derive(Clone, Debug, PartialEq, Eq)]
25pub enum FlycheckConfig { 20pub enum FlycheckConfig {
@@ -61,10 +56,17 @@ pub enum CheckTask {
61 ClearDiagnostics, 56 ClearDiagnostics,
62 57
63 /// Request adding a diagnostic with fixes included to a file 58 /// Request adding a diagnostic with fixes included to a file
64 AddDiagnostic { url: Url, diagnostic: Diagnostic, fixes: Vec<CodeActionOrCommand> }, 59 AddDiagnostic { workspace_root: PathBuf, diagnostic: Diagnostic },
65 60
66 /// Request check progress notification to client 61 /// Request check progress notification to client
67 Status(WorkDoneProgress), 62 Status(Status),
63}
64
65#[derive(Debug)]
66pub enum Status {
67 Being,
68 Progress(String),
69 End,
68} 70}
69 71
70pub enum CheckCommand { 72pub enum CheckCommand {
@@ -131,9 +133,7 @@ impl FlycheckThread {
131 133
132 fn clean_previous_results(&self, task_send: &Sender<CheckTask>) { 134 fn clean_previous_results(&self, task_send: &Sender<CheckTask>) {
133 task_send.send(CheckTask::ClearDiagnostics).unwrap(); 135 task_send.send(CheckTask::ClearDiagnostics).unwrap();
134 task_send 136 task_send.send(CheckTask::Status(Status::End)).unwrap();
135 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd { message: None })))
136 .unwrap();
137 } 137 }
138 138
139 fn should_recheck(&mut self) -> bool { 139 fn should_recheck(&mut self) -> bool {
@@ -155,52 +155,24 @@ impl FlycheckThread {
155 fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) { 155 fn handle_message(&self, msg: CheckEvent, task_send: &Sender<CheckTask>) {
156 match msg { 156 match msg {
157 CheckEvent::Begin => { 157 CheckEvent::Begin => {
158 task_send 158 task_send.send(CheckTask::Status(Status::Being)).unwrap();
159 .send(CheckTask::Status(WorkDoneProgress::Begin(WorkDoneProgressBegin {
160 title: "Running `cargo check`".to_string(),
161 cancellable: Some(false),
162 message: None,
163 percentage: None,
164 })))
165 .unwrap();
166 } 159 }
167 160
168 CheckEvent::End => { 161 CheckEvent::End => {
169 task_send 162 task_send.send(CheckTask::Status(Status::End)).unwrap();
170 .send(CheckTask::Status(WorkDoneProgress::End(WorkDoneProgressEnd {
171 message: None,
172 })))
173 .unwrap();
174 } 163 }
175 164
176 CheckEvent::Msg(Message::CompilerArtifact(msg)) => { 165 CheckEvent::Msg(Message::CompilerArtifact(msg)) => {
177 task_send 166 task_send.send(CheckTask::Status(Status::Progress(msg.target.name))).unwrap();
178 .send(CheckTask::Status(WorkDoneProgress::Report(WorkDoneProgressReport {
179 cancellable: Some(false),
180 message: Some(msg.target.name),
181 percentage: None,
182 })))
183 .unwrap();
184 } 167 }
185 168
186 CheckEvent::Msg(Message::CompilerMessage(msg)) => { 169 CheckEvent::Msg(Message::CompilerMessage(msg)) => {
187 let map_result = map_rust_diagnostic_to_lsp(&msg.message, &self.workspace_root); 170 task_send
188 if map_result.is_empty() { 171 .send(CheckTask::AddDiagnostic {
189 return; 172 workspace_root: self.workspace_root.clone(),
190 } 173 diagnostic: msg.message,
191 174 })
192 for MappedRustDiagnostic { location, diagnostic, fixes } in map_result { 175 .unwrap();
193 let fixes = fixes
194 .into_iter()
195 .map(|fix| {
196 CodeAction { diagnostics: Some(vec![diagnostic.clone()]), ..fix }.into()
197 })
198 .collect();
199
200 task_send
201 .send(CheckTask::AddDiagnostic { url: location.uri, diagnostic, fixes })
202 .unwrap();
203 }
204 } 176 }
205 177
206 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {} 178 CheckEvent::Msg(Message::BuildScriptExecuted(_msg)) => {}
@@ -271,12 +243,6 @@ impl FlycheckThread {
271 } 243 }
272} 244}
273 245
274#[derive(Debug)]
275pub struct DiagnosticWithFixes {
276 diagnostic: Diagnostic,
277 fixes: Vec<CodeAction>,
278}
279
280enum CheckEvent { 246enum CheckEvent {
281 Begin, 247 Begin,
282 Msg(cargo_metadata::Message), 248 Msg(cargo_metadata::Message),
@@ -300,15 +266,11 @@ fn run_cargo(
300 // erroneus output. 266 // erroneus output.
301 let stdout = BufReader::new(child.stdout.take().unwrap()); 267 let stdout = BufReader::new(child.stdout.take().unwrap());
302 let mut read_at_least_one_message = false; 268 let mut read_at_least_one_message = false;
303 269 for message in cargo_metadata::Message::parse_stream(stdout) {
304 for line in stdout.lines() {
305 let line = line?;
306
307 let message = serde_json::from_str::<cargo_metadata::Message>(&line);
308 let message = match message { 270 let message = match message {
309 Ok(message) => message, 271 Ok(message) => message,
310 Err(err) => { 272 Err(err) => {
311 log::error!("Invalid json from cargo check, ignoring ({}): {:?} ", err, line); 273 log::error!("Invalid json from cargo check, ignoring ({})", err);
312 continue; 274 continue;
313 } 275 }
314 }; 276 };
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index e8e3211fc..840cfdfc8 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -989,6 +989,17 @@ impl TypeParam {
989 ty: InEnvironment { value: ty, environment }, 989 ty: InEnvironment { value: ty, environment },
990 } 990 }
991 } 991 }
992
993 pub fn default(self, db: &dyn HirDatabase) -> Option<Type> {
994 let params = db.generic_defaults(self.id.parent);
995 let local_idx = hir_ty::param_idx(db, self.id)?;
996 let resolver = self.id.parent.resolver(db.upcast());
997 let environment = TraitEnvironment::lower(db, &resolver);
998 params.get(local_idx).cloned().map(|ty| Type {
999 krate: self.id.parent.module(db.upcast()).krate,
1000 ty: InEnvironment { value: ty, environment },
1001 })
1002 }
992} 1003}
993 1004
994// FIXME: rename from `ImplDef` to `Impl` 1005// FIXME: rename from `ImplDef` to `Impl`
diff --git a/crates/ra_hir_ty/src/lib.rs b/crates/ra_hir_ty/src/lib.rs
index e8f3482fe..ccc4348f4 100644
--- a/crates/ra_hir_ty/src/lib.rs
+++ b/crates/ra_hir_ty/src/lib.rs
@@ -427,6 +427,11 @@ impl Substs {
427 } 427 }
428} 428}
429 429
430/// Return an index of a parameter in the generic type parameter list by it's id.
431pub fn param_idx(db: &dyn HirDatabase, id: TypeParamId) -> Option<usize> {
432 generics(db.upcast(), id.parent).param_idx(id)
433}
434
430#[derive(Debug, Clone)] 435#[derive(Debug, Clone)]
431pub struct SubstsBuilder { 436pub struct SubstsBuilder {
432 vec: Vec<Ty>, 437 vec: Vec<Ty>,
diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs
index b6b9627de..da336973c 100644
--- a/crates/ra_ide/src/completion/completion_context.rs
+++ b/crates/ra_ide/src/completion/completion_context.rs
@@ -34,7 +34,7 @@ pub(crate) struct CompletionContext<'a> {
34 pub(super) record_pat_syntax: Option<ast::RecordPat>, 34 pub(super) record_pat_syntax: Option<ast::RecordPat>,
35 pub(super) record_field_syntax: Option<ast::RecordField>, 35 pub(super) record_field_syntax: Option<ast::RecordField>,
36 pub(super) impl_def: Option<ast::ImplDef>, 36 pub(super) impl_def: Option<ast::ImplDef>,
37 /// FIXME: `ActiveParameter` is string-based, which is very wrong 37 /// FIXME: `ActiveParameter` is string-based, which is very very wrong
38 pub(super) active_parameter: Option<ActiveParameter>, 38 pub(super) active_parameter: Option<ActiveParameter>,
39 pub(super) is_param: bool, 39 pub(super) is_param: bool,
40 /// If a name-binding or reference to a const in a pattern. 40 /// If a name-binding or reference to a const in a pattern.
diff --git a/crates/ra_ide/src/completion/presentation.rs b/crates/ra_ide/src/completion/presentation.rs
index 2edb130cf..077cf9647 100644
--- a/crates/ra_ide/src/completion/presentation.rs
+++ b/crates/ra_ide/src/completion/presentation.rs
@@ -17,12 +17,11 @@ use crate::{
17impl Completions { 17impl Completions {
18 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) { 18 pub(crate) fn add_field(&mut self, ctx: &CompletionContext, field: hir::Field, ty: &Type) {
19 let is_deprecated = is_deprecated(field, ctx.db); 19 let is_deprecated = is_deprecated(field, ctx.db);
20 let ty = ty.display(ctx.db).to_string();
21 let name = field.name(ctx.db); 20 let name = field.name(ctx.db);
22 let mut completion_item = 21 let mut completion_item =
23 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string()) 22 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), name.to_string())
24 .kind(CompletionItemKind::Field) 23 .kind(CompletionItemKind::Field)
25 .detail(ty.clone()) 24 .detail(ty.display(ctx.db).to_string())
26 .set_documentation(field.docs(ctx.db)) 25 .set_documentation(field.docs(ctx.db))
27 .set_deprecated(is_deprecated); 26 .set_deprecated(is_deprecated);
28 27
@@ -107,6 +106,12 @@ impl Completions {
107 } 106 }
108 }; 107 };
109 108
109 if let ScopeDef::Local(local) = resolution {
110 if let Some(score) = compute_score(ctx, &local.ty(ctx.db), &local_name) {
111 completion_item = completion_item.set_score(score);
112 }
113 }
114
110 // Add `<>` for generic types 115 // Add `<>` for generic types
111 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis { 116 if ctx.is_path_type && !ctx.has_type_args && ctx.config.add_call_parenthesis {
112 if let Some(cap) = ctx.config.snippet_cap { 117 if let Some(cap) = ctx.config.snippet_cap {
@@ -319,10 +324,11 @@ impl Completions {
319 324
320pub(crate) fn compute_score( 325pub(crate) fn compute_score(
321 ctx: &CompletionContext, 326 ctx: &CompletionContext,
322 // FIXME: this definitely should be a `Type` 327 ty: &Type,
323 ty: &str,
324 name: &str, 328 name: &str,
325) -> Option<CompletionScore> { 329) -> Option<CompletionScore> {
330 // FIXME: this should not fall back to string equality.
331 let ty = &ty.display(ctx.db).to_string();
326 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax { 332 let (active_name, active_type) = if let Some(record_field) = &ctx.record_field_syntax {
327 tested_by!(test_struct_field_completion_in_record_lit); 333 tested_by!(test_struct_field_completion_in_record_lit);
328 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?; 334 let (struct_field, _local) = ctx.sema.resolve_record_field(record_field)?;
@@ -1405,4 +1411,48 @@ mod tests {
1405 "### 1411 "###
1406 ); 1412 );
1407 } 1413 }
1414
1415 #[test]
1416 fn prioritize_exact_ref_match() {
1417 assert_debug_snapshot!(
1418 do_reference_completion(
1419 r"
1420 struct WorldSnapshot { _f: () };
1421 fn go(world: &WorldSnapshot) {
1422 go(w<|>)
1423 }
1424 ",
1425 ),
1426 @r###"
1427 [
1428 CompletionItem {
1429 label: "WorldSnapshot",
1430 source_range: 132..133,
1431 delete: 132..133,
1432 insert: "WorldSnapshot",
1433 kind: Struct,
1434 },
1435 CompletionItem {
1436 label: "go(…)",
1437 source_range: 132..133,
1438 delete: 132..133,
1439 insert: "go(${1:world})$0",
1440 kind: Function,
1441 lookup: "go",
1442 detail: "fn go(world: &WorldSnapshot)",
1443 trigger_call_info: true,
1444 },
1445 CompletionItem {
1446 label: "world",
1447 source_range: 132..133,
1448 delete: 132..133,
1449 insert: "world",
1450 kind: Binding,
1451 detail: "&WorldSnapshot",
1452 score: TypeAndNameMatch,
1453 },
1454 ]
1455 "###
1456 );
1457 }
1408} 1458}
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs
index 915199bd8..78149ddfc 100644
--- a/crates/ra_ide/src/lib.rs
+++ b/crates/ra_ide/src/lib.rs
@@ -176,14 +176,10 @@ impl AnalysisHost {
176 pub fn request_cancellation(&mut self) { 176 pub fn request_cancellation(&mut self) {
177 self.db.request_cancellation(); 177 self.db.request_cancellation();
178 } 178 }
179 pub fn raw_database( 179 pub fn raw_database(&self) -> &RootDatabase {
180 &self,
181 ) -> &(impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) {
182 &self.db 180 &self.db
183 } 181 }
184 pub fn raw_database_mut( 182 pub fn raw_database_mut(&mut self) -> &mut RootDatabase {
185 &mut self,
186 ) -> &mut (impl hir::db::HirDatabase + salsa::Database + ra_db::SourceDatabaseExt) {
187 &mut self.db 183 &mut self.db
188 } 184 }
189} 185}
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 4f098b706..a2e9f65ef 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -35,7 +35,7 @@ pub enum ProjectWorkspace {
35/// `PackageRoot` describes a package root folder. 35/// `PackageRoot` describes a package root folder.
36/// Which may be an external dependency, or a member of 36/// Which may be an external dependency, or a member of
37/// the current workspace. 37/// the current workspace.
38#[derive(Clone)] 38#[derive(Debug, Clone)]
39pub struct PackageRoot { 39pub struct PackageRoot {
40 /// Path to the root folder 40 /// Path to the root folder
41 path: PathBuf, 41 path: PathBuf,
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index 12c5228f5..d0e960fb4 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -13,6 +13,10 @@ pub fn name_ref(text: &str) -> ast::NameRef {
13 ast_from_text(&format!("fn f() {{ {}; }}", text)) 13 ast_from_text(&format!("fn f() {{ {}; }}", text))
14} 14}
15 15
16pub fn type_ref(text: &str) -> ast::TypeRef {
17 ast_from_text(&format!("impl {} for D {{}};", text))
18}
19
16pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment { 20pub fn path_segment(name_ref: ast::NameRef) -> ast::PathSegment {
17 ast_from_text(&format!("use {};", name_ref)) 21 ast_from_text(&format!("use {};", name_ref))
18} 22}
diff --git a/crates/ra_text_edit/src/lib.rs b/crates/ra_text_edit/src/lib.rs
index 37f77cc47..c4f945101 100644
--- a/crates/ra_text_edit/src/lib.rs
+++ b/crates/ra_text_edit/src/lib.rs
@@ -21,7 +21,7 @@ pub struct TextEdit {
21 indels: Vec<Indel>, 21 indels: Vec<Indel>,
22} 22}
23 23
24#[derive(Debug, Default)] 24#[derive(Debug, Default, Clone)]
25pub struct TextEditBuilder { 25pub struct TextEditBuilder {
26 indels: Vec<Indel>, 26 indels: Vec<Indel>,
27} 27}
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml
index 8c94f430a..9b2d29b1d 100644
--- a/crates/rust-analyzer/Cargo.toml
+++ b/crates/rust-analyzer/Cargo.toml
@@ -53,6 +53,7 @@ winapi = "0.3.8"
53 53
54[dev-dependencies] 54[dev-dependencies]
55tempfile = "3.1.0" 55tempfile = "3.1.0"
56insta = "0.16.0"
56test_utils = { path = "../test_utils" } 57test_utils = { path = "../test_utils" }
57 58
58[features] 59[features]
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs
index a865a7c7e..39ce77900 100644
--- a/crates/rust-analyzer/src/cli.rs
+++ b/crates/rust-analyzer/src/cli.rs
@@ -16,6 +16,7 @@ use ra_syntax::{AstNode, SourceFile};
16pub use analysis_bench::{analysis_bench, BenchWhat, Position}; 16pub use analysis_bench::{analysis_bench, BenchWhat, Position};
17pub use analysis_stats::analysis_stats; 17pub use analysis_stats::analysis_stats;
18pub use diagnostics::diagnostics; 18pub use diagnostics::diagnostics;
19pub use load_cargo::load_cargo;
19 20
20#[derive(Clone, Copy)] 21#[derive(Clone, Copy)]
21pub enum Verbosity { 22pub enum Verbosity {
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 023ced6cf..8eaf75ff6 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -22,7 +22,7 @@ fn vfs_root_to_id(r: ra_vfs::VfsRoot) -> SourceRootId {
22 SourceRootId(r.0) 22 SourceRootId(r.0)
23} 23}
24 24
25pub(crate) fn load_cargo( 25pub fn load_cargo(
26 root: &Path, 26 root: &Path,
27 load_out_dirs_from_check: bool, 27 load_out_dirs_from_check: bool,
28 with_proc_macro: bool, 28 with_proc_macro: bool,
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index e7924f0a3..4bdd45a7d 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -1,4 +1,5 @@
1//! Book keeping for keeping diagnostics easily in sync with the client. 1//! Book keeping for keeping diagnostics easily in sync with the client.
2pub(crate) mod to_proto;
2 3
3use std::{collections::HashMap, sync::Arc}; 4use std::{collections::HashMap, sync::Arc};
4 5
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap
index 4c9db0385..d7f9ec049 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_clippy_pass_by_ref.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_clippy_pass_by_ref.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap
index 7cde4d867..a59faf254 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_handles_macro_location.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_handles_macro_location.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap
index 1cc37e087..3c78e7f36 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_macro_compiler_error.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_macro_compiler_error.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
index 615ed8378..076b3cf27 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_multi_line_fix.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_multi_line_fix.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap
index 0df0fce18..46d0c56d2 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_incompatible_type_for_trait.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_incompatible_type_for_trait.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap
index 28ebcb3b3..4182929ba 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_mismatched_type.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_mismatched_type.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
index 5e0873281..69138c15b 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_unused_variable.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap
index e500d3cd6..f6ab05004 100644
--- a/crates/ra_flycheck/src/conv/snapshots/ra_flycheck__conv__test__snap_rustc_wrong_number_of_parameters.snap
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_wrong_number_of_parameters.snap
@@ -1,5 +1,5 @@
1--- 1---
2source: crates/ra_flycheck/src/conv/test.rs 2source: crates/rust-analyzer/src/diagnostics/to_proto.rs
3expression: diag 3expression: diag
4--- 4---
5[ 5[
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
new file mode 100644
index 000000000..eabf4908f
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -0,0 +1,1397 @@
1//! This module provides the functionality needed to convert diagnostics from
2//! `cargo check` json format to the LSP diagnostic format.
3use std::{
4 collections::HashMap,
5 path::{Component, Path, Prefix},
6 str::FromStr,
7};
8
9use lsp_types::{
10 CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag,
11 Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit,
12};
13use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
14use stdx::format_to;
15
16use crate::Result;
17
18/// Converts a Rust level string to a LSP severity
19fn map_level_to_severity(val: DiagnosticLevel) -> Option<DiagnosticSeverity> {
20 let res = match val {
21 DiagnosticLevel::Ice => DiagnosticSeverity::Error,
22 DiagnosticLevel::Error => DiagnosticSeverity::Error,
23 DiagnosticLevel::Warning => DiagnosticSeverity::Warning,
24 DiagnosticLevel::Note => DiagnosticSeverity::Information,
25 DiagnosticLevel::Help => DiagnosticSeverity::Hint,
26 DiagnosticLevel::Unknown => return None,
27 };
28 Some(res)
29}
30
31/// Check whether a file name is from macro invocation
32fn is_from_macro(file_name: &str) -> bool {
33 file_name.starts_with('<') && file_name.ends_with('>')
34}
35
36/// Converts a Rust macro span to a LSP location recursively
37fn map_macro_span_to_location(
38 span_macro: &DiagnosticSpanMacroExpansion,
39 workspace_root: &Path,
40) -> Option<Location> {
41 if !is_from_macro(&span_macro.span.file_name) {
42 return Some(map_span_to_location(&span_macro.span, workspace_root));
43 }
44
45 if let Some(expansion) = &span_macro.span.expansion {
46 return map_macro_span_to_location(&expansion, workspace_root);
47 }
48
49 None
50}
51
52/// Converts a Rust span to a LSP location, resolving macro expansion site if neccesary
53fn map_span_to_location(span: &DiagnosticSpan, workspace_root: &Path) -> Location {
54 if span.expansion.is_some() {
55 let expansion = span.expansion.as_ref().unwrap();
56 if let Some(macro_range) = map_macro_span_to_location(&expansion, workspace_root) {
57 return macro_range;
58 }
59 }
60
61 map_span_to_location_naive(span, workspace_root)
62}
63
64/// Converts a Rust span to a LSP location
65fn map_span_to_location_naive(span: &DiagnosticSpan, workspace_root: &Path) -> Location {
66 let mut file_name = workspace_root.to_path_buf();
67 file_name.push(&span.file_name);
68 let uri = url_from_path_with_drive_lowercasing(file_name).unwrap();
69
70 // FIXME: this doesn't handle UTF16 offsets correctly
71 let range = Range::new(
72 Position::new(span.line_start as u64 - 1, span.column_start as u64 - 1),
73 Position::new(span.line_end as u64 - 1, span.column_end as u64 - 1),
74 );
75
76 Location { uri, range }
77}
78
79/// Converts a secondary Rust span to a LSP related information
80///
81/// If the span is unlabelled this will return `None`.
82fn map_secondary_span_to_related(
83 span: &DiagnosticSpan,
84 workspace_root: &Path,
85) -> Option<DiagnosticRelatedInformation> {
86 let message = span.label.clone()?;
87 let location = map_span_to_location(span, workspace_root);
88 Some(DiagnosticRelatedInformation { location, message })
89}
90
91/// Determines if diagnostic is related to unused code
92fn is_unused_or_unnecessary(rd: &ra_flycheck::Diagnostic) -> bool {
93 match &rd.code {
94 Some(code) => match code.code.as_str() {
95 "dead_code" | "unknown_lints" | "unreachable_code" | "unused_attributes"
96 | "unused_imports" | "unused_macros" | "unused_variables" => true,
97 _ => false,
98 },
99 None => false,
100 }
101}
102
103/// Determines if diagnostic is related to deprecated code
104fn is_deprecated(rd: &ra_flycheck::Diagnostic) -> bool {
105 match &rd.code {
106 Some(code) => code.code.as_str() == "deprecated",
107 None => false,
108 }
109}
110
111enum MappedRustChildDiagnostic {
112 Related(DiagnosticRelatedInformation),
113 SuggestedFix(CodeAction),
114 MessageLine(String),
115}
116
117fn map_rust_child_diagnostic(
118 rd: &ra_flycheck::Diagnostic,
119 workspace_root: &Path,
120) -> MappedRustChildDiagnostic {
121 let spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
122 if spans.is_empty() {
123 // `rustc` uses these spanless children as a way to print multi-line
124 // messages
125 return MappedRustChildDiagnostic::MessageLine(rd.message.clone());
126 }
127
128 let mut edit_map: HashMap<Url, Vec<TextEdit>> = HashMap::new();
129 for &span in &spans {
130 match (&span.suggestion_applicability, &span.suggested_replacement) {
131 (Some(Applicability::MachineApplicable), Some(suggested_replacement)) => {
132 let location = map_span_to_location(span, workspace_root);
133 let edit = TextEdit::new(location.range, suggested_replacement.clone());
134 edit_map.entry(location.uri).or_default().push(edit);
135 }
136 _ => {}
137 }
138 }
139
140 if edit_map.is_empty() {
141 MappedRustChildDiagnostic::Related(DiagnosticRelatedInformation {
142 location: map_span_to_location(spans[0], workspace_root),
143 message: rd.message.clone(),
144 })
145 } else {
146 MappedRustChildDiagnostic::SuggestedFix(CodeAction {
147 title: rd.message.clone(),
148 kind: Some("quickfix".to_string()),
149 diagnostics: None,
150 edit: Some(WorkspaceEdit::new(edit_map)),
151 command: None,
152 is_preferred: None,
153 })
154 }
155}
156
157#[derive(Debug)]
158pub(crate) struct MappedRustDiagnostic {
159 pub location: Location,
160 pub diagnostic: Diagnostic,
161 pub fixes: Vec<CodeAction>,
162}
163
164/// Converts a Rust root diagnostic to LSP form
165///
166/// This flattens the Rust diagnostic by:
167///
168/// 1. Creating a LSP diagnostic with the root message and primary span.
169/// 2. Adding any labelled secondary spans to `relatedInformation`
170/// 3. Categorising child diagnostics as either `SuggestedFix`es,
171/// `relatedInformation` or additional message lines.
172///
173/// If the diagnostic has no primary span this will return `None`
174pub(crate) fn map_rust_diagnostic_to_lsp(
175 rd: &ra_flycheck::Diagnostic,
176 workspace_root: &Path,
177) -> Vec<MappedRustDiagnostic> {
178 let primary_spans: Vec<&DiagnosticSpan> = rd.spans.iter().filter(|s| s.is_primary).collect();
179 if primary_spans.is_empty() {
180 return Vec::new();
181 }
182
183 let severity = map_level_to_severity(rd.level);
184
185 let mut source = String::from("rustc");
186 let mut code = rd.code.as_ref().map(|c| c.code.clone());
187 if let Some(code_val) = &code {
188 // See if this is an RFC #2103 scoped lint (e.g. from Clippy)
189 let scoped_code: Vec<&str> = code_val.split("::").collect();
190 if scoped_code.len() == 2 {
191 source = String::from(scoped_code[0]);
192 code = Some(String::from(scoped_code[1]));
193 }
194 }
195
196 let mut needs_primary_span_label = true;
197 let mut related_information = Vec::new();
198 let mut tags = Vec::new();
199
200 for secondary_span in rd.spans.iter().filter(|s| !s.is_primary) {
201 let related = map_secondary_span_to_related(secondary_span, workspace_root);
202 if let Some(related) = related {
203 related_information.push(related);
204 }
205 }
206
207 let mut fixes = Vec::new();
208 let mut message = rd.message.clone();
209 for child in &rd.children {
210 let child = map_rust_child_diagnostic(&child, workspace_root);
211 match child {
212 MappedRustChildDiagnostic::Related(related) => related_information.push(related),
213 MappedRustChildDiagnostic::SuggestedFix(code_action) => fixes.push(code_action),
214 MappedRustChildDiagnostic::MessageLine(message_line) => {
215 format_to!(message, "\n{}", message_line);
216
217 // These secondary messages usually duplicate the content of the
218 // primary span label.
219 needs_primary_span_label = false;
220 }
221 }
222 }
223
224 if is_unused_or_unnecessary(rd) {
225 tags.push(DiagnosticTag::Unnecessary);
226 }
227
228 if is_deprecated(rd) {
229 tags.push(DiagnosticTag::Deprecated);
230 }
231
232 primary_spans
233 .iter()
234 .map(|primary_span| {
235 let location = map_span_to_location(&primary_span, workspace_root);
236
237 let mut message = message.clone();
238 if needs_primary_span_label {
239 if let Some(primary_span_label) = &primary_span.label {
240 format_to!(message, "\n{}", primary_span_label);
241 }
242 }
243
244 // If error occurs from macro expansion, add related info pointing to
245 // where the error originated
246 if !is_from_macro(&primary_span.file_name) && primary_span.expansion.is_some() {
247 let def_loc = map_span_to_location_naive(&primary_span, workspace_root);
248 related_information.push(DiagnosticRelatedInformation {
249 location: def_loc,
250 message: "Error originated from macro here".to_string(),
251 });
252 }
253
254 let diagnostic = Diagnostic {
255 range: location.range,
256 severity,
257 code: code.clone().map(NumberOrString::String),
258 source: Some(source.clone()),
259 message,
260 related_information: if related_information.is_empty() {
261 None
262 } else {
263 Some(related_information.clone())
264 },
265 tags: if tags.is_empty() { None } else { Some(tags.clone()) },
266 };
267
268 MappedRustDiagnostic { location, diagnostic, fixes: fixes.clone() }
269 })
270 .collect()
271}
272
273/// Returns a `Url` object from a given path, will lowercase drive letters if present.
274/// This will only happen when processing windows paths.
275///
276/// When processing non-windows path, this is essentially the same as `Url::from_file_path`.
277pub fn url_from_path_with_drive_lowercasing(path: impl AsRef<Path>) -> Result<Url> {
278 let component_has_windows_drive = path.as_ref().components().any(|comp| {
279 if let Component::Prefix(c) = comp {
280 return matches!(c.kind(), Prefix::Disk(_) | Prefix::VerbatimDisk(_));
281 }
282 false
283 });
284
285 // VSCode expects drive letters to be lowercased, where rust will uppercase the drive letters.
286 let res = if component_has_windows_drive {
287 let url_original = Url::from_file_path(&path)
288 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?;
289
290 let drive_partition: Vec<&str> = url_original.as_str().rsplitn(2, ':').collect();
291
292 // There is a drive partition, but we never found a colon.
293 // This should not happen, but in this case we just pass it through.
294 if drive_partition.len() == 1 {
295 return Ok(url_original);
296 }
297
298 let joined = drive_partition[1].to_ascii_lowercase() + ":" + drive_partition[0];
299 let url = Url::from_str(&joined).expect("This came from a valid `Url`");
300
301 url
302 } else {
303 Url::from_file_path(&path)
304 .map_err(|_| format!("can't convert path to url: {}", path.as_ref().display()))?
305 };
306 Ok(res)
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 // `Url` is not able to parse windows paths on unix machines.
314 #[test]
315 #[cfg(target_os = "windows")]
316 fn test_lowercase_drive_letter_with_drive() {
317 let url = url_from_path_with_drive_lowercasing("C:\\Test").unwrap();
318
319 assert_eq!(url.to_string(), "file:///c:/Test");
320 }
321
322 #[test]
323 #[cfg(target_os = "windows")]
324 fn test_drive_without_colon_passthrough() {
325 let url = url_from_path_with_drive_lowercasing(r#"\\localhost\C$\my_dir"#).unwrap();
326
327 assert_eq!(url.to_string(), "file://localhost/C$/my_dir");
328 }
329
330 #[cfg(not(windows))]
331 fn parse_diagnostic(val: &str) -> ra_flycheck::Diagnostic {
332 serde_json::from_str::<ra_flycheck::Diagnostic>(val).unwrap()
333 }
334
335 #[test]
336 #[cfg(not(windows))]
337 fn snap_rustc_incompatible_type_for_trait() {
338 let diag = parse_diagnostic(
339 r##"{
340 "message": "method `next` has an incompatible type for trait",
341 "code": {
342 "code": "E0053",
343 "explanation": "\nThe parameters of any trait method must match between a trait implementation\nand the trait definition.\n\nHere are a couple examples of this error:\n\n```compile_fail,E0053\ntrait Foo {\n fn foo(x: u16);\n fn bar(&self);\n}\n\nstruct Bar;\n\nimpl Foo for Bar {\n // error, expected u16, found i16\n fn foo(x: i16) { }\n\n // error, types differ in mutability\n fn bar(&mut self) { }\n}\n```\n"
344 },
345 "level": "error",
346 "spans": [
347 {
348 "file_name": "compiler/ty/list_iter.rs",
349 "byte_start": 1307,
350 "byte_end": 1350,
351 "line_start": 52,
352 "line_end": 52,
353 "column_start": 5,
354 "column_end": 48,
355 "is_primary": true,
356 "text": [
357 {
358 "text": " fn next(&self) -> Option<&'list ty::Ref<M>> {",
359 "highlight_start": 5,
360 "highlight_end": 48
361 }
362 ],
363 "label": "types differ in mutability",
364 "suggested_replacement": null,
365 "suggestion_applicability": null,
366 "expansion": null
367 }
368 ],
369 "children": [
370 {
371 "message": "expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`",
372 "code": null,
373 "level": "note",
374 "spans": [],
375 "children": [],
376 "rendered": null
377 }
378 ],
379 "rendered": "error[E0053]: method `next` has an incompatible type for trait\n --> compiler/ty/list_iter.rs:52:5\n |\n52 | fn next(&self) -> Option<&'list ty::Ref<M>> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ in mutability\n |\n = note: expected type `fn(&mut ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&ty::Ref<M>>`\n found type `fn(&ty::list_iter::ListIterator<'list, M>) -> std::option::Option<&'list ty::Ref<M>>`\n\n"
380 }
381 "##,
382 );
383
384 let workspace_root = Path::new("/test/");
385 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
386 insta::assert_debug_snapshot!(diag);
387 }
388
389 #[test]
390 #[cfg(not(windows))]
391 fn snap_rustc_unused_variable() {
392 let diag = parse_diagnostic(
393 r##"{
394 "message": "unused variable: `foo`",
395 "code": {
396 "code": "unused_variables",
397 "explanation": null
398 },
399 "level": "warning",
400 "spans": [
401 {
402 "file_name": "driver/subcommand/repl.rs",
403 "byte_start": 9228,
404 "byte_end": 9231,
405 "line_start": 291,
406 "line_end": 291,
407 "column_start": 9,
408 "column_end": 12,
409 "is_primary": true,
410 "text": [
411 {
412 "text": " let foo = 42;",
413 "highlight_start": 9,
414 "highlight_end": 12
415 }
416 ],
417 "label": null,
418 "suggested_replacement": null,
419 "suggestion_applicability": null,
420 "expansion": null
421 }
422 ],
423 "children": [
424 {
425 "message": "#[warn(unused_variables)] on by default",
426 "code": null,
427 "level": "note",
428 "spans": [],
429 "children": [],
430 "rendered": null
431 },
432 {
433 "message": "consider prefixing with an underscore",
434 "code": null,
435 "level": "help",
436 "spans": [
437 {
438 "file_name": "driver/subcommand/repl.rs",
439 "byte_start": 9228,
440 "byte_end": 9231,
441 "line_start": 291,
442 "line_end": 291,
443 "column_start": 9,
444 "column_end": 12,
445 "is_primary": true,
446 "text": [
447 {
448 "text": " let foo = 42;",
449 "highlight_start": 9,
450 "highlight_end": 12
451 }
452 ],
453 "label": null,
454 "suggested_replacement": "_foo",
455 "suggestion_applicability": "MachineApplicable",
456 "expansion": null
457 }
458 ],
459 "children": [],
460 "rendered": null
461 }
462 ],
463 "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
464 }"##,
465 );
466
467 let workspace_root = Path::new("/test/");
468 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
469 insta::assert_debug_snapshot!(diag);
470 }
471
472 #[test]
473 #[cfg(not(windows))]
474 fn snap_rustc_wrong_number_of_parameters() {
475 let diag = parse_diagnostic(
476 r##"{
477 "message": "this function takes 2 parameters but 3 parameters were supplied",
478 "code": {
479 "code": "E0061",
480 "explanation": "\nThe number of arguments passed to a function must match the number of arguments\nspecified in the function signature.\n\nFor example, a function like:\n\n```\nfn f(a: u16, b: &str) {}\n```\n\nMust always be called with exactly two arguments, e.g., `f(2, \"test\")`.\n\nNote that Rust does not have a notion of optional function arguments or\nvariadic functions (except for its C-FFI).\n"
481 },
482 "level": "error",
483 "spans": [
484 {
485 "file_name": "compiler/ty/select.rs",
486 "byte_start": 8787,
487 "byte_end": 9241,
488 "line_start": 219,
489 "line_end": 231,
490 "column_start": 5,
491 "column_end": 6,
492 "is_primary": false,
493 "text": [
494 {
495 "text": " pub fn add_evidence(",
496 "highlight_start": 5,
497 "highlight_end": 25
498 },
499 {
500 "text": " &mut self,",
501 "highlight_start": 1,
502 "highlight_end": 19
503 },
504 {
505 "text": " target_poly: &ty::Ref<ty::Poly>,",
506 "highlight_start": 1,
507 "highlight_end": 41
508 },
509 {
510 "text": " evidence_poly: &ty::Ref<ty::Poly>,",
511 "highlight_start": 1,
512 "highlight_end": 43
513 },
514 {
515 "text": " ) {",
516 "highlight_start": 1,
517 "highlight_end": 8
518 },
519 {
520 "text": " match target_poly {",
521 "highlight_start": 1,
522 "highlight_end": 28
523 },
524 {
525 "text": " ty::Ref::Var(tvar, _) => self.add_var_evidence(tvar, evidence_poly),",
526 "highlight_start": 1,
527 "highlight_end": 81
528 },
529 {
530 "text": " ty::Ref::Fixed(target_ty) => {",
531 "highlight_start": 1,
532 "highlight_end": 43
533 },
534 {
535 "text": " let evidence_ty = evidence_poly.resolve_to_ty();",
536 "highlight_start": 1,
537 "highlight_end": 65
538 },
539 {
540 "text": " self.add_evidence_ty(target_ty, evidence_poly, evidence_ty)",
541 "highlight_start": 1,
542 "highlight_end": 76
543 },
544 {
545 "text": " }",
546 "highlight_start": 1,
547 "highlight_end": 14
548 },
549 {
550 "text": " }",
551 "highlight_start": 1,
552 "highlight_end": 10
553 },
554 {
555 "text": " }",
556 "highlight_start": 1,
557 "highlight_end": 6
558 }
559 ],
560 "label": "defined here",
561 "suggested_replacement": null,
562 "suggestion_applicability": null,
563 "expansion": null
564 },
565 {
566 "file_name": "compiler/ty/select.rs",
567 "byte_start": 4045,
568 "byte_end": 4057,
569 "line_start": 104,
570 "line_end": 104,
571 "column_start": 18,
572 "column_end": 30,
573 "is_primary": true,
574 "text": [
575 {
576 "text": " self.add_evidence(target_fixed, evidence_fixed, false);",
577 "highlight_start": 18,
578 "highlight_end": 30
579 }
580 ],
581 "label": "expected 2 parameters",
582 "suggested_replacement": null,
583 "suggestion_applicability": null,
584 "expansion": null
585 }
586 ],
587 "children": [],
588 "rendered": "error[E0061]: this function takes 2 parameters but 3 parameters were supplied\n --> compiler/ty/select.rs:104:18\n |\n104 | self.add_evidence(target_fixed, evidence_fixed, false);\n | ^^^^^^^^^^^^ expected 2 parameters\n...\n219 | / pub fn add_evidence(\n220 | | &mut self,\n221 | | target_poly: &ty::Ref<ty::Poly>,\n222 | | evidence_poly: &ty::Ref<ty::Poly>,\n... |\n230 | | }\n231 | | }\n | |_____- defined here\n\n"
589 }"##,
590 );
591
592 let workspace_root = Path::new("/test/");
593 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
594 insta::assert_debug_snapshot!(diag);
595 }
596
597 #[test]
598 #[cfg(not(windows))]
599 fn snap_clippy_pass_by_ref() {
600 let diag = parse_diagnostic(
601 r##"{
602 "message": "this argument is passed by reference, but would be more efficient if passed by value",
603 "code": {
604 "code": "clippy::trivially_copy_pass_by_ref",
605 "explanation": null
606 },
607 "level": "warning",
608 "spans": [
609 {
610 "file_name": "compiler/mir/tagset.rs",
611 "byte_start": 941,
612 "byte_end": 946,
613 "line_start": 42,
614 "line_end": 42,
615 "column_start": 24,
616 "column_end": 29,
617 "is_primary": true,
618 "text": [
619 {
620 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
621 "highlight_start": 24,
622 "highlight_end": 29
623 }
624 ],
625 "label": null,
626 "suggested_replacement": null,
627 "suggestion_applicability": null,
628 "expansion": null
629 }
630 ],
631 "children": [
632 {
633 "message": "lint level defined here",
634 "code": null,
635 "level": "note",
636 "spans": [
637 {
638 "file_name": "compiler/lib.rs",
639 "byte_start": 8,
640 "byte_end": 19,
641 "line_start": 1,
642 "line_end": 1,
643 "column_start": 9,
644 "column_end": 20,
645 "is_primary": true,
646 "text": [
647 {
648 "text": "#![warn(clippy::all)]",
649 "highlight_start": 9,
650 "highlight_end": 20
651 }
652 ],
653 "label": null,
654 "suggested_replacement": null,
655 "suggestion_applicability": null,
656 "expansion": null
657 }
658 ],
659 "children": [],
660 "rendered": null
661 },
662 {
663 "message": "#[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]",
664 "code": null,
665 "level": "note",
666 "spans": [],
667 "children": [],
668 "rendered": null
669 },
670 {
671 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref",
672 "code": null,
673 "level": "help",
674 "spans": [],
675 "children": [],
676 "rendered": null
677 },
678 {
679 "message": "consider passing by value instead",
680 "code": null,
681 "level": "help",
682 "spans": [
683 {
684 "file_name": "compiler/mir/tagset.rs",
685 "byte_start": 941,
686 "byte_end": 946,
687 "line_start": 42,
688 "line_end": 42,
689 "column_start": 24,
690 "column_end": 29,
691 "is_primary": true,
692 "text": [
693 {
694 "text": " pub fn is_disjoint(&self, other: Self) -> bool {",
695 "highlight_start": 24,
696 "highlight_end": 29
697 }
698 ],
699 "label": null,
700 "suggested_replacement": "self",
701 "suggestion_applicability": "Unspecified",
702 "expansion": null
703 }
704 ],
705 "children": [],
706 "rendered": null
707 }
708 ],
709 "rendered": "warning: this argument is passed by reference, but would be more efficient if passed by value\n --> compiler/mir/tagset.rs:42:24\n |\n42 | pub fn is_disjoint(&self, other: Self) -> bool {\n | ^^^^^ help: consider passing by value instead: `self`\n |\nnote: lint level defined here\n --> compiler/lib.rs:1:9\n |\n1 | #![warn(clippy::all)]\n | ^^^^^^^^^^^\n = note: #[warn(clippy::trivially_copy_pass_by_ref)] implied by #[warn(clippy::all)]\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref\n\n"
710 }"##,
711 );
712
713 let workspace_root = Path::new("/test/");
714 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
715 insta::assert_debug_snapshot!(diag);
716 }
717
718 #[test]
719 #[cfg(not(windows))]
720 fn snap_rustc_mismatched_type() {
721 let diag = parse_diagnostic(
722 r##"{
723 "message": "mismatched types",
724 "code": {
725 "code": "E0308",
726 "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
727 },
728 "level": "error",
729 "spans": [
730 {
731 "file_name": "runtime/compiler_support.rs",
732 "byte_start": 1589,
733 "byte_end": 1594,
734 "line_start": 48,
735 "line_end": 48,
736 "column_start": 65,
737 "column_end": 70,
738 "is_primary": true,
739 "text": [
740 {
741 "text": " let layout = alloc::Layout::from_size_align_unchecked(size, align);",
742 "highlight_start": 65,
743 "highlight_end": 70
744 }
745 ],
746 "label": "expected usize, found u32",
747 "suggested_replacement": null,
748 "suggestion_applicability": null,
749 "expansion": null
750 }
751 ],
752 "children": [],
753 "rendered": "error[E0308]: mismatched types\n --> runtime/compiler_support.rs:48:65\n |\n48 | let layout = alloc::Layout::from_size_align_unchecked(size, align);\n | ^^^^^ expected usize, found u32\n\n"
754 }"##,
755 );
756
757 let workspace_root = Path::new("/test/");
758 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
759 insta::assert_debug_snapshot!(diag);
760 }
761
762 #[test]
763 #[cfg(not(windows))]
764 fn snap_handles_macro_location() {
765 let diag = parse_diagnostic(
766 r##"{
767 "rendered": "error[E0277]: can't compare `{integer}` with `&str`\n --> src/main.rs:2:5\n |\n2 | assert_eq!(1, \"love\");\n | ^^^^^^^^^^^^^^^^^^^^^^ no implementation for `{integer} == &str`\n |\n = help: the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`\n = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)\n\n",
768 "children": [
769 {
770 "children": [],
771 "code": null,
772 "level": "help",
773 "message": "the trait `std::cmp::PartialEq<&str>` is not implemented for `{integer}`",
774 "rendered": null,
775 "spans": []
776 }
777 ],
778 "code": {
779 "code": "E0277",
780 "explanation": "\nYou tried to use a type which doesn't implement some trait in a place which\nexpected that trait. Erroneous code example:\n\n```compile_fail,E0277\n// here we declare the Foo trait with a bar method\ntrait Foo {\n fn bar(&self);\n}\n\n// we now declare a function which takes an object implementing the Foo trait\nfn some_func<T: Foo>(foo: T) {\n foo.bar();\n}\n\nfn main() {\n // we now call the method with the i32 type, which doesn't implement\n // the Foo trait\n some_func(5i32); // error: the trait bound `i32 : Foo` is not satisfied\n}\n```\n\nIn order to fix this error, verify that the type you're using does implement\nthe trait. Example:\n\n```\ntrait Foo {\n fn bar(&self);\n}\n\nfn some_func<T: Foo>(foo: T) {\n foo.bar(); // we can now use this method since i32 implements the\n // Foo trait\n}\n\n// we implement the trait on the i32 type\nimpl Foo for i32 {\n fn bar(&self) {}\n}\n\nfn main() {\n some_func(5i32); // ok!\n}\n```\n\nOr in a generic context, an erroneous code example would look like:\n\n```compile_fail,E0277\nfn some_func<T>(foo: T) {\n println!(\"{:?}\", foo); // error: the trait `core::fmt::Debug` is not\n // implemented for the type `T`\n}\n\nfn main() {\n // We now call the method with the i32 type,\n // which *does* implement the Debug trait.\n some_func(5i32);\n}\n```\n\nNote that the error here is in the definition of the generic function: Although\nwe only call it with a parameter that does implement `Debug`, the compiler\nstill rejects the function: It must work with all possible input types. In\norder to make this example compile, we need to restrict the generic type we're\naccepting:\n\n```\nuse std::fmt;\n\n// Restrict the input type to types that implement Debug.\nfn some_func<T: fmt::Debug>(foo: T) {\n println!(\"{:?}\", foo);\n}\n\nfn main() {\n // Calling the method is still fine, as i32 implements Debug.\n some_func(5i32);\n\n // This would fail to compile now:\n // struct WithoutDebug;\n // some_func(WithoutDebug);\n}\n```\n\nRust only looks at the signature of the called function, as such it must\nalready specify all requirements that will be used for every type parameter.\n"
781 },
782 "level": "error",
783 "message": "can't compare `{integer}` with `&str`",
784 "spans": [
785 {
786 "byte_end": 155,
787 "byte_start": 153,
788 "column_end": 33,
789 "column_start": 31,
790 "expansion": {
791 "def_site_span": {
792 "byte_end": 940,
793 "byte_start": 0,
794 "column_end": 6,
795 "column_start": 1,
796 "expansion": null,
797 "file_name": "<::core::macros::assert_eq macros>",
798 "is_primary": false,
799 "label": null,
800 "line_end": 36,
801 "line_start": 1,
802 "suggested_replacement": null,
803 "suggestion_applicability": null,
804 "text": [
805 {
806 "highlight_end": 35,
807 "highlight_start": 1,
808 "text": "($ left : expr, $ right : expr) =>"
809 },
810 {
811 "highlight_end": 3,
812 "highlight_start": 1,
813 "text": "({"
814 },
815 {
816 "highlight_end": 33,
817 "highlight_start": 1,
818 "text": " match (& $ left, & $ right)"
819 },
820 {
821 "highlight_end": 7,
822 "highlight_start": 1,
823 "text": " {"
824 },
825 {
826 "highlight_end": 34,
827 "highlight_start": 1,
828 "text": " (left_val, right_val) =>"
829 },
830 {
831 "highlight_end": 11,
832 "highlight_start": 1,
833 "text": " {"
834 },
835 {
836 "highlight_end": 46,
837 "highlight_start": 1,
838 "text": " if ! (* left_val == * right_val)"
839 },
840 {
841 "highlight_end": 15,
842 "highlight_start": 1,
843 "text": " {"
844 },
845 {
846 "highlight_end": 25,
847 "highlight_start": 1,
848 "text": " panic !"
849 },
850 {
851 "highlight_end": 57,
852 "highlight_start": 1,
853 "text": " (r#\"assertion failed: `(left == right)`"
854 },
855 {
856 "highlight_end": 16,
857 "highlight_start": 1,
858 "text": " left: `{:?}`,"
859 },
860 {
861 "highlight_end": 18,
862 "highlight_start": 1,
863 "text": " right: `{:?}`\"#,"
864 },
865 {
866 "highlight_end": 47,
867 "highlight_start": 1,
868 "text": " & * left_val, & * right_val)"
869 },
870 {
871 "highlight_end": 15,
872 "highlight_start": 1,
873 "text": " }"
874 },
875 {
876 "highlight_end": 11,
877 "highlight_start": 1,
878 "text": " }"
879 },
880 {
881 "highlight_end": 7,
882 "highlight_start": 1,
883 "text": " }"
884 },
885 {
886 "highlight_end": 42,
887 "highlight_start": 1,
888 "text": " }) ; ($ left : expr, $ right : expr,) =>"
889 },
890 {
891 "highlight_end": 49,
892 "highlight_start": 1,
893 "text": "({ $ crate :: assert_eq ! ($ left, $ right) }) ;"
894 },
895 {
896 "highlight_end": 53,
897 "highlight_start": 1,
898 "text": "($ left : expr, $ right : expr, $ ($ arg : tt) +) =>"
899 },
900 {
901 "highlight_end": 3,
902 "highlight_start": 1,
903 "text": "({"
904 },
905 {
906 "highlight_end": 37,
907 "highlight_start": 1,
908 "text": " match (& ($ left), & ($ right))"
909 },
910 {
911 "highlight_end": 7,
912 "highlight_start": 1,
913 "text": " {"
914 },
915 {
916 "highlight_end": 34,
917 "highlight_start": 1,
918 "text": " (left_val, right_val) =>"
919 },
920 {
921 "highlight_end": 11,
922 "highlight_start": 1,
923 "text": " {"
924 },
925 {
926 "highlight_end": 46,
927 "highlight_start": 1,
928 "text": " if ! (* left_val == * right_val)"
929 },
930 {
931 "highlight_end": 15,
932 "highlight_start": 1,
933 "text": " {"
934 },
935 {
936 "highlight_end": 25,
937 "highlight_start": 1,
938 "text": " panic !"
939 },
940 {
941 "highlight_end": 57,
942 "highlight_start": 1,
943 "text": " (r#\"assertion failed: `(left == right)`"
944 },
945 {
946 "highlight_end": 16,
947 "highlight_start": 1,
948 "text": " left: `{:?}`,"
949 },
950 {
951 "highlight_end": 22,
952 "highlight_start": 1,
953 "text": " right: `{:?}`: {}\"#,"
954 },
955 {
956 "highlight_end": 72,
957 "highlight_start": 1,
958 "text": " & * left_val, & * right_val, $ crate :: format_args !"
959 },
960 {
961 "highlight_end": 33,
962 "highlight_start": 1,
963 "text": " ($ ($ arg) +))"
964 },
965 {
966 "highlight_end": 15,
967 "highlight_start": 1,
968 "text": " }"
969 },
970 {
971 "highlight_end": 11,
972 "highlight_start": 1,
973 "text": " }"
974 },
975 {
976 "highlight_end": 7,
977 "highlight_start": 1,
978 "text": " }"
979 },
980 {
981 "highlight_end": 6,
982 "highlight_start": 1,
983 "text": " }) ;"
984 }
985 ]
986 },
987 "macro_decl_name": "assert_eq!",
988 "span": {
989 "byte_end": 38,
990 "byte_start": 16,
991 "column_end": 27,
992 "column_start": 5,
993 "expansion": null,
994 "file_name": "src/main.rs",
995 "is_primary": false,
996 "label": null,
997 "line_end": 2,
998 "line_start": 2,
999 "suggested_replacement": null,
1000 "suggestion_applicability": null,
1001 "text": [
1002 {
1003 "highlight_end": 27,
1004 "highlight_start": 5,
1005 "text": " assert_eq!(1, \"love\");"
1006 }
1007 ]
1008 }
1009 },
1010 "file_name": "<::core::macros::assert_eq macros>",
1011 "is_primary": true,
1012 "label": "no implementation for `{integer} == &str`",
1013 "line_end": 7,
1014 "line_start": 7,
1015 "suggested_replacement": null,
1016 "suggestion_applicability": null,
1017 "text": [
1018 {
1019 "highlight_end": 33,
1020 "highlight_start": 31,
1021 "text": " if ! (* left_val == * right_val)"
1022 }
1023 ]
1024 }
1025 ]
1026 }"##,
1027 );
1028
1029 let workspace_root = Path::new("/test/");
1030 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1031 insta::assert_debug_snapshot!(diag);
1032 }
1033
1034 #[test]
1035 #[cfg(not(windows))]
1036 fn snap_macro_compiler_error() {
1037 let diag = parse_diagnostic(
1038 r##"{
1039 "rendered": "error: Please register your known path in the path module\n --> crates/ra_hir_def/src/path.rs:265:9\n |\n265 | compile_error!(\"Please register your known path in the path module\")\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n | \n ::: crates/ra_hir_def/src/data.rs:80:16\n |\n80 | let path = path![std::future::Future];\n | -------------------------- in this macro invocation\n\n",
1040 "children": [],
1041 "code": null,
1042 "level": "error",
1043 "message": "Please register your known path in the path module",
1044 "spans": [
1045 {
1046 "byte_end": 8285,
1047 "byte_start": 8217,
1048 "column_end": 77,
1049 "column_start": 9,
1050 "expansion": {
1051 "def_site_span": {
1052 "byte_end": 8294,
1053 "byte_start": 7858,
1054 "column_end": 2,
1055 "column_start": 1,
1056 "expansion": null,
1057 "file_name": "crates/ra_hir_def/src/path.rs",
1058 "is_primary": false,
1059 "label": null,
1060 "line_end": 267,
1061 "line_start": 254,
1062 "suggested_replacement": null,
1063 "suggestion_applicability": null,
1064 "text": [
1065 {
1066 "highlight_end": 28,
1067 "highlight_start": 1,
1068 "text": "macro_rules! __known_path {"
1069 },
1070 {
1071 "highlight_end": 37,
1072 "highlight_start": 1,
1073 "text": " (std::iter::IntoIterator) => {};"
1074 },
1075 {
1076 "highlight_end": 33,
1077 "highlight_start": 1,
1078 "text": " (std::result::Result) => {};"
1079 },
1080 {
1081 "highlight_end": 29,
1082 "highlight_start": 1,
1083 "text": " (std::ops::Range) => {};"
1084 },
1085 {
1086 "highlight_end": 33,
1087 "highlight_start": 1,
1088 "text": " (std::ops::RangeFrom) => {};"
1089 },
1090 {
1091 "highlight_end": 33,
1092 "highlight_start": 1,
1093 "text": " (std::ops::RangeFull) => {};"
1094 },
1095 {
1096 "highlight_end": 31,
1097 "highlight_start": 1,
1098 "text": " (std::ops::RangeTo) => {};"
1099 },
1100 {
1101 "highlight_end": 40,
1102 "highlight_start": 1,
1103 "text": " (std::ops::RangeToInclusive) => {};"
1104 },
1105 {
1106 "highlight_end": 38,
1107 "highlight_start": 1,
1108 "text": " (std::ops::RangeInclusive) => {};"
1109 },
1110 {
1111 "highlight_end": 27,
1112 "highlight_start": 1,
1113 "text": " (std::ops::Try) => {};"
1114 },
1115 {
1116 "highlight_end": 22,
1117 "highlight_start": 1,
1118 "text": " ($path:path) => {"
1119 },
1120 {
1121 "highlight_end": 77,
1122 "highlight_start": 1,
1123 "text": " compile_error!(\"Please register your known path in the path module\")"
1124 },
1125 {
1126 "highlight_end": 7,
1127 "highlight_start": 1,
1128 "text": " };"
1129 },
1130 {
1131 "highlight_end": 2,
1132 "highlight_start": 1,
1133 "text": "}"
1134 }
1135 ]
1136 },
1137 "macro_decl_name": "$crate::__known_path!",
1138 "span": {
1139 "byte_end": 8427,
1140 "byte_start": 8385,
1141 "column_end": 51,
1142 "column_start": 9,
1143 "expansion": {
1144 "def_site_span": {
1145 "byte_end": 8611,
1146 "byte_start": 8312,
1147 "column_end": 2,
1148 "column_start": 1,
1149 "expansion": null,
1150 "file_name": "crates/ra_hir_def/src/path.rs",
1151 "is_primary": false,
1152 "label": null,
1153 "line_end": 277,
1154 "line_start": 270,
1155 "suggested_replacement": null,
1156 "suggestion_applicability": null,
1157 "text": [
1158 {
1159 "highlight_end": 22,
1160 "highlight_start": 1,
1161 "text": "macro_rules! __path {"
1162 },
1163 {
1164 "highlight_end": 43,
1165 "highlight_start": 1,
1166 "text": " ($start:ident $(:: $seg:ident)*) => ({"
1167 },
1168 {
1169 "highlight_end": 51,
1170 "highlight_start": 1,
1171 "text": " $crate::__known_path!($start $(:: $seg)*);"
1172 },
1173 {
1174 "highlight_end": 87,
1175 "highlight_start": 1,
1176 "text": " $crate::path::ModPath::from_simple_segments($crate::path::PathKind::Abs, vec!["
1177 },
1178 {
1179 "highlight_end": 76,
1180 "highlight_start": 1,
1181 "text": " $crate::path::__name![$start], $($crate::path::__name![$seg],)*"
1182 },
1183 {
1184 "highlight_end": 11,
1185 "highlight_start": 1,
1186 "text": " ])"
1187 },
1188 {
1189 "highlight_end": 8,
1190 "highlight_start": 1,
1191 "text": " });"
1192 },
1193 {
1194 "highlight_end": 2,
1195 "highlight_start": 1,
1196 "text": "}"
1197 }
1198 ]
1199 },
1200 "macro_decl_name": "path!",
1201 "span": {
1202 "byte_end": 2966,
1203 "byte_start": 2940,
1204 "column_end": 42,
1205 "column_start": 16,
1206 "expansion": null,
1207 "file_name": "crates/ra_hir_def/src/data.rs",
1208 "is_primary": false,
1209 "label": null,
1210 "line_end": 80,
1211 "line_start": 80,
1212 "suggested_replacement": null,
1213 "suggestion_applicability": null,
1214 "text": [
1215 {
1216 "highlight_end": 42,
1217 "highlight_start": 16,
1218 "text": " let path = path![std::future::Future];"
1219 }
1220 ]
1221 }
1222 },
1223 "file_name": "crates/ra_hir_def/src/path.rs",
1224 "is_primary": false,
1225 "label": null,
1226 "line_end": 272,
1227 "line_start": 272,
1228 "suggested_replacement": null,
1229 "suggestion_applicability": null,
1230 "text": [
1231 {
1232 "highlight_end": 51,
1233 "highlight_start": 9,
1234 "text": " $crate::__known_path!($start $(:: $seg)*);"
1235 }
1236 ]
1237 }
1238 },
1239 "file_name": "crates/ra_hir_def/src/path.rs",
1240 "is_primary": true,
1241 "label": null,
1242 "line_end": 265,
1243 "line_start": 265,
1244 "suggested_replacement": null,
1245 "suggestion_applicability": null,
1246 "text": [
1247 {
1248 "highlight_end": 77,
1249 "highlight_start": 9,
1250 "text": " compile_error!(\"Please register your known path in the path module\")"
1251 }
1252 ]
1253 }
1254 ]
1255 }
1256 "##,
1257 );
1258
1259 let workspace_root = Path::new("/test/");
1260 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1261 insta::assert_debug_snapshot!(diag);
1262 }
1263
1264 #[test]
1265 #[cfg(not(windows))]
1266 fn snap_multi_line_fix() {
1267 let diag = parse_diagnostic(
1268 r##"{
1269 "rendered": "warning: returning the result of a let binding from a block\n --> src/main.rs:4:5\n |\n3 | let a = (0..10).collect();\n | -------------------------- unnecessary let binding\n4 | a\n | ^\n |\n = note: `#[warn(clippy::let_and_return)]` on by default\n = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return\nhelp: return the expression directly\n |\n3 | \n4 | (0..10).collect()\n |\n\n",
1270 "children": [
1271 {
1272 "children": [],
1273 "code": null,
1274 "level": "note",
1275 "message": "`#[warn(clippy::let_and_return)]` on by default",
1276 "rendered": null,
1277 "spans": []
1278 },
1279 {
1280 "children": [],
1281 "code": null,
1282 "level": "help",
1283 "message": "for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return",
1284 "rendered": null,
1285 "spans": []
1286 },
1287 {
1288 "children": [],
1289 "code": null,
1290 "level": "help",
1291 "message": "return the expression directly",
1292 "rendered": null,
1293 "spans": [
1294 {
1295 "byte_end": 55,
1296 "byte_start": 29,
1297 "column_end": 31,
1298 "column_start": 5,
1299 "expansion": null,
1300 "file_name": "src/main.rs",
1301 "is_primary": true,
1302 "label": null,
1303 "line_end": 3,
1304 "line_start": 3,
1305 "suggested_replacement": "",
1306 "suggestion_applicability": "MachineApplicable",
1307 "text": [
1308 {
1309 "highlight_end": 31,
1310 "highlight_start": 5,
1311 "text": " let a = (0..10).collect();"
1312 }
1313 ]
1314 },
1315 {
1316 "byte_end": 61,
1317 "byte_start": 60,
1318 "column_end": 6,
1319 "column_start": 5,
1320 "expansion": null,
1321 "file_name": "src/main.rs",
1322 "is_primary": true,
1323 "label": null,
1324 "line_end": 4,
1325 "line_start": 4,
1326 "suggested_replacement": "(0..10).collect()",
1327 "suggestion_applicability": "MachineApplicable",
1328 "text": [
1329 {
1330 "highlight_end": 6,
1331 "highlight_start": 5,
1332 "text": " a"
1333 }
1334 ]
1335 }
1336 ]
1337 }
1338 ],
1339 "code": {
1340 "code": "clippy::let_and_return",
1341 "explanation": null
1342 },
1343 "level": "warning",
1344 "message": "returning the result of a let binding from a block",
1345 "spans": [
1346 {
1347 "byte_end": 55,
1348 "byte_start": 29,
1349 "column_end": 31,
1350 "column_start": 5,
1351 "expansion": null,
1352 "file_name": "src/main.rs",
1353 "is_primary": false,
1354 "label": "unnecessary let binding",
1355 "line_end": 3,
1356 "line_start": 3,
1357 "suggested_replacement": null,
1358 "suggestion_applicability": null,
1359 "text": [
1360 {
1361 "highlight_end": 31,
1362 "highlight_start": 5,
1363 "text": " let a = (0..10).collect();"
1364 }
1365 ]
1366 },
1367 {
1368 "byte_end": 61,
1369 "byte_start": 60,
1370 "column_end": 6,
1371 "column_start": 5,
1372 "expansion": null,
1373 "file_name": "src/main.rs",
1374 "is_primary": true,
1375 "label": null,
1376 "line_end": 4,
1377 "line_start": 4,
1378 "suggested_replacement": null,
1379 "suggestion_applicability": null,
1380 "text": [
1381 {
1382 "highlight_end": 6,
1383 "highlight_start": 5,
1384 "text": " a"
1385 }
1386 ]
1387 }
1388 ]
1389 }
1390 "##,
1391 );
1392
1393 let workspace_root = Path::new("/test/");
1394 let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
1395 insta::assert_debug_snapshot!(diag);
1396 }
1397}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 13d305b97..15e5bb354 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -25,7 +25,7 @@ use lsp_types::{
25 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, 25 WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
26 WorkDoneProgressReport, 26 WorkDoneProgressReport,
27}; 27};
28use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; 28use ra_flycheck::{CheckTask, Status};
29use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId}; 29use ra_ide::{Canceled, FileId, LibraryData, LineIndex, SourceRootId};
30use ra_prof::profile; 30use ra_prof::profile;
31use ra_project_model::{PackageRoot, ProjectWorkspace}; 31use ra_project_model::{PackageRoot, ProjectWorkspace};
@@ -37,7 +37,7 @@ use threadpool::ThreadPool;
37 37
38use crate::{ 38use crate::{
39 config::{Config, FilesWatcher}, 39 config::{Config, FilesWatcher},
40 diagnostics::DiagnosticTask, 40 diagnostics::{to_proto::url_from_path_with_drive_lowercasing, DiagnosticTask},
41 from_proto, lsp_ext, 41 from_proto, lsp_ext,
42 main_loop::{ 42 main_loop::{
43 pending_requests::{PendingRequest, PendingRequests}, 43 pending_requests::{PendingRequest, PendingRequests},
@@ -736,22 +736,61 @@ fn on_check_task(
736 task_sender.send(Task::Diagnostic(DiagnosticTask::ClearCheck))?; 736 task_sender.send(Task::Diagnostic(DiagnosticTask::ClearCheck))?;
737 } 737 }
738 738
739 CheckTask::AddDiagnostic { url, diagnostic, fixes } => { 739 CheckTask::AddDiagnostic { workspace_root, diagnostic } => {
740 let path = url.to_file_path().map_err(|()| format!("invalid uri: {}", url))?; 740 let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
741 let file_id = match world_state.vfs.read().path2file(&path) { 741 &diagnostic,
742 Some(file) => FileId(file.0), 742 &workspace_root,
743 None => { 743 );
744 log::error!("File with cargo diagnostic not found in VFS: {}", path.display()); 744 for diag in diagnostics {
745 return Ok(()); 745 let path = diag
746 } 746 .location
747 }; 747 .uri
748 .to_file_path()
749 .map_err(|()| format!("invalid uri: {}", diag.location.uri))?;
750 let file_id = match world_state.vfs.read().path2file(&path) {
751 Some(file) => FileId(file.0),
752 None => {
753 log::error!(
754 "File with cargo diagnostic not found in VFS: {}",
755 path.display()
756 );
757 return Ok(());
758 }
759 };
748 760
749 task_sender 761 task_sender.send(Task::Diagnostic(DiagnosticTask::AddCheck(
750 .send(Task::Diagnostic(DiagnosticTask::AddCheck(file_id, diagnostic, fixes)))?; 762 file_id,
763 diag.diagnostic,
764 diag.fixes.into_iter().map(|it| it.into()).collect(),
765 )))?;
766 }
751 } 767 }
752 768
753 CheckTask::Status(progress) => { 769 CheckTask::Status(status) => {
754 if world_state.config.client_caps.work_done_progress { 770 if world_state.config.client_caps.work_done_progress {
771 let progress = match status {
772 Status::Being => {
773 lsp_types::WorkDoneProgress::Begin(lsp_types::WorkDoneProgressBegin {
774 title: "Running `cargo check`".to_string(),
775 cancellable: Some(false),
776 message: None,
777 percentage: None,
778 })
779 }
780 Status::Progress(target) => {
781 lsp_types::WorkDoneProgress::Report(lsp_types::WorkDoneProgressReport {
782 cancellable: Some(false),
783 message: Some(target),
784 percentage: None,
785 })
786 }
787 Status::End => {
788 lsp_types::WorkDoneProgress::End(lsp_types::WorkDoneProgressEnd {
789 message: None,
790 })
791 }
792 };
793
755 let params = lsp_types::ProgressParams { 794 let params = lsp_types::ProgressParams {
756 token: lsp_types::ProgressToken::String( 795 token: lsp_types::ProgressToken::String(
757 "rustAnalyzer/cargoWatcher".to_string(), 796 "rustAnalyzer/cargoWatcher".to_string(),
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 33c2fd595..a8e2e535f 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -164,27 +164,27 @@ pub(crate) fn completion_item(
164 additional_text_edits: Some(additional_text_edits), 164 additional_text_edits: Some(additional_text_edits),
165 documentation: completion_item.documentation().map(documentation), 165 documentation: completion_item.documentation().map(documentation),
166 deprecated: Some(completion_item.deprecated()), 166 deprecated: Some(completion_item.deprecated()),
167 command: if completion_item.trigger_call_info() {
168 let cmd = lsp_types::Command {
169 title: "triggerParameterHints".into(),
170 command: "editor.action.triggerParameterHints".into(),
171 arguments: None,
172 };
173 Some(cmd)
174 } else {
175 None
176 },
177 ..Default::default() 167 ..Default::default()
178 }; 168 };
179 169
180 if completion_item.score().is_some() { 170 if completion_item.score().is_some() {
181 res.preselect = Some(true) 171 res.preselect = Some(true);
172 // HACK: sort preselect items first
173 res.sort_text = Some(format!(" {}", completion_item.label()));
182 } 174 }
183 175
184 if completion_item.deprecated() { 176 if completion_item.deprecated() {
185 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated]) 177 res.tags = Some(vec![lsp_types::CompletionItemTag::Deprecated])
186 } 178 }
187 179
180 if completion_item.trigger_call_info() {
181 res.command = Some(lsp_types::Command {
182 title: "triggerParameterHints".into(),
183 command: "editor.action.triggerParameterHints".into(),
184 arguments: None,
185 });
186 }
187
188 res.insert_text_format = Some(insert_text_format(completion_item.insert_text_format())); 188 res.insert_text_format = Some(insert_text_format(completion_item.insert_text_format()));
189 189
190 res 190 res
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 6333c15b2..367272925 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -11,7 +11,7 @@ use std::{
11use crossbeam_channel::{unbounded, Receiver}; 11use crossbeam_channel::{unbounded, Receiver};
12use lsp_types::Url; 12use lsp_types::Url;
13use parking_lot::RwLock; 13use parking_lot::RwLock;
14use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig}; 14use ra_flycheck::{Flycheck, FlycheckConfig};
15use ra_ide::{ 15use ra_ide::{
16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, 16 Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId,
17}; 17};
@@ -22,7 +22,9 @@ use stdx::format_to;
22 22
23use crate::{ 23use crate::{
24 config::Config, 24 config::Config,
25 diagnostics::{CheckFixes, DiagnosticCollection}, 25 diagnostics::{
26 to_proto::url_from_path_with_drive_lowercasing, CheckFixes, DiagnosticCollection,
27 },
26 main_loop::pending_requests::{CompletedRequest, LatestRequests}, 28 main_loop::pending_requests::{CompletedRequest, LatestRequests},
27 vfs_glob::{Glob, RustPackageFilterBuilder}, 29 vfs_glob::{Glob, RustPackageFilterBuilder},
28 LspError, Result, 30 LspError, Result,
diff --git a/editors/code/src/commands/ssr.ts b/editors/code/src/commands/ssr.ts
index 6fee051fd..4ef8cdf04 100644
--- a/editors/code/src/commands/ssr.ts
+++ b/editors/code/src/commands/ssr.ts
@@ -11,7 +11,7 @@ export function ssr(ctx: Ctx): Cmd {
11 11
12 const options: vscode.InputBoxOptions = { 12 const options: vscode.InputBoxOptions = {
13 value: "() ==>> ()", 13 value: "() ==>> ()",
14 prompt: "EnteR request, for example 'Foo($a:expr) ==> Foo::new($a)' ", 14 prompt: "Enter request, for example 'Foo($a:expr) ==> Foo::new($a)' ",
15 validateInput: async (x: string) => { 15 validateInput: async (x: string) => {
16 try { 16 try {
17 await client.sendRequest(ra.ssr, { query: x, parseOnly: true }); 17 await client.sendRequest(ra.ssr, { query: x, parseOnly: true });