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