use rustc_hir as hir;
use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;

crate fn collect(tcx: TyCtxt<'_>) -> Vec<String> {
    let mut collector = Collector { tcx, args: Vec::new() };
    tcx.hir().krate().visit_all_item_likes(&mut collector);

    for attr in tcx.hir().krate().item.attrs.iter() {
        if attr.has_name(sym::link_args) {
            if let Some(linkarg) = attr.value_str() {
                collector.add_link_args(linkarg);
            }
        }
    }

    collector.args
}

struct Collector<'tcx> {
    tcx: TyCtxt<'tcx>,
    args: Vec<String>,
}

impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
    fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
        let fm = match it.kind {
            hir::ItemKind::ForeignMod(ref fm) => fm,
            _ => return,
        };
        if fm.abi == Abi::Rust || fm.abi == Abi::RustIntrinsic || fm.abi == Abi::PlatformIntrinsic {
            return;
        }

        // First, add all of the custom #[link_args] attributes
        let sess = &self.tcx.sess;
        for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link_args)) {
            if let Some(linkarg) = m.value_str() {
                self.add_link_args(linkarg);
            }
        }
    }

    fn visit_trait_item(&mut self, _it: &'tcx hir::TraitItem<'tcx>) {}
    fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {}
}

impl<'tcx> Collector<'tcx> {
    fn add_link_args(&mut self, args: Symbol) {
        self.args.extend(args.as_str().split(' ').filter(|s| !s.is_empty()).map(|s| s.to_string()))
    }
}
