@@ -25,6 +25,7 @@ use crate::{
2525
2626mod cfg;
2727mod derive;
28+ mod diagnostic;
2829mod lint;
2930mod macro_use;
3031mod repr;
@@ -40,23 +41,22 @@ pub(crate) fn complete_known_attribute_input(
4041 extern_crate : Option < & ast:: ExternCrate > ,
4142) -> Option < ( ) > {
4243 let attribute = fake_attribute_under_caret;
43- let name_ref = match attribute. path ( ) {
44- Some ( p) => Some ( p. as_single_name_ref ( ) ?) ,
45- None => None ,
46- } ;
47- let ( path, tt) = name_ref. zip ( attribute. token_tree ( ) ) ?;
48- tt. l_paren_token ( ) ?;
44+ let path = attribute. path ( ) ?;
45+ let segments = path. segments ( ) . map ( |s| s. name_ref ( ) ) . collect :: < Option < Vec < _ > > > ( ) ?;
46+ let segments = segments. iter ( ) . map ( |n| n. text ( ) ) . collect :: < Vec < _ > > ( ) ;
47+ let segments = segments. iter ( ) . map ( |t| t. as_str ( ) ) . collect :: < Vec < _ > > ( ) ;
48+ let tt = attribute. token_tree ( ) ?;
4949
50- match path . text ( ) . as_str ( ) {
51- "repr" => repr:: complete_repr ( acc, ctx, tt) ,
52- "feature" => lint:: complete_lint (
50+ match segments . as_slice ( ) {
51+ [ "repr" ] => repr:: complete_repr ( acc, ctx, tt) ,
52+ [ "feature" ] => lint:: complete_lint (
5353 acc,
5454 ctx,
5555 colon_prefix,
5656 & parse_tt_as_comma_sep_paths ( tt, ctx. edition ) ?,
5757 FEATURES ,
5858 ) ,
59- "allow" | "expect" | "deny" | "forbid" | "warn" => {
59+ [ "allow" ] | [ "expect" ] | [ "deny" ] | [ "forbid" ] | [ "warn" ] => {
6060 let existing_lints = parse_tt_as_comma_sep_paths ( tt, ctx. edition ) ?;
6161
6262 let lints: Vec < Lint > = CLIPPY_LINT_GROUPS
@@ -70,13 +70,14 @@ pub(crate) fn complete_known_attribute_input(
7070
7171 lint:: complete_lint ( acc, ctx, colon_prefix, & existing_lints, & lints) ;
7272 }
73- "cfg" => cfg:: complete_cfg ( acc, ctx) ,
74- "macro_use" => macro_use:: complete_macro_use (
73+ [ "cfg" ] => cfg:: complete_cfg ( acc, ctx) ,
74+ [ "macro_use" ] => macro_use:: complete_macro_use (
7575 acc,
7676 ctx,
7777 extern_crate,
7878 & parse_tt_as_comma_sep_paths ( tt, ctx. edition ) ?,
7979 ) ,
80+ [ "diagnostic" , "on_unimplemented" ] => diagnostic:: complete_on_unimplemented ( acc, ctx, tt) ,
8081 _ => ( ) ,
8182 }
8283 Some ( ( ) )
@@ -139,6 +140,8 @@ pub(crate) fn complete_attribute_path(
139140 }
140141 Qualified :: TypeAnchor { .. } | Qualified :: With { .. } => { }
141142 }
143+ let qualifier_path =
144+ if let Qualified :: With { path, .. } = qualified { Some ( path) } else { None } ;
142145
143146 let attributes = annotated_item_kind. and_then ( |kind| {
144147 if ast:: Expr :: can_cast ( kind) {
@@ -149,18 +152,33 @@ pub(crate) fn complete_attribute_path(
149152 } ) ;
150153
151154 let add_completion = |attr_completion : & AttrCompletion | {
152- let mut item = CompletionItem :: new (
153- SymbolKind :: Attribute ,
154- ctx. source_range ( ) ,
155- attr_completion. label ,
156- ctx. edition ,
157- ) ;
155+ // if we don't already have the qualifiers of the completion, then
156+ // add the missing parts to the label and snippet
157+ let mut label = attr_completion. label . to_owned ( ) ;
158+ let mut snippet = attr_completion. snippet . map ( |s| s. to_owned ( ) ) ;
159+ let segments = qualifier_path. iter ( ) . flat_map ( |q| q. segments ( ) ) . collect :: < Vec < _ > > ( ) ;
160+ let qualifiers = attr_completion. qualifiers ;
161+ let matching_qualifiers = segments
162+ . iter ( )
163+ . zip ( qualifiers)
164+ . take_while ( |( s, q) | s. name_ref ( ) . is_some_and ( |t| t. text ( ) == * * q) )
165+ . count ( ) ;
166+ if matching_qualifiers != qualifiers. len ( ) {
167+ let prefix = qualifiers[ matching_qualifiers..] . join ( "::" ) ;
168+ label = format ! ( "{prefix}::{label}" ) ;
169+ if let Some ( s) = snippet. as_mut ( ) {
170+ * s = format ! ( "{prefix}::{s}" ) ;
171+ }
172+ }
173+
174+ let mut item =
175+ CompletionItem :: new ( SymbolKind :: Attribute , ctx. source_range ( ) , label, ctx. edition ) ;
158176
159177 if let Some ( lookup) = attr_completion. lookup {
160178 item. lookup_by ( lookup) ;
161179 }
162180
163- if let Some ( ( snippet, cap) ) = attr_completion . snippet . zip ( ctx. config . snippet_cap ) {
181+ if let Some ( ( snippet, cap) ) = snippet. zip ( ctx. config . snippet_cap ) {
164182 item. insert_snippet ( cap, snippet) ;
165183 }
166184
@@ -184,6 +202,7 @@ struct AttrCompletion {
184202 label : & ' static str ,
185203 lookup : Option < & ' static str > ,
186204 snippet : Option < & ' static str > ,
205+ qualifiers : & ' static [ & ' static str ] ,
187206 prefer_inner : bool ,
188207}
189208
@@ -192,6 +211,10 @@ impl AttrCompletion {
192211 self . lookup . unwrap_or ( self . label )
193212 }
194213
214+ const fn qualifiers ( self , qualifiers : & ' static [ & ' static str ] ) -> AttrCompletion {
215+ AttrCompletion { qualifiers, ..self }
216+ }
217+
195218 const fn prefer_inner ( self ) -> AttrCompletion {
196219 AttrCompletion { prefer_inner : true , ..self }
197220 }
@@ -202,7 +225,7 @@ const fn attr(
202225 lookup : Option < & ' static str > ,
203226 snippet : Option < & ' static str > ,
204227) -> AttrCompletion {
205- AttrCompletion { label, lookup, snippet, prefer_inner : false }
228+ AttrCompletion { label, lookup, snippet, qualifiers : & [ ] , prefer_inner : false }
206229}
207230
208231macro_rules! attrs {
@@ -264,14 +287,14 @@ static KIND_TO_ATTRIBUTES: LazyLock<FxHashMap<SyntaxKind, &[&str]>> = LazyLock::
264287 FN ,
265288 attrs ! (
266289 item, linkable,
267- "cold" , "ignore" , "inline" , "must_use" , " panic_handler", "proc_macro" ,
290+ "cold" , "ignore" , "inline" , "panic_handler" , "proc_macro" ,
268291 "proc_macro_derive" , "proc_macro_attribute" , "should_panic" , "target_feature" ,
269292 "test" , "track_caller"
270293 ) ,
271294 ) ,
272295 ( STATIC , attrs ! ( item, linkable, "global_allocator" , "used" ) ) ,
273- ( TRAIT , attrs ! ( item, "must_use " ) ) ,
274- ( IMPL , attrs ! ( item, "automatically_derived" ) ) ,
296+ ( TRAIT , attrs ! ( item, "diagnostic::on_unimplemented " ) ) ,
297+ ( IMPL , attrs ! ( item, "automatically_derived" , "diagnostic::do_not_recommend" ) ) ,
275298 ( ASSOC_ITEM_LIST , attrs ! ( item) ) ,
276299 ( EXTERN_BLOCK , attrs ! ( item, "link" ) ) ,
277300 ( EXTERN_ITEM_LIST , attrs ! ( item, "link" ) ) ,
@@ -311,6 +334,14 @@ const ATTRIBUTES: &[AttrCompletion] = &[
311334 attr ( "deny(…)" , Some ( "deny" ) , Some ( "deny(${0:lint})" ) ) ,
312335 attr ( r#"deprecated"# , Some ( "deprecated" ) , Some ( r#"deprecated"# ) ) ,
313336 attr ( "derive(…)" , Some ( "derive" ) , Some ( r#"derive(${0:Debug})"# ) ) ,
337+ attr ( "do_not_recommend" , Some ( "diagnostic::do_not_recommend" ) , None )
338+ . qualifiers ( & [ "diagnostic" ] ) ,
339+ attr (
340+ "on_unimplemented" ,
341+ Some ( "diagnostic::on_unimplemented" ) ,
342+ Some ( r#"on_unimplemented(${0:keys})"# ) ,
343+ )
344+ . qualifiers ( & [ "diagnostic" ] ) ,
314345 attr ( r#"doc = "…""# , Some ( "doc" ) , Some ( r#"doc = "${0:docs}""# ) ) ,
315346 attr ( r#"doc(alias = "…")"# , Some ( "docalias" ) , Some ( r#"doc(alias = "${0:docs}")"# ) ) ,
316347 attr ( r#"doc(hidden)"# , Some ( "dochidden" ) , Some ( r#"doc(hidden)"# ) ) ,
0 commit comments