@@ -24,12 +24,12 @@ impl<S: Stage> AttributeParser<S> for AllowParser {
2424 "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
2525 ) ,
2626 |this, cx, args| {
27- validate_lint_attr ( cx, args, & mut this. lint_ids , & mut this. errored , & mut this. reason ) ;
27+ validate_lint_attr ( cx, args, & mut this. lint_ids , & mut this. errored , & mut this. reason , sym :: allow ) ;
2828 } ,
2929 ) ] ;
3030
3131 fn finalize ( self , _cx : & FinalizeContext < ' _ , ' _ , S > ) -> Option < AttributeKind > {
32- if !self . lint_ids . is_empty ( ) && ! self . errored {
32+ if !self . errored {
3333 Some ( AttributeKind :: Allow { lint_ids : self . lint_ids , reason : self . reason } )
3434 } else {
3535 None
@@ -52,12 +52,12 @@ impl<S: Stage> AttributeParser<S> for DenyParser {
5252 "https://doc.rust-lang.org/reference/attributes/diagnostics.html#lint-check-attributes"
5353 ) ,
5454 |this, cx, args| {
55- validate_lint_attr ( cx, args, & mut this. lint_ids , & mut this. errored , & mut this. reason ) ;
55+ validate_lint_attr ( cx, args, & mut this. lint_ids , & mut this. errored , & mut this. reason , sym :: deny ) ;
5656 } ,
5757 ) ] ;
5858
5959 fn finalize ( self , _cx : & FinalizeContext < ' _ , ' _ , S > ) -> Option < AttributeKind > {
60- if !self . lint_ids . is_empty ( ) && ! self . errored {
60+ if !self . errored {
6161 Some ( AttributeKind :: Deny { lint_ids : self . lint_ids , reason : self . reason } )
6262 } else {
6363 None
@@ -74,20 +74,21 @@ fn validate_lint_attr<S: Stage>(
7474 lint_ids : & mut ThinVec < ( Symbol , Span ) > ,
7575 errored : & mut bool ,
7676 reason : & mut Option < Symbol > ,
77+ attr_name : Symbol ,
7778) {
7879
7980 let Some ( list) = args. list ( ) . map ( MetaItemListParser :: mixed) else {
8081 cx. expected_list ( cx. inner_span , args) ;
8182 return ;
8283 } ;
83-
8484 let mut list = list. peekable ( ) ;
8585
86+ let mut skip_empty_check = false ;
8687 while let Some ( item) = list. next ( ) {
8788 let Some ( meta_item) = item. meta_item ( ) else {
8889 cx. expected_string_literal ( item. span ( ) , item. lit ( ) ) ;
8990 * errored = true ;
90- return ;
91+ continue ;
9192 } ;
9293 if let Some ( args) = meta_item. word_is ( sym:: reason) {
9394 let ArgParser :: NameValue ( nv) = args else {
@@ -96,7 +97,7 @@ fn validate_lint_attr<S: Stage>(
9697 continue ;
9798 } ;
9899 if list. peek ( ) . is_some ( ) {
99- // TODO: proper error
100+ cx . expected_nv_as_last_argument ( nv . args_span ( ) , attr_name , sym :: reason ) ;
100101 * errored = true ;
101102 continue ;
102103 }
@@ -126,9 +127,16 @@ fn validate_lint_attr<S: Stage>(
126127
127128 if let Some ( ids) = check_lint ( cx, & name, tool_name, tool_span, meta_item. span ( ) ) {
128129 lint_ids. extend ( ids) ;
130+ } else {
131+ // Since external tool lints aren't directly checked by rustc, we don't store these ids
132+ // inside the parsed attr, but this still means that something *does* exist in it, so we shouldn't warn
133+ skip_empty_check = true ;
129134 }
130135 }
131136 }
137+ if !skip_empty_check && lint_ids. is_empty ( ) {
138+ cx. warn_empty_attribute ( cx. attr_span ) ;
139+ }
132140}
133141
134142fn check_lint < S : Stage > (
@@ -148,7 +156,6 @@ fn check_lint<S: Stage>(
148156 lint_ids. push ( ( Symbol :: intern ( & id. to_string ( ) ) , meta_item_span) ) ;
149157 }
150158 }
151- // TODO fix tools
152159 CheckLintNameResult :: Tool ( ids, new_lint_name) => {
153160 let _name = match new_lint_name {
154161 None => {
0 commit comments