--- /dev/null
+use rustc_ast::ast;
+use rustc_ast::visit::Visitor;
+use rustc_span::Symbol;
+
+use crate::attr::MetaVisitor;
+use crate::syntux::parser::Parser;
+use crate::syntux::session::ParseSess;
+
+pub(crate) struct ModItem {
+ pub(crate) item: ast::Item,
+}
+
+/// Traverse `cfg_if!` macro and fetch modules.
+pub(crate) struct CfgIfVisitor<'a> {
+ parse_sess: &'a ParseSess,
+ mods: Vec<ModItem>,
+}
+
+impl<'a> CfgIfVisitor<'a> {
+ pub(crate) fn new(parse_sess: &'a ParseSess) -> CfgIfVisitor<'a> {
+ CfgIfVisitor {
+ mods: vec![],
+ parse_sess,
+ }
+ }
+
+ pub(crate) fn mods(self) -> Vec<ModItem> {
+ self.mods
+ }
+}
+
+impl<'a, 'ast: 'a> Visitor<'ast> for CfgIfVisitor<'a> {
+ fn visit_mac_call(&mut self, mac: &'ast ast::MacCall) {
+ match self.visit_mac_inner(mac) {
+ Ok(()) => (),
+ Err(e) => debug!("{}", e),
+ }
+ }
+}
+
+impl<'a, 'ast: 'a> CfgIfVisitor<'a> {
+ fn visit_mac_inner(&mut self, mac: &'ast ast::MacCall) -> Result<(), &'static str> {
+ // Support both:
+ // ```
+ // extern crate cfg_if;
+ // cfg_if::cfg_if! {..}
+ // ```
+ // And:
+ // ```
+ // #[macro_use]
+ // extern crate cfg_if;
+ // cfg_if! {..}
+ // ```
+ match mac.path.segments.first() {
+ Some(first_segment) => {
+ if first_segment.ident.name != Symbol::intern("cfg_if") {
+ return Err("Expected cfg_if");
+ }
+ }
+ None => {
+ return Err("Expected cfg_if");
+ }
+ };
+
+ let items = Parser::parse_cfg_if(self.parse_sess, mac)?;
+ self.mods
+ .append(&mut items.into_iter().map(|item| ModItem { item }).collect());
+
+ Ok(())
+ }
+}
+
+/// Extracts `path = "foo.rs"` from attributes.
+#[derive(Default)]
+pub(crate) struct PathVisitor {
+ /// A list of path defined in attributes.
+ paths: Vec<String>,
+}
+
+impl PathVisitor {
+ pub(crate) fn paths(self) -> Vec<String> {
+ self.paths
+ }
+}
+
+impl<'ast> MetaVisitor<'ast> for PathVisitor {
+ fn visit_meta_name_value(&mut self, meta_item: &'ast ast::MetaItem, lit: &'ast ast::Lit) {
+ if meta_item.has_name(Symbol::intern("path")) && lit.kind.is_str() {
+ self.paths.push(lit_to_str(lit));
+ }
+ }
+}
+
+#[cfg(not(windows))]
+fn lit_to_str(lit: &ast::Lit) -> String {
+ match lit.kind {
+ ast::LitKind::Str(symbol, ..) => symbol.to_string(),
+ _ => unreachable!(),
+ }
+}
+
+#[cfg(windows)]
+fn lit_to_str(lit: &ast::Lit) -> String {
+ match lit.kind {
+ ast::LitKind::Str(symbol, ..) => symbol.as_str().replace("/", "\\"),
+ _ => unreachable!(),
+ }
+}