diff options
Diffstat (limited to 'crates')
48 files changed, 780 insertions, 141 deletions
diff --git a/crates/base_db/src/fixture.rs b/crates/base_db/src/fixture.rs index 04e2be390..0132565e4 100644 --- a/crates/base_db/src/fixture.rs +++ b/crates/base_db/src/fixture.rs | |||
@@ -35,7 +35,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { | 35 | fn with_position(ra_fixture: &str) -> (Self, FilePosition) { |
36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 36 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
37 | let offset = match range_or_offset { | 37 | let offset = match range_or_offset { |
38 | RangeOrOffset::Range(_) => panic!(), | 38 | RangeOrOffset::Range(_) => panic!("Expected a cursor position, got a range instead"), |
39 | RangeOrOffset::Offset(it) => it, | 39 | RangeOrOffset::Offset(it) => it, |
40 | }; | 40 | }; |
41 | (db, FilePosition { file_id, offset }) | 41 | (db, FilePosition { file_id, offset }) |
@@ -45,7 +45,7 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
45 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); | 45 | let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); |
46 | let range = match range_or_offset { | 46 | let range = match range_or_offset { |
47 | RangeOrOffset::Range(it) => it, | 47 | RangeOrOffset::Range(it) => it, |
48 | RangeOrOffset::Offset(_) => panic!(), | 48 | RangeOrOffset::Offset(_) => panic!("Expected a cursor range, got a position instead"), |
49 | }; | 49 | }; |
50 | (db, FileRange { file_id, range }) | 50 | (db, FileRange { file_id, range }) |
51 | } | 51 | } |
@@ -54,7 +54,9 @@ pub trait WithFixture: Default + SourceDatabaseExt + 'static { | |||
54 | let fixture = ChangeFixture::parse(ra_fixture); | 54 | let fixture = ChangeFixture::parse(ra_fixture); |
55 | let mut db = Self::default(); | 55 | let mut db = Self::default(); |
56 | fixture.change.apply(&mut db); | 56 | fixture.change.apply(&mut db); |
57 | let (file_id, range_or_offset) = fixture.file_position.unwrap(); | 57 | let (file_id, range_or_offset) = fixture |
58 | .file_position | ||
59 | .expect("Could not find file position in fixture. Did you forget to add an `$0`?"); | ||
58 | (db, file_id, range_or_offset) | 60 | (db, file_id, range_or_offset) |
59 | } | 61 | } |
60 | 62 | ||
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index eba46a056..0acfa582a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs | |||
@@ -2066,6 +2066,18 @@ impl Type { | |||
2066 | self.ty.dyn_trait().map(Into::into) | 2066 | self.ty.dyn_trait().map(Into::into) |
2067 | } | 2067 | } |
2068 | 2068 | ||
2069 | /// If a type can be represented as `dyn Trait`, returns all traits accessible via this type, | ||
2070 | /// or an empty iterator otherwise. | ||
2071 | pub fn applicable_inherent_traits<'a>( | ||
2072 | &'a self, | ||
2073 | db: &'a dyn HirDatabase, | ||
2074 | ) -> impl Iterator<Item = Trait> + 'a { | ||
2075 | self.autoderef(db) | ||
2076 | .filter_map(|derefed_type| derefed_type.ty.dyn_trait()) | ||
2077 | .flat_map(move |dyn_trait_id| hir_ty::all_super_traits(db.upcast(), dyn_trait_id)) | ||
2078 | .map(Trait::from) | ||
2079 | } | ||
2080 | |||
2069 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { | 2081 | pub fn as_impl_traits(&self, db: &dyn HirDatabase) -> Option<Vec<Trait>> { |
2070 | self.ty.impl_trait_bounds(db).map(|it| { | 2082 | self.ty.impl_trait_bounds(db).map(|it| { |
2071 | it.into_iter() | 2083 | it.into_iter() |
diff --git a/crates/hir_def/src/body/lower.rs b/crates/hir_def/src/body/lower.rs index ed07d6928..c0b0b7841 100644 --- a/crates/hir_def/src/body/lower.rs +++ b/crates/hir_def/src/body/lower.rs | |||
@@ -568,9 +568,13 @@ impl ExprCollector<'_> { | |||
568 | 568 | ||
569 | let res = match res { | 569 | let res = match res { |
570 | Ok(res) => res, | 570 | Ok(res) => res, |
571 | Err(UnresolvedMacro) => { | 571 | Err(UnresolvedMacro { path }) => { |
572 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( | 572 | self.source_map.diagnostics.push(BodyDiagnostic::UnresolvedMacroCall( |
573 | UnresolvedMacroCall { file: outer_file, node: syntax_ptr.cast().unwrap() }, | 573 | UnresolvedMacroCall { |
574 | file: outer_file, | ||
575 | node: syntax_ptr.cast().unwrap(), | ||
576 | path, | ||
577 | }, | ||
574 | )); | 578 | )); |
575 | collector(self, None); | 579 | collector(self, None); |
576 | return; | 580 | return; |
diff --git a/crates/hir_def/src/body/tests.rs b/crates/hir_def/src/body/tests.rs index c1d3e998f..63f5fe88d 100644 --- a/crates/hir_def/src/body/tests.rs +++ b/crates/hir_def/src/body/tests.rs | |||
@@ -180,7 +180,7 @@ fn unresolved_macro_diag() { | |||
180 | r#" | 180 | r#" |
181 | fn f() { | 181 | fn f() { |
182 | m!(); | 182 | m!(); |
183 | //^^^^ unresolved macro call | 183 | //^^^^ unresolved macro `m!` |
184 | } | 184 | } |
185 | "#, | 185 | "#, |
186 | ); | 186 | ); |
diff --git a/crates/hir_def/src/diagnostics.rs b/crates/hir_def/src/diagnostics.rs index 97abf8653..a71ae2668 100644 --- a/crates/hir_def/src/diagnostics.rs +++ b/crates/hir_def/src/diagnostics.rs | |||
@@ -8,7 +8,7 @@ use hir_expand::diagnostics::{Diagnostic, DiagnosticCode, DiagnosticSink}; | |||
8 | use hir_expand::{HirFileId, InFile}; | 8 | use hir_expand::{HirFileId, InFile}; |
9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; | 9 | use syntax::{ast, AstPtr, SyntaxNodePtr, TextRange}; |
10 | 10 | ||
11 | use crate::{db::DefDatabase, DefWithBodyId}; | 11 | use crate::{db::DefDatabase, path::ModPath, DefWithBodyId}; |
12 | 12 | ||
13 | pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { | 13 | pub fn validate_body(db: &dyn DefDatabase, owner: DefWithBodyId, sink: &mut DiagnosticSink<'_>) { |
14 | let source_map = db.body_with_source_map(owner).1; | 14 | let source_map = db.body_with_source_map(owner).1; |
@@ -103,6 +103,7 @@ impl Diagnostic for UnresolvedImport { | |||
103 | pub struct UnresolvedMacroCall { | 103 | pub struct UnresolvedMacroCall { |
104 | pub file: HirFileId, | 104 | pub file: HirFileId, |
105 | pub node: AstPtr<ast::MacroCall>, | 105 | pub node: AstPtr<ast::MacroCall>, |
106 | pub path: ModPath, | ||
106 | } | 107 | } |
107 | 108 | ||
108 | impl Diagnostic for UnresolvedMacroCall { | 109 | impl Diagnostic for UnresolvedMacroCall { |
@@ -110,7 +111,7 @@ impl Diagnostic for UnresolvedMacroCall { | |||
110 | DiagnosticCode("unresolved-macro-call") | 111 | DiagnosticCode("unresolved-macro-call") |
111 | } | 112 | } |
112 | fn message(&self) -> String { | 113 | fn message(&self) -> String { |
113 | "unresolved macro call".to_string() | 114 | format!("unresolved macro `{}!`", self.path) |
114 | } | 115 | } |
115 | fn display_source(&self) -> InFile<SyntaxNodePtr> { | 116 | fn display_source(&self) -> InFile<SyntaxNodePtr> { |
116 | InFile::new(self.file, self.node.clone().into()) | 117 | InFile::new(self.file, self.node.clone().into()) |
diff --git a/crates/hir_def/src/find_path.rs b/crates/hir_def/src/find_path.rs index 41da3bc2d..2c4bbe585 100644 --- a/crates/hir_def/src/find_path.rs +++ b/crates/hir_def/src/find_path.rs | |||
@@ -955,6 +955,29 @@ fn main() { | |||
955 | } | 955 | } |
956 | 956 | ||
957 | #[test] | 957 | #[test] |
958 | fn from_inside_module() { | ||
959 | // This worked correctly, but the test suite logic was broken. | ||
960 | cov_mark::check!(submodule_in_testdb); | ||
961 | check_found_path( | ||
962 | r#" | ||
963 | mod baz { | ||
964 | pub struct Foo {} | ||
965 | } | ||
966 | |||
967 | mod bar { | ||
968 | fn bar() { | ||
969 | $0 | ||
970 | } | ||
971 | } | ||
972 | "#, | ||
973 | "crate::baz::Foo", | ||
974 | "crate::baz::Foo", | ||
975 | "crate::baz::Foo", | ||
976 | "crate::baz::Foo", | ||
977 | ) | ||
978 | } | ||
979 | |||
980 | #[test] | ||
958 | fn recursive_pub_mod_reexport() { | 981 | fn recursive_pub_mod_reexport() { |
959 | cov_mark::check!(recursive_imports); | 982 | cov_mark::check!(recursive_imports); |
960 | check_found_path( | 983 | check_found_path( |
diff --git a/crates/hir_def/src/lib.rs b/crates/hir_def/src/lib.rs index ffee05500..5ac1670b5 100644 --- a/crates/hir_def/src/lib.rs +++ b/crates/hir_def/src/lib.rs | |||
@@ -66,6 +66,7 @@ use hir_expand::{ | |||
66 | }; | 66 | }; |
67 | use la_arena::Idx; | 67 | use la_arena::Idx; |
68 | use nameres::DefMap; | 68 | use nameres::DefMap; |
69 | use path::ModPath; | ||
69 | use syntax::ast; | 70 | use syntax::ast; |
70 | 71 | ||
71 | use crate::builtin_type::BuiltinType; | 72 | use crate::builtin_type::BuiltinType; |
@@ -107,6 +108,18 @@ impl ModuleId { | |||
107 | pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { | 108 | pub fn containing_module(&self, db: &dyn db::DefDatabase) -> Option<ModuleId> { |
108 | self.def_map(db).containing_module(self.local_id) | 109 | self.def_map(db).containing_module(self.local_id) |
109 | } | 110 | } |
111 | |||
112 | /// Returns `true` if this module represents a block expression. | ||
113 | /// | ||
114 | /// Returns `false` if this module is a submodule *inside* a block expression | ||
115 | /// (eg. `m` in `{ mod m {} }`). | ||
116 | pub fn is_block_root(&self, db: &dyn db::DefDatabase) -> bool { | ||
117 | if self.block.is_none() { | ||
118 | return false; | ||
119 | } | ||
120 | |||
121 | self.def_map(db)[self.local_id].parent.is_none() | ||
122 | } | ||
110 | } | 123 | } |
111 | 124 | ||
112 | /// An ID of a module, **local** to a specific crate | 125 | /// An ID of a module, **local** to a specific crate |
@@ -675,7 +688,9 @@ impl<T: ast::AstNode> AstIdWithPath<T> { | |||
675 | } | 688 | } |
676 | } | 689 | } |
677 | 690 | ||
678 | pub struct UnresolvedMacro; | 691 | pub struct UnresolvedMacro { |
692 | pub path: ModPath, | ||
693 | } | ||
679 | 694 | ||
680 | fn macro_call_as_call_id( | 695 | fn macro_call_as_call_id( |
681 | call: &AstIdWithPath<ast::MacroCall>, | 696 | call: &AstIdWithPath<ast::MacroCall>, |
@@ -684,7 +699,8 @@ fn macro_call_as_call_id( | |||
684 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 699 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
685 | error_sink: &mut dyn FnMut(mbe::ExpandError), | 700 | error_sink: &mut dyn FnMut(mbe::ExpandError), |
686 | ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { | 701 | ) -> Result<Result<MacroCallId, ErrorEmitted>, UnresolvedMacro> { |
687 | let def: MacroDefId = resolver(call.path.clone()).ok_or(UnresolvedMacro)?; | 702 | let def: MacroDefId = |
703 | resolver(call.path.clone()).ok_or_else(|| UnresolvedMacro { path: call.path.clone() })?; | ||
688 | 704 | ||
689 | let res = if let MacroDefKind::BuiltInEager(..) = def.kind { | 705 | let res = if let MacroDefKind::BuiltInEager(..) = def.kind { |
690 | let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); | 706 | let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db.upcast())); |
@@ -714,8 +730,13 @@ fn derive_macro_as_call_id( | |||
714 | krate: CrateId, | 730 | krate: CrateId, |
715 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, | 731 | resolver: impl Fn(path::ModPath) -> Option<MacroDefId>, |
716 | ) -> Result<MacroCallId, UnresolvedMacro> { | 732 | ) -> Result<MacroCallId, UnresolvedMacro> { |
717 | let def: MacroDefId = resolver(item_attr.path.clone()).ok_or(UnresolvedMacro)?; | 733 | let def: MacroDefId = resolver(item_attr.path.clone()) |
718 | let last_segment = item_attr.path.segments().last().ok_or(UnresolvedMacro)?; | 734 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; |
735 | let last_segment = item_attr | ||
736 | .path | ||
737 | .segments() | ||
738 | .last() | ||
739 | .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; | ||
719 | let res = def | 740 | let res = def |
720 | .as_lazy_macro( | 741 | .as_lazy_macro( |
721 | db.upcast(), | 742 | db.upcast(), |
diff --git a/crates/hir_def/src/nameres.rs b/crates/hir_def/src/nameres.rs index 9e181751c..542f190a1 100644 --- a/crates/hir_def/src/nameres.rs +++ b/crates/hir_def/src/nameres.rs | |||
@@ -481,7 +481,7 @@ mod diagnostics { | |||
481 | 481 | ||
482 | UnresolvedProcMacro { ast: MacroCallKind }, | 482 | UnresolvedProcMacro { ast: MacroCallKind }, |
483 | 483 | ||
484 | UnresolvedMacroCall { ast: AstId<ast::MacroCall> }, | 484 | UnresolvedMacroCall { ast: AstId<ast::MacroCall>, path: ModPath }, |
485 | 485 | ||
486 | MacroError { ast: MacroCallKind, message: String }, | 486 | MacroError { ast: MacroCallKind, message: String }, |
487 | } | 487 | } |
@@ -546,8 +546,9 @@ mod diagnostics { | |||
546 | pub(super) fn unresolved_macro_call( | 546 | pub(super) fn unresolved_macro_call( |
547 | container: LocalModuleId, | 547 | container: LocalModuleId, |
548 | ast: AstId<ast::MacroCall>, | 548 | ast: AstId<ast::MacroCall>, |
549 | path: ModPath, | ||
549 | ) -> Self { | 550 | ) -> Self { |
550 | Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast } } | 551 | Self { in_module: container, kind: DiagnosticKind::UnresolvedMacroCall { ast, path } } |
551 | } | 552 | } |
552 | 553 | ||
553 | pub(super) fn add_to( | 554 | pub(super) fn add_to( |
@@ -662,9 +663,13 @@ mod diagnostics { | |||
662 | }); | 663 | }); |
663 | } | 664 | } |
664 | 665 | ||
665 | DiagnosticKind::UnresolvedMacroCall { ast } => { | 666 | DiagnosticKind::UnresolvedMacroCall { ast, path } => { |
666 | let node = ast.to_node(db.upcast()); | 667 | let node = ast.to_node(db.upcast()); |
667 | sink.push(UnresolvedMacroCall { file: ast.file_id, node: AstPtr::new(&node) }); | 668 | sink.push(UnresolvedMacroCall { |
669 | file: ast.file_id, | ||
670 | node: AstPtr::new(&node), | ||
671 | path: path.clone(), | ||
672 | }); | ||
668 | } | 673 | } |
669 | 674 | ||
670 | DiagnosticKind::MacroError { ast, message } => { | 675 | DiagnosticKind::MacroError { ast, message } => { |
diff --git a/crates/hir_def/src/nameres/collector.rs b/crates/hir_def/src/nameres/collector.rs index fb4ddff5e..05ceb1efb 100644 --- a/crates/hir_def/src/nameres/collector.rs +++ b/crates/hir_def/src/nameres/collector.rs | |||
@@ -829,7 +829,7 @@ impl DefCollector<'_> { | |||
829 | res = ReachedFixedPoint::No; | 829 | res = ReachedFixedPoint::No; |
830 | return false; | 830 | return false; |
831 | } | 831 | } |
832 | Err(UnresolvedMacro) | Ok(Err(_)) => {} | 832 | Err(UnresolvedMacro { .. }) | Ok(Err(_)) => {} |
833 | } | 833 | } |
834 | } | 834 | } |
835 | MacroDirectiveKind::Derive { ast_id, derive_attr } => { | 835 | MacroDirectiveKind::Derive { ast_id, derive_attr } => { |
@@ -845,7 +845,7 @@ impl DefCollector<'_> { | |||
845 | res = ReachedFixedPoint::No; | 845 | res = ReachedFixedPoint::No; |
846 | return false; | 846 | return false; |
847 | } | 847 | } |
848 | Err(UnresolvedMacro) => (), | 848 | Err(UnresolvedMacro { .. }) => (), |
849 | } | 849 | } |
850 | } | 850 | } |
851 | } | 851 | } |
@@ -943,10 +943,11 @@ impl DefCollector<'_> { | |||
943 | &mut |_| (), | 943 | &mut |_| (), |
944 | ) { | 944 | ) { |
945 | Ok(_) => (), | 945 | Ok(_) => (), |
946 | Err(UnresolvedMacro) => { | 946 | Err(UnresolvedMacro { path }) => { |
947 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( | 947 | self.def_map.diagnostics.push(DefDiagnostic::unresolved_macro_call( |
948 | directive.module_id, | 948 | directive.module_id, |
949 | ast_id.ast_id, | 949 | ast_id.ast_id, |
950 | path, | ||
950 | )); | 951 | )); |
951 | } | 952 | } |
952 | }, | 953 | }, |
@@ -1530,7 +1531,7 @@ impl ModCollector<'_, '_> { | |||
1530 | )); | 1531 | )); |
1531 | return; | 1532 | return; |
1532 | } | 1533 | } |
1533 | Err(UnresolvedMacro) => (), | 1534 | Err(UnresolvedMacro { .. }) => (), |
1534 | } | 1535 | } |
1535 | 1536 | ||
1536 | // Case 2: resolve in module scope, expand during name resolution. | 1537 | // Case 2: resolve in module scope, expand during name resolution. |
diff --git a/crates/hir_def/src/nameres/path_resolution.rs b/crates/hir_def/src/nameres/path_resolution.rs index ccc9f22eb..c984148c3 100644 --- a/crates/hir_def/src/nameres/path_resolution.rs +++ b/crates/hir_def/src/nameres/path_resolution.rs | |||
@@ -387,7 +387,13 @@ impl DefMap { | |||
387 | .get_legacy_macro(name) | 387 | .get_legacy_macro(name) |
388 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); | 388 | .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); |
389 | let from_scope = self[module].scope.get(name); | 389 | let from_scope = self[module].scope.get(name); |
390 | let from_builtin = BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none); | 390 | let from_builtin = match self.block { |
391 | Some(_) => { | ||
392 | // Only resolve to builtins in the root `DefMap`. | ||
393 | PerNs::none() | ||
394 | } | ||
395 | None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none), | ||
396 | }; | ||
391 | let from_scope_or_builtin = match shadow { | 397 | let from_scope_or_builtin = match shadow { |
392 | BuiltinShadowMode::Module => from_scope.or(from_builtin), | 398 | BuiltinShadowMode::Module => from_scope.or(from_builtin), |
393 | BuiltinShadowMode::Other => { | 399 | BuiltinShadowMode::Other => { |
diff --git a/crates/hir_def/src/nameres/tests/diagnostics.rs b/crates/hir_def/src/nameres/tests/diagnostics.rs index 1ac88fc89..543975e07 100644 --- a/crates/hir_def/src/nameres/tests/diagnostics.rs +++ b/crates/hir_def/src/nameres/tests/diagnostics.rs | |||
@@ -170,7 +170,7 @@ fn unresolved_legacy_scope_macro() { | |||
170 | 170 | ||
171 | m!(); | 171 | m!(); |
172 | m2!(); | 172 | m2!(); |
173 | //^^^^^^ unresolved macro call | 173 | //^^^^^^ unresolved macro `self::m2!` |
174 | "#, | 174 | "#, |
175 | ); | 175 | ); |
176 | } | 176 | } |
@@ -187,7 +187,7 @@ fn unresolved_module_scope_macro() { | |||
187 | 187 | ||
188 | self::m!(); | 188 | self::m!(); |
189 | self::m2!(); | 189 | self::m2!(); |
190 | //^^^^^^^^^^^^ unresolved macro call | 190 | //^^^^^^^^^^^^ unresolved macro `self::m2!` |
191 | "#, | 191 | "#, |
192 | ); | 192 | ); |
193 | } | 193 | } |
diff --git a/crates/hir_def/src/test_db.rs b/crates/hir_def/src/test_db.rs index dd36106f8..8fa703a57 100644 --- a/crates/hir_def/src/test_db.rs +++ b/crates/hir_def/src/test_db.rs | |||
@@ -15,7 +15,12 @@ use rustc_hash::FxHashSet; | |||
15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; | 15 | use syntax::{algo, ast, AstNode, TextRange, TextSize}; |
16 | use test_utils::extract_annotations; | 16 | use test_utils::extract_annotations; |
17 | 17 | ||
18 | use crate::{db::DefDatabase, nameres::DefMap, src::HasSource, Lookup, ModuleDefId, ModuleId}; | 18 | use crate::{ |
19 | db::DefDatabase, | ||
20 | nameres::{DefMap, ModuleSource}, | ||
21 | src::HasSource, | ||
22 | LocalModuleId, Lookup, ModuleDefId, ModuleId, | ||
23 | }; | ||
19 | 24 | ||
20 | #[salsa::database( | 25 | #[salsa::database( |
21 | base_db::SourceDatabaseExtStorage, | 26 | base_db::SourceDatabaseExtStorage, |
@@ -87,10 +92,11 @@ impl TestDB { | |||
87 | pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { | 92 | pub(crate) fn module_at_position(&self, position: FilePosition) -> ModuleId { |
88 | let file_module = self.module_for_file(position.file_id); | 93 | let file_module = self.module_for_file(position.file_id); |
89 | let mut def_map = file_module.def_map(self); | 94 | let mut def_map = file_module.def_map(self); |
95 | let module = self.mod_at_position(&def_map, position); | ||
90 | 96 | ||
91 | def_map = match self.block_at_position(&def_map, position) { | 97 | def_map = match self.block_at_position(&def_map, position) { |
92 | Some(it) => it, | 98 | Some(it) => it, |
93 | None => return file_module, | 99 | None => return def_map.module_id(module), |
94 | }; | 100 | }; |
95 | loop { | 101 | loop { |
96 | let new_map = self.block_at_position(&def_map, position); | 102 | let new_map = self.block_at_position(&def_map, position); |
@@ -106,6 +112,47 @@ impl TestDB { | |||
106 | } | 112 | } |
107 | } | 113 | } |
108 | 114 | ||
115 | /// Finds the smallest/innermost module in `def_map` containing `position`. | ||
116 | fn mod_at_position(&self, def_map: &DefMap, position: FilePosition) -> LocalModuleId { | ||
117 | let mut size = None; | ||
118 | let mut res = def_map.root(); | ||
119 | for (module, data) in def_map.modules() { | ||
120 | let src = data.definition_source(self); | ||
121 | if src.file_id != position.file_id.into() { | ||
122 | continue; | ||
123 | } | ||
124 | |||
125 | let range = match src.value { | ||
126 | ModuleSource::SourceFile(it) => it.syntax().text_range(), | ||
127 | ModuleSource::Module(it) => it.syntax().text_range(), | ||
128 | ModuleSource::BlockExpr(it) => it.syntax().text_range(), | ||
129 | }; | ||
130 | |||
131 | if !range.contains(position.offset) { | ||
132 | continue; | ||
133 | } | ||
134 | |||
135 | let new_size = match size { | ||
136 | None => range.len(), | ||
137 | Some(size) => { | ||
138 | if range.len() < size { | ||
139 | range.len() | ||
140 | } else { | ||
141 | size | ||
142 | } | ||
143 | } | ||
144 | }; | ||
145 | |||
146 | if size != Some(new_size) { | ||
147 | cov_mark::hit!(submodule_in_testdb); | ||
148 | size = Some(new_size); | ||
149 | res = module; | ||
150 | } | ||
151 | } | ||
152 | |||
153 | res | ||
154 | } | ||
155 | |||
109 | fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> { | 156 | fn block_at_position(&self, def_map: &DefMap, position: FilePosition) -> Option<Arc<DefMap>> { |
110 | // Find the smallest (innermost) function in `def_map` containing the cursor. | 157 | // Find the smallest (innermost) function in `def_map` containing the cursor. |
111 | let mut size = None; | 158 | let mut size = None; |
diff --git a/crates/hir_def/src/visibility.rs b/crates/hir_def/src/visibility.rs index 9908cd926..d4b7c9970 100644 --- a/crates/hir_def/src/visibility.rs +++ b/crates/hir_def/src/visibility.rs | |||
@@ -123,11 +123,19 @@ impl Visibility { | |||
123 | def_map: &DefMap, | 123 | def_map: &DefMap, |
124 | mut from_module: crate::LocalModuleId, | 124 | mut from_module: crate::LocalModuleId, |
125 | ) -> bool { | 125 | ) -> bool { |
126 | let to_module = match self { | 126 | let mut to_module = match self { |
127 | Visibility::Module(m) => m, | 127 | Visibility::Module(m) => m, |
128 | Visibility::Public => return true, | 128 | Visibility::Public => return true, |
129 | }; | 129 | }; |
130 | 130 | ||
131 | // `to_module` might be the root module of a block expression. Those have the same | ||
132 | // visibility as the containing module (even though no items are directly nameable from | ||
133 | // there, getting this right is important for method resolution). | ||
134 | // In that case, we adjust the visibility of `to_module` to point to the containing module. | ||
135 | if to_module.is_block_root(db) { | ||
136 | to_module = to_module.containing_module(db).unwrap(); | ||
137 | } | ||
138 | |||
131 | // from_module needs to be a descendant of to_module | 139 | // from_module needs to be a descendant of to_module |
132 | let mut def_map = def_map; | 140 | let mut def_map = def_map; |
133 | let mut parent_arc; | 141 | let mut parent_arc; |
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs index 80365fc16..179de61f9 100644 --- a/crates/hir_expand/src/builtin_macro.rs +++ b/crates/hir_expand/src/builtin_macro.rs | |||
@@ -110,6 +110,7 @@ register_builtin! { | |||
110 | (format_args_nl, FormatArgsNl) => format_args_expand, | 110 | (format_args_nl, FormatArgsNl) => format_args_expand, |
111 | (llvm_asm, LlvmAsm) => asm_expand, | 111 | (llvm_asm, LlvmAsm) => asm_expand, |
112 | (asm, Asm) => asm_expand, | 112 | (asm, Asm) => asm_expand, |
113 | (global_asm, GlobalAsm) => global_asm_expand, | ||
113 | (cfg, Cfg) => cfg_expand, | 114 | (cfg, Cfg) => cfg_expand, |
114 | (core_panic, CorePanic) => panic_expand, | 115 | (core_panic, CorePanic) => panic_expand, |
115 | (std_panic, StdPanic) => panic_expand, | 116 | (std_panic, StdPanic) => panic_expand, |
@@ -274,6 +275,15 @@ fn asm_expand( | |||
274 | ExpandResult::ok(expanded) | 275 | ExpandResult::ok(expanded) |
275 | } | 276 | } |
276 | 277 | ||
278 | fn global_asm_expand( | ||
279 | _db: &dyn AstDatabase, | ||
280 | _id: LazyMacroId, | ||
281 | _tt: &tt::Subtree, | ||
282 | ) -> ExpandResult<tt::Subtree> { | ||
283 | // Expand to nothing (at item-level) | ||
284 | ExpandResult::ok(quote! {}) | ||
285 | } | ||
286 | |||
277 | fn cfg_expand( | 287 | fn cfg_expand( |
278 | db: &dyn AstDatabase, | 288 | db: &dyn AstDatabase, |
279 | id: LazyMacroId, | 289 | id: LazyMacroId, |
diff --git a/crates/hir_expand/src/name.rs b/crates/hir_expand/src/name.rs index a0f8766b0..bcfd3e524 100644 --- a/crates/hir_expand/src/name.rs +++ b/crates/hir_expand/src/name.rs | |||
@@ -221,6 +221,7 @@ pub mod known { | |||
221 | option_env, | 221 | option_env, |
222 | llvm_asm, | 222 | llvm_asm, |
223 | asm, | 223 | asm, |
224 | global_asm, | ||
224 | // Builtin derives | 225 | // Builtin derives |
225 | Copy, | 226 | Copy, |
226 | Clone, | 227 | Clone, |
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs index 113234fa4..0505fa4ae 100644 --- a/crates/hir_ty/src/lib.rs +++ b/crates/hir_ty/src/lib.rs | |||
@@ -56,6 +56,7 @@ pub use mapping::{ | |||
56 | to_foreign_def_id, to_placeholder_idx, | 56 | to_foreign_def_id, to_placeholder_idx, |
57 | }; | 57 | }; |
58 | pub use traits::TraitEnvironment; | 58 | pub use traits::TraitEnvironment; |
59 | pub use utils::all_super_traits; | ||
59 | pub use walk::TypeWalk; | 60 | pub use walk::TypeWalk; |
60 | 61 | ||
61 | pub use chalk_ir::{ | 62 | pub use chalk_ir::{ |
diff --git a/crates/hir_ty/src/method_resolution.rs b/crates/hir_ty/src/method_resolution.rs index 3693e3284..48bbcfd9f 100644 --- a/crates/hir_ty/src/method_resolution.rs +++ b/crates/hir_ty/src/method_resolution.rs | |||
@@ -246,29 +246,39 @@ pub struct InherentImpls { | |||
246 | 246 | ||
247 | impl InherentImpls { | 247 | impl InherentImpls { |
248 | pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { | 248 | pub(crate) fn inherent_impls_in_crate_query(db: &dyn HirDatabase, krate: CrateId) -> Arc<Self> { |
249 | let mut map: FxHashMap<_, Vec<_>> = FxHashMap::default(); | 249 | let mut impls = Self { map: FxHashMap::default() }; |
250 | 250 | ||
251 | let crate_def_map = db.crate_def_map(krate); | 251 | let crate_def_map = db.crate_def_map(krate); |
252 | for (_module_id, module_data) in crate_def_map.modules() { | 252 | collect_def_map(db, &crate_def_map, &mut impls); |
253 | for impl_id in module_data.scope.impls() { | 253 | |
254 | let data = db.impl_data(impl_id); | 254 | return Arc::new(impls); |
255 | if data.target_trait.is_some() { | 255 | |
256 | continue; | 256 | fn collect_def_map(db: &dyn HirDatabase, def_map: &DefMap, impls: &mut InherentImpls) { |
257 | for (_module_id, module_data) in def_map.modules() { | ||
258 | for impl_id in module_data.scope.impls() { | ||
259 | let data = db.impl_data(impl_id); | ||
260 | if data.target_trait.is_some() { | ||
261 | continue; | ||
262 | } | ||
263 | |||
264 | let self_ty = db.impl_self_ty(impl_id); | ||
265 | let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); | ||
266 | if let Some(fp) = fp { | ||
267 | impls.map.entry(fp).or_default().push(impl_id); | ||
268 | } | ||
269 | // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) | ||
257 | } | 270 | } |
258 | 271 | ||
259 | let self_ty = db.impl_self_ty(impl_id); | 272 | // To better support custom derives, collect impls in all unnamed const items. |
260 | let fp = TyFingerprint::for_inherent_impl(self_ty.skip_binders()); | 273 | // const _: () = { ... }; |
261 | if let Some(fp) = fp { | 274 | for konst in module_data.scope.unnamed_consts() { |
262 | map.entry(fp).or_default().push(impl_id); | 275 | let body = db.body(konst.into()); |
276 | for (_, block_def_map) in body.blocks(db.upcast()) { | ||
277 | collect_def_map(db, &block_def_map, impls); | ||
278 | } | ||
263 | } | 279 | } |
264 | // `fp` should only be `None` in error cases (either erroneous code or incomplete name resolution) | ||
265 | } | 280 | } |
266 | } | 281 | } |
267 | |||
268 | // NOTE: We're not collecting inherent impls from unnamed consts here, we intentionally only | ||
269 | // support trait impls there. | ||
270 | |||
271 | Arc::new(Self { map }) | ||
272 | } | 282 | } |
273 | 283 | ||
274 | pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { | 284 | pub fn for_self_ty(&self, self_ty: &Ty) -> &[ImplId] { |
diff --git a/crates/hir_ty/src/tests/method_resolution.rs b/crates/hir_ty/src/tests/method_resolution.rs index 4b2c82b41..a4c132bc5 100644 --- a/crates/hir_ty/src/tests/method_resolution.rs +++ b/crates/hir_ty/src/tests/method_resolution.rs | |||
@@ -1294,7 +1294,7 @@ mod b { | |||
1294 | } | 1294 | } |
1295 | 1295 | ||
1296 | #[test] | 1296 | #[test] |
1297 | fn impl_in_unnamed_const() { | 1297 | fn trait_impl_in_unnamed_const() { |
1298 | check_types( | 1298 | check_types( |
1299 | r#" | 1299 | r#" |
1300 | struct S; | 1300 | struct S; |
@@ -1314,3 +1314,38 @@ fn f() { | |||
1314 | "#, | 1314 | "#, |
1315 | ); | 1315 | ); |
1316 | } | 1316 | } |
1317 | |||
1318 | #[test] | ||
1319 | fn inherent_impl_in_unnamed_const() { | ||
1320 | check_types( | ||
1321 | r#" | ||
1322 | struct S; | ||
1323 | |||
1324 | const _: () = { | ||
1325 | impl S { | ||
1326 | fn method(&self) -> u16 { 0 } | ||
1327 | |||
1328 | pub(super) fn super_method(&self) -> u16 { 0 } | ||
1329 | |||
1330 | pub(crate) fn crate_method(&self) -> u16 { 0 } | ||
1331 | |||
1332 | pub fn pub_method(&self) -> u16 { 0 } | ||
1333 | } | ||
1334 | }; | ||
1335 | |||
1336 | fn f() { | ||
1337 | S.method(); | ||
1338 | //^^^^^^^^^^ u16 | ||
1339 | |||
1340 | S.super_method(); | ||
1341 | //^^^^^^^^^^^^^^^^ u16 | ||
1342 | |||
1343 | S.crate_method(); | ||
1344 | //^^^^^^^^^^^^^^^^ u16 | ||
1345 | |||
1346 | S.pub_method(); | ||
1347 | //^^^^^^^^^^^^^^ u16 | ||
1348 | } | ||
1349 | "#, | ||
1350 | ); | ||
1351 | } | ||
diff --git a/crates/hir_ty/src/tests/simple.rs b/crates/hir_ty/src/tests/simple.rs index 84c5c05fd..5948d0bc2 100644 --- a/crates/hir_ty/src/tests/simple.rs +++ b/crates/hir_ty/src/tests/simple.rs | |||
@@ -1765,6 +1765,24 @@ fn main() { | |||
1765 | } | 1765 | } |
1766 | 1766 | ||
1767 | #[test] | 1767 | #[test] |
1768 | fn shadowing_primitive_with_inner_items() { | ||
1769 | check_types( | ||
1770 | r#" | ||
1771 | struct i32; | ||
1772 | struct Foo; | ||
1773 | |||
1774 | impl i32 { fn foo(&self) -> Foo { Foo } } | ||
1775 | |||
1776 | fn main() { | ||
1777 | fn inner() {} | ||
1778 | let x: i32 = i32; | ||
1779 | x.foo(); | ||
1780 | //^ Foo | ||
1781 | }"#, | ||
1782 | ); | ||
1783 | } | ||
1784 | |||
1785 | #[test] | ||
1768 | fn not_shadowing_primitive_by_module() { | 1786 | fn not_shadowing_primitive_by_module() { |
1769 | check_types( | 1787 | check_types( |
1770 | r#" | 1788 | r#" |
diff --git a/crates/hir_ty/src/utils.rs b/crates/hir_ty/src/utils.rs index 5f6cb052a..2f04ee57a 100644 --- a/crates/hir_ty/src/utils.rs +++ b/crates/hir_ty/src/utils.rs | |||
@@ -78,7 +78,7 @@ fn direct_super_trait_refs(db: &dyn HirDatabase, trait_ref: &TraitRef) -> Vec<Tr | |||
78 | 78 | ||
79 | /// Returns an iterator over the whole super trait hierarchy (including the | 79 | /// Returns an iterator over the whole super trait hierarchy (including the |
80 | /// trait itself). | 80 | /// trait itself). |
81 | pub(super) fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { | 81 | pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> { |
82 | // we need to take care a bit here to avoid infinite loops in case of cycles | 82 | // we need to take care a bit here to avoid infinite loops in case of cycles |
83 | // (i.e. if we have `trait A: B; trait B: A;`) | 83 | // (i.e. if we have `trait A: B; trait B: A;`) |
84 | let mut result = vec![trait_]; | 84 | let mut result = vec![trait_]; |
diff --git a/crates/ide/src/diagnostics.rs b/crates/ide/src/diagnostics.rs index 9a883acb9..1c911a8b2 100644 --- a/crates/ide/src/diagnostics.rs +++ b/crates/ide/src/diagnostics.rs | |||
@@ -725,7 +725,7 @@ fn test_fn() { | |||
725 | expect![[r#" | 725 | expect![[r#" |
726 | [ | 726 | [ |
727 | Diagnostic { | 727 | Diagnostic { |
728 | message: "unresolved macro call", | 728 | message: "unresolved macro `foo::bar!`", |
729 | range: 5..8, | 729 | range: 5..8, |
730 | severity: Error, | 730 | severity: Error, |
731 | fix: None, | 731 | fix: None, |
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index b24c664ba..99e45633e 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs | |||
@@ -244,6 +244,12 @@ impl Analysis { | |||
244 | self.with_db(|db| db.parse(file_id).tree()) | 244 | self.with_db(|db| db.parse(file_id).tree()) |
245 | } | 245 | } |
246 | 246 | ||
247 | /// Returns true if this file belongs to an immutable library. | ||
248 | pub fn is_library_file(&self, file_id: FileId) -> Cancelable<bool> { | ||
249 | use ide_db::base_db::SourceDatabaseExt; | ||
250 | self.with_db(|db| db.source_root(db.file_source_root(file_id)).is_library) | ||
251 | } | ||
252 | |||
247 | /// Gets the file's `LineIndex`: data structure to convert between absolute | 253 | /// Gets the file's `LineIndex`: data structure to convert between absolute |
248 | /// offsets and line/column representation. | 254 | /// offsets and line/column representation. |
249 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { | 255 | pub fn file_line_index(&self, file_id: FileId) -> Cancelable<Arc<LineIndex>> { |
diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index 04fafd244..855c7fba8 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs | |||
@@ -90,6 +90,13 @@ const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[ | |||
90 | "edition2021", | 90 | "edition2021", |
91 | ]; | 91 | ]; |
92 | 92 | ||
93 | fn is_rustdoc_fence_token(token: &str) -> bool { | ||
94 | if RUSTDOC_FENCE_TOKENS.contains(&token) { | ||
95 | return true; | ||
96 | } | ||
97 | token.starts_with('E') && token.len() == 5 && token[1..].parse::<u32>().is_ok() | ||
98 | } | ||
99 | |||
93 | /// Injection of syntax highlighting of doctests. | 100 | /// Injection of syntax highlighting of doctests. |
94 | pub(super) fn doc_comment( | 101 | pub(super) fn doc_comment( |
95 | hl: &mut Highlights, | 102 | hl: &mut Highlights, |
@@ -174,8 +181,7 @@ pub(super) fn doc_comment( | |||
174 | is_codeblock = !is_codeblock; | 181 | is_codeblock = !is_codeblock; |
175 | // Check whether code is rust by inspecting fence guards | 182 | // Check whether code is rust by inspecting fence guards |
176 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; | 183 | let guards = &line[idx + RUSTDOC_FENCE.len()..]; |
177 | let is_rust = | 184 | let is_rust = guards.split(',').all(|sub| is_rustdoc_fence_token(sub.trim())); |
178 | guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim())); | ||
179 | is_doctest = is_codeblock && is_rust; | 185 | is_doctest = is_codeblock && is_rust; |
180 | continue; | 186 | continue; |
181 | } | 187 | } |
diff --git a/crates/ide_assists/src/handlers/generate_deref.rs b/crates/ide_assists/src/handlers/generate_deref.rs new file mode 100644 index 000000000..4998ff7a4 --- /dev/null +++ b/crates/ide_assists/src/handlers/generate_deref.rs | |||
@@ -0,0 +1,227 @@ | |||
1 | use std::fmt::Display; | ||
2 | |||
3 | use ide_db::{helpers::FamousDefs, RootDatabase}; | ||
4 | use syntax::{ | ||
5 | ast::{self, NameOwner}, | ||
6 | AstNode, SyntaxNode, | ||
7 | }; | ||
8 | |||
9 | use crate::{ | ||
10 | assist_context::{AssistBuilder, AssistContext, Assists}, | ||
11 | utils::generate_trait_impl_text, | ||
12 | AssistId, AssistKind, | ||
13 | }; | ||
14 | |||
15 | // Assist: generate_deref | ||
16 | // | ||
17 | // Generate `Deref` impl using the given struct field. | ||
18 | // | ||
19 | // ``` | ||
20 | // struct A; | ||
21 | // struct B { | ||
22 | // $0a: A | ||
23 | // } | ||
24 | // ``` | ||
25 | // -> | ||
26 | // ``` | ||
27 | // struct A; | ||
28 | // struct B { | ||
29 | // a: A | ||
30 | // } | ||
31 | // | ||
32 | // impl std::ops::Deref for B { | ||
33 | // type Target = A; | ||
34 | // | ||
35 | // fn deref(&self) -> &Self::Target { | ||
36 | // &self.a | ||
37 | // } | ||
38 | // } | ||
39 | // ``` | ||
40 | pub(crate) fn generate_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
41 | generate_record_deref(acc, ctx).or_else(|| generate_tuple_deref(acc, ctx)) | ||
42 | } | ||
43 | |||
44 | fn generate_record_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
45 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
46 | let field = ctx.find_node_at_offset::<ast::RecordField>()?; | ||
47 | |||
48 | if existing_deref_impl(&ctx.sema, &strukt).is_some() { | ||
49 | cov_mark::hit!(test_add_record_deref_impl_already_exists); | ||
50 | return None; | ||
51 | } | ||
52 | |||
53 | let field_type = field.ty()?; | ||
54 | let field_name = field.name()?; | ||
55 | let target = field.syntax().text_range(); | ||
56 | acc.add( | ||
57 | AssistId("generate_deref", AssistKind::Generate), | ||
58 | format!("Generate `Deref` impl using `{}`", field_name), | ||
59 | target, | ||
60 | |edit| generate_edit(edit, strukt, field_type.syntax(), field_name.syntax()), | ||
61 | ) | ||
62 | } | ||
63 | |||
64 | fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | ||
65 | let strukt = ctx.find_node_at_offset::<ast::Struct>()?; | ||
66 | let field = ctx.find_node_at_offset::<ast::TupleField>()?; | ||
67 | let field_list = ctx.find_node_at_offset::<ast::TupleFieldList>()?; | ||
68 | let field_list_index = | ||
69 | field_list.syntax().children().into_iter().position(|s| &s == field.syntax())?; | ||
70 | |||
71 | if existing_deref_impl(&ctx.sema, &strukt).is_some() { | ||
72 | cov_mark::hit!(test_add_field_deref_impl_already_exists); | ||
73 | return None; | ||
74 | } | ||
75 | |||
76 | let field_type = field.ty()?; | ||
77 | let target = field.syntax().text_range(); | ||
78 | acc.add( | ||
79 | AssistId("generate_deref", AssistKind::Generate), | ||
80 | format!("Generate `Deref` impl using `{}`", field.syntax()), | ||
81 | target, | ||
82 | |edit| generate_edit(edit, strukt, field_type.syntax(), field_list_index), | ||
83 | ) | ||
84 | } | ||
85 | |||
86 | fn generate_edit( | ||
87 | edit: &mut AssistBuilder, | ||
88 | strukt: ast::Struct, | ||
89 | field_type_syntax: &SyntaxNode, | ||
90 | field_name: impl Display, | ||
91 | ) { | ||
92 | let start_offset = strukt.syntax().text_range().end(); | ||
93 | let impl_code = format!( | ||
94 | r#" type Target = {0}; | ||
95 | |||
96 | fn deref(&self) -> &Self::Target {{ | ||
97 | &self.{1} | ||
98 | }}"#, | ||
99 | field_type_syntax, field_name | ||
100 | ); | ||
101 | let strukt_adt = ast::Adt::Struct(strukt); | ||
102 | let deref_impl = generate_trait_impl_text(&strukt_adt, "std::ops::Deref", &impl_code); | ||
103 | edit.insert(start_offset, deref_impl); | ||
104 | } | ||
105 | |||
106 | fn existing_deref_impl( | ||
107 | sema: &'_ hir::Semantics<'_, RootDatabase>, | ||
108 | strukt: &ast::Struct, | ||
109 | ) -> Option<()> { | ||
110 | let strukt = sema.to_def(strukt)?; | ||
111 | let krate = strukt.module(sema.db).krate(); | ||
112 | |||
113 | let deref_trait = FamousDefs(sema, Some(krate)).core_ops_Deref()?; | ||
114 | let strukt_type = strukt.ty(sema.db); | ||
115 | |||
116 | if strukt_type.impls_trait(sema.db, deref_trait, &[]) { | ||
117 | Some(()) | ||
118 | } else { | ||
119 | None | ||
120 | } | ||
121 | } | ||
122 | |||
123 | #[cfg(test)] | ||
124 | mod tests { | ||
125 | use crate::tests::{check_assist, check_assist_not_applicable}; | ||
126 | |||
127 | use super::*; | ||
128 | |||
129 | #[test] | ||
130 | fn test_generate_record_deref() { | ||
131 | check_assist( | ||
132 | generate_deref, | ||
133 | r#"struct A { } | ||
134 | struct B { $0a: A }"#, | ||
135 | r#"struct A { } | ||
136 | struct B { a: A } | ||
137 | |||
138 | impl std::ops::Deref for B { | ||
139 | type Target = A; | ||
140 | |||
141 | fn deref(&self) -> &Self::Target { | ||
142 | &self.a | ||
143 | } | ||
144 | }"#, | ||
145 | ); | ||
146 | } | ||
147 | |||
148 | #[test] | ||
149 | fn test_generate_field_deref_idx_0() { | ||
150 | check_assist( | ||
151 | generate_deref, | ||
152 | r#"struct A { } | ||
153 | struct B($0A);"#, | ||
154 | r#"struct A { } | ||
155 | struct B(A); | ||
156 | |||
157 | impl std::ops::Deref for B { | ||
158 | type Target = A; | ||
159 | |||
160 | fn deref(&self) -> &Self::Target { | ||
161 | &self.0 | ||
162 | } | ||
163 | }"#, | ||
164 | ); | ||
165 | } | ||
166 | #[test] | ||
167 | fn test_generate_field_deref_idx_1() { | ||
168 | check_assist( | ||
169 | generate_deref, | ||
170 | r#"struct A { } | ||
171 | struct B(u8, $0A);"#, | ||
172 | r#"struct A { } | ||
173 | struct B(u8, A); | ||
174 | |||
175 | impl std::ops::Deref for B { | ||
176 | type Target = A; | ||
177 | |||
178 | fn deref(&self) -> &Self::Target { | ||
179 | &self.1 | ||
180 | } | ||
181 | }"#, | ||
182 | ); | ||
183 | } | ||
184 | |||
185 | fn check_not_applicable(ra_fixture: &str) { | ||
186 | let fixture = format!( | ||
187 | "//- /main.rs crate:main deps:core,std\n{}\n{}", | ||
188 | ra_fixture, | ||
189 | FamousDefs::FIXTURE | ||
190 | ); | ||
191 | check_assist_not_applicable(generate_deref, &fixture) | ||
192 | } | ||
193 | |||
194 | #[test] | ||
195 | fn test_generate_record_deref_not_applicable_if_already_impl() { | ||
196 | cov_mark::check!(test_add_record_deref_impl_already_exists); | ||
197 | check_not_applicable( | ||
198 | r#"struct A { } | ||
199 | struct B { $0a: A } | ||
200 | |||
201 | impl std::ops::Deref for B { | ||
202 | type Target = A; | ||
203 | |||
204 | fn deref(&self) -> &Self::Target { | ||
205 | &self.a | ||
206 | } | ||
207 | }"#, | ||
208 | ) | ||
209 | } | ||
210 | |||
211 | #[test] | ||
212 | fn test_generate_field_deref_not_applicable_if_already_impl() { | ||
213 | cov_mark::check!(test_add_field_deref_impl_already_exists); | ||
214 | check_not_applicable( | ||
215 | r#"struct A { } | ||
216 | struct B($0A) | ||
217 | |||
218 | impl std::ops::Deref for B { | ||
219 | type Target = A; | ||
220 | |||
221 | fn deref(&self) -> &Self::Target { | ||
222 | &self.0 | ||
223 | } | ||
224 | }"#, | ||
225 | ) | ||
226 | } | ||
227 | } | ||
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs index 3694f468f..8996c1b61 100644 --- a/crates/ide_assists/src/lib.rs +++ b/crates/ide_assists/src/lib.rs | |||
@@ -134,6 +134,7 @@ mod handlers { | |||
134 | mod generate_default_from_enum_variant; | 134 | mod generate_default_from_enum_variant; |
135 | mod generate_default_from_new; | 135 | mod generate_default_from_new; |
136 | mod generate_is_empty_from_len; | 136 | mod generate_is_empty_from_len; |
137 | mod generate_deref; | ||
137 | mod generate_derive; | 138 | mod generate_derive; |
138 | mod generate_enum_is_method; | 139 | mod generate_enum_is_method; |
139 | mod generate_enum_projection_method; | 140 | mod generate_enum_projection_method; |
@@ -201,6 +202,7 @@ mod handlers { | |||
201 | generate_default_from_enum_variant::generate_default_from_enum_variant, | 202 | generate_default_from_enum_variant::generate_default_from_enum_variant, |
202 | generate_default_from_new::generate_default_from_new, | 203 | generate_default_from_new::generate_default_from_new, |
203 | generate_is_empty_from_len::generate_is_empty_from_len, | 204 | generate_is_empty_from_len::generate_is_empty_from_len, |
205 | generate_deref::generate_deref, | ||
204 | generate_derive::generate_derive, | 206 | generate_derive::generate_derive, |
205 | generate_enum_is_method::generate_enum_is_method, | 207 | generate_enum_is_method::generate_enum_is_method, |
206 | generate_enum_projection_method::generate_enum_as_method, | 208 | generate_enum_projection_method::generate_enum_as_method, |
diff --git a/crates/ide_assists/src/tests.rs b/crates/ide_assists/src/tests.rs index a7a923beb..49533e7d2 100644 --- a/crates/ide_assists/src/tests.rs +++ b/crates/ide_assists/src/tests.rs | |||
@@ -84,7 +84,8 @@ fn check_doc_test(assist_id: &str, before: &str, after: &str) { | |||
84 | }); | 84 | }); |
85 | 85 | ||
86 | let actual = { | 86 | let actual = { |
87 | let source_change = assist.source_change.unwrap(); | 87 | let source_change = |
88 | assist.source_change.expect("Assist did not contain any source changes"); | ||
88 | let mut actual = before; | 89 | let mut actual = before; |
89 | if let Some(source_file_edit) = source_change.get_source_edit(file_id) { | 90 | if let Some(source_file_edit) = source_change.get_source_edit(file_id) { |
90 | source_file_edit.apply(&mut actual); | 91 | source_file_edit.apply(&mut actual); |
@@ -121,7 +122,8 @@ fn check(handler: Handler, before: &str, expected: ExpectedResult, assist_label: | |||
121 | 122 | ||
122 | match (assist, expected) { | 123 | match (assist, expected) { |
123 | (Some(assist), ExpectedResult::After(after)) => { | 124 | (Some(assist), ExpectedResult::After(after)) => { |
124 | let source_change = assist.source_change.unwrap(); | 125 | let source_change = |
126 | assist.source_change.expect("Assist did not contain any source changes"); | ||
125 | assert!(!source_change.source_file_edits.is_empty()); | 127 | assert!(!source_change.source_file_edits.is_empty()); |
126 | let skip_header = source_change.source_file_edits.len() == 1 | 128 | let skip_header = source_change.source_file_edits.len() == 1 |
127 | && source_change.file_system_edits.len() == 0; | 129 | && source_change.file_system_edits.len() == 0; |
@@ -191,6 +193,7 @@ fn assist_order_field_struct() { | |||
191 | let mut assists = assists.iter(); | 193 | let mut assists = assists.iter(); |
192 | 194 | ||
193 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); | 195 | assert_eq!(assists.next().expect("expected assist").label, "Change visibility to pub(crate)"); |
196 | assert_eq!(assists.next().expect("expected assist").label, "Generate `Deref` impl using `bar`"); | ||
194 | assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); | 197 | assert_eq!(assists.next().expect("expected assist").label, "Generate a mut getter method"); |
195 | assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); | 198 | assert_eq!(assists.next().expect("expected assist").label, "Generate a getter method"); |
196 | assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); | 199 | assert_eq!(assists.next().expect("expected assist").label, "Generate a setter method"); |
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index 27a22ca10..41559b43a 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs | |||
@@ -552,6 +552,33 @@ impl Default for Example { | |||
552 | } | 552 | } |
553 | 553 | ||
554 | #[test] | 554 | #[test] |
555 | fn doctest_generate_deref() { | ||
556 | check_doc_test( | ||
557 | "generate_deref", | ||
558 | r#####" | ||
559 | struct A; | ||
560 | struct B { | ||
561 | $0a: A | ||
562 | } | ||
563 | "#####, | ||
564 | r#####" | ||
565 | struct A; | ||
566 | struct B { | ||
567 | a: A | ||
568 | } | ||
569 | |||
570 | impl std::ops::Deref for B { | ||
571 | type Target = A; | ||
572 | |||
573 | fn deref(&self) -> &Self::Target { | ||
574 | &self.a | ||
575 | } | ||
576 | } | ||
577 | "#####, | ||
578 | ) | ||
579 | } | ||
580 | |||
581 | #[test] | ||
555 | fn doctest_generate_derive() { | 582 | fn doctest_generate_derive() { |
556 | check_doc_test( | 583 | check_doc_test( |
557 | "generate_derive", | 584 | "generate_derive", |
diff --git a/crates/ide_completion/src/completions/flyimport.rs b/crates/ide_completion/src/completions/flyimport.rs index 5e61ecb4d..8e211ae1e 100644 --- a/crates/ide_completion/src/completions/flyimport.rs +++ b/crates/ide_completion/src/completions/flyimport.rs | |||
@@ -1127,4 +1127,27 @@ impl Bar for Foo { | |||
1127 | expect![[r#""#]], | 1127 | expect![[r#""#]], |
1128 | ); | 1128 | ); |
1129 | } | 1129 | } |
1130 | |||
1131 | #[test] | ||
1132 | fn no_inherent_candidates_proposed() { | ||
1133 | check( | ||
1134 | r#" | ||
1135 | mod baz { | ||
1136 | pub trait DefDatabase { | ||
1137 | fn method1(&self); | ||
1138 | } | ||
1139 | pub trait HirDatabase: DefDatabase { | ||
1140 | fn method2(&self); | ||
1141 | } | ||
1142 | } | ||
1143 | |||
1144 | mod bar { | ||
1145 | fn test(db: &dyn crate::baz::HirDatabase) { | ||
1146 | db.metho$0 | ||
1147 | } | ||
1148 | } | ||
1149 | "#, | ||
1150 | expect![[r#""#]], | ||
1151 | ); | ||
1152 | } | ||
1130 | } | 1153 | } |
diff --git a/crates/ide_db/src/call_info.rs b/crates/ide_db/src/call_info.rs index e583a52f4..bad277a95 100644 --- a/crates/ide_db/src/call_info.rs +++ b/crates/ide_db/src/call_info.rs | |||
@@ -4,8 +4,9 @@ use either::Either; | |||
4 | use hir::{HasAttrs, HirDisplay, Semantics, Type}; | 4 | use hir::{HasAttrs, HirDisplay, Semantics, Type}; |
5 | use stdx::format_to; | 5 | use stdx::format_to; |
6 | use syntax::{ | 6 | use syntax::{ |
7 | algo, | ||
7 | ast::{self, ArgListOwner, NameOwner}, | 8 | ast::{self, ArgListOwner, NameOwner}, |
8 | match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize, | 9 | match_ast, AstNode, Direction, SyntaxNode, SyntaxToken, TextRange, TextSize, |
9 | }; | 10 | }; |
10 | 11 | ||
11 | use crate::RootDatabase; | 12 | use crate::RootDatabase; |
@@ -43,7 +44,12 @@ pub fn call_info(db: &RootDatabase, position: FilePosition) -> Option<CallInfo> | |||
43 | let sema = Semantics::new(db); | 44 | let sema = Semantics::new(db); |
44 | let file = sema.parse(position.file_id); | 45 | let file = sema.parse(position.file_id); |
45 | let file = file.syntax(); | 46 | let file = file.syntax(); |
46 | let token = file.token_at_offset(position.offset).next()?; | 47 | let token = file |
48 | .token_at_offset(position.offset) | ||
49 | .left_biased() | ||
50 | // if the cursor is sandwiched between two space tokens and the call is unclosed | ||
51 | // this prevents us from leaving the CallExpression | ||
52 | .and_then(|tok| algo::skip_trivia_token(tok, Direction::Prev))?; | ||
47 | let token = sema.descend_into_macros(token); | 53 | let token = sema.descend_into_macros(token); |
48 | 54 | ||
49 | let (callable, active_parameter) = call_info_impl(&sema, token)?; | 55 | let (callable, active_parameter) = call_info_impl(&sema, token)?; |
diff --git a/crates/ide_db/src/call_info/tests.rs b/crates/ide_db/src/call_info/tests.rs index 281a081a3..be1cc12de 100644 --- a/crates/ide_db/src/call_info/tests.rs +++ b/crates/ide_db/src/call_info/tests.rs | |||
@@ -522,3 +522,30 @@ fn main(f: fn(i32, f64) -> char) { | |||
522 | "#]], | 522 | "#]], |
523 | ) | 523 | ) |
524 | } | 524 | } |
525 | |||
526 | #[test] | ||
527 | fn call_info_for_unclosed_call() { | ||
528 | check( | ||
529 | r#" | ||
530 | fn foo(foo: u32, bar: u32) {} | ||
531 | fn main() { | ||
532 | foo($0 | ||
533 | }"#, | ||
534 | expect![[r#" | ||
535 | fn foo(foo: u32, bar: u32) | ||
536 | (<foo: u32>, bar: u32) | ||
537 | "#]], | ||
538 | ); | ||
539 | // check with surrounding space | ||
540 | check( | ||
541 | r#" | ||
542 | fn foo(foo: u32, bar: u32) {} | ||
543 | fn main() { | ||
544 | foo( $0 | ||
545 | }"#, | ||
546 | expect![[r#" | ||
547 | fn foo(foo: u32, bar: u32) | ||
548 | (<foo: u32>, bar: u32) | ||
549 | "#]], | ||
550 | ) | ||
551 | } | ||
diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index 66798ea3a..83a665b37 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs | |||
@@ -113,6 +113,10 @@ impl FamousDefs<'_, '_> { | |||
113 | self.find_module("core:iter") | 113 | self.find_module("core:iter") |
114 | } | 114 | } |
115 | 115 | ||
116 | pub fn core_ops_Deref(&self) -> Option<Trait> { | ||
117 | self.find_trait("core:ops:Deref") | ||
118 | } | ||
119 | |||
116 | fn find_trait(&self, path: &str) -> Option<Trait> { | 120 | fn find_trait(&self, path: &str) -> Option<Trait> { |
117 | match self.find_def(path)? { | 121 | match self.find_def(path)? { |
118 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), | 122 | hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), |
diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs index 4d79e064e..29ae12dcf 100644 --- a/crates/ide_db/src/helpers/famous_defs_fixture.rs +++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs | |||
@@ -112,6 +112,12 @@ pub mod ops { | |||
112 | type Output; | 112 | type Output; |
113 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; | 113 | extern "rust-call" fn call_once(self, args: Args) -> Self::Output; |
114 | } | 114 | } |
115 | |||
116 | #[lang = "deref"] | ||
117 | pub trait Deref { | ||
118 | type Target: ?Sized; | ||
119 | fn deref(&self) -> &Self::Target; | ||
120 | } | ||
115 | } | 121 | } |
116 | 122 | ||
117 | pub mod option { | 123 | pub mod option { |
@@ -141,3 +147,5 @@ mod return_keyword {} | |||
141 | 147 | ||
142 | /// Docs for prim_str | 148 | /// Docs for prim_str |
143 | mod prim_str {} | 149 | mod prim_str {} |
150 | |||
151 | pub use core::ops; \ No newline at end of file | ||
diff --git a/crates/ide_db/src/helpers/import_assets.rs b/crates/ide_db/src/helpers/import_assets.rs index 8ce648367..91d6a4665 100644 --- a/crates/ide_db/src/helpers/import_assets.rs +++ b/crates/ide_db/src/helpers/import_assets.rs | |||
@@ -436,6 +436,8 @@ fn trait_applicable_items( | |||
436 | }) | 436 | }) |
437 | .collect(); | 437 | .collect(); |
438 | 438 | ||
439 | let related_dyn_traits = | ||
440 | trait_candidate.receiver_ty.applicable_inherent_traits(db).collect::<FxHashSet<_>>(); | ||
439 | let mut located_imports = FxHashSet::default(); | 441 | let mut located_imports = FxHashSet::default(); |
440 | 442 | ||
441 | if trait_assoc_item { | 443 | if trait_assoc_item { |
@@ -451,12 +453,16 @@ fn trait_applicable_items( | |||
451 | return None; | 453 | return None; |
452 | } | 454 | } |
453 | } | 455 | } |
456 | let located_trait = assoc.containing_trait(db)?; | ||
457 | if related_dyn_traits.contains(&located_trait) { | ||
458 | return None; | ||
459 | } | ||
454 | 460 | ||
455 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 461 | let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); |
456 | let original_item = assoc_to_item(assoc); | 462 | let original_item = assoc_to_item(assoc); |
457 | located_imports.insert(LocatedImport::new( | 463 | located_imports.insert(LocatedImport::new( |
458 | mod_path(item)?, | 464 | mod_path(trait_item)?, |
459 | item, | 465 | trait_item, |
460 | original_item, | 466 | original_item, |
461 | mod_path(original_item), | 467 | mod_path(original_item), |
462 | )); | 468 | )); |
@@ -473,11 +479,15 @@ fn trait_applicable_items( | |||
473 | |_, function| { | 479 | |_, function| { |
474 | let assoc = function.as_assoc_item(db)?; | 480 | let assoc = function.as_assoc_item(db)?; |
475 | if required_assoc_items.contains(&assoc) { | 481 | if required_assoc_items.contains(&assoc) { |
476 | let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?)); | 482 | let located_trait = assoc.containing_trait(db)?; |
483 | if related_dyn_traits.contains(&located_trait) { | ||
484 | return None; | ||
485 | } | ||
486 | let trait_item = ItemInNs::from(ModuleDef::from(located_trait)); | ||
477 | let original_item = assoc_to_item(assoc); | 487 | let original_item = assoc_to_item(assoc); |
478 | located_imports.insert(LocatedImport::new( | 488 | located_imports.insert(LocatedImport::new( |
479 | mod_path(item)?, | 489 | mod_path(trait_item)?, |
480 | item, | 490 | trait_item, |
481 | original_item, | 491 | original_item, |
482 | mod_path(original_item), | 492 | mod_path(original_item), |
483 | )); | 493 | )); |
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 9ba98f7fb..a7c8c13c6 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -213,7 +213,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { | |||
213 | 213 | ||
214 | // Quote the string | 214 | // Quote the string |
215 | // Note that `tt::Literal` expect an escaped string | 215 | // Note that `tt::Literal` expect an escaped string |
216 | let text = format!("{:?}", text.escape_debug().to_string()); | 216 | let text = format!("\"{}\"", text.escape_debug()); |
217 | text.into() | 217 | text.into() |
218 | } | 218 | } |
219 | 219 | ||
diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs index e02d038b6..3a1d840ea 100644 --- a/crates/mbe/src/tests/expand.rs +++ b/crates/mbe/src/tests/expand.rs | |||
@@ -936,7 +936,25 @@ fn test_meta_doc_comments() { | |||
936 | MultiLines Doc | 936 | MultiLines Doc |
937 | */ | 937 | */ |
938 | }"#, | 938 | }"#, |
939 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", | 939 | "# [doc = \" Single Line Doc 1\"] # [doc = \"\\n MultiLines Doc\\n \"] fn bar () {}", |
940 | ); | ||
941 | } | ||
942 | |||
943 | #[test] | ||
944 | fn test_meta_extended_key_value_attributes() { | ||
945 | parse_macro( | ||
946 | r#" | ||
947 | macro_rules! foo { | ||
948 | (#[$i:meta]) => ( | ||
949 | #[$ i] | ||
950 | fn bar() {} | ||
951 | ) | ||
952 | } | ||
953 | "#, | ||
954 | ) | ||
955 | .assert_expand_items( | ||
956 | r#"foo! { #[doc = concat!("The `", "bla", "` lang item.")] }"#, | ||
957 | r#"# [doc = concat ! ("The `" , "bla" , "` lang item.")] fn bar () {}"#, | ||
940 | ); | 958 | ); |
941 | } | 959 | } |
942 | 960 | ||
@@ -959,7 +977,27 @@ fn test_meta_doc_comments_non_latin() { | |||
959 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 | 977 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 |
960 | */ | 978 | */ |
961 | }"#, | 979 | }"#, |
962 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", | 980 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\n \"] fn bar () {}", |
981 | ); | ||
982 | } | ||
983 | |||
984 | #[test] | ||
985 | fn test_meta_doc_comments_escaped_characters() { | ||
986 | parse_macro( | ||
987 | r#" | ||
988 | macro_rules! foo { | ||
989 | ($(#[$ i:meta])+) => ( | ||
990 | $(#[$ i])+ | ||
991 | fn bar() {} | ||
992 | ) | ||
993 | } | ||
994 | "#, | ||
995 | ) | ||
996 | .assert_expand_items( | ||
997 | r#"foo! { | ||
998 | /// \ " ' | ||
999 | }"#, | ||
1000 | r#"# [doc = " \\ \" \'"] fn bar () {}"#, | ||
963 | ); | 1001 | ); |
964 | } | 1002 | } |
965 | 1003 | ||
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index cebb8f400..9bdf0b5fa 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs | |||
@@ -76,42 +76,7 @@ pub(crate) mod fragments { | |||
76 | 76 | ||
77 | // Parse a meta item , which excluded [], e.g : #[ MetaItem ] | 77 | // Parse a meta item , which excluded [], e.g : #[ MetaItem ] |
78 | pub(crate) fn meta_item(p: &mut Parser) { | 78 | pub(crate) fn meta_item(p: &mut Parser) { |
79 | fn is_delimiter(p: &mut Parser) -> bool { | 79 | attributes::meta(p); |
80 | matches!(p.current(), T!['{'] | T!['('] | T!['[']) | ||
81 | } | ||
82 | |||
83 | if is_delimiter(p) { | ||
84 | items::token_tree(p); | ||
85 | return; | ||
86 | } | ||
87 | |||
88 | let m = p.start(); | ||
89 | while !p.at(EOF) { | ||
90 | if is_delimiter(p) { | ||
91 | items::token_tree(p); | ||
92 | break; | ||
93 | } else { | ||
94 | // https://doc.rust-lang.org/reference/attributes.html | ||
95 | // https://doc.rust-lang.org/reference/paths.html#simple-paths | ||
96 | // The start of an meta must be a simple path | ||
97 | match p.current() { | ||
98 | IDENT | T![super] | T![self] | T![crate] => p.bump_any(), | ||
99 | T![=] => { | ||
100 | p.bump_any(); | ||
101 | match p.current() { | ||
102 | c if c.is_literal() => p.bump_any(), | ||
103 | T![true] | T![false] => p.bump_any(), | ||
104 | _ => {} | ||
105 | } | ||
106 | break; | ||
107 | } | ||
108 | _ if p.at(T![::]) => p.bump(T![::]), | ||
109 | _ => break, | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | m.complete(p, TOKEN_TREE); | ||
115 | } | 80 | } |
116 | 81 | ||
117 | pub(crate) fn item(p: &mut Parser) { | 82 | pub(crate) fn item(p: &mut Parser) { |
diff --git a/crates/parser/src/grammar/attributes.rs b/crates/parser/src/grammar/attributes.rs index 96791ffc2..124a10eb2 100644 --- a/crates/parser/src/grammar/attributes.rs +++ b/crates/parser/src/grammar/attributes.rs | |||
@@ -14,6 +14,21 @@ pub(super) fn outer_attrs(p: &mut Parser) { | |||
14 | } | 14 | } |
15 | } | 15 | } |
16 | 16 | ||
17 | pub(super) fn meta(p: &mut Parser) { | ||
18 | paths::use_path(p); | ||
19 | |||
20 | match p.current() { | ||
21 | T![=] => { | ||
22 | p.bump(T![=]); | ||
23 | if expressions::expr(p).0.is_none() { | ||
24 | p.error("expected expression"); | ||
25 | } | ||
26 | } | ||
27 | T!['('] | T!['['] | T!['{'] => items::token_tree(p), | ||
28 | _ => {} | ||
29 | } | ||
30 | } | ||
31 | |||
17 | fn attr(p: &mut Parser, inner: bool) { | 32 | fn attr(p: &mut Parser, inner: bool) { |
18 | let attr = p.start(); | 33 | let attr = p.start(); |
19 | assert!(p.at(T![#])); | 34 | assert!(p.at(T![#])); |
@@ -25,18 +40,7 @@ fn attr(p: &mut Parser, inner: bool) { | |||
25 | } | 40 | } |
26 | 41 | ||
27 | if p.eat(T!['[']) { | 42 | if p.eat(T!['[']) { |
28 | paths::use_path(p); | 43 | meta(p); |
29 | |||
30 | match p.current() { | ||
31 | T![=] => { | ||
32 | p.bump(T![=]); | ||
33 | if expressions::expr(p).0.is_none() { | ||
34 | p.error("expected expression"); | ||
35 | } | ||
36 | } | ||
37 | T!['('] | T!['['] | T!['{'] => items::token_tree(p), | ||
38 | _ => {} | ||
39 | } | ||
40 | 44 | ||
41 | if !p.eat(T![']']) { | 45 | if !p.eat(T![']']) { |
42 | p.error("expected `]`"); | 46 | p.error("expected `]`"); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1edaa394a..7ddea22c8 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -400,6 +400,17 @@ impl Config { | |||
400 | pub fn will_rename(&self) -> bool { | 400 | pub fn will_rename(&self) -> bool { |
401 | try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) | 401 | try_or!(self.caps.workspace.as_ref()?.file_operations.as_ref()?.will_rename?, false) |
402 | } | 402 | } |
403 | pub fn change_annotation_support(&self) -> bool { | ||
404 | try_!(self | ||
405 | .caps | ||
406 | .workspace | ||
407 | .as_ref()? | ||
408 | .workspace_edit | ||
409 | .as_ref()? | ||
410 | .change_annotation_support | ||
411 | .as_ref()?) | ||
412 | .is_some() | ||
413 | } | ||
403 | pub fn code_action_resolve(&self) -> bool { | 414 | pub fn code_action_resolve(&self) -> bool { |
404 | try_or!( | 415 | try_or!( |
405 | self.caps | 416 | self.caps |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt index 23ec2efba..227d96d51 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/clippy_pass_by_ref.txt | |||
@@ -326,6 +326,7 @@ | |||
326 | }, | 326 | }, |
327 | ), | 327 | ), |
328 | document_changes: None, | 328 | document_changes: None, |
329 | change_annotations: None, | ||
329 | }, | 330 | }, |
330 | ), | 331 | ), |
331 | is_preferred: Some( | 332 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt index b6acb5f42..f8adfad3b 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt index d765257c4..5a70d2ed7 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_hint.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt index 6b0d94878..04ca0c9c2 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/rustc_unused_variable_as_info.txt | |||
@@ -179,6 +179,7 @@ | |||
179 | }, | 179 | }, |
180 | ), | 180 | ), |
181 | document_changes: None, | 181 | document_changes: None, |
182 | change_annotations: None, | ||
182 | }, | 183 | }, |
183 | ), | 184 | ), |
184 | is_preferred: Some( | 185 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt index a0cfb8d33..57d2f1ae3 100644 --- a/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt +++ b/crates/rust-analyzer/src/diagnostics/test_data/snap_multi_line_fix.txt | |||
@@ -339,6 +339,7 @@ | |||
339 | }, | 339 | }, |
340 | ), | 340 | ), |
341 | document_changes: None, | 341 | document_changes: None, |
342 | change_annotations: None, | ||
342 | }, | 343 | }, |
343 | ), | 344 | ), |
344 | is_preferred: Some( | 345 | is_preferred: Some( |
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index e2f319f6b..ca18997e4 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs | |||
@@ -136,6 +136,7 @@ fn map_rust_child_diagnostic( | |||
136 | // FIXME: there's no good reason to use edit_map here.... | 136 | // FIXME: there's no good reason to use edit_map here.... |
137 | changes: Some(edit_map), | 137 | changes: Some(edit_map), |
138 | document_changes: None, | 138 | document_changes: None, |
139 | change_annotations: None, | ||
139 | }), | 140 | }), |
140 | is_preferred: Some(true), | 141 | is_preferred: Some(true), |
141 | data: None, | 142 | data: None, |
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index d648cda32..b8835a534 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs | |||
@@ -312,6 +312,9 @@ pub struct SnippetWorkspaceEdit { | |||
312 | pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, | 312 | pub changes: Option<HashMap<lsp_types::Url, Vec<lsp_types::TextEdit>>>, |
313 | #[serde(skip_serializing_if = "Option::is_none")] | 313 | #[serde(skip_serializing_if = "Option::is_none")] |
314 | pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, | 314 | pub document_changes: Option<Vec<SnippetDocumentChangeOperation>>, |
315 | #[serde(skip_serializing_if = "Option::is_none")] | ||
316 | pub change_annotations: | ||
317 | Option<HashMap<lsp_types::ChangeAnnotationIdentifier, lsp_types::ChangeAnnotation>>, | ||
315 | } | 318 | } |
316 | 319 | ||
317 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] | 320 | #[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize)] |
@@ -335,6 +338,9 @@ pub struct SnippetTextEdit { | |||
335 | pub new_text: String, | 338 | pub new_text: String, |
336 | #[serde(skip_serializing_if = "Option::is_none")] | 339 | #[serde(skip_serializing_if = "Option::is_none")] |
337 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, | 340 | pub insert_text_format: Option<lsp_types::InsertTextFormat>, |
341 | /// The annotation id if this is an annotated | ||
342 | #[serde(skip_serializing_if = "Option::is_none")] | ||
343 | pub annotation_id: Option<lsp_types::ChangeAnnotationIdentifier>, | ||
338 | } | 344 | } |
339 | 345 | ||
340 | pub enum HoverRequest {} | 346 | pub enum HoverRequest {} |
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs index 865eaae9b..a51ff89e4 100644 --- a/crates/rust-analyzer/src/markdown.rs +++ b/crates/rust-analyzer/src/markdown.rs | |||
@@ -27,9 +27,8 @@ pub(crate) fn format_docs(src: &str) -> String { | |||
27 | in_code_block ^= true; | 27 | in_code_block ^= true; |
28 | 28 | ||
29 | if in_code_block { | 29 | if in_code_block { |
30 | is_rust = header | 30 | is_rust = |
31 | .split(',') | 31 | header.split(',').all(|sub| is_rust_specific_code_block_attribute(sub.trim())); |
32 | .all(|sub| RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&sub.trim())); | ||
33 | 32 | ||
34 | if is_rust { | 33 | if is_rust { |
35 | line = "```rust"; | 34 | line = "```rust"; |
@@ -42,6 +41,13 @@ pub(crate) fn format_docs(src: &str) -> String { | |||
42 | processed_lines.join("\n") | 41 | processed_lines.join("\n") |
43 | } | 42 | } |
44 | 43 | ||
44 | fn is_rust_specific_code_block_attribute(attr: &str) -> bool { | ||
45 | if RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&attr) { | ||
46 | return true; | ||
47 | } | ||
48 | attr.starts_with('E') && attr.len() == 5 && attr[1..].parse::<u32>().is_ok() | ||
49 | } | ||
50 | |||
45 | fn code_line_ignored_by_rustdoc(line: &str) -> bool { | 51 | fn code_line_ignored_by_rustdoc(line: &str) -> bool { |
46 | let trimmed = line.trim(); | 52 | let trimmed = line.trim(); |
47 | trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t") | 53 | trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t") |
@@ -82,6 +88,12 @@ mod tests { | |||
82 | } | 88 | } |
83 | 89 | ||
84 | #[test] | 90 | #[test] |
91 | fn test_format_docs_handles_error_codes() { | ||
92 | let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```"; | ||
93 | assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```"); | ||
94 | } | ||
95 | |||
96 | #[test] | ||
85 | fn test_format_docs_skips_comments_in_rust_block() { | 97 | fn test_format_docs_skips_comments_in_rust_block() { |
86 | let comment = | 98 | let comment = |
87 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; | 99 | "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```"; |
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 1a1f65f3b..fe4d0733d 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs | |||
@@ -1,15 +1,16 @@ | |||
1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. | 1 | //! Conversion of rust-analyzer specific types to lsp_types equivalents. |
2 | use std::{ | 2 | use std::{ |
3 | iter::once, | ||
3 | path::{self, Path}, | 4 | path::{self, Path}, |
4 | sync::atomic::{AtomicU32, Ordering}, | 5 | sync::atomic::{AtomicU32, Ordering}, |
5 | }; | 6 | }; |
6 | 7 | ||
7 | use ide::{ | 8 | use ide::{ |
8 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, CompletionItem, CompletionItemKind, | 9 | Annotation, AnnotationKind, Assist, AssistKind, CallInfo, Cancelable, CompletionItem, |
9 | CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, | 10 | CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, |
10 | Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, InlayKind, | 11 | Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, InlayHint, |
11 | InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, Severity, | 12 | InlayKind, InsertTextFormat, Markup, NavigationTarget, ReferenceAccess, RenameError, Runnable, |
12 | SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, | 13 | Severity, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, |
13 | }; | 14 | }; |
14 | use itertools::Itertools; | 15 | use itertools::Itertools; |
15 | use serde_json::to_value; | 16 | use serde_json::to_value; |
@@ -174,6 +175,7 @@ pub(crate) fn snippet_text_edit( | |||
174 | range: text_edit.range, | 175 | range: text_edit.range, |
175 | new_text: text_edit.new_text, | 176 | new_text: text_edit.new_text, |
176 | insert_text_format, | 177 | insert_text_format, |
178 | annotation_id: None, | ||
177 | } | 179 | } |
178 | } | 180 | } |
179 | 181 | ||
@@ -688,6 +690,10 @@ pub(crate) fn goto_definition_response( | |||
688 | } | 690 | } |
689 | } | 691 | } |
690 | 692 | ||
693 | fn outside_workspace_annotation_id() -> String { | ||
694 | String::from("OutsideWorkspace") | ||
695 | } | ||
696 | |||
691 | pub(crate) fn snippet_text_document_edit( | 697 | pub(crate) fn snippet_text_document_edit( |
692 | snap: &GlobalStateSnapshot, | 698 | snap: &GlobalStateSnapshot, |
693 | is_snippet: bool, | 699 | is_snippet: bool, |
@@ -696,14 +702,21 @@ pub(crate) fn snippet_text_document_edit( | |||
696 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { | 702 | ) -> Result<lsp_ext::SnippetTextDocumentEdit> { |
697 | let text_document = optional_versioned_text_document_identifier(snap, file_id); | 703 | let text_document = optional_versioned_text_document_identifier(snap, file_id); |
698 | let line_index = snap.file_line_index(file_id)?; | 704 | let line_index = snap.file_line_index(file_id)?; |
699 | let edits = edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); | 705 | let mut edits: Vec<_> = |
706 | edit.into_iter().map(|it| snippet_text_edit(&line_index, is_snippet, it)).collect(); | ||
707 | |||
708 | if snap.analysis.is_library_file(file_id)? && snap.config.change_annotation_support() { | ||
709 | for edit in &mut edits { | ||
710 | edit.annotation_id = Some(outside_workspace_annotation_id()) | ||
711 | } | ||
712 | } | ||
700 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) | 713 | Ok(lsp_ext::SnippetTextDocumentEdit { text_document, edits }) |
701 | } | 714 | } |
702 | 715 | ||
703 | pub(crate) fn snippet_text_document_ops( | 716 | pub(crate) fn snippet_text_document_ops( |
704 | snap: &GlobalStateSnapshot, | 717 | snap: &GlobalStateSnapshot, |
705 | file_system_edit: FileSystemEdit, | 718 | file_system_edit: FileSystemEdit, |
706 | ) -> Vec<lsp_ext::SnippetDocumentChangeOperation> { | 719 | ) -> Cancelable<Vec<lsp_ext::SnippetDocumentChangeOperation>> { |
707 | let mut ops = Vec::new(); | 720 | let mut ops = Vec::new(); |
708 | match file_system_edit { | 721 | match file_system_edit { |
709 | FileSystemEdit::CreateFile { dst, initial_contents } => { | 722 | FileSystemEdit::CreateFile { dst, initial_contents } => { |
@@ -721,6 +734,7 @@ pub(crate) fn snippet_text_document_ops( | |||
721 | range: lsp_types::Range::default(), | 734 | range: lsp_types::Range::default(), |
722 | new_text: initial_contents, | 735 | new_text: initial_contents, |
723 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), | 736 | insert_text_format: Some(lsp_types::InsertTextFormat::PlainText), |
737 | annotation_id: None, | ||
724 | }; | 738 | }; |
725 | let edit_file = | 739 | let edit_file = |
726 | lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; | 740 | lsp_ext::SnippetTextDocumentEdit { text_document, edits: vec![text_edit] }; |
@@ -730,16 +744,19 @@ pub(crate) fn snippet_text_document_ops( | |||
730 | FileSystemEdit::MoveFile { src, dst } => { | 744 | FileSystemEdit::MoveFile { src, dst } => { |
731 | let old_uri = snap.file_id_to_url(src); | 745 | let old_uri = snap.file_id_to_url(src); |
732 | let new_uri = snap.anchored_path(&dst); | 746 | let new_uri = snap.anchored_path(&dst); |
733 | let rename_file = lsp_types::ResourceOp::Rename(lsp_types::RenameFile { | 747 | let mut rename_file = |
734 | old_uri, | 748 | lsp_types::RenameFile { old_uri, new_uri, options: None, annotation_id: None }; |
735 | new_uri, | 749 | if snap.analysis.is_library_file(src) == Ok(true) |
736 | options: None, | 750 | && snap.config.change_annotation_support() |
737 | annotation_id: None, | 751 | { |
738 | }); | 752 | rename_file.annotation_id = Some(outside_workspace_annotation_id()) |
739 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(rename_file)) | 753 | } |
754 | ops.push(lsp_ext::SnippetDocumentChangeOperation::Op(lsp_types::ResourceOp::Rename( | ||
755 | rename_file, | ||
756 | ))) | ||
740 | } | 757 | } |
741 | } | 758 | } |
742 | ops | 759 | Ok(ops) |
743 | } | 760 | } |
744 | 761 | ||
745 | pub(crate) fn snippet_workspace_edit( | 762 | pub(crate) fn snippet_workspace_edit( |
@@ -747,16 +764,35 @@ pub(crate) fn snippet_workspace_edit( | |||
747 | source_change: SourceChange, | 764 | source_change: SourceChange, |
748 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { | 765 | ) -> Result<lsp_ext::SnippetWorkspaceEdit> { |
749 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); | 766 | let mut document_changes: Vec<lsp_ext::SnippetDocumentChangeOperation> = Vec::new(); |
767 | |||
750 | for op in source_change.file_system_edits { | 768 | for op in source_change.file_system_edits { |
751 | let ops = snippet_text_document_ops(snap, op); | 769 | let ops = snippet_text_document_ops(snap, op)?; |
752 | document_changes.extend_from_slice(&ops); | 770 | document_changes.extend_from_slice(&ops); |
753 | } | 771 | } |
754 | for (file_id, edit) in source_change.source_file_edits { | 772 | for (file_id, edit) in source_change.source_file_edits { |
755 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; | 773 | let edit = snippet_text_document_edit(&snap, source_change.is_snippet, file_id, edit)?; |
756 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); | 774 | document_changes.push(lsp_ext::SnippetDocumentChangeOperation::Edit(edit)); |
757 | } | 775 | } |
758 | let workspace_edit = | 776 | let mut workspace_edit = lsp_ext::SnippetWorkspaceEdit { |
759 | lsp_ext::SnippetWorkspaceEdit { changes: None, document_changes: Some(document_changes) }; | 777 | changes: None, |
778 | document_changes: Some(document_changes), | ||
779 | change_annotations: None, | ||
780 | }; | ||
781 | if snap.config.change_annotation_support() { | ||
782 | workspace_edit.change_annotations = Some( | ||
783 | once(( | ||
784 | outside_workspace_annotation_id(), | ||
785 | lsp_types::ChangeAnnotation { | ||
786 | label: String::from("Edit outside of the workspace"), | ||
787 | needs_confirmation: Some(true), | ||
788 | description: Some(String::from( | ||
789 | "This edit lies outside of the workspace and may affect dependencies", | ||
790 | )), | ||
791 | }, | ||
792 | )) | ||
793 | .collect(), | ||
794 | ) | ||
795 | } | ||
760 | Ok(workspace_edit) | 796 | Ok(workspace_edit) |
761 | } | 797 | } |
762 | 798 | ||
@@ -784,16 +820,7 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
784 | lsp_types::DocumentChangeOperation::Edit( | 820 | lsp_types::DocumentChangeOperation::Edit( |
785 | lsp_types::TextDocumentEdit { | 821 | lsp_types::TextDocumentEdit { |
786 | text_document: edit.text_document, | 822 | text_document: edit.text_document, |
787 | edits: edit | 823 | edits: edit.edits.into_iter().map(From::from).collect(), |
788 | .edits | ||
789 | .into_iter() | ||
790 | .map(|edit| { | ||
791 | lsp_types::OneOf::Left(lsp_types::TextEdit { | ||
792 | range: edit.range, | ||
793 | new_text: edit.new_text, | ||
794 | }) | ||
795 | }) | ||
796 | .collect(), | ||
797 | }, | 824 | }, |
798 | ) | 825 | ) |
799 | } | 826 | } |
@@ -801,7 +828,23 @@ impl From<lsp_ext::SnippetWorkspaceEdit> for lsp_types::WorkspaceEdit { | |||
801 | .collect(), | 828 | .collect(), |
802 | ) | 829 | ) |
803 | }), | 830 | }), |
804 | change_annotations: None, | 831 | change_annotations: snippet_workspace_edit.change_annotations, |
832 | } | ||
833 | } | ||
834 | } | ||
835 | |||
836 | impl From<lsp_ext::SnippetTextEdit> | ||
837 | for lsp_types::OneOf<lsp_types::TextEdit, lsp_types::AnnotatedTextEdit> | ||
838 | { | ||
839 | fn from( | ||
840 | lsp_ext::SnippetTextEdit { annotation_id, insert_text_format:_, new_text, range }: lsp_ext::SnippetTextEdit, | ||
841 | ) -> Self { | ||
842 | match annotation_id { | ||
843 | Some(annotation_id) => lsp_types::OneOf::Right(lsp_types::AnnotatedTextEdit { | ||
844 | text_edit: lsp_types::TextEdit { range, new_text }, | ||
845 | annotation_id, | ||
846 | }), | ||
847 | None => lsp_types::OneOf::Left(lsp_types::TextEdit { range, new_text }), | ||
805 | } | 848 | } |
806 | } | 849 | } |
807 | } | 850 | } |
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index f09cdf28a..a8c1a8075 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml | |||
@@ -13,7 +13,7 @@ doctest = false | |||
13 | [dependencies] | 13 | [dependencies] |
14 | cov-mark = { version = "1.1", features = ["thread-local"] } | 14 | cov-mark = { version = "1.1", features = ["thread-local"] } |
15 | itertools = "0.10.0" | 15 | itertools = "0.10.0" |
16 | rowan = "0.13.0-pre.3" | 16 | rowan = "=0.13.0-pre.3" |
17 | rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" } | 17 | rustc_lexer = { version = "714.0.0", package = "rustc-ap-rustc_lexer" } |
18 | rustc-hash = "1.1.0" | 18 | rustc-hash = "1.1.0" |
19 | arrayvec = "0.7" | 19 | arrayvec = "0.7" |