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