diff options
Diffstat (limited to 'crates')
40 files changed, 703 insertions, 128 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/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/fill_match_arms.rs b/crates/ide_assists/src/handlers/fill_match_arms.rs index 878b3a3fa..80bd1b7e8 100644 --- a/crates/ide_assists/src/handlers/fill_match_arms.rs +++ b/crates/ide_assists/src/handlers/fill_match_arms.rs | |||
@@ -71,12 +71,6 @@ pub(crate) fn fill_match_arms(acc: &mut Assists, ctx: &AssistContext) -> Option< | |||
71 | return None; | 71 | return None; |
72 | } | 72 | } |
73 | 73 | ||
74 | // We do not currently support filling match arms for a tuple | ||
75 | // containing a single enum. | ||
76 | if enum_defs.len() < 2 { | ||
77 | return None; | ||
78 | } | ||
79 | |||
80 | // When calculating the match arms for a tuple of enums, we want | 74 | // When calculating the match arms for a tuple of enums, we want |
81 | // to create a match arm for each possible combination of enum | 75 | // to create a match arm for each possible combination of enum |
82 | // values. The `multi_cartesian_product` method transforms | 76 | // values. The `multi_cartesian_product` method transforms |
@@ -514,10 +508,7 @@ fn main() { | |||
514 | 508 | ||
515 | #[test] | 509 | #[test] |
516 | fn fill_match_arms_single_element_tuple_of_enum() { | 510 | fn fill_match_arms_single_element_tuple_of_enum() { |
517 | // For now we don't hande the case of a single element tuple, but | 511 | check_assist( |
518 | // we could handle this in the future if `make::tuple_pat` allowed | ||
519 | // creating a tuple with a single pattern. | ||
520 | check_assist_not_applicable( | ||
521 | fill_match_arms, | 512 | fill_match_arms, |
522 | r#" | 513 | r#" |
523 | enum A { One, Two } | 514 | enum A { One, Two } |
@@ -528,6 +519,17 @@ fn main() { | |||
528 | } | 519 | } |
529 | } | 520 | } |
530 | "#, | 521 | "#, |
522 | r#" | ||
523 | enum A { One, Two } | ||
524 | |||
525 | fn main() { | ||
526 | let a = A::One; | ||
527 | match (a, ) { | ||
528 | $0(A::One,) => {} | ||
529 | (A::Two,) => {} | ||
530 | } | ||
531 | } | ||
532 | "#, | ||
531 | ); | 533 | ); |
532 | } | 534 | } |
533 | 535 | ||
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/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/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" |
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index c6a7b99b7..94d4f2cf0 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs | |||
@@ -29,9 +29,13 @@ pub fn ty(text: &str) -> ast::Type { | |||
29 | pub fn ty_unit() -> ast::Type { | 29 | pub fn ty_unit() -> ast::Type { |
30 | ty("()") | 30 | ty("()") |
31 | } | 31 | } |
32 | // FIXME: handle types of length == 1 | ||
33 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { | 32 | pub fn ty_tuple(types: impl IntoIterator<Item = ast::Type>) -> ast::Type { |
34 | let contents = types.into_iter().join(", "); | 33 | let mut count: usize = 0; |
34 | let mut contents = types.into_iter().inspect(|_| count += 1).join(", "); | ||
35 | if count == 1 { | ||
36 | contents.push(','); | ||
37 | } | ||
38 | |||
35 | ty(&format!("({})", contents)) | 39 | ty(&format!("({})", contents)) |
36 | } | 40 | } |
37 | // FIXME: handle path to type | 41 | // FIXME: handle path to type |
@@ -292,11 +296,13 @@ pub fn wildcard_pat() -> ast::WildcardPat { | |||
292 | 296 | ||
293 | /// Creates a tuple of patterns from an iterator of patterns. | 297 | /// Creates a tuple of patterns from an iterator of patterns. |
294 | /// | 298 | /// |
295 | /// Invariant: `pats` must be length > 1 | 299 | /// Invariant: `pats` must be length > 0 |
296 | /// | ||
297 | /// FIXME handle `pats` length == 1 | ||
298 | pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { | 300 | pub fn tuple_pat(pats: impl IntoIterator<Item = ast::Pat>) -> ast::TuplePat { |
299 | let pats_str = pats.into_iter().map(|p| p.to_string()).join(", "); | 301 | let mut count: usize = 0; |
302 | let mut pats_str = pats.into_iter().inspect(|_| count += 1).join(", "); | ||
303 | if count == 1 { | ||
304 | pats_str.push(','); | ||
305 | } | ||
300 | return from_text(&format!("({})", pats_str)); | 306 | return from_text(&format!("({})", pats_str)); |
301 | 307 | ||
302 | fn from_text(text: &str) -> ast::TuplePat { | 308 | fn from_text(text: &str) -> ast::TuplePat { |