blob: d302a9ede6cb7bd5fde0599ad1ea257f4c4dace4 [file] [log] [blame]
use rustc_ast::ast;
use rustc_ast::visit::Visitor;
use rustc_span::{Symbol, sym};
use tracing::debug;
use crate::attr::MetaVisitor;
use crate::parse::macros::cfg_if::parse_cfg_if;
use crate::parse::macros::cfg_match::parse_cfg_match;
use crate::parse::session::ParseSess;
pub(crate) struct ModItem {
pub(crate) item: ast::Item,
}
/// Traverse `cfg_if!` macro and fetch modules.
pub(crate) struct CfgIfVisitor<'a> {
psess: &'a ParseSess,
mods: Vec<ModItem>,
}
impl<'a> CfgIfVisitor<'a> {
pub(crate) fn new(psess: &'a ParseSess) -> CfgIfVisitor<'a> {
CfgIfVisitor {
mods: vec![],
psess,
}
}
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 = parse_cfg_if(self.psess, mac)?;
self.mods
.append(&mut items.into_iter().map(|item| ModItem { item }).collect());
Ok(())
}
}
/// Traverse `cfg_match!` macro and fetch modules.
pub(crate) struct CfgMatchVisitor<'a> {
psess: &'a ParseSess,
mods: Vec<ModItem>,
}
impl<'a> CfgMatchVisitor<'a> {
pub(crate) fn new(psess: &'a ParseSess) -> CfgMatchVisitor<'a> {
CfgMatchVisitor {
mods: vec![],
psess,
}
}
pub(crate) fn mods(self) -> Vec<ModItem> {
self.mods
}
}
impl<'a, 'ast: 'a> Visitor<'ast> for CfgMatchVisitor<'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> CfgMatchVisitor<'a> {
fn visit_mac_inner(&mut self, mac: &'ast ast::MacCall) -> Result<(), &'static str> {
// Support both:
// ```
// std::cfg_match! {..}
// core::cfg_match! {..}
// ```
// And:
// ```
// use std::cfg_match;
// cfg_match! {..}
// ```
match mac.path.segments.last() {
Some(last_segment) => {
if last_segment.ident.name != Symbol::intern("cfg_match") {
return Err("Expected cfg_match");
}
}
None => {
return Err("Expected cfg_match");
}
};
let items = parse_cfg_match(self.psess, 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::MetaItemLit,
) {
if meta_item.has_name(sym::path) && lit.kind.is_str() {
self.paths.push(meta_item_lit_to_str(lit));
}
}
}
#[cfg(not(windows))]
fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String {
match lit.kind {
ast::LitKind::Str(symbol, ..) => symbol.to_string(),
_ => unreachable!(),
}
}
#[cfg(windows)]
fn meta_item_lit_to_str(lit: &ast::MetaItemLit) -> String {
match lit.kind {
ast::LitKind::Str(symbol, ..) => symbol.as_str().replace("/", "\\"),
_ => unreachable!(),
}
}