@@ -9,9 +9,9 @@ use rustc_middle::ty::query::Providers;
9
9
use rustc_middle:: ty:: TyCtxt ;
10
10
11
11
use rustc_ast:: { ast, AttrStyle , Attribute , Lit , LitKind , NestedMetaItem } ;
12
- use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
12
+ use rustc_data_structures:: fx:: FxHashMap ;
13
13
use rustc_errors:: { pluralize, struct_span_err, Applicability } ;
14
- use rustc_feature:: { AttributeType , BuiltinAttribute , BUILTIN_ATTRIBUTE_MAP } ;
14
+ use rustc_feature:: { AttributeDuplicates , AttributeType , BuiltinAttribute , BUILTIN_ATTRIBUTE_MAP } ;
15
15
use rustc_hir as hir;
16
16
use rustc_hir:: def_id:: LocalDefId ;
17
17
use rustc_hir:: intravisit:: { self , NestedVisitorMap , Visitor } ;
@@ -23,6 +23,7 @@ use rustc_session::lint::builtin::{
23
23
use rustc_session:: parse:: feature_err;
24
24
use rustc_span:: symbol:: { sym, Symbol } ;
25
25
use rustc_span:: { MultiSpan , Span , DUMMY_SP } ;
26
+ use std:: collections:: hash_map:: Entry ;
26
27
27
28
pub ( crate ) fn target_from_impl_item < ' tcx > (
28
29
tcx : TyCtxt < ' tcx > ,
@@ -69,7 +70,7 @@ impl CheckAttrVisitor<'tcx> {
69
70
let mut doc_aliases = FxHashMap :: default ( ) ;
70
71
let mut is_valid = true ;
71
72
let mut specified_inline = None ;
72
- let mut seen = FxHashSet :: default ( ) ;
73
+ let mut seen = FxHashMap :: default ( ) ;
73
74
let attrs = self . tcx . hir ( ) . attrs ( hir_id) ;
74
75
for attr in attrs {
75
76
let attr_is_valid = match attr. name_or_empty ( ) {
@@ -148,6 +149,8 @@ impl CheckAttrVisitor<'tcx> {
148
149
_ => { }
149
150
}
150
151
152
+ let builtin = attr. ident ( ) . and_then ( |ident| BUILTIN_ATTRIBUTE_MAP . get ( & ident. name ) ) ;
153
+
151
154
if hir_id != CRATE_HIR_ID {
152
155
if let Some ( BuiltinAttribute { type_ : AttributeType :: CrateLevel , .. } ) =
153
156
attr. ident ( ) . and_then ( |ident| BUILTIN_ATTRIBUTE_MAP . get ( & ident. name ) )
@@ -165,21 +168,37 @@ impl CheckAttrVisitor<'tcx> {
165
168
}
166
169
}
167
170
168
- // Duplicate attributes
169
- match attr. name_or_empty ( ) {
170
- name @ sym:: macro_use => {
171
- let args = attr. meta_item_list ( ) . unwrap_or_else ( Vec :: new) ;
172
- let args: Vec < _ > = args. iter ( ) . map ( |arg| arg. name_or_empty ( ) ) . collect ( ) ;
173
- if !seen. insert ( ( name, args) ) {
174
- self . tcx . struct_span_lint_hir (
175
- UNUSED_ATTRIBUTES ,
176
- hir_id,
171
+ if let Some ( BuiltinAttribute { duplicates, .. } ) = builtin {
172
+ check_duplicates ( self . tcx , attr, hir_id, * duplicates, & mut seen) ;
173
+ }
174
+
175
+ // Warn on useless empty attributes.
176
+ if matches ! (
177
+ attr. name_or_empty( ) ,
178
+ sym:: macro_use
179
+ | sym:: allow
180
+ | sym:: warn
181
+ | sym:: deny
182
+ | sym:: forbid
183
+ | sym:: feature
184
+ | sym:: repr
185
+ | sym:: target_feature
186
+ ) && attr. meta_item_list ( ) . map_or ( false , |list| list. is_empty ( ) )
187
+ {
188
+ self . tcx . struct_span_lint_hir ( UNUSED_ATTRIBUTES , hir_id, attr. span , |lint| {
189
+ lint. build ( "unused attribute" )
190
+ . span_suggestion (
177
191
attr. span ,
178
- |lint| lint. build ( "unused attribute" ) . emit ( ) ,
179
- ) ;
180
- }
181
- }
182
- _ => { }
192
+ "remove this attribute" ,
193
+ String :: new ( ) ,
194
+ Applicability :: MachineApplicable ,
195
+ )
196
+ . note ( & format ! (
197
+ "attribute `{}` with an empty list has no effect" ,
198
+ attr. name_or_empty( )
199
+ ) )
200
+ . emit ( ) ;
201
+ } ) ;
183
202
}
184
203
}
185
204
@@ -1990,3 +2009,77 @@ fn check_mod_attrs(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
1990
2009
pub ( crate ) fn provide ( providers : & mut Providers ) {
1991
2010
* providers = Providers { check_mod_attrs, ..* providers } ;
1992
2011
}
2012
+
2013
+ fn check_duplicates (
2014
+ tcx : TyCtxt < ' _ > ,
2015
+ attr : & Attribute ,
2016
+ hir_id : HirId ,
2017
+ duplicates : AttributeDuplicates ,
2018
+ seen : & mut FxHashMap < Symbol , Span > ,
2019
+ ) {
2020
+ use AttributeDuplicates :: * ;
2021
+ if matches ! ( duplicates, WarnFollowingWordOnly ) && !attr. is_word ( ) {
2022
+ return ;
2023
+ }
2024
+ match duplicates {
2025
+ DuplicatesOk => { }
2026
+ WarnFollowing | FutureWarnFollowing | WarnFollowingWordOnly | FutureWarnPreceding => {
2027
+ match seen. entry ( attr. name_or_empty ( ) ) {
2028
+ Entry :: Occupied ( mut entry) => {
2029
+ let ( this, other) = if matches ! ( duplicates, FutureWarnPreceding ) {
2030
+ let to_remove = entry. insert ( attr. span ) ;
2031
+ ( to_remove, attr. span )
2032
+ } else {
2033
+ ( attr. span , * entry. get ( ) )
2034
+ } ;
2035
+ tcx. struct_span_lint_hir ( UNUSED_ATTRIBUTES , hir_id, this, |lint| {
2036
+ let mut db = lint. build ( "unused attribute" ) ;
2037
+ db. span_note ( other, "attribute also specified here" ) . span_suggestion (
2038
+ this,
2039
+ "remove this attribute" ,
2040
+ String :: new ( ) ,
2041
+ Applicability :: MachineApplicable ,
2042
+ ) ;
2043
+ if matches ! ( duplicates, FutureWarnFollowing | FutureWarnPreceding ) {
2044
+ db. warn (
2045
+ "this was previously accepted by the compiler but is \
2046
+ being phased out; it will become a hard error in \
2047
+ a future release!",
2048
+ ) ;
2049
+ }
2050
+ db. emit ( ) ;
2051
+ } ) ;
2052
+ }
2053
+ Entry :: Vacant ( entry) => {
2054
+ entry. insert ( attr. span ) ;
2055
+ }
2056
+ }
2057
+ }
2058
+ ErrorFollowing | ErrorPreceding => match seen. entry ( attr. name_or_empty ( ) ) {
2059
+ Entry :: Occupied ( mut entry) => {
2060
+ let ( this, other) = if matches ! ( duplicates, ErrorPreceding ) {
2061
+ let to_remove = entry. insert ( attr. span ) ;
2062
+ ( to_remove, attr. span )
2063
+ } else {
2064
+ ( attr. span , * entry. get ( ) )
2065
+ } ;
2066
+ tcx. sess
2067
+ . struct_span_err (
2068
+ this,
2069
+ & format ! ( "multiple `{}` attributes" , attr. name_or_empty( ) ) ,
2070
+ )
2071
+ . span_note ( other, "attribute also specified here" )
2072
+ . span_suggestion (
2073
+ this,
2074
+ "remove this attribute" ,
2075
+ String :: new ( ) ,
2076
+ Applicability :: MachineApplicable ,
2077
+ )
2078
+ . emit ( ) ;
2079
+ }
2080
+ Entry :: Vacant ( entry) => {
2081
+ entry. insert ( attr. span ) ;
2082
+ }
2083
+ } ,
2084
+ }
2085
+ }
0 commit comments