//! The compiler code necessary to support the cfg! extension, which expands to //! a literal `true` or `false` based on whether the given cfg matches the //! current compilation environment. use rustc_ast::tokenstream::TokenStream; use rustc_ast::{AttrStyle, CRATE_NODE_ID, token}; use rustc_attr_parsing as attr; use rustc_attr_parsing::parser::MetaItemOrLitParser; use rustc_attr_parsing::{ AttributeParser, CFG_TEMPLATE, ParsedDescription, ShouldEmit, parse_cfg_entry, }; use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpanderResult}; use rustc_hir::AttrPath; use rustc_hir::attrs::CfgEntry; use rustc_parse::exp; use rustc_span::{ErrorGuaranteed, Ident, Span}; use crate::errors; pub(crate) fn expand_cfg( cx: &mut ExtCtxt<'_>, sp: Span, tts: TokenStream, ) -> MacroExpanderResult<'static> { let sp = cx.with_def_site_ctxt(sp); ExpandResult::Ready(match parse_cfg(cx, sp, tts) { Ok(cfg) => { let matches_cfg = attr::eval_config_entry( cx.sess, &cfg, cx.current_expansion.lint_node_id, Some(cx.ecfg.features), ShouldEmit::ErrorsAndLints, ) .as_bool(); MacEager::expr(cx.expr_bool(sp, matches_cfg)) } Err(guar) => DummyResult::any(sp, guar), }) } fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result { let mut parser = cx.new_parser_from_tts(tts); if parser.token == token::Eof { return Err(cx.dcx().emit_err(errors::RequiresCfgPattern { span })); } let meta = MetaItemOrLitParser::parse_single(&mut parser, ShouldEmit::ErrorsAndLints) .map_err(|diag| diag.emit())?; let cfg = AttributeParser::parse_single_args( cx.sess, span, span, AttrStyle::Inner, AttrPath { segments: vec![Ident::from_str("cfg")].into_boxed_slice(), span }, ParsedDescription::Macro, span, CRATE_NODE_ID, Some(cx.ecfg.features), ShouldEmit::ErrorsAndLints, &meta, parse_cfg_entry, &CFG_TEMPLATE, )?; let _ = parser.eat(exp!(Comma)); if !parser.eat(exp!(Eof)) { return Err(cx.dcx().emit_err(errors::OneCfgPattern { span })); } Ok(cfg) }