diff options
Diffstat (limited to 'crates')
52 files changed, 4006 insertions, 1744 deletions
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. |
2 | use rustc_hash::FxHashMap; | 2 | use rustc_hash::FxHashMap; |
3 | 3 | ||
4 | use hir::{PathResolution, SemanticsScope}; | 4 | use hir::{HirDisplay, PathResolution, SemanticsScope}; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_syntax::{ | 6 | use 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_custom_impl.rs b/crates/ra_assists/src/handlers/add_custom_impl.rs index 795a225a4..2baeb8607 100644 --- a/crates/ra_assists/src/handlers/add_custom_impl.rs +++ b/crates/ra_assists/src/handlers/add_custom_impl.rs | |||
@@ -49,7 +49,7 @@ pub(crate) fn add_custom_impl(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
49 | let start_offset = annotated.syntax().parent()?.text_range().end(); | 49 | let start_offset = annotated.syntax().parent()?.text_range().end(); |
50 | 50 | ||
51 | let label = | 51 | let label = |
52 | format!("Add custom impl '{}' for '{}'", trait_token.text().as_str(), annotated_name); | 52 | format!("Add custom impl `{}` for `{}`", trait_token.text().as_str(), annotated_name); |
53 | 53 | ||
54 | let target = attr.syntax().text_range(); | 54 | let target = attr.syntax().text_range(); |
55 | acc.add(AssistId("add_custom_impl"), label, target, |edit| { | 55 | acc.add(AssistId("add_custom_impl"), label, target, |edit| { |
diff --git a/crates/ra_assists/src/handlers/add_explicit_type.rs b/crates/ra_assists/src/handlers/add_explicit_type.rs index 7ced00626..0c7d5e355 100644 --- a/crates/ra_assists/src/handlers/add_explicit_type.rs +++ b/crates/ra_assists/src/handlers/add_explicit_type.rs | |||
@@ -61,7 +61,7 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Optio | |||
61 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; | 61 | let inferred_type = ty.display_source_code(ctx.db, module.into()).ok()?; |
62 | acc.add( | 62 | acc.add( |
63 | AssistId("add_explicit_type"), | 63 | AssistId("add_explicit_type"), |
64 | format!("Insert explicit type '{}'", inferred_type), | 64 | format!("Insert explicit type `{}`", inferred_type), |
65 | pat_range, | 65 | pat_range, |
66 | |builder| match ascribed_ty { | 66 | |builder| match ascribed_ty { |
67 | Some(ascribed_ty) => { | 67 | Some(ascribed_ty) => { |
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#" | ||
627 | trait Foo<T = Self> { | ||
628 | fn bar(&self, other: &T); | ||
629 | } | ||
630 | |||
631 | struct S; | ||
632 | impl Foo for S { <|> }"#, | ||
633 | r#" | ||
634 | trait Foo<T = Self> { | ||
635 | fn bar(&self, other: &T); | ||
636 | } | ||
637 | |||
638 | struct S; | ||
639 | impl 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#" | ||
652 | trait Foo<T1, T2 = Self> { | ||
653 | fn bar(&self, this: &T1, that: &T2); | ||
654 | } | ||
655 | |||
656 | struct S<T>; | ||
657 | impl Foo<T> for S<T> { <|> }"#, | ||
658 | r#" | ||
659 | trait Foo<T1, T2 = Self> { | ||
660 | fn bar(&self, this: &T1, that: &T2); | ||
661 | } | ||
662 | |||
663 | struct S<T>; | ||
664 | impl 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 | |||
11 | use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; | ||
10 | use test_utils::tested_by; | 12 | use test_utils::tested_by; |
11 | 13 | ||
12 | use crate::{AssistContext, AssistId, Assists}; | 14 | use crate::{AssistContext, AssistId, Assists}; |
15 | use 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 | ||
32 | fn add_vis(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 37 | fn 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 | ||
80 | fn 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 | |||
113 | fn 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 | |||
154 | fn 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 | |||
75 | fn vis_offset(node: &SyntaxNode) -> TextSize { | 217 | fn 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_assists/src/utils.rs b/crates/ra_assists/src/utils.rs index 2f15a3f15..f3fc92ebf 100644 --- a/crates/ra_assists/src/utils.rs +++ b/crates/ra_assists/src/utils.rs | |||
@@ -103,7 +103,7 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { | |||
103 | } | 103 | } |
104 | 104 | ||
105 | #[derive(Clone, Copy)] | 105 | #[derive(Clone, Copy)] |
106 | pub(crate) enum TryEnum { | 106 | pub enum TryEnum { |
107 | Result, | 107 | Result, |
108 | Option, | 108 | Option, |
109 | } | 109 | } |
@@ -111,7 +111,7 @@ pub(crate) enum TryEnum { | |||
111 | impl TryEnum { | 111 | impl TryEnum { |
112 | const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; | 112 | const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result]; |
113 | 113 | ||
114 | pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> { | 114 | pub fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> { |
115 | let enum_ = match ty.as_adt() { | 115 | let enum_ = match ty.as_adt() { |
116 | Some(Adt::Enum(it)) => it, | 116 | Some(Adt::Enum(it)) => it, |
117 | _ => return None, | 117 | _ => return None, |
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] |
11 | crossbeam-channel = "0.4.0" | 11 | crossbeam-channel = "0.4.0" |
12 | lsp-types = { version = "0.74.0", features = ["proposed"] } | ||
13 | log = "0.4.8" | 12 | log = "0.4.8" |
14 | cargo_metadata = "0.10.0" | 13 | cargo_metadata = "0.10.0" |
15 | serde_json = "1.0.48" | 14 | serde_json = "1.0.48" |
16 | jod-thread = "0.1.1" | 15 | jod-thread = "0.1.1" |
17 | ra_toolchain = { path = "../ra_toolchain" } | 16 | ra_toolchain = { path = "../ra_toolchain" } |
18 | |||
19 | [dev-dependencies] | ||
20 | insta = "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. | ||
3 | use cargo_metadata::diagnostic::{ | ||
4 | Applicability, Diagnostic as RustDiagnostic, DiagnosticLevel, DiagnosticSpan, | ||
5 | DiagnosticSpanMacroExpansion, | ||
6 | }; | ||
7 | use lsp_types::{ | ||
8 | CodeAction, Diagnostic, DiagnosticRelatedInformation, DiagnosticSeverity, DiagnosticTag, | ||
9 | Location, NumberOrString, Position, Range, TextEdit, Url, WorkspaceEdit, | ||
10 | }; | ||
11 | use std::{ | ||
12 | collections::HashMap, | ||
13 | fmt::Write, | ||
14 | path::{Component, Path, PathBuf, Prefix}, | ||
15 | str::FromStr, | ||
16 | }; | ||
17 | |||
18 | #[cfg(test)] | ||
19 | mod test; | ||
20 | |||
21 | /// Converts a Rust level string to a LSP severity | ||
22 | fn 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 | ||
34 | fn 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 | ||
39 | fn 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 | ||
55 | fn 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 | ||
67 | fn 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`. | ||
83 | fn 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 | ||
97 | fn 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 | ||
110 | fn 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 | |||
121 | enum MappedRustChildDiagnostic { | ||
122 | Related(DiagnosticRelatedInformation), | ||
123 | SuggestedFix(CodeAction), | ||
124 | MessageLine(String), | ||
125 | } | ||
126 | |||
127 | fn 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)] | ||
168 | pub(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` | ||
184 | pub(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`. | ||
287 | pub 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)] | ||
326 | mod 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))] | ||
4 | use crate::*; | ||
5 | |||
6 | #[cfg(not(windows))] | ||
7 | fn 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))] | ||
13 | fn 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))] | ||
67 | fn 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))] | ||
150 | fn 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))] | ||
275 | fn 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))] | ||
396 | fn 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))] | ||
440 | fn 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))] | ||
712 | fn 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))] | ||
942 | fn 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 24af75c95..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. |
4 | mod conv; | ||
5 | 4 | ||
6 | use std::{ | 5 | use 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 | ||
13 | use cargo_metadata::Message; | 12 | use cargo_metadata::Message; |
14 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; | 13 | use crossbeam_channel::{never, select, unbounded, Receiver, RecvError, Sender}; |
15 | use lsp_types::{ | ||
16 | CodeAction, CodeActionOrCommand, Diagnostic, Url, WorkDoneProgress, WorkDoneProgressBegin, | ||
17 | WorkDoneProgressEnd, WorkDoneProgressReport, | ||
18 | }; | ||
19 | |||
20 | use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; | ||
21 | 14 | ||
22 | pub use crate::conv::url_from_path_with_drive_lowercasing; | 15 | pub 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)] |
25 | pub enum FlycheckConfig { | 20 | pub 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)] | ||
66 | pub enum Status { | ||
67 | Being, | ||
68 | Progress(String), | ||
69 | End, | ||
68 | } | 70 | } |
69 | 71 | ||
70 | pub enum CheckCommand { | 72 | pub 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)] | ||
275 | pub struct DiagnosticWithFixes { | ||
276 | diagnostic: Diagnostic, | ||
277 | fixes: Vec<CodeAction>, | ||
278 | } | ||
279 | |||
280 | enum CheckEvent { | 246 | enum 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 3fc2eccdd..840cfdfc8 100644 --- a/crates/ra_hir/src/code_model.rs +++ b/crates/ra_hir/src/code_model.rs | |||
@@ -148,6 +148,26 @@ impl ModuleDef { | |||
148 | ModuleDef::BuiltinType(_) => None, | 148 | ModuleDef::BuiltinType(_) => None, |
149 | } | 149 | } |
150 | } | 150 | } |
151 | |||
152 | pub fn definition_visibility(&self, db: &dyn HirDatabase) -> Option<Visibility> { | ||
153 | let module = match self { | ||
154 | ModuleDef::Module(it) => it.parent(db)?, | ||
155 | ModuleDef::Function(it) => return Some(it.visibility(db)), | ||
156 | ModuleDef::Adt(it) => it.module(db), | ||
157 | ModuleDef::EnumVariant(it) => { | ||
158 | let parent = it.parent_enum(db); | ||
159 | let module = it.module(db); | ||
160 | return module.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))); | ||
161 | } | ||
162 | ModuleDef::Const(it) => return Some(it.visibility(db)), | ||
163 | ModuleDef::Static(it) => it.module(db), | ||
164 | ModuleDef::Trait(it) => it.module(db), | ||
165 | ModuleDef::TypeAlias(it) => return Some(it.visibility(db)), | ||
166 | ModuleDef::BuiltinType(_) => return None, | ||
167 | }; | ||
168 | |||
169 | module.visibility_of(db, self) | ||
170 | } | ||
151 | } | 171 | } |
152 | 172 | ||
153 | pub use hir_def::{ | 173 | pub use hir_def::{ |
@@ -969,6 +989,17 @@ impl TypeParam { | |||
969 | ty: InEnvironment { value: ty, environment }, | 989 | ty: InEnvironment { value: ty, environment }, |
970 | } | 990 | } |
971 | } | 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 | } | ||
972 | } | 1003 | } |
973 | 1004 | ||
974 | // FIXME: rename from `ImplDef` to `Impl` | 1005 | // FIXME: rename from `ImplDef` to `Impl` |
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index d8b3d342c..3bce8f673 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs | |||
@@ -360,7 +360,7 @@ fn env_expand( | |||
360 | // However, we cannot use an empty string here, because for | 360 | // However, we cannot use an empty string here, because for |
361 | // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become | 361 | // `include!(concat!(env!("OUT_DIR"), "/foo.rs"))` will become |
362 | // `include!("foo.rs"), which might go to infinite loop | 362 | // `include!("foo.rs"), which might go to infinite loop |
363 | let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTATED__".to_string()); | 363 | let s = get_env_inner(db, arg_id, &key).unwrap_or_else(|| "__RA_UNIMPLEMENTED__".to_string()); |
364 | let expanded = quote! { #s }; | 364 | let expanded = quote! { #s }; |
365 | 365 | ||
366 | Ok((expanded, FragmentKind::Expr)) | 366 | Ok((expanded, FragmentKind::Expr)) |
@@ -508,7 +508,7 @@ mod tests { | |||
508 | "#, | 508 | "#, |
509 | ); | 509 | ); |
510 | 510 | ||
511 | assert_eq!(expanded, "\"__RA_UNIMPLEMENTATED__\""); | 511 | assert_eq!(expanded, "\"__RA_UNIMPLEMENTED__\""); |
512 | } | 512 | } |
513 | 513 | ||
514 | #[test] | 514 | #[test] |
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 4c12d0a15..bf30d7151 100644 --- a/crates/ra_hir_expand/src/db.rs +++ b/crates/ra_hir_expand/src/db.rs | |||
@@ -34,7 +34,12 @@ impl TokenExpander { | |||
34 | // FIXME switch these to ExpandResult as well | 34 | // FIXME switch these to ExpandResult as well |
35 | TokenExpander::Builtin(it) => it.expand(db, id, tt).into(), | 35 | TokenExpander::Builtin(it) => it.expand(db, id, tt).into(), |
36 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), | 36 | TokenExpander::BuiltinDerive(it) => it.expand(db, id, tt).into(), |
37 | TokenExpander::ProcMacro(it) => it.expand(db, id, tt).into(), | 37 | TokenExpander::ProcMacro(_) => { |
38 | // We store the result in salsa db to prevent non-determinisc behavior in | ||
39 | // some proc-macro implementation | ||
40 | // See #4315 for details | ||
41 | db.expand_proc_macro(id.into()).into() | ||
42 | } | ||
38 | } | 43 | } |
39 | } | 44 | } |
40 | 45 | ||
@@ -75,6 +80,8 @@ pub trait AstDatabase: SourceDatabase { | |||
75 | 80 | ||
76 | #[salsa::interned] | 81 | #[salsa::interned] |
77 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; | 82 | fn intern_eager_expansion(&self, eager: EagerCallLoc) -> EagerMacroId; |
83 | |||
84 | fn expand_proc_macro(&self, call: MacroCallId) -> Result<tt::Subtree, mbe::ExpandError>; | ||
78 | } | 85 | } |
79 | 86 | ||
80 | /// This expands the given macro call, but with different arguments. This is | 87 | /// This expands the given macro call, but with different arguments. This is |
@@ -216,6 +223,33 @@ fn macro_expand_with_arg( | |||
216 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) | 223 | (Some(Arc::new(tt)), err.map(|e| format!("{:?}", e))) |
217 | } | 224 | } |
218 | 225 | ||
226 | pub(crate) fn expand_proc_macro( | ||
227 | db: &dyn AstDatabase, | ||
228 | id: MacroCallId, | ||
229 | ) -> Result<tt::Subtree, mbe::ExpandError> { | ||
230 | let lazy_id = match id { | ||
231 | MacroCallId::LazyMacro(id) => id, | ||
232 | MacroCallId::EagerMacro(_) => unreachable!(), | ||
233 | }; | ||
234 | |||
235 | let loc = db.lookup_intern_macro(lazy_id); | ||
236 | let macro_arg = match db.macro_arg(id) { | ||
237 | Some(it) => it, | ||
238 | None => { | ||
239 | return Err( | ||
240 | tt::ExpansionError::Unknown("No arguments for proc-macro".to_string()).into() | ||
241 | ) | ||
242 | } | ||
243 | }; | ||
244 | |||
245 | let expander = match loc.def.kind { | ||
246 | MacroDefKind::CustomDerive(expander) => expander, | ||
247 | _ => unreachable!(), | ||
248 | }; | ||
249 | |||
250 | expander.expand(db, lazy_id, ¯o_arg.0) | ||
251 | } | ||
252 | |||
219 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { | 253 | pub(crate) fn parse_or_expand(db: &dyn AstDatabase, file_id: HirFileId) -> Option<SyntaxNode> { |
220 | match file_id.0 { | 254 | match file_id.0 { |
221 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), | 255 | HirFileIdRepr::FileId(file_id) => Some(db.parse(file_id).tree().syntax().clone()), |
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. | ||
431 | pub 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)] |
431 | pub struct SubstsBuilder { | 436 | pub struct SubstsBuilder { |
432 | vec: Vec<Ty>, | 437 | vec: Vec<Ty>, |
diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 6a0f0c72e..f2a52a407 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs | |||
@@ -14,6 +14,7 @@ use crate::{ | |||
14 | }, | 14 | }, |
15 | CompletionItem, | 15 | CompletionItem, |
16 | }; | 16 | }; |
17 | use ra_assists::utils::TryEnum; | ||
17 | 18 | ||
18 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | 19 | pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { |
19 | if !ctx.config.enable_postfix_completions { | 20 | if !ctx.config.enable_postfix_completions { |
@@ -37,8 +38,53 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
37 | Some(it) => it, | 38 | Some(it) => it, |
38 | None => return, | 39 | None => return, |
39 | }; | 40 | }; |
41 | let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty); | ||
42 | if let Some(try_enum) = &try_enum { | ||
43 | match try_enum { | ||
44 | TryEnum::Result => { | ||
45 | postfix_snippet( | ||
46 | ctx, | ||
47 | cap, | ||
48 | &dot_receiver, | ||
49 | "ifl", | ||
50 | "if let Ok {}", | ||
51 | &format!("if let Ok($1) = {} {{\n $0\n}}", receiver_text), | ||
52 | ) | ||
53 | .add_to(acc); | ||
40 | 54 | ||
41 | if receiver_ty.is_bool() || receiver_ty.is_unknown() { | 55 | postfix_snippet( |
56 | ctx, | ||
57 | cap, | ||
58 | &dot_receiver, | ||
59 | "while", | ||
60 | "while let Ok {}", | ||
61 | &format!("while let Ok($1) = {} {{\n $0\n}}", receiver_text), | ||
62 | ) | ||
63 | .add_to(acc); | ||
64 | } | ||
65 | TryEnum::Option => { | ||
66 | postfix_snippet( | ||
67 | ctx, | ||
68 | cap, | ||
69 | &dot_receiver, | ||
70 | "ifl", | ||
71 | "if let Some {}", | ||
72 | &format!("if let Some($1) = {} {{\n $0\n}}", receiver_text), | ||
73 | ) | ||
74 | .add_to(acc); | ||
75 | |||
76 | postfix_snippet( | ||
77 | ctx, | ||
78 | cap, | ||
79 | &dot_receiver, | ||
80 | "while", | ||
81 | "while let Some {}", | ||
82 | &format!("while let Some($1) = {} {{\n $0\n}}", receiver_text), | ||
83 | ) | ||
84 | .add_to(acc); | ||
85 | } | ||
86 | } | ||
87 | } else if receiver_ty.is_bool() || receiver_ty.is_unknown() { | ||
42 | postfix_snippet( | 88 | postfix_snippet( |
43 | ctx, | 89 | ctx, |
44 | cap, | 90 | cap, |
@@ -58,7 +104,6 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
58 | ) | 104 | ) |
59 | .add_to(acc); | 105 | .add_to(acc); |
60 | } | 106 | } |
61 | |||
62 | // !&&&42 is a compiler error, ergo process it before considering the references | 107 | // !&&&42 is a compiler error, ergo process it before considering the references |
63 | postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) | 108 | postfix_snippet(ctx, cap, &dot_receiver, "not", "!expr", &format!("!{}", receiver_text)) |
64 | .add_to(acc); | 109 | .add_to(acc); |
@@ -80,16 +125,45 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { | |||
80 | let dot_receiver = include_references(dot_receiver); | 125 | let dot_receiver = include_references(dot_receiver); |
81 | let receiver_text = | 126 | let receiver_text = |
82 | get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); | 127 | get_receiver_text(&dot_receiver, ctx.dot_receiver_is_ambiguous_float_literal); |
83 | 128 | match try_enum { | |
84 | postfix_snippet( | 129 | Some(try_enum) => { |
85 | ctx, | 130 | match try_enum { |
86 | cap, | 131 | TryEnum::Result => { |
87 | &dot_receiver, | 132 | postfix_snippet( |
88 | "match", | 133 | ctx, |
89 | "match expr {}", | 134 | cap, |
90 | &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), | 135 | &dot_receiver, |
91 | ) | 136 | "match", |
92 | .add_to(acc); | 137 | "match expr {}", |
138 | &format!("match {} {{\n Ok(${{1:_}}) => {{$2\\}},\n Err(${{3:_}}) => {{$0\\}},\n}}", receiver_text), | ||
139 | ) | ||
140 | .add_to(acc); | ||
141 | } | ||
142 | TryEnum::Option => { | ||
143 | postfix_snippet( | ||
144 | ctx, | ||
145 | cap, | ||
146 | &dot_receiver, | ||
147 | "match", | ||
148 | "match expr {}", | ||
149 | &format!("match {} {{\n Some(${{1:_}}) => {{$2\\}},\n None => {{$0\\}},\n}}", receiver_text), | ||
150 | ) | ||
151 | .add_to(acc); | ||
152 | } | ||
153 | } | ||
154 | } | ||
155 | None => { | ||
156 | postfix_snippet( | ||
157 | ctx, | ||
158 | cap, | ||
159 | &dot_receiver, | ||
160 | "match", | ||
161 | "match expr {}", | ||
162 | &format!("match {} {{\n ${{1:_}} => {{$0\\}},\n}}", receiver_text), | ||
163 | ) | ||
164 | .add_to(acc); | ||
165 | } | ||
166 | } | ||
93 | 167 | ||
94 | postfix_snippet( | 168 | postfix_snippet( |
95 | ctx, | 169 | ctx, |
@@ -236,6 +310,164 @@ mod tests { | |||
236 | } | 310 | } |
237 | 311 | ||
238 | #[test] | 312 | #[test] |
313 | fn postfix_completion_works_for_option() { | ||
314 | assert_debug_snapshot!( | ||
315 | do_postfix_completion( | ||
316 | r#" | ||
317 | enum Option<T> { | ||
318 | Some(T), | ||
319 | None, | ||
320 | } | ||
321 | |||
322 | fn main() { | ||
323 | let bar = Option::Some(true); | ||
324 | bar.<|> | ||
325 | } | ||
326 | "#, | ||
327 | ), | ||
328 | @r###" | ||
329 | [ | ||
330 | CompletionItem { | ||
331 | label: "box", | ||
332 | source_range: 210..210, | ||
333 | delete: 206..210, | ||
334 | insert: "Box::new(bar)", | ||
335 | detail: "Box::new(expr)", | ||
336 | }, | ||
337 | CompletionItem { | ||
338 | label: "dbg", | ||
339 | source_range: 210..210, | ||
340 | delete: 206..210, | ||
341 | insert: "dbg!(bar)", | ||
342 | detail: "dbg!(expr)", | ||
343 | }, | ||
344 | CompletionItem { | ||
345 | label: "ifl", | ||
346 | source_range: 210..210, | ||
347 | delete: 206..210, | ||
348 | insert: "if let Some($1) = bar {\n $0\n}", | ||
349 | detail: "if let Some {}", | ||
350 | }, | ||
351 | CompletionItem { | ||
352 | label: "match", | ||
353 | source_range: 210..210, | ||
354 | delete: 206..210, | ||
355 | insert: "match bar {\n Some(${1:_}) => {$2\\},\n None => {$0\\},\n}", | ||
356 | detail: "match expr {}", | ||
357 | }, | ||
358 | CompletionItem { | ||
359 | label: "not", | ||
360 | source_range: 210..210, | ||
361 | delete: 206..210, | ||
362 | insert: "!bar", | ||
363 | detail: "!expr", | ||
364 | }, | ||
365 | CompletionItem { | ||
366 | label: "ref", | ||
367 | source_range: 210..210, | ||
368 | delete: 206..210, | ||
369 | insert: "&bar", | ||
370 | detail: "&expr", | ||
371 | }, | ||
372 | CompletionItem { | ||
373 | label: "refm", | ||
374 | source_range: 210..210, | ||
375 | delete: 206..210, | ||
376 | insert: "&mut bar", | ||
377 | detail: "&mut expr", | ||
378 | }, | ||
379 | CompletionItem { | ||
380 | label: "while", | ||
381 | source_range: 210..210, | ||
382 | delete: 206..210, | ||
383 | insert: "while let Some($1) = bar {\n $0\n}", | ||
384 | detail: "while let Some {}", | ||
385 | }, | ||
386 | ] | ||
387 | "### | ||
388 | ); | ||
389 | } | ||
390 | |||
391 | #[test] | ||
392 | fn postfix_completion_works_for_result() { | ||
393 | assert_debug_snapshot!( | ||
394 | do_postfix_completion( | ||
395 | r#" | ||
396 | enum Result<T, E> { | ||
397 | Ok(T), | ||
398 | Err(E), | ||
399 | } | ||
400 | |||
401 | fn main() { | ||
402 | let bar = Result::Ok(true); | ||
403 | bar.<|> | ||
404 | } | ||
405 | "#, | ||
406 | ), | ||
407 | @r###" | ||
408 | [ | ||
409 | CompletionItem { | ||
410 | label: "box", | ||
411 | source_range: 211..211, | ||
412 | delete: 207..211, | ||
413 | insert: "Box::new(bar)", | ||
414 | detail: "Box::new(expr)", | ||
415 | }, | ||
416 | CompletionItem { | ||
417 | label: "dbg", | ||
418 | source_range: 211..211, | ||
419 | delete: 207..211, | ||
420 | insert: "dbg!(bar)", | ||
421 | detail: "dbg!(expr)", | ||
422 | }, | ||
423 | CompletionItem { | ||
424 | label: "ifl", | ||
425 | source_range: 211..211, | ||
426 | delete: 207..211, | ||
427 | insert: "if let Ok($1) = bar {\n $0\n}", | ||
428 | detail: "if let Ok {}", | ||
429 | }, | ||
430 | CompletionItem { | ||
431 | label: "match", | ||
432 | source_range: 211..211, | ||
433 | delete: 207..211, | ||
434 | insert: "match bar {\n Ok(${1:_}) => {$2\\},\n Err(${3:_}) => {$0\\},\n}", | ||
435 | detail: "match expr {}", | ||
436 | }, | ||
437 | CompletionItem { | ||
438 | label: "not", | ||
439 | source_range: 211..211, | ||
440 | delete: 207..211, | ||
441 | insert: "!bar", | ||
442 | detail: "!expr", | ||
443 | }, | ||
444 | CompletionItem { | ||
445 | label: "ref", | ||
446 | source_range: 211..211, | ||
447 | delete: 207..211, | ||
448 | insert: "&bar", | ||
449 | detail: "&expr", | ||
450 | }, | ||
451 | CompletionItem { | ||
452 | label: "refm", | ||
453 | source_range: 211..211, | ||
454 | delete: 207..211, | ||
455 | insert: "&mut bar", | ||
456 | detail: "&mut expr", | ||
457 | }, | ||
458 | CompletionItem { | ||
459 | label: "while", | ||
460 | source_range: 211..211, | ||
461 | delete: 207..211, | ||
462 | insert: "while let Ok($1) = bar {\n $0\n}", | ||
463 | detail: "while let Ok {}", | ||
464 | }, | ||
465 | ] | ||
466 | "### | ||
467 | ); | ||
468 | } | ||
469 | |||
470 | #[test] | ||
239 | fn some_postfix_completions_ignored() { | 471 | fn some_postfix_completions_ignored() { |
240 | assert_debug_snapshot!( | 472 | assert_debug_snapshot!( |
241 | do_postfix_completion( | 473 | do_postfix_completion( |
diff --git a/crates/ra_ide/src/completion/complete_snippet.rs b/crates/ra_ide/src/completion/complete_snippet.rs index a3f5d1b6a..0568d9ccf 100644 --- a/crates/ra_ide/src/completion/complete_snippet.rs +++ b/crates/ra_ide/src/completion/complete_snippet.rs | |||
@@ -36,6 +36,24 @@ pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &CompletionConte | |||
36 | snippet( | 36 | snippet( |
37 | ctx, | 37 | ctx, |
38 | cap, | 38 | cap, |
39 | "Test module", | ||
40 | "\ | ||
41 | #[cfg(test)] | ||
42 | mod tests { | ||
43 | use super::*; | ||
44 | |||
45 | #[test] | ||
46 | fn ${1:test_name}() { | ||
47 | $0 | ||
48 | } | ||
49 | }", | ||
50 | ) | ||
51 | .lookup_by("tmod") | ||
52 | .add_to(acc); | ||
53 | |||
54 | snippet( | ||
55 | ctx, | ||
56 | cap, | ||
39 | "Test function", | 57 | "Test function", |
40 | "\ | 58 | "\ |
41 | #[test] | 59 | #[test] |
@@ -118,6 +136,14 @@ mod tests { | |||
118 | lookup: "tfn", | 136 | lookup: "tfn", |
119 | }, | 137 | }, |
120 | CompletionItem { | 138 | CompletionItem { |
139 | label: "Test module", | ||
140 | source_range: 78..78, | ||
141 | delete: 78..78, | ||
142 | insert: "#[cfg(test)]\nmod tests {\n use super::*;\n\n #[test]\n fn ${1:test_name}() {\n $0\n }\n}", | ||
143 | kind: Snippet, | ||
144 | lookup: "tmod", | ||
145 | }, | ||
146 | CompletionItem { | ||
121 | label: "macro_rules", | 147 | label: "macro_rules", |
122 | source_range: 78..78, | 148 | source_range: 78..78, |
123 | delete: 78..78, | 149 | delete: 78..78, |
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::{ | |||
17 | impl Completions { | 17 | impl 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 | ||
320 | pub(crate) fn compute_score( | 325 | pub(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_ide/src/references.rs b/crates/ra_ide/src/references.rs index 555ccf295..074284b42 100644 --- a/crates/ra_ide/src/references.rs +++ b/crates/ra_ide/src/references.rs | |||
@@ -593,6 +593,31 @@ mod tests { | |||
593 | check_result(refs, "i BIND_PAT FileId(1) 36..37 Other", &["FileId(1) 51..52 Other Write"]); | 593 | check_result(refs, "i BIND_PAT FileId(1) 36..37 Other", &["FileId(1) 51..52 Other Write"]); |
594 | } | 594 | } |
595 | 595 | ||
596 | #[test] | ||
597 | fn test_find_struct_function_refs_outside_module() { | ||
598 | let code = r#" | ||
599 | mod foo { | ||
600 | pub struct Foo; | ||
601 | |||
602 | impl Foo { | ||
603 | pub fn new<|>() -> Foo { | ||
604 | Foo | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | |||
609 | fn main() { | ||
610 | let _f = foo::Foo::new(); | ||
611 | }"#; | ||
612 | |||
613 | let refs = get_all_refs(code); | ||
614 | check_result( | ||
615 | refs, | ||
616 | "new FN_DEF FileId(1) 87..150 94..97 Other", | ||
617 | &["FileId(1) 227..230 StructLiteral"], | ||
618 | ); | ||
619 | } | ||
620 | |||
596 | fn get_all_refs(text: &str) -> ReferenceSearchResult { | 621 | fn get_all_refs(text: &str) -> ReferenceSearchResult { |
597 | let (analysis, position) = single_file_with_position(text); | 622 | let (analysis, position) = single_file_with_position(text); |
598 | analysis.find_all_refs(position, None).unwrap().unwrap() | 623 | analysis.find_all_refs(position, None).unwrap().unwrap() |
diff --git a/crates/ra_ide/src/snapshots/highlighting.html b/crates/ra_ide/src/snapshots/highlighting.html index 4c27aade4..635fe5cf9 100644 --- a/crates/ra_ide/src/snapshots/highlighting.html +++ b/crates/ra_ide/src/snapshots/highlighting.html | |||
@@ -33,6 +33,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
33 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, | 33 | <span class="keyword">pub</span> <span class="field declaration">y</span>: <span class="builtin_type">i32</span>, |
34 | } | 34 | } |
35 | 35 | ||
36 | <span class="keyword">trait</span> <span class="trait declaration">Bar</span> { | ||
37 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -> <span class="builtin_type">i32</span>; | ||
38 | } | ||
39 | |||
40 | <span class="keyword">impl</span> <span class="trait">Bar</span> <span class="keyword">for</span> <span class="struct">Foo</span> { | ||
41 | <span class="keyword">fn</span> <span class="function declaration">bar</span>(&<span class="keyword">self</span>) -> <span class="builtin_type">i32</span> { | ||
42 | <span class="keyword">self</span>.<span class="field">x</span> | ||
43 | } | ||
44 | } | ||
45 | |||
36 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span>: <span class="builtin_type">i32</span> = <span class="numeric_literal">0</span>; | 46 | <span class="keyword">static</span> <span class="keyword">mut</span> <span class="static declaration mutable">STATIC_MUT</span>: <span class="builtin_type">i32</span> = <span class="numeric_literal">0</span>; |
37 | 47 | ||
38 | <span class="keyword">fn</span> <span class="function declaration">foo</span><<span class="lifetime declaration">'a</span>, <span class="type_param declaration">T</span>>() -> <span class="type_param">T</span> { | 48 | <span class="keyword">fn</span> <span class="function declaration">foo</span><<span class="lifetime declaration">'a</span>, <span class="type_param declaration">T</span>>() -> <span class="type_param">T</span> { |
@@ -63,6 +73,10 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd | |||
63 | <span class="static mutable">STATIC_MUT</span> = <span class="numeric_literal">1</span>; | 73 | <span class="static mutable">STATIC_MUT</span> = <span class="numeric_literal">1</span>; |
64 | } | 74 | } |
65 | 75 | ||
76 | <span class="keyword control">for</span> <span class="variable declaration">e</span> <span class="keyword control">in</span> <span class="variable mutable">vec</span> { | ||
77 | <span class="comment">// Do nothing</span> | ||
78 | } | ||
79 | |||
66 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>; | 80 | <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">x</span> = <span class="numeric_literal">42</span>; |
67 | <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; | 81 | <span class="keyword">let</span> <span class="variable declaration mutable">y</span> = &<span class="keyword">mut</span> <span class="variable mutable">x</span>; |
68 | <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>; | 82 | <span class="keyword">let</span> <span class="variable declaration">z</span> = &<span class="variable mutable">y</span>; |
diff --git a/crates/ra_ide/src/syntax_highlighting.rs b/crates/ra_ide/src/syntax_highlighting.rs index d53a39f57..be57eeb0a 100644 --- a/crates/ra_ide/src/syntax_highlighting.rs +++ b/crates/ra_ide/src/syntax_highlighting.rs | |||
@@ -403,12 +403,13 @@ fn highlight_element( | |||
403 | T![break] | 403 | T![break] |
404 | | T![continue] | 404 | | T![continue] |
405 | | T![else] | 405 | | T![else] |
406 | | T![for] | ||
407 | | T![if] | 406 | | T![if] |
408 | | T![loop] | 407 | | T![loop] |
409 | | T![match] | 408 | | T![match] |
410 | | T![return] | 409 | | T![return] |
411 | | T![while] => h | HighlightModifier::ControlFlow, | 410 | | T![while] |
411 | | T![in] => h | HighlightModifier::ControlFlow, | ||
412 | T![for] if !is_child_of_impl(element) => h | HighlightModifier::ControlFlow, | ||
412 | T![unsafe] => h | HighlightModifier::Unsafe, | 413 | T![unsafe] => h | HighlightModifier::Unsafe, |
413 | _ => h, | 414 | _ => h, |
414 | } | 415 | } |
@@ -432,6 +433,13 @@ fn highlight_element( | |||
432 | } | 433 | } |
433 | } | 434 | } |
434 | 435 | ||
436 | fn is_child_of_impl(element: SyntaxElement) -> bool { | ||
437 | match element.parent() { | ||
438 | Some(e) => e.kind() == IMPL_DEF, | ||
439 | _ => false, | ||
440 | } | ||
441 | } | ||
442 | |||
435 | fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { | 443 | fn highlight_name(db: &RootDatabase, def: Definition) -> Highlight { |
436 | match def { | 444 | match def { |
437 | Definition::Macro(_) => HighlightTag::Macro, | 445 | Definition::Macro(_) => HighlightTag::Macro, |
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs index 13894869c..eb43a23da 100644 --- a/crates/ra_ide/src/syntax_highlighting/tests.rs +++ b/crates/ra_ide/src/syntax_highlighting/tests.rs | |||
@@ -17,6 +17,16 @@ struct Foo { | |||
17 | pub y: i32, | 17 | pub y: i32, |
18 | } | 18 | } |
19 | 19 | ||
20 | trait Bar { | ||
21 | fn bar(&self) -> i32; | ||
22 | } | ||
23 | |||
24 | impl Bar for Foo { | ||
25 | fn bar(&self) -> i32 { | ||
26 | self.x | ||
27 | } | ||
28 | } | ||
29 | |||
20 | static mut STATIC_MUT: i32 = 0; | 30 | static mut STATIC_MUT: i32 = 0; |
21 | 31 | ||
22 | fn foo<'a, T>() -> T { | 32 | fn foo<'a, T>() -> T { |
@@ -47,6 +57,10 @@ fn main() { | |||
47 | STATIC_MUT = 1; | 57 | STATIC_MUT = 1; |
48 | } | 58 | } |
49 | 59 | ||
60 | for e in vec { | ||
61 | // Do nothing | ||
62 | } | ||
63 | |||
50 | let mut x = 42; | 64 | let mut x = 42; |
51 | let y = &mut x; | 65 | let y = &mut x; |
52 | let z = &y; | 66 | let z = &y; |
diff --git a/crates/ra_ide_db/src/defs.rs b/crates/ra_ide_db/src/defs.rs index f990e3bb9..60c11178e 100644 --- a/crates/ra_ide_db/src/defs.rs +++ b/crates/ra_ide_db/src/defs.rs | |||
@@ -6,7 +6,7 @@ | |||
6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). | 6 | // FIXME: this badly needs rename/rewrite (matklad, 2020-02-06). |
7 | 7 | ||
8 | use hir::{ | 8 | use hir::{ |
9 | Adt, Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, | 9 | Field, HasVisibility, ImplDef, Local, MacroDef, Module, ModuleDef, Name, PathResolution, |
10 | Semantics, TypeParam, Visibility, | 10 | Semantics, TypeParam, Visibility, |
11 | }; | 11 | }; |
12 | use ra_prof::profile; | 12 | use ra_prof::profile; |
@@ -42,18 +42,10 @@ impl Definition { | |||
42 | } | 42 | } |
43 | 43 | ||
44 | pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> { | 44 | pub fn visibility(&self, db: &RootDatabase) -> Option<Visibility> { |
45 | let module = self.module(db); | ||
46 | |||
47 | match self { | 45 | match self { |
48 | Definition::Macro(_) => None, | 46 | Definition::Macro(_) => None, |
49 | Definition::Field(sf) => Some(sf.visibility(db)), | 47 | Definition::Field(sf) => Some(sf.visibility(db)), |
50 | Definition::ModuleDef(def) => match def { | 48 | Definition::ModuleDef(def) => def.definition_visibility(db), |
51 | ModuleDef::EnumVariant(id) => { | ||
52 | let parent = id.parent_enum(db); | ||
53 | module?.visibility_of(db, &ModuleDef::Adt(Adt::Enum(parent))) | ||
54 | } | ||
55 | _ => module?.visibility_of(db, def), | ||
56 | }, | ||
57 | Definition::SelfType(_) => None, | 49 | Definition::SelfType(_) => None, |
58 | Definition::Local(_) => None, | 50 | Definition::Local(_) => None, |
59 | Definition::TypeParam(_) => None, | 51 | Definition::TypeParam(_) => None, |
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 1a020398e..9c450eaba 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -22,7 +22,7 @@ pub enum ParseError { | |||
22 | RepetitionEmtpyTokenTree, | 22 | RepetitionEmtpyTokenTree, |
23 | } | 23 | } |
24 | 24 | ||
25 | #[derive(Debug, PartialEq, Eq)] | 25 | #[derive(Debug, PartialEq, Eq, Clone)] |
26 | pub enum ExpandError { | 26 | pub enum ExpandError { |
27 | NoMatchingRule, | 27 | NoMatchingRule, |
28 | UnexpectedToken, | 28 | UnexpectedToken, |
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)] |
39 | pub struct PackageRoot { | 39 | pub 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/generated/nodes.rs b/crates/ra_syntax/src/ast/generated/nodes.rs index b00c15608..cf6067e57 100644 --- a/crates/ra_syntax/src/ast/generated/nodes.rs +++ b/crates/ra_syntax/src/ast/generated/nodes.rs | |||
@@ -5,7 +5,9 @@ use crate::{ | |||
5 | SyntaxKind::{self, *}, | 5 | SyntaxKind::{self, *}, |
6 | SyntaxNode, SyntaxToken, T, | 6 | SyntaxNode, SyntaxToken, T, |
7 | }; | 7 | }; |
8 | 8 | /// The entire Rust source file. Includes all top-level inner attributes and module items. | |
9 | /// | ||
10 | /// [Reference](https://doc.rust-lang.org/reference/crates-and-source-files.html) | ||
9 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 11 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
10 | pub struct SourceFile { | 12 | pub struct SourceFile { |
11 | pub(crate) syntax: SyntaxNode, | 13 | pub(crate) syntax: SyntaxNode, |
@@ -16,7 +18,28 @@ impl ast::DocCommentsOwner for SourceFile {} | |||
16 | impl SourceFile { | 18 | impl SourceFile { |
17 | pub fn modules(&self) -> AstChildren<Module> { support::children(&self.syntax) } | 19 | pub fn modules(&self) -> AstChildren<Module> { support::children(&self.syntax) } |
18 | } | 20 | } |
19 | 21 | /// Function definition either with body or not. | |
22 | /// Includes all of its attributes and doc comments. | ||
23 | /// | ||
24 | /// ``` | ||
25 | /// ❰ | ||
26 | /// /// Docs | ||
27 | /// #[attr] | ||
28 | /// pub extern "C" fn foo<T>(#[attr] Patern {p}: Pattern) -> u32 | ||
29 | /// where | ||
30 | /// T: Debug | ||
31 | /// { | ||
32 | /// 42 | ||
33 | /// } | ||
34 | /// ❱ | ||
35 | /// | ||
36 | /// extern "C" { | ||
37 | /// ❰ fn fn_decl(also_variadic_ffi: u32, ...) -> u32; ❱ | ||
38 | /// } | ||
39 | /// ``` | ||
40 | /// | ||
41 | /// - [Reference](https://doc.rust-lang.org/reference/items/functions.html) | ||
42 | /// - [Nomicon](https://doc.rust-lang.org/nomicon/ffi.html#variadic-functions) | ||
20 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 43 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
21 | pub struct FnDef { | 44 | pub struct FnDef { |
22 | pub(crate) syntax: SyntaxNode, | 45 | pub(crate) syntax: SyntaxNode, |
@@ -38,7 +61,13 @@ impl FnDef { | |||
38 | pub fn body(&self) -> Option<BlockExpr> { support::child(&self.syntax) } | 61 | pub fn body(&self) -> Option<BlockExpr> { support::child(&self.syntax) } |
39 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } | 62 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } |
40 | } | 63 | } |
41 | 64 | /// Return type annotation. | |
65 | /// | ||
66 | /// ``` | ||
67 | /// fn foo(a: u32) ❰ -> Option<u32> ❱ { Some(a) } | ||
68 | /// ``` | ||
69 | /// | ||
70 | /// [Reference](https://doc.rust-lang.org/reference/items/functions.html) | ||
42 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 71 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
43 | pub struct RetType { | 72 | pub struct RetType { |
44 | pub(crate) syntax: SyntaxNode, | 73 | pub(crate) syntax: SyntaxNode, |
@@ -47,7 +76,26 @@ impl RetType { | |||
47 | pub fn thin_arrow_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![->]) } | 76 | pub fn thin_arrow_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![->]) } |
48 | pub fn type_ref(&self) -> Option<TypeRef> { support::child(&self.syntax) } | 77 | pub fn type_ref(&self) -> Option<TypeRef> { support::child(&self.syntax) } |
49 | } | 78 | } |
50 | 79 | /// Struct definition. | |
80 | /// Includes all of its attributes and doc comments. | ||
81 | /// | ||
82 | /// ``` | ||
83 | /// ❰ | ||
84 | /// /// Docs | ||
85 | /// #[attr] | ||
86 | /// struct Foo<T> where T: Debug { | ||
87 | /// /// Docs | ||
88 | /// #[attr] | ||
89 | /// pub a: u32, | ||
90 | /// b: T, | ||
91 | /// } | ||
92 | /// ❱ | ||
93 | /// | ||
94 | /// ❰ struct Foo; ❱ | ||
95 | /// ❰ struct Foo<T>(#[attr] T) where T: Debug; ❱ | ||
96 | /// ``` | ||
97 | /// | ||
98 | /// [Reference](https://doc.rust-lang.org/reference/items/structs.html) | ||
51 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 99 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
52 | pub struct StructDef { | 100 | pub struct StructDef { |
53 | pub(crate) syntax: SyntaxNode, | 101 | pub(crate) syntax: SyntaxNode, |
@@ -62,7 +110,23 @@ impl StructDef { | |||
62 | pub fn field_def_list(&self) -> Option<FieldDefList> { support::child(&self.syntax) } | 110 | pub fn field_def_list(&self) -> Option<FieldDefList> { support::child(&self.syntax) } |
63 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } | 111 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } |
64 | } | 112 | } |
65 | 113 | /// Union definition. | |
114 | /// Includes all of its attributes and doc comments. | ||
115 | /// | ||
116 | /// ``` | ||
117 | /// ❰ | ||
118 | /// /// Docs | ||
119 | /// #[attr] | ||
120 | /// pub union Foo<T> where T: Debug { | ||
121 | /// /// Docs | ||
122 | /// #[attr] | ||
123 | /// a: T, | ||
124 | /// b: u32, | ||
125 | /// } | ||
126 | /// ❱ | ||
127 | /// ``` | ||
128 | /// | ||
129 | /// [Reference](https://doc.rust-lang.org/reference/items/unions.html) | ||
66 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 130 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
67 | pub struct UnionDef { | 131 | pub struct UnionDef { |
68 | pub(crate) syntax: SyntaxNode, | 132 | pub(crate) syntax: SyntaxNode, |
@@ -78,7 +142,19 @@ impl UnionDef { | |||
78 | support::child(&self.syntax) | 142 | support::child(&self.syntax) |
79 | } | 143 | } |
80 | } | 144 | } |
81 | 145 | /// Record field definition list including enclosing curly braces. | |
146 | /// | ||
147 | /// ``` | ||
148 | /// struct Foo // same for union | ||
149 | /// ❰ | ||
150 | /// { | ||
151 | /// a: u32, | ||
152 | /// b: bool, | ||
153 | /// } | ||
154 | /// ❱ | ||
155 | /// ``` | ||
156 | /// | ||
157 | /// [Reference](https://doc.rust-lang.org/reference/items/structs.html) | ||
82 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 158 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
83 | pub struct RecordFieldDefList { | 159 | pub struct RecordFieldDefList { |
84 | pub(crate) syntax: SyntaxNode, | 160 | pub(crate) syntax: SyntaxNode, |
@@ -88,7 +164,22 @@ impl RecordFieldDefList { | |||
88 | pub fn fields(&self) -> AstChildren<RecordFieldDef> { support::children(&self.syntax) } | 164 | pub fn fields(&self) -> AstChildren<RecordFieldDef> { support::children(&self.syntax) } |
89 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } | 165 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } |
90 | } | 166 | } |
91 | 167 | /// Record field definition including its attributes and doc comments. | |
168 | /// | ||
169 | /// ` `` | ||
170 | /// same for union | ||
171 | /// struct Foo { | ||
172 | /// ❰ | ||
173 | /// /// Docs | ||
174 | /// #[attr] | ||
175 | /// pub a: u32 | ||
176 | /// ❱ | ||
177 | /// | ||
178 | /// ❰ b: bool ❱ | ||
179 | /// } | ||
180 | /// ``` | ||
181 | /// | ||
182 | /// [Reference](https://doc.rust-lang.org/reference/items/structs.html) | ||
92 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 183 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
93 | pub struct RecordFieldDef { | 184 | pub struct RecordFieldDef { |
94 | pub(crate) syntax: SyntaxNode, | 185 | pub(crate) syntax: SyntaxNode, |
@@ -99,7 +190,13 @@ impl ast::AttrsOwner for RecordFieldDef {} | |||
99 | impl ast::DocCommentsOwner for RecordFieldDef {} | 190 | impl ast::DocCommentsOwner for RecordFieldDef {} |
100 | impl ast::TypeAscriptionOwner for RecordFieldDef {} | 191 | impl ast::TypeAscriptionOwner for RecordFieldDef {} |
101 | impl RecordFieldDef {} | 192 | impl RecordFieldDef {} |
102 | 193 | /// Tuple field definition list including enclosing parens. | |
194 | /// | ||
195 | /// ``` | ||
196 | /// struct Foo ❰ (u32, String, Vec<u32>) ❱; | ||
197 | /// ``` | ||
198 | /// | ||
199 | /// [Reference](https://doc.rust-lang.org/reference/items/structs.html) | ||
103 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 200 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
104 | pub struct TupleFieldDefList { | 201 | pub struct TupleFieldDefList { |
105 | pub(crate) syntax: SyntaxNode, | 202 | pub(crate) syntax: SyntaxNode, |
@@ -109,7 +206,13 @@ impl TupleFieldDefList { | |||
109 | pub fn fields(&self) -> AstChildren<TupleFieldDef> { support::children(&self.syntax) } | 206 | pub fn fields(&self) -> AstChildren<TupleFieldDef> { support::children(&self.syntax) } |
110 | pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } | 207 | pub fn r_paren_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![')']) } |
111 | } | 208 | } |
112 | 209 | /// Tuple field definition including its attributes. | |
210 | /// | ||
211 | /// ``` | ||
212 | /// struct Foo(❰ #[attr] u32 ❱); | ||
213 | /// ``` | ||
214 | /// | ||
215 | /// [Reference](https://doc.rust-lang.org/reference/items/structs.html) | ||
113 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 216 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
114 | pub struct TupleFieldDef { | 217 | pub struct TupleFieldDef { |
115 | pub(crate) syntax: SyntaxNode, | 218 | pub(crate) syntax: SyntaxNode, |
@@ -119,7 +222,29 @@ impl ast::AttrsOwner for TupleFieldDef {} | |||
119 | impl TupleFieldDef { | 222 | impl TupleFieldDef { |
120 | pub fn type_ref(&self) -> Option<TypeRef> { support::child(&self.syntax) } | 223 | pub fn type_ref(&self) -> Option<TypeRef> { support::child(&self.syntax) } |
121 | } | 224 | } |
122 | 225 | /// Enum definition. | |
226 | /// Includes all of its attributes and doc comments. | ||
227 | /// | ||
228 | /// ``` | ||
229 | /// ❰ | ||
230 | /// /// Docs | ||
231 | /// #[attr] | ||
232 | /// pub enum Foo<T> where T: Debug { | ||
233 | /// /// Docs | ||
234 | /// #[attr] | ||
235 | /// Bar, | ||
236 | /// Baz(#[attr] u32), | ||
237 | /// Bruh { | ||
238 | /// a: u32, | ||
239 | /// /// Docs | ||
240 | /// #[attr] | ||
241 | /// b: T, | ||
242 | /// } | ||
243 | /// } | ||
244 | /// ❱ | ||
245 | /// ``` | ||
246 | /// | ||
247 | /// [Reference](https://doc.rust-lang.org/reference/items/enumerations.html) | ||
123 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 248 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
124 | pub struct EnumDef { | 249 | pub struct EnumDef { |
125 | pub(crate) syntax: SyntaxNode, | 250 | pub(crate) syntax: SyntaxNode, |
@@ -133,7 +258,22 @@ impl EnumDef { | |||
133 | pub fn enum_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![enum]) } | 258 | pub fn enum_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![enum]) } |
134 | pub fn variant_list(&self) -> Option<EnumVariantList> { support::child(&self.syntax) } | 259 | pub fn variant_list(&self) -> Option<EnumVariantList> { support::child(&self.syntax) } |
135 | } | 260 | } |
136 | 261 | /// Enum variant definition list including enclosing curly braces. | |
262 | /// | ||
263 | /// ``` | ||
264 | /// enum Foo | ||
265 | /// ❰ | ||
266 | /// { | ||
267 | /// Bar, | ||
268 | /// Baz(u32), | ||
269 | /// Bruh { | ||
270 | /// a: u32 | ||
271 | /// } | ||
272 | /// } | ||
273 | /// ❱ | ||
274 | /// ``` | ||
275 | /// | ||
276 | /// [Reference](https://doc.rust-lang.org/reference/items/enumerations.html) | ||
137 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 277 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
138 | pub struct EnumVariantList { | 278 | pub struct EnumVariantList { |
139 | pub(crate) syntax: SyntaxNode, | 279 | pub(crate) syntax: SyntaxNode, |
@@ -143,7 +283,21 @@ impl EnumVariantList { | |||
143 | pub fn variants(&self) -> AstChildren<EnumVariant> { support::children(&self.syntax) } | 283 | pub fn variants(&self) -> AstChildren<EnumVariant> { support::children(&self.syntax) } |
144 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } | 284 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } |
145 | } | 285 | } |
146 | 286 | /// Enum variant definition including its attributes and discriminant value definition. | |
287 | /// | ||
288 | /// ``` | ||
289 | /// enum Foo { | ||
290 | /// ❰ | ||
291 | /// /// Docs | ||
292 | /// #[attr] | ||
293 | /// Bar | ||
294 | /// ❱ | ||
295 | /// | ||
296 | /// // same for tuple and record variants | ||
297 | /// } | ||
298 | /// ``` | ||
299 | /// | ||
300 | /// [Reference](https://doc.rust-lang.org/reference/items/enumerations.html) | ||
147 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 301 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
148 | pub struct EnumVariant { | 302 | pub struct EnumVariant { |
149 | pub(crate) syntax: SyntaxNode, | 303 | pub(crate) syntax: SyntaxNode, |
@@ -157,7 +311,20 @@ impl EnumVariant { | |||
157 | pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } | 311 | pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } |
158 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } | 312 | pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } |
159 | } | 313 | } |
160 | 314 | /// Trait definition. | |
315 | /// Includes all of its attributes and doc comments. | ||
316 | /// | ||
317 | /// ``` | ||
318 | /// ❰ | ||
319 | /// /// Docs | ||
320 | /// #[attr] | ||
321 | /// pub unsafe trait Foo<T>: Debug where T: Debug { | ||
322 | /// // ... | ||
323 | /// } | ||
324 | /// ❱ | ||
325 | /// ``` | ||
326 | /// | ||
327 | /// [Reference](https://doc.rust-lang.org/reference/items/traits.html) | ||
161 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 328 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
162 | pub struct TraitDef { | 329 | pub struct TraitDef { |
163 | pub(crate) syntax: SyntaxNode, | 330 | pub(crate) syntax: SyntaxNode, |
@@ -174,7 +341,27 @@ impl TraitDef { | |||
174 | pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) } | 341 | pub fn trait_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![trait]) } |
175 | pub fn item_list(&self) -> Option<ItemList> { support::child(&self.syntax) } | 342 | pub fn item_list(&self) -> Option<ItemList> { support::child(&self.syntax) } |
176 | } | 343 | } |
177 | 344 | /// Module definition either with body or not. | |
345 | /// Includes all of its inner and outer attributes, module items, doc comments. | ||
346 | /// | ||
347 | /// ``` | ||
348 | /// ❰ | ||
349 | /// /// Docs | ||
350 | /// #[attr] | ||
351 | /// pub mod foo; | ||
352 | /// ❱ | ||
353 | /// | ||
354 | /// ❰ | ||
355 | /// /// Docs | ||
356 | /// #[attr] | ||
357 | /// pub mod bar { | ||
358 | /// //! Inner docs | ||
359 | /// #![inner_attr] | ||
360 | /// } | ||
361 | /// ❱ | ||
362 | /// ``` | ||
363 | /// | ||
364 | /// [Reference](https://doc.rust-lang.org/reference/items/modules.html) | ||
178 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 365 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
179 | pub struct Module { | 366 | pub struct Module { |
180 | pub(crate) syntax: SyntaxNode, | 367 | pub(crate) syntax: SyntaxNode, |
@@ -188,7 +375,28 @@ impl Module { | |||
188 | pub fn item_list(&self) -> Option<ItemList> { support::child(&self.syntax) } | 375 | pub fn item_list(&self) -> Option<ItemList> { support::child(&self.syntax) } |
189 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } | 376 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } |
190 | } | 377 | } |
191 | 378 | /// Item defintion list. | |
379 | /// This is used for both top-level items and impl block items. | ||
380 | /// | ||
381 | /// ``` | ||
382 | /// ❰ | ||
383 | /// fn foo {} | ||
384 | /// struct Bar; | ||
385 | /// enum Baz; | ||
386 | /// trait Bruh; | ||
387 | /// const BRUUH: u32 = 42; | ||
388 | /// ❱ | ||
389 | /// | ||
390 | /// impl Foo | ||
391 | /// ❰ | ||
392 | /// { | ||
393 | /// fn bar() {} | ||
394 | /// const BAZ: u32 = 42; | ||
395 | /// } | ||
396 | /// ❱ | ||
397 | /// ``` | ||
398 | /// | ||
399 | /// [Reference](https://doc.rust-lang.org/reference/items.html) | ||
192 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 400 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
193 | pub struct ItemList { | 401 | pub struct ItemList { |
194 | pub(crate) syntax: SyntaxNode, | 402 | pub(crate) syntax: SyntaxNode, |
@@ -199,7 +407,18 @@ impl ItemList { | |||
199 | pub fn assoc_items(&self) -> AstChildren<AssocItem> { support::children(&self.syntax) } | 407 | pub fn assoc_items(&self) -> AstChildren<AssocItem> { support::children(&self.syntax) } |
200 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } | 408 | pub fn r_curly_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T!['}']) } |
201 | } | 409 | } |
202 | 410 | /// Constant variable definition. | |
411 | /// Includes all of its attributes and doc comments. | ||
412 | /// | ||
413 | /// ``` | ||
414 | /// ❰ | ||
415 | /// /// Docs | ||
416 | /// #[attr] | ||
417 | /// pub const FOO: u32 = 42; | ||
418 | /// ❱ | ||
419 | /// ``` | ||
420 | /// | ||
421 | /// [Reference](https://doc.rust-lang.org/reference/items/constant-items.html) | ||
203 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 422 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
204 | pub struct ConstDef { | 423 | pub struct ConstDef { |
205 | pub(crate) syntax: SyntaxNode, | 424 | pub(crate) syntax: SyntaxNode, |
@@ -217,7 +436,18 @@ impl ConstDef { | |||
217 | pub fn body(&self) -> Option<Expr> { support::child(&self.syntax) } | 436 | pub fn body(&self) -> Option<Expr> { support::child(&self.syntax) } |
218 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } | 437 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } |
219 | } | 438 | } |
220 | 439 | /// Static variable definition. | |
440 | /// Includes all of its attributes and doc comments. | ||
441 | /// | ||
442 | /// ``` | ||
443 | /// ❰ | ||
444 | /// /// Docs | ||
445 | /// #[attr] | ||
446 | /// pub static mut FOO: u32 = 42; | ||
447 | /// ❱ | ||
448 | /// ``` | ||
449 | /// | ||
450 | /// [Reference](https://doc.rust-lang.org/reference/items/static-items.html) | ||
221 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 451 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
222 | pub struct StaticDef { | 452 | pub struct StaticDef { |
223 | pub(crate) syntax: SyntaxNode, | 453 | pub(crate) syntax: SyntaxNode, |
@@ -235,7 +465,24 @@ impl StaticDef { | |||
235 | pub fn body(&self) -> Option<Expr> { support::child(&self.syntax) } | 465 | pub fn body(&self) -> Option<Expr> { support::child(&self.syntax) } |
236 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } | 466 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } |
237 | } | 467 | } |
238 | 468 | /// Type alias definition. | |
469 | /// Includes associated type clauses with type bounds. | ||
470 | /// | ||
471 | /// ``` | ||
472 | /// ❰ | ||
473 | /// /// Docs | ||
474 | /// #[attr] | ||
475 | /// pub type Foo<T> where T: Debug = T; | ||
476 | /// ❱ | ||
477 | /// | ||
478 | /// trait Bar { | ||
479 | /// ❰ type Baz: Debug; ❱ | ||
480 | /// ❰ type Bruh = String; ❱ | ||
481 | /// ❰ type Bruuh: Debug = u32; ❱ | ||
482 | /// } | ||
483 | /// ``` | ||
484 | /// | ||
485 | /// [Reference](https://doc.rust-lang.org/reference/items/type-aliases.html) | ||
239 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 486 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
240 | pub struct TypeAliasDef { | 487 | pub struct TypeAliasDef { |
241 | pub(crate) syntax: SyntaxNode, | 488 | pub(crate) syntax: SyntaxNode, |
@@ -253,7 +500,20 @@ impl TypeAliasDef { | |||
253 | pub fn type_ref(&self) -> Option<TypeRef> { support::child(&self.syntax) } | 500 | pub fn type_ref(&self) -> Option<TypeRef> { support::child(&self.syntax) } |
254 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } | 501 | pub fn semicolon_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![;]) } |
255 | } | 502 | } |
256 | 503 | /// Inherent and trait impl definition. | |
504 | /// Includes all of its inner and outer attributes. | ||
505 | /// | ||
506 | /// ``` | ||
507 | /// ❰ | ||
508 | /// #[attr] | ||
509 | /// unsafe impl<T> const !Foo for Bar where T: Debug { | ||
510 | /// #![inner_attr] | ||
511 | /// // ... | ||
512 | /// } | ||
513 | /// ❱ | ||
514 | /// ``` | ||
515 | /// | ||
516 | /// [Reference](https://doc.rust-lang.org/reference/items/implementations.html) | ||
257 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | 517 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
258 | pub struct ImplDef { | 518 | pub struct ImplDef { |
259 | pub(crate) syntax: SyntaxNode, | 519 | pub(crate) syntax: SyntaxNode, |
@@ -270,7 +530,16 @@ impl ImplDef { | |||
270 | pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) } | 530 | pub fn for_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![for]) } |
271 | pub fn item_list(&self) -> Option<ItemList> { support::child(&self.syntax) } | 531 | pub fn item_list(&self) -> |