aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src')
-rw-r--r--crates/ide_completion/src/completions.rs113
-rw-r--r--crates/ide_completion/src/completions/keyword.rs36
-rw-r--r--crates/ide_completion/src/completions/pattern.rs2
-rw-r--r--crates/ide_completion/src/completions/qualified_path.rs18
-rw-r--r--crates/ide_completion/src/completions/snippet.rs17
-rw-r--r--crates/ide_completion/src/completions/unqualified_path.rs2
-rw-r--r--crates/ide_completion/src/lib.rs125
-rw-r--r--crates/ide_completion/src/tests.rs64
-rw-r--r--crates/ide_completion/src/tests/item_list.rs172
9 files changed, 302 insertions, 247 deletions
diff --git a/crates/ide_completion/src/completions.rs b/crates/ide_completion/src/completions.rs
index 783305005..cba5eb0c6 100644
--- a/crates/ide_completion/src/completions.rs
+++ b/crates/ide_completion/src/completions.rs
@@ -41,9 +41,9 @@ pub struct Completions {
41 buf: Vec<CompletionItem>, 41 buf: Vec<CompletionItem>,
42} 42}
43 43
44impl Into<Vec<CompletionItem>> for Completions { 44impl From<Completions> for Vec<CompletionItem> {
45 fn into(self) -> Vec<CompletionItem> { 45 fn from(val: Completions) -> Self {
46 self.buf 46 val.buf
47 } 47 }
48} 48}
49 49
@@ -74,35 +74,6 @@ impl Completions {
74 items.into_iter().for_each(|item| self.add(item.into())) 74 items.into_iter().for_each(|item| self.add(item.into()))
75 } 75 }
76 76
77 pub(crate) fn add_field(
78 &mut self,
79 ctx: &CompletionContext,
80 receiver: Option<hir::Name>,
81 field: hir::Field,
82 ty: &hir::Type,
83 ) {
84 let item = render_field(RenderContext::new(ctx), receiver, field, ty);
85 self.add(item);
86 }
87
88 pub(crate) fn add_tuple_field(
89 &mut self,
90 ctx: &CompletionContext,
91 receiver: Option<hir::Name>,
92 field: usize,
93 ty: &hir::Type,
94 ) {
95 let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
96 self.add(item);
97 }
98
99 pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
100 let mut item =
101 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
102 item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
103 self.add(item.build());
104 }
105
106 pub(crate) fn add_resolution( 77 pub(crate) fn add_resolution(
107 &mut self, 78 &mut self,
108 ctx: &CompletionContext, 79 ctx: &CompletionContext,
@@ -144,72 +115,102 @@ impl Completions {
144 self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func)); 115 self.add_opt(render_method(RenderContext::new(ctx), None, receiver, local_name, func));
145 } 116 }
146 117
147 pub(crate) fn add_variant_pat( 118 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) {
119 self.add_opt(render_const(RenderContext::new(ctx), constant));
120 }
121
122 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) {
123 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias));
124 }
125
126 pub(crate) fn add_type_alias_with_eq(
148 &mut self, 127 &mut self,
149 ctx: &CompletionContext, 128 ctx: &CompletionContext,
150 variant: hir::Variant, 129 type_alias: hir::TypeAlias,
151 local_name: Option<hir::Name>,
152 ) { 130 ) {
153 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None)); 131 self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias));
154 } 132 }
155 133
156 pub(crate) fn add_qualified_variant_pat( 134 pub(crate) fn add_qualified_enum_variant(
157 &mut self, 135 &mut self,
158 ctx: &CompletionContext, 136 ctx: &CompletionContext,
159 variant: hir::Variant, 137 variant: hir::Variant,
160 path: hir::ModPath, 138 path: hir::ModPath,
161 ) { 139 ) {
162 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path))); 140 let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path));
141 self.add(item);
163 } 142 }
164 143
165 pub(crate) fn add_struct_pat( 144 pub(crate) fn add_enum_variant(
166 &mut self, 145 &mut self,
167 ctx: &CompletionContext, 146 ctx: &CompletionContext,
168 strukt: hir::Struct, 147 variant: hir::Variant,
169 local_name: Option<hir::Name>, 148 local_name: Option<hir::Name>,
170 ) { 149 ) {
171 self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name)); 150 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None);
151 self.add(item);
172 } 152 }
173 153
174 pub(crate) fn add_const(&mut self, ctx: &CompletionContext, constant: hir::Const) { 154 pub(crate) fn add_field(
175 self.add_opt(render_const(RenderContext::new(ctx), constant)); 155 &mut self,
156 ctx: &CompletionContext,
157 receiver: Option<hir::Name>,
158 field: hir::Field,
159 ty: &hir::Type,
160 ) {
161 let item = render_field(RenderContext::new(ctx), receiver, field, ty);
162 self.add(item);
176 } 163 }
177 164
178 pub(crate) fn add_type_alias(&mut self, ctx: &CompletionContext, type_alias: hir::TypeAlias) { 165 pub(crate) fn add_tuple_field(
179 self.add_opt(render_type_alias(RenderContext::new(ctx), type_alias)); 166 &mut self,
167 ctx: &CompletionContext,
168 receiver: Option<hir::Name>,
169 field: usize,
170 ty: &hir::Type,
171 ) {
172 let item = render_tuple_field(RenderContext::new(ctx), receiver, field, ty);
173 self.add(item);
180 } 174 }
181 175
182 pub(crate) fn add_type_alias_with_eq( 176 pub(crate) fn add_static_lifetime(&mut self, ctx: &CompletionContext) {
177 let mut item =
178 CompletionItem::new(CompletionKind::Reference, ctx.source_range(), "'static");
179 item.kind(CompletionItemKind::SymbolKind(SymbolKind::LifetimeParam));
180 self.add(item.build());
181 }
182
183 pub(crate) fn add_variant_pat(
183 &mut self, 184 &mut self,
184 ctx: &CompletionContext, 185 ctx: &CompletionContext,
185 type_alias: hir::TypeAlias, 186 variant: hir::Variant,
187 local_name: Option<hir::Name>,
186 ) { 188 ) {
187 self.add_opt(render_type_alias_with_eq(RenderContext::new(ctx), type_alias)); 189 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, local_name, None));
188 } 190 }
189 191
190 pub(crate) fn add_qualified_enum_variant( 192 pub(crate) fn add_qualified_variant_pat(
191 &mut self, 193 &mut self,
192 ctx: &CompletionContext, 194 ctx: &CompletionContext,
193 variant: hir::Variant, 195 variant: hir::Variant,
194 path: hir::ModPath, 196 path: hir::ModPath,
195 ) { 197 ) {
196 let item = render_variant(RenderContext::new(ctx), None, None, variant, Some(path)); 198 self.add_opt(render_variant_pat(RenderContext::new(ctx), variant, None, Some(path)));
197 self.add(item);
198 } 199 }
199 200
200 pub(crate) fn add_enum_variant( 201 pub(crate) fn add_struct_pat(
201 &mut self, 202 &mut self,
202 ctx: &CompletionContext, 203 ctx: &CompletionContext,
203 variant: hir::Variant, 204 strukt: hir::Struct,
204 local_name: Option<hir::Name>, 205 local_name: Option<hir::Name>,
205 ) { 206 ) {
206 let item = render_variant(RenderContext::new(ctx), None, local_name, variant, None); 207 self.add_opt(render_struct_pat(RenderContext::new(ctx), strukt, local_name));
207 self.add(item);
208 } 208 }
209} 209}
210 210
211/// Calls the callback for each variant of the provided enum with the path to the variant. 211/// Calls the callback for each variant of the provided enum with the path to the variant.
212fn complete_enum_variants( 212/// Skips variants that are visible with single segment paths.
213fn enum_variants_with_paths(
213 acc: &mut Completions, 214 acc: &mut Completions,
214 ctx: &CompletionContext, 215 ctx: &CompletionContext,
215 enum_: hir::Enum, 216 enum_: hir::Enum,
diff --git a/crates/ide_completion/src/completions/keyword.rs b/crates/ide_completion/src/completions/keyword.rs
index 0fccbeccf..2c42438d6 100644
--- a/crates/ide_completion/src/completions/keyword.rs
+++ b/crates/ide_completion/src/completions/keyword.rs
@@ -231,30 +231,6 @@ mod tests {
231 } 231 }
232 232
233 #[test] 233 #[test]
234 fn test_keywords_at_source_file_level() {
235 check(
236 r"m$0",
237 expect![[r#"
238 kw pub(crate)
239 kw pub
240 kw unsafe
241 kw fn
242 kw const
243 kw type
244 kw use
245 kw impl
246 kw trait
247 kw static
248 kw extern
249 kw mod
250 kw enum
251 kw struct
252 kw union
253 "#]],
254 );
255 }
256
257 #[test]
258 fn test_keywords_in_function() { 234 fn test_keywords_in_function() {
259 check( 235 check(
260 r"fn quux() { $0 }", 236 r"fn quux() { $0 }",
@@ -443,18 +419,6 @@ fn quux() -> i32 {
443 } 419 }
444 420
445 #[test] 421 #[test]
446 fn test_keywords_after_unsafe_in_item_list() {
447 check(
448 r"unsafe $0",
449 expect![[r#"
450 kw fn
451 kw trait
452 kw impl
453 "#]],
454 );
455 }
456
457 #[test]
458 fn test_keywords_after_unsafe_in_block_expr() { 422 fn test_keywords_after_unsafe_in_block_expr() {
459 check( 423 check(
460 r"fn my_fn() { unsafe $0 }", 424 r"fn my_fn() { unsafe $0 }",
diff --git a/crates/ide_completion/src/completions/pattern.rs b/crates/ide_completion/src/completions/pattern.rs
index 1daa8595a..e45b2a1ea 100644
--- a/crates/ide_completion/src/completions/pattern.rs
+++ b/crates/ide_completion/src/completions/pattern.rs
@@ -13,7 +13,7 @@ pub(crate) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) {
13 if let Some(hir::Adt::Enum(e)) = 13 if let Some(hir::Adt::Enum(e)) =
14 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 14 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
15 { 15 {
16 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { 16 super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
17 acc.add_qualified_variant_pat(ctx, variant, path.clone()); 17 acc.add_qualified_variant_pat(ctx, variant, path.clone());
18 acc.add_qualified_enum_variant(ctx, variant, path); 18 acc.add_qualified_enum_variant(ctx, variant, path);
19 }); 19 });
diff --git a/crates/ide_completion/src/completions/qualified_path.rs b/crates/ide_completion/src/completions/qualified_path.rs
index 1643eeed4..165b9e6a5 100644
--- a/crates/ide_completion/src/completions/qualified_path.rs
+++ b/crates/ide_completion/src/completions/qualified_path.rs
@@ -714,24 +714,6 @@ impl MyStruct {
714 } 714 }
715 715
716 #[test] 716 #[test]
717 fn completes_in_item_list() {
718 check(
719 r#"
720struct MyStruct {}
721#[macro_export]
722macro_rules! foo {}
723mod bar {}
724
725crate::$0
726"#,
727 expect![[r#"
728 md bar
729 ma foo!(…) #[macro_export] macro_rules! foo
730 "#]],
731 )
732 }
733
734 #[test]
735 fn test_super_super_completion() { 717 fn test_super_super_completion() {
736 check( 718 check(
737 r#" 719 r#"
diff --git a/crates/ide_completion/src/completions/snippet.rs b/crates/ide_completion/src/completions/snippet.rs
index b9862de67..5560f1acf 100644
--- a/crates/ide_completion/src/completions/snippet.rs
+++ b/crates/ide_completion/src/completions/snippet.rs
@@ -105,21 +105,4 @@ mod tests {
105 check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]); 105 check(r#"fn foo(x: i32) { ::foo$0 }"#, expect![[""]]);
106 check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]); 106 check(r#"fn foo(x: i32) { ::$0 }"#, expect![[""]]);
107 } 107 }
108
109 #[test]
110 fn completes_snippets_in_items() {
111 check(
112 r#"
113#[cfg(test)]
114mod tests {
115 $0
116}
117"#,
118 expect![[r#"
119 sn tmod (Test module)
120 sn tfn (Test function)
121 sn macro_rules
122 "#]],
123 )
124 }
125} 108}
diff --git a/crates/ide_completion/src/completions/unqualified_path.rs b/crates/ide_completion/src/completions/unqualified_path.rs
index b5af1c810..2c623bf7a 100644
--- a/crates/ide_completion/src/completions/unqualified_path.rs
+++ b/crates/ide_completion/src/completions/unqualified_path.rs
@@ -40,7 +40,7 @@ pub(crate) fn complete_unqualified_path(acc: &mut Completions, ctx: &CompletionC
40 if let Some(hir::Adt::Enum(e)) = 40 if let Some(hir::Adt::Enum(e)) =
41 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt()) 41 ctx.expected_type.as_ref().and_then(|ty| ty.strip_references().as_adt())
42 { 42 {
43 super::complete_enum_variants(acc, ctx, e, |acc, ctx, variant, path| { 43 super::enum_variants_with_paths(acc, ctx, e, |acc, ctx, variant, path| {
44 acc.add_qualified_enum_variant(ctx, variant, path) 44 acc.add_qualified_enum_variant(ctx, variant, path)
45 }); 45 });
46 } 46 }
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs
index 18983aa01..be6442426 100644
--- a/crates/ide_completion/src/lib.rs
+++ b/crates/ide_completion/src/lib.rs
@@ -1,14 +1,16 @@
1//! `completions` crate provides utilities for generating completions of user input. 1//! `completions` crate provides utilities for generating completions of user input.
2 2
3mod completions;
3mod config; 4mod config;
4mod item;
5mod context; 5mod context;
6mod item;
6mod patterns; 7mod patterns;
7#[cfg(test)]
8mod test_utils;
9mod render; 8mod render;
10 9
11mod completions; 10#[cfg(test)]
11mod tests;
12#[cfg(test)]
13mod test_utils;
12 14
13use completions::flyimport::position_for_import; 15use completions::flyimport::position_for_import;
14use ide_db::{ 16use ide_db::{
@@ -141,6 +143,7 @@ pub fn completions(
141 let ctx = CompletionContext::new(db, position, config)?; 143 let ctx = CompletionContext::new(db, position, config)?;
142 144
143 if ctx.no_completion_required() { 145 if ctx.no_completion_required() {
146 cov_mark::hit!(no_completion_required);
144 // No work required here. 147 // No work required here.
145 return None; 148 return None;
146 } 149 }
@@ -200,117 +203,3 @@ pub fn resolve_completion_edits(
200 203
201 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit]) 204 ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
202} 205}
203
204#[cfg(test)]
205mod tests {
206 use crate::test_utils::{self, TEST_CONFIG};
207
208 struct DetailAndDocumentation<'a> {
209 detail: &'a str,
210 documentation: &'a str,
211 }
212
213 fn check_detail_and_documentation(ra_fixture: &str, expected: DetailAndDocumentation) {
214 let (db, position) = test_utils::position(ra_fixture);
215 let config = TEST_CONFIG;
216 let completions: Vec<_> = crate::completions(&db, &config, position).unwrap().into();
217 for item in completions {
218 if item.detail() == Some(expected.detail) {
219 let opt = item.documentation();
220 let doc = opt.as_ref().map(|it| it.as_str());
221 assert_eq!(doc, Some(expected.documentation));
222 return;
223 }
224 }
225 panic!("completion detail not found: {}", expected.detail)
226 }
227
228 fn check_no_completion(ra_fixture: &str) {
229 let (db, position) = test_utils::position(ra_fixture);
230 let config = TEST_CONFIG;
231
232 let completions: Option<Vec<String>> = crate::completions(&db, &config, position)
233 .and_then(|completions| {
234 let completions: Vec<_> = completions.into();
235 if completions.is_empty() {
236 None
237 } else {
238 Some(completions)
239 }
240 })
241 .map(|completions| {
242 completions.into_iter().map(|completion| format!("{:?}", completion)).collect()
243 });
244
245 // `assert_eq` instead of `assert!(completions.is_none())` to get the list of completions if test will panic.
246 assert_eq!(completions, None, "Completions were generated, but weren't expected");
247 }
248
249 #[test]
250 fn test_completion_detail_from_macro_generated_struct_fn_doc_attr() {
251 check_detail_and_documentation(
252 r#"
253macro_rules! bar {
254 () => {
255 struct Bar;
256 impl Bar {
257 #[doc = "Do the foo"]
258 fn foo(&self) {}
259 }
260 }
261}
262
263bar!();
264
265fn foo() {
266 let bar = Bar;
267 bar.fo$0;
268}
269"#,
270 DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
271 );
272 }
273
274 #[test]
275 fn test_completion_detail_from_macro_generated_struct_fn_doc_comment() {
276 check_detail_and_documentation(
277 r#"
278macro_rules! bar {
279 () => {
280 struct Bar;
281 impl Bar {
282 /// Do the foo
283 fn foo(&self) {}
284 }
285 }
286}
287
288bar!();
289
290fn foo() {
291 let bar = Bar;
292 bar.fo$0;
293}
294"#,
295 DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" },
296 );
297 }
298
299 #[test]
300 fn test_no_completions_required() {
301 // There must be no hint for 'in' keyword.
302 check_no_completion(r#"fn foo() { for i i$0 }"#);
303 // After 'in' keyword hints may be spawned.
304 check_detail_and_documentation(
305 r#"
306/// Do the foo
307fn foo() -> &'static str { "foo" }
308
309fn bar() {
310 for c in fo$0
311}
312"#,
313 DetailAndDocumentation { detail: "fn() -> &str", documentation: "Do the foo" },
314 );
315 }
316}
diff --git a/crates/ide_completion/src/tests.rs b/crates/ide_completion/src/tests.rs
new file mode 100644
index 000000000..4485a908e
--- /dev/null
+++ b/crates/ide_completion/src/tests.rs
@@ -0,0 +1,64 @@
1mod item_list;
2
3use expect_test::Expect;
4use stdx::format_to;
5
6use crate::{
7 test_utils::{self, get_all_items, TEST_CONFIG},
8 CompletionConfig, CompletionItem,
9};
10
11fn completion_list(code: &str) -> String {
12 completion_list_with_config(TEST_CONFIG, code)
13}
14
15fn completion_list_with_config(config: CompletionConfig, code: &str) -> String {
16 fn monospace_width(s: &str) -> usize {
17 s.chars().count()
18 }
19
20 let kind_completions: Vec<CompletionItem> = get_all_items(config, code).into_iter().collect();
21 let label_width = kind_completions
22 .iter()
23 .map(|it| monospace_width(it.label()))
24 .max()
25 .unwrap_or_default()
26 .min(16);
27 kind_completions
28 .into_iter()
29 .map(|it| {
30 let tag = it.kind().unwrap().tag();
31 let var_name = format!("{} {}", tag, it.label());
32 let mut buf = var_name;
33 if let Some(detail) = it.detail() {
34 let width = label_width.saturating_sub(monospace_width(it.label()));
35 format_to!(buf, "{:width$} {}", "", detail, width = width);
36 }
37 if it.deprecated() {
38 format_to!(buf, " DEPRECATED");
39 }
40 format_to!(buf, "\n");
41 buf
42 })
43 .collect()
44}
45
46fn check(ra_fixture: &str, expect: Expect) {
47 let actual = completion_list(ra_fixture);
48 expect.assert_eq(&actual)
49}
50
51fn check_no_completion(ra_fixture: &str) {
52 let (db, position) = test_utils::position(ra_fixture);
53
54 assert!(
55 crate::completions(&db, &TEST_CONFIG, position).is_none(),
56 "Completions were generated, but weren't expected"
57 );
58}
59
60#[test]
61fn test_no_completions_required() {
62 cov_mark::check!(no_completion_required);
63 check_no_completion(r#"fn foo() { for i i$0 }"#);
64}
diff --git a/crates/ide_completion/src/tests/item_list.rs b/crates/ide_completion/src/tests/item_list.rs
new file mode 100644
index 000000000..bd060a632
--- /dev/null
+++ b/crates/ide_completion/src/tests/item_list.rs
@@ -0,0 +1,172 @@
1use expect_test::expect;
2
3use crate::tests::check;
4
5#[test]
6fn in_mod_item_list() {
7 check(
8 r#"mod tests {
9 $0
10}
11"#,
12 expect![[r#"
13 kw pub(crate)
14 kw pub
15 kw unsafe
16 kw fn
17 kw const
18 kw type
19 kw use
20 kw impl
21 kw trait
22 kw static
23 kw extern
24 kw mod
25 kw enum
26 kw struct
27 kw union
28 sn tmod (Test module)
29 sn tfn (Test function)
30 sn macro_rules
31 "#]],
32 )
33}
34
35#[test]
36fn in_source_file_item_list() {
37 check(
38 r#"
39enum Enum { Variant }
40struct MyStruct {}
41#[macro_export]
42macro_rules! foo {}
43mod bar {}
44const CONST: () = ();
45
46$0"#,
47 expect![[r##"
48 kw pub(crate)
49 kw pub
50 kw unsafe
51 kw fn
52 kw const
53 kw type
54 kw use
55 kw impl
56 kw trait
57 kw static
58 kw extern
59 kw mod
60 kw enum
61 kw struct
62 kw union
63 sn tmod (Test module)
64 sn tfn (Test function)
65 sn macro_rules
66 md bar
67 ma foo!(…) #[macro_export] macro_rules! foo
68 ma foo!(…) #[macro_export] macro_rules! foo
69 "##]],
70 )
71}
72
73#[test]
74fn in_qualified_path() {
75 check(
76 r#"
77enum Enum { Variant }
78struct MyStruct {}
79#[macro_export]
80macro_rules! foo {}
81mod bar {}
82const CONST: () = ();
83
84crate::$0"#,
85 expect![[r##"
86 kw pub(crate)
87 kw pub
88 kw unsafe
89 kw fn
90 kw const
91 kw type
92 kw use
93 kw impl
94 kw trait
95 kw static
96 kw extern
97 kw mod
98 kw enum
99 kw struct
100 kw union
101 sn tmod (Test module)
102 sn tfn (Test function)
103 sn macro_rules
104 md bar
105 ma foo!(…) #[macro_export] macro_rules! foo
106 "##]],
107 )
108}
109
110#[test]
111fn after_unsafe_token() {
112 check(
113 r#"
114enum Enum { Variant }
115struct MyStruct {}
116#[macro_export]
117macro_rules! foo {}
118mod bar {}
119const CONST: () = ();
120
121unsafe $0"#,
122 expect![[r##"
123 kw fn
124 kw trait
125 kw impl
126 sn tmod (Test module)
127 sn tfn (Test function)
128 sn macro_rules
129 md bar
130 ma foo!(…) #[macro_export] macro_rules! foo
131 ma foo!(…) #[macro_export] macro_rules! foo
132 "##]],
133 );
134}
135
136#[test]
137fn after_visibility() {
138 check(
139 r#"
140enum Enum { Variant }
141struct MyStruct {}
142#[macro_export]
143macro_rules! foo {}
144mod bar {}
145const CONST: () = ();
146
147pub $0"#,
148 expect![[r##"
149 kw pub(crate)
150 kw pub
151 kw unsafe
152 kw fn
153 kw const
154 kw type
155 kw use
156 kw impl
157 kw trait
158 kw static
159 kw extern
160 kw mod
161 kw enum
162 kw struct
163 kw union
164 sn tmod (Test module)
165 sn tfn (Test function)
166 sn macro_rules
167 md bar
168 ma foo!(…) #[macro_export] macro_rules! foo
169 ma foo!(…) #[macro_export] macro_rules! foo
170 "##]],
171 );
172}