]> git.lizzy.rs Git - rust.git/blob - crates/ide_completion/src/completions/attribute.rs
Merge #9069
[rust.git] / crates / ide_completion / src / completions / attribute.rs
1 //! Completion for attributes
2 //!
3 //! This module uses a bit of static metadata to provide completions
4 //! for built-in attributes.
5
6 use once_cell::sync::Lazy;
7 use rustc_hash::{FxHashMap, FxHashSet};
8 use syntax::{ast, AstNode, NodeOrToken, SyntaxKind, T};
9
10 use crate::{
11     context::CompletionContext,
12     generated_lint_completions::{CLIPPY_LINTS, FEATURES},
13     item::{CompletionItem, CompletionItemKind, CompletionKind},
14     Completions,
15 };
16
17 mod derive;
18 mod lint;
19 pub(crate) use self::lint::LintCompletion;
20
21 pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
22     let attribute = ctx.attribute_under_caret.as_ref()?;
23     match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) {
24         (Some(path), Some(token_tree)) => match path.text().as_str() {
25             "derive" => derive::complete_derive(acc, ctx, token_tree),
26             "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
27             "allow" | "warn" | "deny" | "forbid" => {
28                 lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS);
29                 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30             }
31             _ => (),
32         },
33         (None, Some(_)) => (),
34         _ => complete_new_attribute(acc, ctx, attribute),
35     }
36     Some(())
37 }
38
39 fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
40     let attribute_annotated_item_kind = attribute.syntax().parent().map(|it| it.kind());
41     let attributes = attribute_annotated_item_kind.and_then(|kind| {
42         if ast::Expr::can_cast(kind) {
43             Some(EXPR_ATTRIBUTES)
44         } else {
45             KIND_TO_ATTRIBUTES.get(&kind).copied()
46         }
47     });
48     let is_inner = attribute.kind() == ast::AttrKind::Inner;
49
50     let add_completion = |attr_completion: &AttrCompletion| {
51         let mut item = CompletionItem::new(
52             CompletionKind::Attribute,
53             ctx.source_range(),
54             attr_completion.label,
55         );
56         item.kind(CompletionItemKind::Attribute);
57
58         if let Some(lookup) = attr_completion.lookup {
59             item.lookup_by(lookup);
60         }
61
62         if let Some((snippet, cap)) = attr_completion.snippet.zip(ctx.config.snippet_cap) {
63             item.insert_snippet(cap, snippet);
64         }
65
66         if is_inner || !attr_completion.prefer_inner {
67             acc.add(item.build());
68         }
69     };
70
71     match attributes {
72         Some(applicable) => applicable
73             .iter()
74             .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok())
75             .flat_map(|idx| ATTRIBUTES.get(idx))
76             .for_each(add_completion),
77         None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
78         None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
79     }
80 }
81
82 struct AttrCompletion {
83     label: &'static str,
84     lookup: Option<&'static str>,
85     snippet: Option<&'static str>,
86     prefer_inner: bool,
87 }
88
89 impl AttrCompletion {
90     fn key(&self) -> &'static str {
91         self.lookup.unwrap_or(self.label)
92     }
93
94     const fn prefer_inner(self) -> AttrCompletion {
95         AttrCompletion { prefer_inner: true, ..self }
96     }
97 }
98
99 const fn attr(
100     label: &'static str,
101     lookup: Option<&'static str>,
102     snippet: Option<&'static str>,
103 ) -> AttrCompletion {
104     AttrCompletion { label, lookup, snippet, prefer_inner: false }
105 }
106
107 macro_rules! attrs {
108     // attributes applicable to all items
109     [@ { item $($tt:tt)* } {$($acc:tt)*}] => {
110         attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" })
111     };
112     // attributes applicable to all adts
113     [@ { adt $($tt:tt)* } {$($acc:tt)*}] => {
114         attrs!(@ { $($tt)* } { $($acc)*, "derive", "repr" })
115     };
116     // attributes applicable to all linkable things aka functions/statics
117     [@ { linkable $($tt:tt)* } {$($acc:tt)*}] => {
118         attrs!(@ { $($tt)* } { $($acc)*, "export_name", "link_name", "link_section" })
119     };
120     // error fallback for nicer error message
121     [@ { $ty:ident $($tt:tt)* } {$($acc:tt)*}] => {
122         compile_error!(concat!("unknown attr subtype ", stringify!($ty)))
123     };
124     // general push down accumulation
125     [@ { $lit:literal $($tt:tt)*} {$($acc:tt)*}] => {
126         attrs!(@ { $($tt)* } { $($acc)*, $lit })
127     };
128     [@ {$($tt:tt)+} {$($tt2:tt)*}] => {
129         compile_error!(concat!("Unexpected input ", stringify!($($tt)+)))
130     };
131     // final output construction
132     [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ };
133     // starting matcher
134     [$($tt:tt),*] => {
135         attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" })
136     };
137 }
138
139 #[rustfmt::skip]
140 static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
141     use SyntaxKind::*;
142     std::array::IntoIter::new([
143         (
144             SOURCE_FILE,
145             attrs!(
146                 item,
147                 "crate_name", "feature", "no_implicit_prelude", "no_main", "no_std",
148                 "recursion_limit", "type_length_limit", "windows_subsystem"
149             ),
150         ),
151         (MODULE, attrs!(item, "no_implicit_prelude", "path")),
152         (ITEM_LIST, attrs!(item, "no_implicit_prelude")),
153         (MACRO_RULES, attrs!(item, "macro_export", "macro_use")),
154         (MACRO_DEF, attrs!(item)),
155         (EXTERN_CRATE, attrs!(item, "macro_use", "no_link")),
156         (USE, attrs!(item)),
157         (TYPE_ALIAS, attrs!(item)),
158         (STRUCT, attrs!(item, adt, "non_exhaustive")),
159         (ENUM, attrs!(item, adt, "non_exhaustive")),
160         (UNION, attrs!(item, adt)),
161         (CONST, attrs!(item)),
162         (
163             FN,
164             attrs!(
165                 item, linkable,
166                 "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro",
167                 "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature",
168                 "test", "track_caller"
169             ),
170         ),
171         (STATIC, attrs!(item, linkable, "global_allocator", "used")),
172         (TRAIT, attrs!(item, "must_use")),
173         (IMPL, attrs!(item, "automatically_derived")),
174         (ASSOC_ITEM_LIST, attrs!(item)),
175         (EXTERN_BLOCK, attrs!(item, "link")),
176         (EXTERN_ITEM_LIST, attrs!(item, "link")),
177         (MACRO_CALL, attrs!()),
178         (SELF_PARAM, attrs!()),
179         (PARAM, attrs!()),
180         (RECORD_FIELD, attrs!()),
181         (VARIANT, attrs!("non_exhaustive")),
182         (TYPE_PARAM, attrs!()),
183         (CONST_PARAM, attrs!()),
184         (LIFETIME_PARAM, attrs!()),
185         (LET_STMT, attrs!()),
186         (EXPR_STMT, attrs!()),
187         (LITERAL, attrs!()),
188         (RECORD_EXPR_FIELD_LIST, attrs!()),
189         (RECORD_EXPR_FIELD, attrs!()),
190         (MATCH_ARM_LIST, attrs!()),
191         (MATCH_ARM, attrs!()),
192         (IDENT_PAT, attrs!()),
193         (RECORD_PAT_FIELD, attrs!()),
194     ])
195     .collect()
196 });
197 const EXPR_ATTRIBUTES: &[&str] = attrs!();
198
199 /// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
200 // Keep these sorted for the binary search!
201 const ATTRIBUTES: &[AttrCompletion] = &[
202     attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
203     attr("automatically_derived", None, None),
204     attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
205     attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
206     attr("cold", None, None),
207     attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
208         .prefer_inner(),
209     attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
210     attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
211     attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
212     attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
213     attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
214     attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
215     attr(
216         r#"export_name = "…""#,
217         Some("export_name"),
218         Some(r#"export_name = "${0:exported_symbol_name}""#),
219     ),
220     attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
221     attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
222     attr("global_allocator", None, None),
223     attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
224     attr("inline", Some("inline"), Some("inline")),
225     attr("link", None, None),
226     attr(r#"link_name = "…""#, Some("link_name"), Some(r#"link_name = "${0:symbol_name}""#)),
227     attr(
228         r#"link_section = "…""#,
229         Some("link_section"),
230         Some(r#"link_section = "${0:section_name}""#),
231     ),
232     attr("macro_export", None, None),
233     attr("macro_use", None, None),
234     attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
235     attr("no_implicit_prelude", None, None).prefer_inner(),
236     attr("no_link", None, None).prefer_inner(),
237     attr("no_main", None, None).prefer_inner(),
238     attr("no_mangle", None, None),
239     attr("no_std", None, None).prefer_inner(),
240     attr("non_exhaustive", None, None),
241     attr("panic_handler", None, None),
242     attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
243     attr("proc_macro", None, None),
244     attr("proc_macro_attribute", None, None),
245     attr("proc_macro_derive(…)", Some("proc_macro_derive"), Some("proc_macro_derive(${0:Trait})")),
246     attr("recursion_limit = …", Some("recursion_limit"), Some("recursion_limit = ${0:128}"))
247         .prefer_inner(),
248     attr("repr(…)", Some("repr"), Some("repr(${0:C})")),
249     attr("should_panic", Some("should_panic"), Some(r#"should_panic"#)),
250     attr(
251         r#"target_feature = "…""#,
252         Some("target_feature"),
253         Some(r#"target_feature = "${0:feature}""#),
254     ),
255     attr("test", None, None),
256     attr("track_caller", None, None),
257     attr("type_length_limit = …", Some("type_length_limit"), Some("type_length_limit = ${0:128}"))
258         .prefer_inner(),
259     attr("used", None, None),
260     attr("warn(…)", Some("warn"), Some("warn(${0:lint})")),
261     attr(
262         r#"windows_subsystem = "…""#,
263         Some("windows_subsystem"),
264         Some(r#"windows_subsystem = "${0:subsystem}""#),
265     )
266     .prefer_inner(),
267 ];
268
269 fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
270     let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
271     let mut input_derives = FxHashSet::default();
272     let mut tokens = derive_input
273         .syntax()
274         .children_with_tokens()
275         .filter_map(NodeOrToken::into_token)
276         .skip_while(|token| token != &l_paren)
277         .skip(1)
278         .take_while(|token| token != &r_paren)
279         .peekable();
280     let mut input = String::new();
281     while tokens.peek().is_some() {
282         for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) {
283             input.push_str(token.text());
284         }
285
286         if !input.is_empty() {
287             input_derives.insert(input.trim().to_owned());
288         }
289
290         input.clear();
291     }
292
293     Some(input_derives)
294 }
295
296 #[cfg(test)]
297 mod tests {
298     use super::*;
299
300     use expect_test::{expect, Expect};
301
302     use crate::{test_utils::completion_list, CompletionKind};
303
304     #[test]
305     fn attributes_are_sorted() {
306         let mut attrs = ATTRIBUTES.iter().map(|attr| attr.key());
307         let mut prev = attrs.next().unwrap();
308
309         attrs.for_each(|next| {
310             assert!(
311                 prev < next,
312                 r#"ATTRIBUTES array is not sorted, "{}" should come after "{}""#,
313                 prev,
314                 next
315             );
316             prev = next;
317         });
318     }
319
320     fn check(ra_fixture: &str, expect: Expect) {
321         let actual = completion_list(ra_fixture, CompletionKind::Attribute);
322         expect.assert_eq(&actual);
323     }
324
325     #[test]
326     fn test_attribute_completion_inside_nested_attr() {
327         check(r#"#[cfg($0)]"#, expect![[]])
328     }
329
330     #[test]
331     fn test_attribute_completion_with_existing_attr() {
332         check(
333             r#"#[no_mangle] #[$0] mcall!();"#,
334             expect![[r#"
335                 at allow(…)
336                 at cfg(…)
337                 at cfg_attr(…)
338                 at deny(…)
339                 at forbid(…)
340                 at warn(…)
341             "#]],
342         )
343     }
344
345     #[test]
346     fn complete_attribute_on_source_file() {
347         check(
348             r#"#![$0]"#,
349             expect![[r#"
350                 at allow(…)
351                 at cfg(…)
352                 at cfg_attr(…)
353                 at deny(…)
354                 at forbid(…)
355                 at warn(…)
356                 at deprecated
357                 at doc = "…"
358                 at doc(hidden)
359                 at doc(alias = "…")
360                 at must_use
361                 at no_mangle
362                 at crate_name = ""
363                 at feature(…)
364                 at no_implicit_prelude
365                 at no_main
366                 at no_std
367                 at recursion_limit = …
368                 at type_length_limit = …
369                 at windows_subsystem = "…"
370             "#]],
371         );
372     }
373
374     #[test]
375     fn complete_attribute_on_module() {
376         check(
377             r#"#[$0] mod foo;"#,
378             expect![[r#"
379             at allow(…)
380             at cfg(…)
381             at cfg_attr(…)
382             at deny(…)
383             at forbid(…)
384             at warn(…)
385             at deprecated
386             at doc = "…"
387             at doc(hidden)
388             at doc(alias = "…")
389             at must_use
390             at no_mangle
391             at path = "…"
392         "#]],
393         );
394         check(
395             r#"mod foo {#![$0]}"#,
396             expect![[r#"
397                 at allow(…)
398                 at cfg(…)
399                 at cfg_attr(…)
400                 at deny(…)
401                 at forbid(…)
402                 at warn(…)
403                 at deprecated
404                 at doc = "…"
405                 at doc(hidden)
406                 at doc(alias = "…")
407                 at must_use
408                 at no_mangle
409                 at no_implicit_prelude
410             "#]],
411         );
412     }
413
414     #[test]
415     fn complete_attribute_on_macro_rules() {
416         check(
417             r#"#[$0] macro_rules! foo {}"#,
418             expect![[r#"
419                 at allow(…)
420                 at cfg(…)
421                 at cfg_attr(…)
422                 at deny(…)
423                 at forbid(…)
424                 at warn(…)
425                 at deprecated
426                 at doc = "…"
427                 at doc(hidden)
428                 at doc(alias = "…")
429                 at must_use
430                 at no_mangle
431                 at macro_export
432                 at macro_use
433             "#]],
434         );
435     }
436
437     #[test]
438     fn complete_attribute_on_macro_def() {
439         check(
440             r#"#[$0] macro foo {}"#,
441             expect![[r#"
442                 at allow(…)
443                 at cfg(…)
444                 at cfg_attr(…)
445                 at deny(…)
446                 at forbid(…)
447                 at warn(…)
448                 at deprecated
449                 at doc = "…"
450                 at doc(hidden)
451                 at doc(alias = "…")
452                 at must_use
453                 at no_mangle
454             "#]],
455         );
456     }
457
458     #[test]
459     fn complete_attribute_on_extern_crate() {
460         check(
461             r#"#[$0] extern crate foo;"#,
462             expect![[r#"
463                 at allow(…)
464                 at cfg(…)
465                 at cfg_attr(…)
466                 at deny(…)
467                 at forbid(…)
468                 at warn(…)
469                 at deprecated
470                 at doc = "…"
471                 at doc(hidden)
472                 at doc(alias = "…")
473                 at must_use
474                 at no_mangle
475                 at macro_use
476             "#]],
477         );
478     }
479
480     #[test]
481     fn complete_attribute_on_use() {
482         check(
483             r#"#[$0] use foo;"#,
484             expect![[r#"
485                 at allow(…)
486                 at cfg(…)
487                 at cfg_attr(…)
488                 at deny(…)
489                 at forbid(…)
490                 at warn(…)
491                 at deprecated
492                 at doc = "…"
493                 at doc(hidden)
494                 at doc(alias = "…")
495                 at must_use
496                 at no_mangle
497             "#]],
498         );
499     }
500
501     #[test]
502     fn complete_attribute_on_type_alias() {
503         check(
504             r#"#[$0] type foo = ();"#,
505             expect![[r#"
506                 at allow(…)
507                 at cfg(…)
508                 at cfg_attr(…)
509                 at deny(…)
510                 at forbid(…)
511                 at warn(…)
512                 at deprecated
513                 at doc = "…"
514                 at doc(hidden)
515                 at doc(alias = "…")
516                 at must_use
517                 at no_mangle
518             "#]],
519         );
520     }
521
522     #[test]
523     fn complete_attribute_on_struct() {
524         check(
525             r#"#[$0] struct Foo;"#,
526             expect![[r#"
527                 at allow(…)
528                 at cfg(…)
529                 at cfg_attr(…)
530                 at deny(…)
531                 at forbid(…)
532                 at warn(…)
533                 at deprecated
534                 at doc = "…"
535                 at doc(hidden)
536                 at doc(alias = "…")
537                 at must_use
538                 at no_mangle
539                 at derive(…)
540                 at repr(…)
541                 at non_exhaustive
542             "#]],
543         );
544     }
545
546     #[test]
547     fn complete_attribute_on_enum() {
548         check(
549             r#"#[$0] enum Foo {}"#,
550             expect![[r#"
551                 at allow(…)
552                 at cfg(…)
553                 at cfg_attr(…)
554                 at deny(…)
555                 at forbid(…)
556                 at warn(…)
557                 at deprecated
558                 at doc = "…"
559                 at doc(hidden)
560                 at doc(alias = "…")
561                 at must_use
562                 at no_mangle
563                 at derive(…)
564                 at repr(…)
565                 at non_exhaustive
566             "#]],
567         );
568     }
569
570     #[test]
571     fn complete_attribute_on_const() {
572         check(
573             r#"#[$0] const FOO: () = ();"#,
574             expect![[r#"
575                 at allow(…)
576                 at cfg(…)
577                 at cfg_attr(…)
578                 at deny(…)
579                 at forbid(…)
580                 at warn(…)
581                 at deprecated
582                 at doc = "…"
583                 at doc(hidden)
584                 at doc(alias = "…")
585                 at must_use
586                 at no_mangle
587             "#]],
588         );
589     }
590
591     #[test]
592     fn complete_attribute_on_static() {
593         check(
594             r#"#[$0] static FOO: () = ()"#,
595             expect![[r#"
596                 at allow(…)
597                 at cfg(…)
598                 at cfg_attr(…)
599                 at deny(…)
600                 at forbid(…)
601                 at warn(…)
602                 at deprecated
603                 at doc = "…"
604                 at doc(hidden)
605                 at doc(alias = "…")
606                 at must_use
607                 at no_mangle
608                 at export_name = "…"
609                 at link_name = "…"
610                 at link_section = "…"
611                 at global_allocator
612                 at used
613             "#]],
614         );
615     }
616
617     #[test]
618     fn complete_attribute_on_trait() {
619         check(
620             r#"#[$0] trait Foo {}"#,
621             expect![[r#"
622                 at allow(…)
623                 at cfg(…)
624                 at cfg_attr(…)
625                 at deny(…)
626                 at forbid(…)
627                 at warn(…)
628                 at deprecated
629                 at doc = "…"
630                 at doc(hidden)
631                 at doc(alias = "…")
632                 at must_use
633                 at no_mangle
634                 at must_use
635             "#]],
636         );
637     }
638
639     #[test]
640     fn complete_attribute_on_impl() {
641         check(
642             r#"#[$0] impl () {}"#,
643             expect![[r#"
644                 at allow(…)
645                 at cfg(…)
646                 at cfg_attr(…)
647                 at deny(…)
648                 at forbid(…)
649                 at warn(…)
650                 at deprecated
651                 at doc = "…"
652                 at doc(hidden)
653                 at doc(alias = "…")
654                 at must_use
655                 at no_mangle
656                 at automatically_derived
657             "#]],
658         );
659         check(
660             r#"impl () {#![$0]}"#,
661             expect![[r#"
662                 at allow(…)
663                 at cfg(…)
664                 at cfg_attr(…)
665                 at deny(…)
666                 at forbid(…)
667                 at warn(…)
668                 at deprecated
669                 at doc = "…"
670                 at doc(hidden)
671                 at doc(alias = "…")
672                 at must_use
673                 at no_mangle
674             "#]],
675         );
676     }
677
678     #[test]
679     fn complete_attribute_on_extern_block() {
680         check(
681             r#"#[$0] extern {}"#,
682             expect![[r#"
683                 at allow(…)
684                 at cfg(…)
685                 at cfg_attr(…)
686                 at deny(…)
687                 at forbid(…)
688                 at warn(…)
689                 at deprecated
690                 at doc = "…"
691                 at doc(hidden)
692                 at doc(alias = "…")
693                 at must_use
694                 at no_mangle
695                 at link
696             "#]],
697         );
698         check(
699             r#"extern {#![$0]}"#,
700             expect![[r#"
701                 at allow(…)
702                 at cfg(…)
703                 at cfg_attr(…)
704                 at deny(…)
705                 at forbid(…)
706                 at warn(…)
707                 at deprecated
708                 at doc = "…"
709                 at doc(hidden)
710                 at doc(alias = "…")
711                 at must_use
712                 at no_mangle
713                 at link
714             "#]],
715         );
716     }
717
718     #[test]
719     fn complete_attribute_on_variant() {
720         check(
721             r#"enum Foo { #[$0] Bar }"#,
722             expect![[r#"
723                 at allow(…)
724                 at cfg(…)
725                 at cfg_attr(…)
726                 at deny(…)
727                 at forbid(…)
728                 at warn(…)
729                 at non_exhaustive
730             "#]],
731         );
732     }
733
734     #[test]
735     fn complete_attribute_on_fn() {
736         check(
737             r#"#[$0] fn main() {}"#,
738             expect![[r#"
739                 at allow(…)
740                 at cfg(…)
741                 at cfg_attr(…)
742                 at deny(…)
743                 at forbid(…)
744                 at warn(…)
745                 at deprecated
746                 at doc = "…"
747                 at doc(hidden)
748                 at doc(alias = "…")
749                 at must_use
750                 at no_mangle
751                 at export_name = "…"
752                 at link_name = "…"
753                 at link_section = "…"
754                 at cold
755                 at ignore = "…"
756                 at inline
757                 at must_use
758                 at panic_handler
759                 at proc_macro
760                 at proc_macro_derive(…)
761                 at proc_macro_attribute
762                 at should_panic
763                 at target_feature = "…"
764                 at test
765                 at track_caller
766             "#]],
767         );
768     }
769
770     #[test]
771     fn complete_attribute_on_expr() {
772         check(
773             r#"fn main() { #[$0] foo() }"#,
774             expect![[r#"
775                 at allow(…)
776                 at cfg(…)
777                 at cfg_attr(…)
778                 at deny(…)
779                 at forbid(…)
780                 at warn(…)
781             "#]],
782         );
783     }
784 }