9
9
// except according to those terms.
10
10
11
11
use attr:: HasAttrs ;
12
- use feature_gate:: { feature_err, EXPLAIN_STMT_ATTR_SYNTAX , Features , get_features, GateIssue } ;
12
+ use feature_gate:: {
13
+ feature_err,
14
+ EXPLAIN_STMT_ATTR_SYNTAX ,
15
+ Features ,
16
+ get_features,
17
+ GateIssue ,
18
+ emit_feature_err,
19
+ } ;
13
20
use { fold, attr} ;
14
21
use ast;
15
22
use source_map:: Spanned ;
@@ -97,6 +104,13 @@ impl<'a> StripUnconfigured<'a> {
97
104
return vec ! [ attr] ;
98
105
}
99
106
107
+ let gate_cfg_attr_multi = if let Some ( ref features) = self . features {
108
+ !features. cfg_attr_multi
109
+ } else {
110
+ false
111
+ } ;
112
+ let cfg_attr_span = attr. span ;
113
+
100
114
let ( cfg_predicate, expanded_attrs) = match attr. parse ( self . sess , |parser| {
101
115
parser. expect ( & token:: OpenDelim ( token:: Paren ) ) ?;
102
116
@@ -123,6 +137,26 @@ impl<'a> StripUnconfigured<'a> {
123
137
}
124
138
} ;
125
139
140
+ // Check feature gate and lint on zero attributes in source. Even if the feature is gated,
141
+ // we still compute as if it wasn't, since the emitted error will stop compilation futher
142
+ // along the compilation.
143
+ match ( expanded_attrs. len ( ) , gate_cfg_attr_multi) {
144
+ ( 0 , false ) => {
145
+ // FIXME: Emit unused attribute lint here.
146
+ } ,
147
+ ( 1 , _) => { } ,
148
+ ( _, true ) => {
149
+ emit_feature_err (
150
+ self . sess ,
151
+ "cfg_attr_multi" ,
152
+ cfg_attr_span,
153
+ GateIssue :: Language ,
154
+ "cfg_attr with zero or more than one attributes is experimental" ,
155
+ ) ;
156
+ } ,
157
+ ( _, false ) => { }
158
+ }
159
+
126
160
if attr:: cfg_matches ( & cfg_predicate, self . sess , self . features ) {
127
161
// We call `process_cfg_attr` recursively in case there's a
128
162
// `cfg_attr` inside of another `cfg_attr`. E.g.
0 commit comments