]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_expand/src/config.rs
New upstream version 1.49.0~beta.4+dfsg1
[rustc.git] / compiler / rustc_expand / src / config.rs
CommitLineData
ba9703b0
XL
1//! Conditional compilation stripping.
2
74b04a01
XL
3use rustc_ast::attr::HasAttrs;
4use rustc_ast::mut_visit::*;
5use rustc_ast::ptr::P;
29967ef6
XL
6use rustc_ast::token::{DelimToken, Token, TokenKind};
7use rustc_ast::tokenstream::{DelimSpan, LazyTokenStream, Spacing, TokenStream, TokenTree};
3dfed10e 8use rustc_ast::{self as ast, AttrItem, Attribute, MetaItem};
74b04a01 9use rustc_attr as attr;
dfeec247 10use rustc_data_structures::fx::FxHashMap;
ba9703b0 11use rustc_data_structures::map_in_place::MapInPlace;
dfeec247
XL
12use rustc_errors::{error_code, struct_span_err, Applicability, Handler};
13use rustc_feature::{Feature, Features, State as FeatureState};
14use rustc_feature::{
15 ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
16};
ba9703b0 17use rustc_parse::{parse_in, validate_attr};
3dfed10e
XL
18use rustc_session::parse::feature_err;
19use rustc_session::Session;
dfeec247
XL
20use rustc_span::edition::{Edition, ALL_EDITIONS};
21use rustc_span::symbol::{sym, Symbol};
22use rustc_span::{Span, DUMMY_SP};
60c5eb7d 23
9fa01778 24use smallvec::SmallVec;
1a4d82fc 25
3157f602
XL
26/// A folder that strips out items that do not belong in the current configuration.
27pub struct StripUnconfigured<'a> {
3dfed10e 28 pub sess: &'a Session,
3157f602 29 pub features: Option<&'a Features>,
1a4d82fc
JJ
30}
31
dfeec247 32fn get_features(
3dfed10e 33 sess: &Session,
dfeec247
XL
34 span_handler: &Handler,
35 krate_attrs: &[ast::Attribute],
dfeec247
XL
36) -> Features {
37 fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
38 let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
39 err.span_label(span, "feature has been removed");
40 if let Some(reason) = reason {
41 err.note(reason);
42 }
43 err.emit();
44 }
45
46 fn active_features_up_to(edition: Edition) -> impl Iterator<Item = &'static Feature> {
47 ACTIVE_FEATURES.iter().filter(move |feature| {
48 if let Some(feature_edition) = feature.edition {
49 feature_edition <= edition
50 } else {
51 false
52 }
53 })
54 }
55
56 let mut features = Features::default();
57 let mut edition_enabled_features = FxHashMap::default();
3dfed10e 58 let crate_edition = sess.edition();
dfeec247
XL
59
60 for &edition in ALL_EDITIONS {
61 if edition <= crate_edition {
62 // The `crate_edition` implies its respective umbrella feature-gate
63 // (i.e., `#![feature(rust_20XX_preview)]` isn't needed on edition 20XX).
64 edition_enabled_features.insert(edition.feature_name(), edition);
65 }
66 }
67
68 for feature in active_features_up_to(crate_edition) {
69 feature.set(&mut features, DUMMY_SP);
70 edition_enabled_features.insert(feature.name, crate_edition);
71 }
72
73 // Process the edition umbrella feature-gates first, to ensure
74 // `edition_enabled_features` is completed before it's queried.
75 for attr in krate_attrs {
3dfed10e 76 if !sess.check_name(attr, sym::feature) {
dfeec247
XL
77 continue;
78 }
79
80 let list = match attr.meta_item_list() {
81 Some(list) => list,
82 None => continue,
83 };
84
85 for mi in list {
86 if !mi.is_word() {
87 continue;
88 }
89
90 let name = mi.name_or_empty();
91
92 let edition = ALL_EDITIONS.iter().find(|e| name == e.feature_name()).copied();
93 if let Some(edition) = edition {
94 if edition <= crate_edition {
95 continue;
96 }
97
98 for feature in active_features_up_to(edition) {
99 // FIXME(Manishearth) there is currently no way to set
100 // lib features by edition
101 feature.set(&mut features, DUMMY_SP);
102 edition_enabled_features.insert(feature.name, edition);
103 }
104 }
105 }
106 }
107
108 for attr in krate_attrs {
3dfed10e 109 if !sess.check_name(attr, sym::feature) {
dfeec247
XL
110 continue;
111 }
112
113 let list = match attr.meta_item_list() {
114 Some(list) => list,
115 None => continue,
116 };
117
118 let bad_input = |span| {
119 struct_span_err!(span_handler, span, E0556, "malformed `feature` attribute input")
120 };
121
122 for mi in list {
123 let name = match mi.ident() {
124 Some(ident) if mi.is_word() => ident.name,
125 Some(ident) => {
126 bad_input(mi.span())
127 .span_suggestion(
128 mi.span(),
129 "expected just one word",
130 format!("{}", ident.name),
131 Applicability::MaybeIncorrect,
132 )
133 .emit();
134 continue;
135 }
136 None => {
137 bad_input(mi.span()).span_label(mi.span(), "expected just one word").emit();
138 continue;
139 }
140 };
141
142 if let Some(edition) = edition_enabled_features.get(&name) {
143 let msg =
144 &format!("the feature `{}` is included in the Rust {} edition", name, edition);
145 span_handler.struct_span_warn_with_code(mi.span(), msg, error_code!(E0705)).emit();
146 continue;
147 }
148
149 if ALL_EDITIONS.iter().any(|e| name == e.feature_name()) {
150 // Handled in the separate loop above.
151 continue;
152 }
153
154 let removed = REMOVED_FEATURES.iter().find(|f| name == f.name);
155 let stable_removed = STABLE_REMOVED_FEATURES.iter().find(|f| name == f.name);
156 if let Some(Feature { state, .. }) = removed.or(stable_removed) {
157 if let FeatureState::Removed { reason } | FeatureState::Stabilized { reason } =
158 state
159 {
160 feature_removed(span_handler, mi.span(), *reason);
161 continue;
162 }
163 }
164
165 if let Some(Feature { since, .. }) = ACCEPTED_FEATURES.iter().find(|f| name == f.name) {
166 let since = Some(Symbol::intern(since));
167 features.declared_lang_features.push((name, mi.span(), since));
168 continue;
169 }
170
3dfed10e 171 if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() {
dfeec247
XL
172 if allowed.iter().find(|&f| name.as_str() == *f).is_none() {
173 struct_span_err!(
174 span_handler,
175 mi.span(),
176 E0725,
177 "the feature `{}` is not in the list of allowed features",
178 name
179 )
180 .emit();
181 continue;
182 }
183 }
184
185 if let Some(f) = ACTIVE_FEATURES.iter().find(|f| name == f.name) {
186 f.set(&mut features, mi.span());
187 features.declared_lang_features.push((name, mi.span(), None));
188 continue;
189 }
190
191 features.declared_lib_features.push((name, mi.span()));
192 }
193 }
194
195 features
196}
197
9e0c209e 198// `cfg_attr`-process the crate's attributes and compute the crate's features.
3dfed10e 199pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) {
74b04a01 200 let mut strip_unconfigured = StripUnconfigured { sess, features: None };
9e0c209e 201
74b04a01 202 let unconfigured_attrs = krate.attrs.clone();
3dfed10e 203 let diag = &sess.parse_sess.span_diagnostic;
74b04a01
XL
204 let err_count = diag.err_count();
205 let features = match strip_unconfigured.configure(krate.attrs) {
206 None => {
207 // The entire crate is unconfigured.
9e0c209e
SL
208 krate.attrs = Vec::new();
209 krate.module.items = Vec::new();
74b04a01 210 Features::default()
9e0c209e 211 }
74b04a01
XL
212 Some(attrs) => {
213 krate.attrs = attrs;
3dfed10e 214 let features = get_features(sess, diag, &krate.attrs);
74b04a01
XL
215 if err_count == diag.err_count() {
216 // Avoid reconfiguring malformed `cfg_attr`s.
217 strip_unconfigured.features = Some(&features);
218 strip_unconfigured.configure(unconfigured_attrs);
219 }
220 features
9e0c209e 221 }
74b04a01 222 };
9e0c209e
SL
223 (krate, features)
224}
225
e74abb32 226#[macro_export]
9e0c209e
SL
227macro_rules! configure {
228 ($this:ident, $node:ident) => {
229 match $this.configure($node) {
230 Some(node) => node,
231 None => return Default::default(),
232 }
dfeec247 233 };
9e0c209e
SL
234}
235
60c5eb7d
XL
236const CFG_ATTR_GRAMMAR_HELP: &str = "#[cfg_attr(condition, attribute, other_attribute, ...)]";
237const CFG_ATTR_NOTE_REF: &str = "for more information, visit \
238 <https://doc.rust-lang.org/reference/conditional-compilation.html\
239 #the-cfg_attr-attribute>";
240
3157f602 241impl<'a> StripUnconfigured<'a> {
9fa01778
XL
242 pub fn configure<T: HasAttrs>(&mut self, mut node: T) -> Option<T> {
243 self.process_cfg_attrs(&mut node);
60c5eb7d 244 self.in_cfg(node.attrs()).then_some(node)
1a4d82fc 245 }
1a4d82fc 246
0bf4aa26
XL
247 /// Parse and expand all `cfg_attr` attributes into a list of attributes
248 /// that are within each `cfg_attr` that has a true configuration predicate.
249 ///
74b04a01 250 /// Gives compiler warnings if any `cfg_attr` does not contain any
0bf4aa26
XL
251 /// attributes and is in the original source code. Gives compiler errors if
252 /// the syntax of any `cfg_attr` is incorrect.
9fa01778
XL
253 pub fn process_cfg_attrs<T: HasAttrs>(&mut self, node: &mut T) {
254 node.visit_attrs(|attrs| {
255 attrs.flat_map_in_place(|attr| self.process_cfg_attr(attr));
256 });
1a4d82fc 257 }
85aaf69f 258
0bf4aa26
XL
259 /// Parse and expand a single `cfg_attr` attribute into a list of attributes
260 /// when the configuration predicate is true, or otherwise expand into an
261 /// empty list of attributes.
262 ///
263 /// Gives a compiler warning when the `cfg_attr` contains no attributes and
264 /// is in the original source file. Gives a compiler error if the syntax of
9fa01778 265 /// the attribute is incorrect.
60c5eb7d
XL
266 fn process_cfg_attr(&mut self, attr: Attribute) -> Vec<Attribute> {
267 if !attr.has_name(sym::cfg_attr) {
0bf4aa26 268 return vec![attr];
85aaf69f
SL
269 }
270
60c5eb7d
XL
271 let (cfg_predicate, expanded_attrs) = match self.parse_cfg_attr(&attr) {
272 None => return vec![],
273 Some(r) => r,
d9579d0f 274 };
9e0c209e 275
dc9dc135
XL
276 // Lint on zero attributes in source.
277 if expanded_attrs.is_empty() {
278 return vec![attr];
0bf4aa26
XL
279 }
280
dc9dc135 281 // At this point we know the attribute is considered used.
3dfed10e 282 self.sess.mark_attr_used(&attr);
dc9dc135 283
3dfed10e 284 if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
60c5eb7d
XL
285 return vec![];
286 }
287
288 // We call `process_cfg_attr` recursively in case there's a
289 // `cfg_attr` inside of another `cfg_attr`. E.g.
290 // `#[cfg_attr(false, cfg_attr(true, some_attr))]`.
291 expanded_attrs
292 .into_iter()
293 .flat_map(|(item, span)| {
29967ef6
XL
294 let orig_tokens = attr.tokens();
295
296 // We are taking an attribute of the form `#[cfg_attr(pred, attr)]`
297 // and producing an attribute of the form `#[attr]`. We
298 // have captured tokens for `attr` itself, but we need to
299 // synthesize tokens for the wrapper `#` and `[]`, which
300 // we do below.
301
302 // Use the `#` in `#[cfg_attr(pred, attr)]` as the `#` token
303 // for `attr` when we expand it to `#[attr]`
304 let pound_token = orig_tokens.trees().next().unwrap();
305 if !matches!(pound_token, TokenTree::Token(Token { kind: TokenKind::Pound, .. })) {
306 panic!("Bad tokens for attribute {:?}", attr);
307 }
308 // We don't really have a good span to use for the syntheized `[]`
309 // in `#[attr]`, so just use the span of the `#` token.
310 let bracket_group = TokenTree::Delimited(
311 DelimSpan::from_single(pound_token.span()),
312 DelimToken::Bracket,
313 item.tokens
314 .as_ref()
315 .unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
316 .create_token_stream(),
317 );
318 let tokens = Some(LazyTokenStream::new(TokenStream::new(vec![
319 (pound_token, Spacing::Alone),
320 (bracket_group, Spacing::Alone),
321 ])));
322
323 self.process_cfg_attr(attr::mk_attr_from_item(item, tokens, attr.style, span))
60c5eb7d 324 })
0bf4aa26 325 .collect()
60c5eb7d
XL
326 }
327
328 fn parse_cfg_attr(&self, attr: &Attribute) -> Option<(MetaItem, Vec<(AttrItem, Span)>)> {
329 match attr.get_normal_item().args {
330 ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
331 let msg = "wrong `cfg_attr` delimiters";
3dfed10e
XL
332 validate_attr::check_meta_bad_delim(&self.sess.parse_sess, dspan, delim, msg);
333 match parse_in(&self.sess.parse_sess, tts.clone(), "`cfg_attr` input", |p| {
334 p.parse_cfg_attr()
335 }) {
60c5eb7d 336 Ok(r) => return Some(r),
74b04a01
XL
337 Err(mut e) => {
338 e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
339 .note(CFG_ATTR_NOTE_REF)
340 .emit();
341 }
60c5eb7d
XL
342 }
343 }
344 _ => self.error_malformed_cfg_attr_missing(attr.span),
85aaf69f 345 }
60c5eb7d
XL
346 None
347 }
348
349 fn error_malformed_cfg_attr_missing(&self, span: Span) {
350 self.sess
3dfed10e 351 .parse_sess
60c5eb7d
XL
352 .span_diagnostic
353 .struct_span_err(span, "malformed `cfg_attr` attribute input")
354 .span_suggestion(
355 span,
356 "missing condition and attribute",
357 CFG_ATTR_GRAMMAR_HELP.to_string(),
358 Applicability::HasPlaceholders,
359 )
360 .note(CFG_ATTR_NOTE_REF)
361 .emit();
85aaf69f
SL
362 }
363
9fa01778 364 /// Determines if a node with the given attributes should be included in this configuration.
60c5eb7d 365 pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
3157f602 366 attrs.iter().all(|attr| {
3dfed10e 367 if !is_cfg(self.sess, attr) {
cc61c64b 368 return true;
8faf50e0 369 }
3dfed10e 370 let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
74b04a01
XL
371 Ok(meta_item) => meta_item,
372 Err(mut err) => {
373 err.emit();
374 return true;
375 }
376 };
8faf50e0 377 let error = |span, msg, suggestion: &str| {
3dfed10e 378 let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
8faf50e0 379 if !suggestion.is_empty() {
9fa01778 380 err.span_suggestion(
0bf4aa26
XL
381 span,
382 "expected syntax is",
383 suggestion.into(),
384 Applicability::MaybeIncorrect,
385 );
8faf50e0
XL
386 }
387 err.emit();
388 true
389 };
74b04a01
XL
390 let span = meta_item.span;
391 match meta_item.meta_item_list() {
392 None => error(span, "`cfg` is not followed by parentheses", "cfg(/* predicate */)"),
393 Some([]) => error(span, "`cfg` predicate is not specified", ""),
394 Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
395 Some([single]) => match single.meta_item() {
3dfed10e
XL
396 Some(meta_item) => {
397 attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
398 }
74b04a01
XL
399 None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
400 },
9e0c209e 401 }
3157f602
XL
402 })
403 }
92a42be0 404
0bf4aa26 405 /// Visit attributes on expression and statements (but not attributes on items in blocks).
60c5eb7d 406 fn visit_expr_attrs(&mut self, attrs: &[Attribute]) {
3157f602
XL
407 // flag the offending attributes
408 for attr in attrs.iter() {
0531ce1d
XL
409 self.maybe_emit_expr_attr_err(attr);
410 }
411 }
412
413 /// If attributes are not allowed on expressions, emit an error for `attr`
60c5eb7d 414 pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
0531ce1d 415 if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
dfeec247 416 let mut err = feature_err(
3dfed10e 417 &self.sess.parse_sess,
dfeec247
XL
418 sym::stmt_expr_attributes,
419 attr.span,
420 "attributes on expressions are experimental",
421 );
0531ce1d 422
60c5eb7d 423 if attr.is_doc_comment() {
0531ce1d 424 err.help("`///` is for documentation comments. For a plain comment, use `//`.");
92a42be0 425 }
0531ce1d
XL
426
427 err.emit();
92a42be0
SL
428 }
429 }
92a42be0 430
9fa01778 431 pub fn configure_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
1b1a35ee 432 let ast::ForeignMod { unsafety: _, abi: _, items } = foreign_mod;
9fa01778 433 items.flat_map_in_place(|item| self.configure(item));
92a42be0
SL
434 }
435
9fa01778 436 fn configure_variant_data(&mut self, vdata: &mut ast::VariantData) {
9e0c209e 437 match vdata {
dfeec247
XL
438 ast::VariantData::Struct(fields, ..) | ast::VariantData::Tuple(fields, _) => {
439 fields.flat_map_in_place(|field| self.configure(field))
440 }
532ac7d7 441 ast::VariantData::Unit(_) => {}
9e0c209e
SL
442 }
443 }
3157f602 444
9fa01778 445 pub fn configure_item_kind(&mut self, item: &mut ast::ItemKind) {
9e0c209e 446 match item {
dfeec247
XL
447 ast::ItemKind::Struct(def, _generics) | ast::ItemKind::Union(def, _generics) => {
448 self.configure_variant_data(def)
449 }
9fa01778
XL
450 ast::ItemKind::Enum(ast::EnumDef { variants }, _generics) => {
451 variants.flat_map_in_place(|variant| self.configure(variant));
452 for variant in variants {
e1599b0c 453 self.configure_variant_data(&mut variant.data);
9fa01778 454 }
3157f602 455 }
9fa01778 456 _ => {}
9e0c209e
SL
457 }
458 }
3157f602 459
9fa01778 460 pub fn configure_expr_kind(&mut self, expr_kind: &mut ast::ExprKind) {
32a655c1 461 match expr_kind {
9fa01778
XL
462 ast::ExprKind::Match(_m, arms) => {
463 arms.flat_map_in_place(|arm| self.configure(arm));
32a655c1 464 }
9fa01778
XL
465 ast::ExprKind::Struct(_path, fields, _base) => {
466 fields.flat_map_in_place(|field| self.configure(field));
32a655c1 467 }
9fa01778 468 _ => {}
9e0c209e 469 }
92a42be0
SL
470 }
471
9fa01778 472 pub fn configure_expr(&mut self, expr: &mut P<ast::Expr>) {
9e0c209e 473 self.visit_expr_attrs(expr.attrs());
3157f602
XL
474
475 // If an expr is valid to cfg away it will have been removed by the
476 // outer stmt or expression folder before descending in here.
477 // Anything else is always required, and thus has to error out
478 // in case of a cfg attr.
479 //
9fa01778
XL
480 // N.B., this is intentionally not part of the visit_expr() function
481 // in order for filter_map_expr() to be able to avoid this check
3dfed10e 482 if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(self.sess, a)) {
3157f602 483 let msg = "removing an expression is not supported in this position";
3dfed10e 484 self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg);
92a42be0 485 }
3157f602 486
9e0c209e 487 self.process_cfg_attrs(expr)
92a42be0
SL
488 }
489
9fa01778 490 pub fn configure_pat(&mut self, pat: &mut P<ast::Pat>) {
e74abb32 491 if let ast::PatKind::Struct(_path, fields, _etc) = &mut pat.kind {
9fa01778
XL
492 fields.flat_map_in_place(|field| self.configure(field));
493 }
32a655c1 494 }
83c7162d 495
dc9dc135
XL
496 pub fn configure_fn_decl(&mut self, fn_decl: &mut ast::FnDecl) {
497 fn_decl.inputs.flat_map_in_place(|arg| self.configure(arg));
83c7162d 498 }
9e0c209e 499}
92a42be0 500
9fa01778
XL
501impl<'a> MutVisitor for StripUnconfigured<'a> {
502 fn visit_foreign_mod(&mut self, foreign_mod: &mut ast::ForeignMod) {
503 self.configure_foreign_mod(foreign_mod);
504 noop_visit_foreign_mod(foreign_mod, self);
92a42be0
SL
505 }
506
9fa01778
XL
507 fn visit_item_kind(&mut self, item: &mut ast::ItemKind) {
508 self.configure_item_kind(item);
509 noop_visit_item_kind(item, self);
9e0c209e
SL
510 }
511
9fa01778
XL
512 fn visit_expr(&mut self, expr: &mut P<ast::Expr>) {
513 self.configure_expr(expr);
e74abb32 514 self.configure_expr_kind(&mut expr.kind);
9fa01778 515 noop_visit_expr(expr, self);
9e0c209e
SL
516 }
517
9fa01778
XL
518 fn filter_map_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
519 let mut expr = configure!(self, expr);
e74abb32 520 self.configure_expr_kind(&mut expr.kind);
9fa01778
XL
521 noop_visit_expr(&mut expr, self);
522 Some(expr)
9e0c209e
SL
523 }
524
1b1a35ee
XL
525 fn flat_map_generic_param(
526 &mut self,
527 param: ast::GenericParam,
528 ) -> SmallVec<[ast::GenericParam; 1]> {
529 noop_flat_map_generic_param(configure!(self, param), self)
530 }
531
9fa01778
XL
532 fn flat_map_stmt(&mut self, stmt: ast::Stmt) -> SmallVec<[ast::Stmt; 1]> {
533 noop_flat_map_stmt(configure!(self, stmt), self)
92a42be0 534 }
92a42be0 535
9fa01778
XL
536 fn flat_map_item(&mut self, item: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
537 noop_flat_map_item(configure!(self, item), self)
3157f602 538 }
92a42be0 539
74b04a01 540 fn flat_map_impl_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
dfeec247 541 noop_flat_map_assoc_item(configure!(self, item), self)
3157f602 542 }
92a42be0 543
74b04a01 544 fn flat_map_trait_item(&mut self, item: P<ast::AssocItem>) -> SmallVec<[P<ast::AssocItem>; 1]> {
dfeec247 545 noop_flat_map_assoc_item(configure!(self, item), self)
92a42be0 546 }
3157f602 547
9fa01778
XL
548 fn visit_pat(&mut self, pat: &mut P<ast::Pat>) {
549 self.configure_pat(pat);
550 noop_visit_pat(pat, self)
32a655c1 551 }
dc9dc135
XL
552
553 fn visit_fn_decl(&mut self, mut fn_decl: &mut P<ast::FnDecl>) {
554 self.configure_fn_decl(&mut fn_decl);
555 noop_visit_fn_decl(fn_decl, self);
556 }
92a42be0
SL
557}
558
3dfed10e
XL
559fn is_cfg(sess: &Session, attr: &Attribute) -> bool {
560 sess.check_name(attr, sym::cfg)
92a42be0 561}