<commit_before>//! The Rust AST Visitor. Extracts useful information and massages it into a form
//! usable for `clean`.

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::Node;
use rustc_middle::middle::privacy::AccessLevel;
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{self, Span};

use std::mem;

use crate::clean::{self, AttributesExt, NestedAttributesExt};
use crate::core;
use crate::doctree::*;

// FIXME: Should this be replaced with tcx.def_path_str?
fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<String> {
    let crate_name = tcx.crate_name(did.krate).to_string();
    let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| {
        // extern blocks have an empty name
        let s = elem.data.to_string();
        if !s.is_empty() { Some(s) } else { None }
    });
    std::iter::once(crate_name).chain(relative).collect()
}

crate fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool {
    while let Some(id) = tcx.hir().get_enclosing_scope(node) {
        node = id;
        if tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) {
            return true;
        }
    }
    false
}

// Also, is there some reason that this doesn't use the 'visit'
// framework from syntax?.

crate struct RustdocVisitor<'a, 'tcx> {
    cx: &'a mut core::DocContext<'tcx>,
    view_item_stack: FxHashSet<hir::HirId>,
    inlining: bool,
    /// Are the current module and all of its parents public?
    inside_public_path: bool,
    exact_paths: FxHashMap<DefId, Vec<String>>,
}

impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
    crate fn new(cx: &'a mut core::DocContext<'tcx>) -> RustdocVisitor<'a, 'tcx> {
        // If the root is re-exported, terminate all recursion.
        let mut stack = FxHashSet::default();
        stack.insert(hir::CRATE_HIR_ID);
        RustdocVisitor {
            cx,
            view_item_stack: stack,
            inlining: false,
            inside_public_path: true,
            exact_paths: FxHashMap::default(),
        }
    }

    fn store_path(&mut self, did: DefId) {
        let tcx = self.cx.tcx;
        self.exact_paths.entry(did).or_insert_with(|| def_id_to_path(tcx, did));
    }

    crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> {
        let mut top_level_module = self.visit_mod_contents(
            krate.item.inner,
            &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Public },
            hir::CRATE_HIR_ID,
            &krate.item,
            self.cx.tcx.crate_name,
        );
        top_level_module.is_crate = true;
        // Attach the crate's exported macros to the top-level module.
        // In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as
        // well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by
        // moving them back to their correct locations.
        'exported_macros: for def in krate.exported_macros {
            // The `def` of a macro in `exported_macros` should correspond to either:
            //  - a `#[macro_export] macro_rules!` macro,
            //  - a built-in `derive` (or attribute) macro such as the ones in `::core`,
            //  - a `pub macro`.
            // Only the last two need to be fixed, thus:
            if def.ast.macro_rules {
                top_level_module.macros.push((def, None));
                continue 'exported_macros;
            }
            let tcx = self.cx.tcx;
            // Note: this is not the same as `.parent_module()`. Indeed, the latter looks
            // for the closest module _ancestor_, which is not necessarily a direct parent
            // (since a direct parent isn't necessarily a module, c.f. #77828).
            let macro_parent_def_id = {
                use rustc_middle::ty::DefIdTree;
                tcx.parent(def.def_id.to_def_id()).unwrap()
            };
            let macro_parent_path = tcx.def_path(macro_parent_def_id);
            // HACK: rustdoc has no way to lookup `doctree::Module`s by their HirId. Instead,
            // lookup the module by its name, by looking at each path segment one at a time.
            let mut cur_mod = &mut top_level_module;
            for path_segment in macro_parent_path.data {
                // Path segments may refer to a module (in which case they belong to the type
                // namespace), which is _necessary_ for the macro to be accessible outside it
                // (no "associated macros" as of yet). Else we bail with an outer `continue`.
                let path_segment_ty_ns = match path_segment.data {
                    rustc_hir::definitions::DefPathData::TypeNs(symbol) => symbol,
                    _ => continue 'exported_macros,
                };
                // Descend into the child module that matches this path segment (if any).
                match cur_mod.mods.iter_mut().find(|child| child.name == path_segment_ty_ns) {
                    Some(child_mod) => cur_mod = &mut *child_mod,
                    None => continue 'exported_macros,
                }
            }
            let cur_mod_def_id = tcx.hir().local_def_id(cur_mod.id).to_def_id();
            assert_eq!(cur_mod_def_id, macro_parent_def_id);
            cur_mod.macros.push((def, None));
        }
        self.cx.cache.exact_paths = self.exact_paths;
        top_level_module
    }

    fn visit_mod_contents(
        &mut self,
        span: Span,
        vis: &'tcx hir::Visibility<'_>,
        id: hir::HirId,
        m: &'tcx hir::Mod<'tcx>,
        name: Symbol,
    ) -> Module<'tcx> {
        let mut om = Module::new(name);
        om.where_outer = span;
        om.where_inner = m.inner;
        om.id = id;
        // Keep track of if there were any private modules in the path.
        let orig_inside_public_path = self.inside_public_path;
        self.inside_public_path &= vis.node.is_pub();
        for &i in m.item_ids {
            let item = self.cx.tcx.hir().item(i);
            self.visit_item(item, None, &mut om);
        }
        self.inside_public_path = orig_inside_public_path;
        om
    }

    /// Tries to resolve the target of a `crate use` statement and inlines the
    /// target if it is defined locally and would not be documented otherwise,
    /// or when it is specifically requested with `please_inline`.
    /// (the latter is the case when the import is marked `doc(inline)`)
    ///
    /// Cross-crate inlining occurs later on during crate cleaning
    /// and follows different rules.
    ///
    /// Returns `true` if the target has been inlined.
    fn maybe_inline_local(
        &mut self,
        id: hir::HirId,
        res: Res,
        renamed: Option<Symbol>,
        glob: bool,
        om: &mut Module<'tcx>,
        please_inline: bool,
    ) -> bool {
        debug!("maybe_inline_local res: {:?}", res);

        let tcx = self.cx.tcx;
        let res_did = if let Some(did) = res.opt_def_id() {
            did
        } else {
            return false;
        };

        let use_attrs = tcx.hir().attrs(id);
        // Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
        let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
            || use_attrs.lists(sym::doc).has_word(sym::hidden);

        // For cross-crate impl inlining we need to know whether items are
        // reachable in documentation -- a previously nonreachable item can be
        // made reachable by cross-crate inlining which we're checking here.
        // (this is done here because we need to know this upfront).
        if !res_did.is_local() && !is_no_inline {
            let attrs = clean::inline::load_attrs(self.cx, res_did);
            let self_is_hidden = attrs.lists(sym::doc).has_word(sym::hidden);
            if !self_is_hidden {
                if let Res::Def(kind, did) = res {
                    if kind == DefKind::Mod {
                        crate::visit_lib::LibEmbargoVisitor::new(self.cx).visit_mod(did)
                    } else {
                        // All items need to be handled here in case someone wishes to link
                        // to them with intra-doc links
                        self.cx.cache.access_levels.map.insert(did, AccessLevel::Public);
                    }
                }
            }
            return false;
        }

        let res_hir_id = match res_did.as_local() {
            Some(n) => tcx.hir().local_def_id_to_hir_id(n),
            None => return false,
        };

        let is_private = !self.cx.cache.access_levels.is_public(res_did);
        let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id);

        // Only inline if requested or if the item would otherwise be stripped.
        if (!please_inline && !is_private && !is_hidden) || is_no_inline {
            return false;
        }

        if !self.view_item_stack.insert(res_hir_id) {
            return false;
        }

        let ret = match tcx.hir().get(res_hir_id) {
            Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
                let prev = mem::replace(&mut self.inlining, true);
                for &i in m.item_ids {
                    let i = self.cx.tcx.hir().item(i);
                    self.visit_item(i, None, om);
                }
                self.inlining = prev;
                true
            }
            Node::Item(it) if !glob => {
                let prev = mem::replace(&mut self.inlining, true);
                self.visit_item(it, renamed, om);
                self.inlining = prev;
                true
            }
            Node::ForeignItem(it) if !glob => {
                let prev = mem::replace(&mut self.inlining, true);
                self.visit_foreign_item(it, renamed, om);
                self.inlining = prev;
                true
            }
            Node::MacroDef(def) if !glob => {
                om.macros.push((def, renamed));
                true
            }
            _ => false,
        };
        self.view_item_stack.remove(&res_hir_id);
        ret
    }

    fn visit_item(
        &mut self,
        item: &'tcx hir::Item<'_>,
        renamed: Option<Symbol>,
        om: &mut Module<'tcx>,
    ) {
        debug!("visiting item {:?}", item);
        let name = renamed.unwrap_or(item.ident.name);

        if item.vis.node.is_pub() {
            self.store_path(item.def_id.to_def_id());
        }

        match item.kind {
            hir::ItemKind::ForeignMod { items, .. } => {
                for item in items {
                    let item = self.cx.tcx.hir().foreign_item(item.id);
                    self.visit_foreign_item(item, None, om);
                }
            }
            // If we're inlining, skip private items.
            _ if self.inlining && !item.vis.node.is_pub() => {}
            hir::ItemKind::GlobalAsm(..) => {}
            hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
            hir::ItemKind::Use(ref path, kind) => {
                let is_glob = kind == hir::UseKind::Glob;

                // Struct and variant constructors and proc macro stubs always show up alongside
                // their definitions, we've already processed them so just discard these.
                if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
                    return;
                }

                let attrs = self.cx.tcx.hir().attrs(item.hir_id());

                // If there was a private module in the current path then don't bother inlining
                // anything as it will probably be stripped anyway.
                if item.vis.node.is_pub() && self.inside_public_path {
                    let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
                        Some(ref list) if item.has_name(sym::doc) => {
                            list.iter().any(|i| i.has_name(sym::inline))
                        }
                        _ => false,
                    });
                    let ident = if is_glob { None } else { Some(name) };
                    if self.maybe_inline_local(
                        item.hir_id(),
                        path.res,
                        ident,
                        is_glob,
                        om,
                        please_inline,
                    ) {
                        return;
                    }
                }

                om.items.push((item, renamed))
            }
            hir::ItemKind::Mod(ref m) => {
                om.mods.push(self.visit_mod_contents(item.span, &item.vis, item.hir_id(), m, name));
            }
            hir::ItemKind::Fn(..)
            | hir::ItemKind::ExternCrate(..)
            | hir::ItemKind::Enum(..)
            | hir::ItemKind::Struct(..)
            | hir::ItemKind::Union(..)
            | hir::ItemKind::TyAlias(..)
            | hir::ItemKind::OpaqueTy(..)
            | hir::ItemKind::Static(..)
            | hir::ItemKind::Trait(..)
            | hir::ItemKind::TraitAlias(..) => om.items.push((item, renamed)),
            hir::ItemKind::Const(..) => {
                // Underscore constants do not correspond to a nameable item and
                // so are never useful in documentation.
                if name != kw::Underscore {
                    om.items.push((item, renamed));
                }
            }
            hir::ItemKind::Impl(ref impl_) => {
                // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
                // them up regardless of where they're located.
                if !self.inlining && impl_.of_trait.is_none() {
                    om.items.push((item, None));
                }
            }
        }
    }

    fn visit_foreign_item(
        &mut self,
        item: &'tcx hir::ForeignItem<'_>,
        renamed: Option<Symbol>,
        om: &mut Module<'tcx>,
    ) {
        // If inlining we only want to include public functions.
        if !self.inlining || item.vis.node.is_pub() {
            om.foreigns.push((item, renamed));
        }
    }
}
<commit_msg>Rollup merge of #84492 - jyn514:span, r=camelid<commit_after>//! The Rust AST Visitor. Extracts useful information and massages it into a form
//! usable for `clean`.

use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::Node;
use rustc_middle::middle::privacy::AccessLevel;
use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_span::{self, Span};

use std::mem;

use crate::clean::{self, AttributesExt, NestedAttributesExt};
use crate::core;
use crate::doctree::*;

// FIXME: Should this be replaced with tcx.def_path_str?
fn def_id_to_path(tcx: TyCtxt<'_>, did: DefId) -> Vec<String> {
    let crate_name = tcx.crate_name(did.krate).to_string();
    let relative = tcx.def_path(did).data.into_iter().filter_map(|elem| {
        // extern blocks have an empty name
        let s = elem.data.to_string();
        if !s.is_empty() { Some(s) } else { None }
    });
    std::iter::once(crate_name).chain(relative).collect()
}

crate fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool {
    while let Some(id) = tcx.hir().get_enclosing_scope(node) {
        node = id;
        if tcx.hir().attrs(node).lists(sym::doc).has_word(sym::hidden) {
            return true;
        }
    }
    false
}

// Also, is there some reason that this doesn't use the 'visit'
// framework from syntax?.

crate struct RustdocVisitor<'a, 'tcx> {
    cx: &'a mut core::DocContext<'tcx>,
    view_item_stack: FxHashSet<hir::HirId>,
    inlining: bool,
    /// Are the current module and all of its parents public?
    inside_public_path: bool,
    exact_paths: FxHashMap<DefId, Vec<String>>,
}

impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
    crate fn new(cx: &'a mut core::DocContext<'tcx>) -> RustdocVisitor<'a, 'tcx> {
        // If the root is re-exported, terminate all recursion.
        let mut stack = FxHashSet::default();
        stack.insert(hir::CRATE_HIR_ID);
        RustdocVisitor {
            cx,
            view_item_stack: stack,
            inlining: false,
            inside_public_path: true,
            exact_paths: FxHashMap::default(),
        }
    }

    fn store_path(&mut self, did: DefId) {
        let tcx = self.cx.tcx;
        self.exact_paths.entry(did).or_insert_with(|| def_id_to_path(tcx, did));
    }

    crate fn visit(mut self, krate: &'tcx hir::Crate<'_>) -> Module<'tcx> {
        let span = krate.item.inner;
        let mut top_level_module = self.visit_mod_contents(
            span,
            &Spanned { span, node: hir::VisibilityKind::Public },
            hir::CRATE_HIR_ID,
            &krate.item,
            self.cx.tcx.crate_name,
        );
        top_level_module.is_crate = true;
        // Attach the crate's exported macros to the top-level module.
        // In the case of macros 2.0 (`pub macro`), and for built-in `derive`s or attributes as
        // well (_e.g._, `Copy`), these are wrongly bundled in there too, so we need to fix that by
        // moving them back to their correct locations.
        'exported_macros: for def in krate.exported_macros {
            // The `def` of a macro in `exported_macros` should correspond to either:
            //  - a `#[macro_export] macro_rules!` macro,
            //  - a built-in `derive` (or attribute) macro such as the ones in `::core`,
            //  - a `pub macro`.
            // Only the last two need to be fixed, thus:
            if def.ast.macro_rules {
                top_level_module.macros.push((def, None));
                continue 'exported_macros;
            }
            let tcx = self.cx.tcx;
            // Note: this is not the same as `.parent_module()`. Indeed, the latter looks
            // for the closest module _ancestor_, which is not necessarily a direct parent
            // (since a direct parent isn't necessarily a module, c.f. #77828).
            let macro_parent_def_id = {
                use rustc_middle::ty::DefIdTree;
                tcx.parent(def.def_id.to_def_id()).unwrap()
            };
            let macro_parent_path = tcx.def_path(macro_parent_def_id);
            // HACK: rustdoc has no way to lookup `doctree::Module`s by their HirId. Instead,
            // lookup the module by its name, by looking at each path segment one at a time.
            let mut cur_mod = &mut top_level_module;
            for path_segment in macro_parent_path.data {
                // Path segments may refer to a module (in which case they belong to the type
                // namespace), which is _necessary_ for the macro to be accessible outside it
                // (no "associated macros" as of yet). Else we bail with an outer `continue`.
                let path_segment_ty_ns = match path_segment.data {
                    rustc_hir::definitions::DefPathData::TypeNs(symbol) => symbol,
                    _ => continue 'exported_macros,
                };
                // Descend into the child module that matches this path segment (if any).
                match cur_mod.mods.iter_mut().find(|child| child.name == path_segment_ty_ns) {
                    Some(child_mod) => cur_mod = &mut *child_mod,
                    None => continue 'exported_macros,
                }
            }
            let cur_mod_def_id = tcx.hir().local_def_id(cur_mod.id).to_def_id();
            assert_eq!(cur_mod_def_id, macro_parent_def_id);
            cur_mod.macros.push((def, None));
        }
        self.cx.cache.exact_paths = self.exact_paths;
        top_level_module
    }

    fn visit_mod_contents(
        &mut self,
        span: Span,
        vis: &hir::Visibility<'_>,
        id: hir::HirId,
        m: &'tcx hir::Mod<'tcx>,
        name: Symbol,
    ) -> Module<'tcx> {
        let mut om = Module::new(name);
        om.where_outer = span;
        om.where_inner = m.inner;
        om.id = id;
        // Keep track of if there were any private modules in the path.
        let orig_inside_public_path = self.inside_public_path;
        self.inside_public_path &= vis.node.is_pub();
        for &i in m.item_ids {
            let item = self.cx.tcx.hir().item(i);
            self.visit_item(item, None, &mut om);
        }
        self.inside_public_path = orig_inside_public_path;
        om
    }

    /// Tries to resolve the target of a `crate use` statement and inlines the
    /// target if it is defined locally and would not be documented otherwise,
    /// or when it is specifically requested with `please_inline`.
    /// (the latter is the case when the import is marked `doc(inline)`)
    ///
    /// Cross-crate inlining occurs later on during crate cleaning
    /// and follows different rules.
    ///
    /// Returns `true` if the target has been inlined.
    fn maybe_inline_local(
        &mut self,
        id: hir::HirId,
        res: Res,
        renamed: Option<Symbol>,
        glob: bool,
        om: &mut Module<'tcx>,
        please_inline: bool,
    ) -> bool {
        debug!("maybe_inline_local res: {:?}", res);

        let tcx = self.cx.tcx;
        let res_did = if let Some(did) = res.opt_def_id() {
            did
        } else {
            return false;
        };

        let use_attrs = tcx.hir().attrs(id);
        // Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
        let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
            || use_attrs.lists(sym::doc).has_word(sym::hidden);

        // For cross-crate impl inlining we need to know whether items are
        // reachable in documentation -- a previously nonreachable item can be
        // made reachable by cross-crate inlining which we're checking here.
        // (this is done here because we need to know this upfront).
        if !res_did.is_local() && !is_no_inline {
            let attrs = clean::inline::load_attrs(self.cx, res_did);
            let self_is_hidden = attrs.lists(sym::doc).has_word(sym::hidden);
            if !self_is_hidden {
                if let Res::Def(kind, did) = res {
                    if kind == DefKind::Mod {
                        crate::visit_lib::LibEmbargoVisitor::new(self.cx).visit_mod(did)
                    } else {
                        // All items need to be handled here in case someone wishes to link
                        // to them with intra-doc links
                        self.cx.cache.access_levels.map.insert(did, AccessLevel::Public);
                    }
                }
            }
            return false;
        }

        let res_hir_id = match res_did.as_local() {
            Some(n) => tcx.hir().local_def_id_to_hir_id(n),
            None => return false,
        };

        let is_private = !self.cx.cache.access_levels.is_public(res_did);
        let is_hidden = inherits_doc_hidden(self.cx.tcx, res_hir_id);

        // Only inline if requested or if the item would otherwise be stripped.
        if (!please_inline && !is_private && !is_hidden) || is_no_inline {
            return false;
        }

        if !self.view_item_stack.insert(res_hir_id) {
            return false;
        }

        let ret = match tcx.hir().get(res_hir_id) {
            Node::Item(&hir::Item { kind: hir::ItemKind::Mod(ref m), .. }) if glob => {
                let prev = mem::replace(&mut self.inlining, true);
                for &i in m.item_ids {
                    let i = self.cx.tcx.hir().item(i);
                    self.visit_item(i, None, om);
                }
                self.inlining = prev;
                true
            }
            Node::Item(it) if !glob => {
                let prev = mem::replace(&mut self.inlining, true);
                self.visit_item(it, renamed, om);
                self.inlining = prev;
                true
            }
            Node::ForeignItem(it) if !glob => {
                let prev = mem::replace(&mut self.inlining, true);
                self.visit_foreign_item(it, renamed, om);
                self.inlining = prev;
                true
            }
            Node::MacroDef(def) if !glob => {
                om.macros.push((def, renamed));
                true
            }
            _ => false,
        };
        self.view_item_stack.remove(&res_hir_id);
        ret
    }

    fn visit_item(
        &mut self,
        item: &'tcx hir::Item<'_>,
        renamed: Option<Symbol>,
        om: &mut Module<'tcx>,
    ) {
        debug!("visiting item {:?}", item);
        let name = renamed.unwrap_or(item.ident.name);

        if item.vis.node.is_pub() {
            self.store_path(item.def_id.to_def_id());
        }

        match item.kind {
            hir::ItemKind::ForeignMod { items, .. } => {
                for item in items {
                    let item = self.cx.tcx.hir().foreign_item(item.id);
                    self.visit_foreign_item(item, None, om);
                }
            }
            // If we're inlining, skip private items.
            _ if self.inlining && !item.vis.node.is_pub() => {}
            hir::ItemKind::GlobalAsm(..) => {}
            hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
            hir::ItemKind::Use(ref path, kind) => {
                let is_glob = kind == hir::UseKind::Glob;

                // Struct and variant constructors and proc macro stubs always show up alongside
                // their definitions, we've already processed them so just discard these.
                if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
                    return;
                }

                let attrs = self.cx.tcx.hir().attrs(item.hir_id());

                // If there was a private module in the current path then don't bother inlining
                // anything as it will probably be stripped anyway.
                if item.vis.node.is_pub() && self.inside_public_path {
                    let please_inline = attrs.iter().any(|item| match item.meta_item_list() {
                        Some(ref list) if item.has_name(sym::doc) => {
                            list.iter().any(|i| i.has_name(sym::inline))
                        }
                        _ => false,
                    });
                    let ident = if is_glob { None } else { Some(name) };
                    if self.maybe_inline_local(
                        item.hir_id(),
                        path.res,
                        ident,
                        is_glob,
                        om,
                        please_inline,
                    ) {
                        return;
                    }
                }

                om.items.push((item, renamed))
            }
            hir::ItemKind::Mod(ref m) => {
                om.mods.push(self.visit_mod_contents(item.span, &item.vis, item.hir_id(), m, name));
            }
            hir::ItemKind::Fn(..)
            | hir::ItemKind::ExternCrate(..)
            | hir::ItemKind::Enum(..)
            | hir::ItemKind::Struct(..)
            | hir::ItemKind::Union(..)
            | hir::ItemKind::TyAlias(..)
            | hir::ItemKind::OpaqueTy(..)
            | hir::ItemKind::Static(..)
            | hir::ItemKind::Trait(..)
            | hir::ItemKind::TraitAlias(..) => om.items.push((item, renamed)),
            hir::ItemKind::Const(..) => {
                // Underscore constants do not correspond to a nameable item and
                // so are never useful in documentation.
                if name != kw::Underscore {
                    om.items.push((item, renamed));
                }
            }
            hir::ItemKind::Impl(ref impl_) => {
                // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
                // them up regardless of where they're located.
                if !self.inlining && impl_.of_trait.is_none() {
                    om.items.push((item, None));
                }
            }
        }
    }

    fn visit_foreign_item(
        &mut self,
        item: &'tcx hir::ForeignItem<'_>,
        renamed: Option<Symbol>,
        om: &mut Module<'tcx>,
    ) {
        // If inlining we only want to include public functions.
        if !self.inlining || item.vis.node.is_pub() {
            om.foreigns.push((item, renamed));
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use mem;
use slice;

pub fn hashmap_random_keys() -> (u64, u64) {
    let mut v = (0, 0);
    unsafe {
        let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8,
                                             mem::size_of_val(&v));
        imp::fill_bytes(view);
    }
    return v
}

#[cfg(all(unix,
          not(target_os = "ios"),
          not(target_os = "openbsd"),
          not(target_os = "freebsd"),
          not(target_os = "fuchsia")))]
mod imp {
    use fs::File;
    use io::Read;
    use libc;
    use sys::os::errno;

    #[cfg(any(target_os = "linux", target_os = "android"))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        unsafe {
            libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
        }
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    fn getrandom(_buf: &mut [u8]) -> libc::c_long { -1 }

    fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
        let mut read = 0;
        while read < v.len() {
            let result = getrandom(&mut v[read..]);
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else if err == libc::EAGAIN {
                    return false
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as usize;
            }
        }

        return true
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    fn is_getrandom_available() -> bool {
        use io;
        use sync::atomic::{AtomicBool, Ordering};
        use sync::Once;

        static CHECKER: Once = Once::new();
        static AVAILABLE: AtomicBool = AtomicBool::new(false);

        CHECKER.call_once(|| {
            let mut buf: [u8; 0] = [];
            let result = getrandom(&mut buf);
            let available = if result == -1 {
                let err = io::Error::last_os_error().raw_os_error();
                err != Some(libc::ENOSYS)
            } else {
                true
            };
            AVAILABLE.store(available, Ordering::Relaxed);
        });

        AVAILABLE.load(Ordering::Relaxed)
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    fn is_getrandom_available() -> bool { false }

    pub fn fill_bytes(v: &mut [u8]) {
        // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
        // meaning it would have blocked because the non-blocking pool (urandom)
        // has not initialized in the kernel yet due to a lack of entropy the
        // fallback we do here is to avoid blocking applications which could
        // depend on this call without ever knowing they do and don't have a
        // work around.  The PRNG of /dev/urandom will still be used but not
        // over a completely full entropy pool
        if is_getrandom_available() && getrandom_fill_bytes(v) {
            return
        }

        let mut file = File::open("/dev/urandom")
            .expect("failed to open /dev/urandom");
        file.read_exact(v).expect("failed to read /dev/urandom");
    }
}

#[cfg(target_os = "openbsd")]
mod imp {
    use libc;
    use sys::os::errno;

    pub fn fill_bytes(v: &mut [u8]) {
        // getentropy(2) permits a maximum buffer size of 256 bytes
        for s in v.chunks_mut(256) {
            let ret = unsafe {
                libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
            };
            if ret == -1 {
                panic!("unexpected getentropy error: {}", errno());
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    use io;
    use libc::{c_int, size_t};
    use ptr;

    enum SecRandom {}

    #[allow(non_upper_case_globals)]
    const kSecRandomDefault: *const SecRandom = ptr::null();

    extern {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t,
                              bytes: *mut u8) -> c_int;
    }

    pub fn fill_bytes(v: &mut [u8]) {
        let ret = unsafe {
            SecRandomCopyBytes(kSecRandomDefault,
                               v.len(),
                               v.as_mut_ptr())
        };
        if ret == -1 {
            panic!("couldn't generate random bytes: {}",
                   io::Error::last_os_error());
        }
    }
}

#[cfg(target_os = "freebsd")]
mod imp {
    use libc;
    use ptr;

    pub fn fill_bytes(v: &mut [u8]) {
        let mib = [libc::CTL_KERN, libc::KERN_ARND];
        // kern.arandom permits a maximum buffer size of 256 bytes
        for s in v.chunks_mut(256) {
            let mut s_len = s.len();
            let ret = unsafe {
                libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
                             s.as_mut_ptr() as *mut _, &mut s_len,
                             ptr::null(), 0)
            };
            if ret == -1 || s_len != s.len() {
                panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
                       ret, s.len(), s_len);
            }
        }
    }
}

#[cfg(target_os = "fuchsia")]
mod imp {
    #[link(name = "zircon")]
    extern {
        fn zx_cprng_draw(buffer: *mut u8, len: usize);
    }

    pub fn fill_bytes(v: &mut [u8]) {
        unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }
    }
}
<commit_msg>Reduce number of syscalls in `rand`<commit_after>// Copyright 2013-2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use mem;
use slice;

pub fn hashmap_random_keys() -> (u64, u64) {
    let mut v = (0, 0);
    unsafe {
        let view = slice::from_raw_parts_mut(&mut v as *mut _ as *mut u8,
                                             mem::size_of_val(&v));
        imp::fill_bytes(view);
    }
    return v
}

#[cfg(all(unix,
          not(target_os = "ios"),
          not(target_os = "openbsd"),
          not(target_os = "freebsd"),
          not(target_os = "fuchsia")))]
mod imp {
    use fs::File;
    use io::Read;
    use libc;
    use sync::atomic::{AtomicBool, AtomicI32, Ordering};
    use sys::os::errno;

    static GETRANDOM_URANDOM_FD: AtomicI32 = AtomicI32::new(-1);
    #[cfg(any(target_os = "linux", target_os = "android"))]
    static GETRANDOM_UNAVAILABLE: AtomicBool = AtomicBool::new(false);

    #[cfg(any(target_os = "linux", target_os = "android"))]
    fn is_getrandom_permanently_unavailable() -> bool {
        GETRANDOM_UNAVAILABLE.load(Ordering::Relaxed)
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    fn is_getrandom_permanently_unavailable() -> bool {
        true
    }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    fn getrandom(buf: &mut [u8]) -> libc::c_long {
        unsafe {
            libc::syscall(libc::SYS_getrandom, buf.as_mut_ptr(), buf.len(), libc::GRND_NONBLOCK)
        }
    }

    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    fn getrandom_fill_bytes(_buf: &mut [u8]) -> bool { false }

    #[cfg(any(target_os = "linux", target_os = "android"))]
    fn getrandom_fill_bytes(v: &mut [u8]) -> bool {
        if is_getrandom_permanently_unavailable() {
            return false;
        }

        let mut read = 0;
        while read < v.len() {
            let result = getrandom(&mut v[read..]);
            if result == -1 {
                let err = errno() as libc::c_int;
                if err == libc::EINTR {
                    continue;
                } else if err == libc::ENOSYS {
                    GETRANDOM_UNAVAILABLE.store(true, Ordering::Relaxed);
                } else if err == libc::EAGAIN {
                    return false;
                } else {
                    panic!("unexpected getrandom error: {}", err);
                }
            } else {
                read += result as usize;
            }
        }
        true
    }

    pub fn fill_bytes(v: &mut [u8]) {
        // getrandom_fill_bytes here can fail if getrandom() returns EAGAIN,
        // meaning it would have blocked because the non-blocking pool (urandom)
        // has not initialized in the kernel yet due to a lack of entropy. The
        // fallback we do here is to avoid blocking applications which could
        // depend on this call without ever knowing they do and don't have a
        // work around. The PRNG of /dev/urandom will still be used but over a
        // possibly predictable entropy pool.
        if getrandom_fill_bytes(v) {
            return;
        }

        // getrandom failed for some reason. If the getrandom call is
        // permanently unavailable (OS without getrandom, or OS version without
        // getrandom), we'll keep around the fd for /dev/urandom for future
        // requests, to avoid re-opening the file on every call.
        //
        // Otherwise, open /dev/urandom, read from it, and close it again.
        use super::super::ext::io::{FromRawFd, IntoRawFd};
        let mut fd = GETRANDOM_URANDOM_FD.load(Ordering::Relaxed);
        let mut close_fd = false;
        if fd == -1 {
            if !is_getrandom_permanently_unavailable() {
                close_fd = true;
            }
            let file = File::open("/dev/urandom").expect("failed to open /dev/urandom");
            fd = file.into_raw_fd();
            // If some other thread also opened /dev/urandom and set the global
            // fd already, we close our fd at the end of the function.
            if !close_fd && GETRANDOM_URANDOM_FD.compare_and_swap(-1, fd, Ordering::Relaxed) != -1 {
                close_fd = true;
            }
        }
        let mut file = unsafe { File::from_raw_fd(fd) };
        let res = file.read_exact(v);
        if !close_fd {
            let _ = file.into_raw_fd();
        }
        res.expect("failed to read /dev/urandom");
    }
}

#[cfg(target_os = "openbsd")]
mod imp {
    use libc;
    use sys::os::errno;

    pub fn fill_bytes(v: &mut [u8]) {
        // getentropy(2) permits a maximum buffer size of 256 bytes
        for s in v.chunks_mut(256) {
            let ret = unsafe {
                libc::getentropy(s.as_mut_ptr() as *mut libc::c_void, s.len())
            };
            if ret == -1 {
                panic!("unexpected getentropy error: {}", errno());
            }
        }
    }
}

#[cfg(target_os = "ios")]
mod imp {
    use io;
    use libc::{c_int, size_t};
    use ptr;

    enum SecRandom {}

    #[allow(non_upper_case_globals)]
    const kSecRandomDefault: *const SecRandom = ptr::null();

    extern {
        fn SecRandomCopyBytes(rnd: *const SecRandom,
                              count: size_t,
                              bytes: *mut u8) -> c_int;
    }

    pub fn fill_bytes(v: &mut [u8]) {
        let ret = unsafe {
            SecRandomCopyBytes(kSecRandomDefault,
                               v.len(),
                               v.as_mut_ptr())
        };
        if ret == -1 {
            panic!("couldn't generate random bytes: {}",
                   io::Error::last_os_error());
        }
    }
}

#[cfg(target_os = "freebsd")]
mod imp {
    use libc;
    use ptr;

    pub fn fill_bytes(v: &mut [u8]) {
        let mib = [libc::CTL_KERN, libc::KERN_ARND];
        // kern.arandom permits a maximum buffer size of 256 bytes
        for s in v.chunks_mut(256) {
            let mut s_len = s.len();
            let ret = unsafe {
                libc::sysctl(mib.as_ptr(), mib.len() as libc::c_uint,
                             s.as_mut_ptr() as *mut _, &mut s_len,
                             ptr::null(), 0)
            };
            if ret == -1 || s_len != s.len() {
                panic!("kern.arandom sysctl failed! (returned {}, s.len() {}, oldlenp {})",
                       ret, s.len(), s_len);
            }
        }
    }
}

#[cfg(target_os = "fuchsia")]
mod imp {
    #[link(name = "zircon")]
    extern {
        fn zx_cprng_draw(buffer: *mut u8, len: usize);
    }

    pub fn fill_bytes(v: &mut [u8]) {
        unsafe { zx_cprng_draw(v.as_mut_ptr(), v.len()) }
    }
}
<|endoftext|>


<commit_before>#![macro_use]

// For testing purposes, we want to generate valid `Ast`s,
//  but `ast!` is clunky and makes it *super* easy to leave off `Quote*` and `ExtendEnv`
//  and actually running the parser is a big dependency (and requires huge string literals).
//  so we introduce a `u!` macro that looks up forms but infers the internal structure.

// It's weird to be relying on the grammar while ignoring parts of it, hence "flimsy",
//  but errors are much more likely to be compile-time than inscrutable test problems.

// It's not unsafe to use `u!` for runtime operations, but there's a runtime cost, so don't do it.

use crate::{
    ast::Ast::{self, *},
    grammar::FormPat,
    name::*,
    util::mbe::EnvMBE,
};
use std::iter::{Iterator, Peekable};

// First, transforms from `[a b c; d e f; g h i]` to `[g h i] {[a b c] [d e f]}`
//   to get around a Rust macro parsing restriction,
//  then makes a REP flimsy shape for it.
macro_rules! u_rep {
    ([; $( $ts:tt )*]   $acc_cur:tt { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [] {  $( $acc_rest )*  $acc_cur })
    };
    ([$t:tt $( $ts:tt )*]  [ $( $acc_cur:tt )* ] { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [$($acc_cur)* $t] { $( $acc_rest )* })
    };
    ([] [] {}) => {
        // Empty repeat
        crate::ast::Shape(vec![crate::ast::Atom(n("REP"))])
    };
    ([]  [ $( $acc_cur:tt )* ] { $( [ $( $acc_rest:tt )* ] )* }) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("REP")),
            $( u_shape_if_many!(  $($acc_rest)* ), )*
            u_shape_if_many!(  $($acc_cur)* )
        ])
    };
}

macro_rules! u_shape_if_many {
    ($t:tt) => {
        u!($t)
    };
    () => {
        compile_error!("empty u!")
    };
    ( $($ts:tt)* ) => {
        u!((~ $($ts)* ))
    };
}

thread_local! {
    pub static default_nt: std::cell::RefCell<String> = std::cell::RefCell::new("Expr".to_owned());
}

macro_rules! u {
    ($atom:ident) => {
        // Default to this, because `Call` will use whatever it's given, without a grammar:
        crate::ast::VariableReference(n(stringify!($atom)))
    };
    ( [ , $seq:expr ] ) => {
        {
            let mut contents: Vec<Ast> = $seq;
            contents.insert(0, crate::ast::Atom(n("REP")));
            crate::ast::Shape(contents)
        }
    };
    ( [ $( $ts:tt )*  ] ) => {
        u_rep!( [$($ts)*] [] {} )
    };
    ( { $form:ident : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ( { $nt:ident $form:ident : $( $ts:tt )*} ) => {
        {
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });
            let res = crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing);
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    // The need for explicit exports is unfortunate;
    //  that information is part of `Scope`, not `Form` (maybe we should change that?)
    ( { $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                ebeta!($ebeta))
        }
    };
    ( { $nt:ident $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            // code duplication from above ) :
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });

            let res =crate::ast::Node(f.clone(),
                    crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                        .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                    ebeta!($ebeta));
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    ( { $form:expr ; $( $ts:tt )*} ) => {
        {
            let f = $form;
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ({ $( $anything:tt )* }) => {
        compile_error!("Needed a : or ; in u!");
    };
    // Currently, nested `Seq`s need to correspond to nested `SEQ`s, so this creates one explicitly:
    ((~ $($ts:tt)*)) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("SEQ")),
            $( u!( $ts ) ),*
        ])
    };
    ((at $t:tt)) => {
        crate::ast::Atom(n(stringify!($t)))
    };
    ((prim $t:tt)) => {
        crate::core_type_forms::get__primitive_type(n(stringify!($t)))
    };
    ((, $interpolate:expr)) => {
        $interpolate
    };
    // Two or more token trees (avoid infinite regress by not handling the one-element case)
    ( $t_first:tt $t_second:tt $( $t:tt )* ) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            u!( $t_first ), u!( $t_second ), $( u!( $t ) ),*
        ])
    };
}

macro_rules! uty {
    ($( $ts:tt )*) => {
        {
            let mut old_default_nt = "".to_owned();
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                *def_nt.borrow_mut() = "Type".to_owned();
            });
            let res = u!( $($ts)* );

            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
}

fn parse_flimsy_seq<'a, I>(flimsy_seq: &mut Peekable<I>, grammar: &FormPat) -> EnvMBE<Ast>
where I: Iterator<Item = &'a Ast> {
    use crate::grammar::FormPat::*;

    match grammar {
        Seq(ref grammar_parts) => {
            let mut result = EnvMBE::new();

            for grammar_part in grammar_parts {
                result = result.combine_overriding(&parse_flimsy_seq(flimsy_seq, grammar_part));
            }
            result
        }
        _ => {
            // `Anyways`es shouldn't consume anything (and they'll always be `Named`):
            let consuming = match grammar {
                Named(_, ref body) => match **body {
                    Anyways(_) => false,
                    _ => true,
                },
                _ => true,
            };
            let flimsy = *match flimsy_seq.peek() {
                None if consuming => return EnvMBE::new(), // Or is this an error?
                None => &&Ast::Trivial,
                Some(f) => f,
            };
            match parse_flimsy_mbe(flimsy, grammar) {
                None => EnvMBE::new(),
                Some(res) => {
                    if consuming {
                        let _ = flimsy_seq.next();
                    }
                    res
                }
            }
        }
    }
}

pub fn parse_flimsy_mbe(flimsy: &Ast, grammar: &FormPat) -> Option<EnvMBE<Ast>> {
    use crate::grammar::FormPat::*;

    match grammar {
        Literal(_, _) => None,
        Call(_) => None,
        Scan(_) => None,
        Seq(_) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("SEQ")) {
                    panic!("Needed a SEQ, got {}", flimsy)
                }
                let mut fpi = flimsy_parts[1..].iter().peekable();

                Some(parse_flimsy_seq(&mut fpi, grammar))
            }
            _ => panic!("Needed a SEQ shape, got {}", flimsy),
        },
        Star(ref body) | Plus(ref body) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("REP")) {
                    panic!("Need a REP, got {}", flimsy_parts[0])
                }

                let mut reps = vec![];
                for flimsy_part in flimsy_parts[1..].iter() {
                    reps.push(parse_flimsy_mbe(flimsy_part, &*body).unwrap());
                }
                Some(EnvMBE::new_from_anon_repeat(reps))
            }
            _ => panic!("Needed a REP shape, got {}", flimsy),
        },
        Alt(ref subs) => {
            // HACK: always pick the first branch of the `Alt`
            // (mainly affects unquotation, where it skips the type annotation)
            parse_flimsy_mbe(flimsy, &*subs[0])
        }
        Named(name, ref body) => Some(EnvMBE::new_from_leaves(
            crate::util::assoc::Assoc::new().set(*name, parse_flimsy_ast(flimsy, &*body)),
        )),
        SynImport(_, _, _) => panic!("`SynImport` can't work without a real parser"),
        NameImport(_, _) => panic!("`NameImport` should live underneath `Named`: {:?}", grammar),
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}

fn parse_flimsy_ast(flimsy: &Ast, grammar: &FormPat) -> Ast {
    use crate::grammar::FormPat::*;

    match grammar {
        Anyways(ref a) => a.clone(),
        Impossible => unimplemented!(),
        Scan(_) => flimsy.clone(),
        Literal(_, _) => Trivial,
        VarRef(_) => match flimsy {
            VariableReference(a) => VariableReference(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        NameImport(body, beta) => {
            ExtendEnv(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        NameImportPhaseless(body, beta) => {
            ExtendEnvPhaseless(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        QuoteDeepen(body, pos) => QuoteMore(Box::new(parse_flimsy_ast(flimsy, &*body)), *pos),
        QuoteEscape(body, depth) => QuoteLess(Box::new(parse_flimsy_ast(flimsy, &*body)), *depth),

        Call(name) => {
            // HACK: don't descend into `Call(n("DefaultAtom"))
            if *name == n("DefaultAtom") || *name == n("AtomNotInPat") {
                match flimsy {
                    VariableReference(a) => Atom(*a),
                    non_atom => panic!("Needed an atom, got {}", non_atom),
                }
            } else {
                flimsy.clone()
            }
        }
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}
<commit_msg>Oops; fix a test.<commit_after>#![macro_use]

// For testing purposes, we want to generate valid `Ast`s,
//  but `ast!` is clunky and makes it *super* easy to leave off `Quote*` and `ExtendEnv`
//  and actually running the parser is a big dependency (and requires huge string literals).
//  so we introduce a `u!` macro that looks up forms but infers the internal structure.

// It's weird to be relying on the grammar while ignoring parts of it, hence "flimsy",
//  but errors are much more likely to be compile-time than inscrutable test problems.

// It's not unsafe to use `u!` for runtime operations, but there's a runtime cost, so don't do it.

use crate::{
    ast::Ast::{self, *},
    grammar::FormPat,
    name::*,
    util::mbe::EnvMBE,
};
use std::iter::{Iterator, Peekable};

// First, transforms from `[a b c; d e f; g h i]` to `[g h i] {[a b c] [d e f]}`
//   to get around a Rust macro parsing restriction,
//  then makes a REP flimsy shape for it.
macro_rules! u_rep {
    ([; $( $ts:tt )*]   $acc_cur:tt { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [] {  $( $acc_rest )*  $acc_cur })
    };
    ([$t:tt $( $ts:tt )*]  [ $( $acc_cur:tt )* ] { $( $acc_rest:tt )* }) => {
        u_rep!( [ $( $ts )* ] [$($acc_cur)* $t] { $( $acc_rest )* })
    };
    ([] [] {}) => {
        // Empty repeat
        crate::ast::Shape(vec![crate::ast::Atom(n("REP"))])
    };
    ([]  [ $( $acc_cur:tt )* ] { $( [ $( $acc_rest:tt )* ] )* }) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("REP")),
            $( u_shape_if_many!(  $($acc_rest)* ), )*
            u_shape_if_many!(  $($acc_cur)* )
        ])
    };
}

macro_rules! u_shape_if_many {
    ($t:tt) => {
        u!($t)
    };
    () => {
        compile_error!("empty u!")
    };
    ( $($ts:tt)* ) => {
        u!((~ $($ts)* ))
    };
}

thread_local! {
    pub static default_nt: std::cell::RefCell<String> = std::cell::RefCell::new("Expr".to_owned());
}

macro_rules! u {
    ($atom:ident) => {
        // Default to this, because `Call` will use whatever it's given, without a grammar:
        crate::ast::VariableReference(n(stringify!($atom)))
    };
    ( [ , $seq:expr ] ) => {
        {
            let mut contents: Vec<Ast> = $seq;
            contents.insert(0, crate::ast::Atom(n("REP")));
            crate::ast::Shape(contents)
        }
    };
    ( [ $( $ts:tt )*  ] ) => {
        u_rep!( [$($ts)*] [] {} )
    };
    ( { $form:ident : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ( { $nt:ident $form:ident : $( $ts:tt )*} ) => {
        {
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });
            let res = crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing);
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    // The need for explicit exports is unfortunate;
    //  that information is part of `Scope`, not `Form` (maybe we should change that?)
    ( { $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                crate::core_forms::find_core_form(&def_nt.borrow(), stringify!($form))
            });
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                ebeta!($ebeta))
        }
    };
    ( { $nt:ident $form:ident => $ebeta:tt : $( $ts:tt )*} ) => {
        {
            // code duplication from above ) :
            let mut old_default_nt = "".to_owned();
            let f = crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                let nt = stringify!($nt);
                *def_nt.borrow_mut() = nt.to_string();

                crate::core_forms::find_core_form(&nt, stringify!($form))
            });

            let res =crate::ast::Node(f.clone(),
                    crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                        .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                    ebeta!($ebeta));
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
    ( { $form:expr ; $( $ts:tt )*} ) => {
        {
            let f = $form;
            crate::ast::Node(f.clone(),
                crate::macros::flimsy_syntax::parse_flimsy_mbe(&u!( (~ $($ts)* ) ), &f.grammar)
                    .unwrap_or_else(crate::util::mbe::EnvMBE::new),
                crate::beta::ExportBeta::Nothing)
        }
    };
    ({ $( $anything:tt )* }) => {
        compile_error!("Needed a : or ; in u!");
    };
    // Currently, nested `Seq`s need to correspond to nested `SEQ`s, so this creates one explicitly:
    ((~ $($ts:tt)*)) => {
        crate::ast::Shape(vec![
            crate::ast::Atom(n("SEQ")),
            $( u!( $ts ) ),*
        ])
    };
    ((at $t:tt)) => {
        crate::ast::Atom(n(stringify!($t)))
    };
    ((prim $t:tt)) => {
        crate::core_type_forms::get__primitive_type(n(stringify!($t)))
    };
    ((, $interpolate:expr)) => {
        $interpolate
    };
    // Two or more token trees (avoid infinite regress by not handling the one-element case)
    ( $t_first:tt $t_second:tt $( $t:tt )* ) => {
        ::ast::Shape(vec![
            ::ast::Atom(n("SEQ")),
            u!( $t_first ), u!( $t_second ), $( u!( $t ) ),*
        ])
    };
}

macro_rules! uty {
    ($( $ts:tt )*) => {
        {
            let mut old_default_nt = "".to_owned();
            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                old_default_nt = def_nt.borrow().clone();
                *def_nt.borrow_mut() = "Type".to_owned();
            });
            let res = u!( $($ts)* );

            crate::macros::flimsy_syntax::default_nt.with(|def_nt| {
                *def_nt.borrow_mut() = old_default_nt;
            });
            res
        }
    };
}

fn parse_flimsy_seq<'a, I>(flimsy_seq: &mut Peekable<I>, grammar: &FormPat) -> EnvMBE<Ast>
where I: Iterator<Item = &'a Ast> {
    use crate::grammar::FormPat::*;

    match grammar {
        Seq(ref grammar_parts) => {
            let mut result = EnvMBE::new();

            for grammar_part in grammar_parts {
                result = result.combine_overriding(&parse_flimsy_seq(flimsy_seq, grammar_part));
            }
            result
        }
        _ => {
            // `Anyways`es shouldn't consume anything (and they'll always be `Named`):
            let consuming = match grammar {
                Named(_, ref body) => match **body {
                    Anyways(_) => false,
                    // HACK: special case for core_macro_forms::macro_invocation.
                    // There has to be a less flimsy way of doing this.
                    QuoteDeepen(ref body, _) | QuoteEscape(ref body, _) => {
                        match **body {
                            Anyways(_) => false,
                            _ => true
                        }
                    }
                    _ => true,
                },
                _ => true,
            };
            let flimsy = *match flimsy_seq.peek() {
                None if consuming => return EnvMBE::new(), // Or is this an error?
                None => &&Ast::Trivial,
                Some(f) => f,
            };
            match parse_flimsy_mbe(flimsy, grammar) {
                None => EnvMBE::new(),
                Some(res) => {
                    if consuming {
                        let _ = flimsy_seq.next();
                    }
                    res
                }
            }
        }
    }
}

pub fn parse_flimsy_mbe(flimsy: &Ast, grammar: &FormPat) -> Option<EnvMBE<Ast>> {
    use crate::grammar::FormPat::*;

    match grammar {
        Literal(_, _) => None,
        Call(_) => None,
        Scan(_) => None,
        Seq(_) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("SEQ")) {
                    panic!("Needed a SEQ, got {}", flimsy)
                }
                let mut fpi = flimsy_parts[1..].iter().peekable();

                Some(parse_flimsy_seq(&mut fpi, grammar))
            }
            _ => panic!("Needed a SEQ shape, got {}", flimsy),
        },
        Star(ref body) | Plus(ref body) => match flimsy {
            Shape(flimsy_parts) => {
                if flimsy_parts[0] != Atom(n("REP")) {
                    panic!("Need a REP, got {}", flimsy_parts[0])
                }

                let mut reps = vec![];
                for flimsy_part in flimsy_parts[1..].iter() {
                    reps.push(parse_flimsy_mbe(flimsy_part, &*body).unwrap());
                }
                Some(EnvMBE::new_from_anon_repeat(reps))
            }
            _ => panic!("Needed a REP shape, got {}", flimsy),
        },
        Alt(ref subs) => {
            // HACK: always pick the first branch of the `Alt`
            // (mainly affects unquotation, where it skips the type annotation)
            parse_flimsy_mbe(flimsy, &*subs[0])
        }
        Named(name, ref body) => Some(EnvMBE::new_from_leaves(
            crate::util::assoc::Assoc::new().set(*name, parse_flimsy_ast(flimsy, &*body)),
        )),
        SynImport(_, _, _) => panic!("`SynImport` can't work without a real parser"),
        NameImport(_, _) => panic!("`NameImport` should live underneath `Named`: {:?}", grammar),
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}

fn parse_flimsy_ast(flimsy: &Ast, grammar: &FormPat) -> Ast {
    use crate::grammar::FormPat::*;

    match grammar {
        Anyways(ref a) => a.clone(),
        Impossible => unimplemented!(),
        Scan(_) => flimsy.clone(),
        Literal(_, _) => Trivial,
        VarRef(_) => match flimsy {
            VariableReference(a) => VariableReference(*a),
            non_atom => panic!("Needed an atom, got {}", non_atom),
        },
        NameImport(body, beta) => {
            ExtendEnv(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        NameImportPhaseless(body, beta) => {
            ExtendEnvPhaseless(Box::new(parse_flimsy_ast(flimsy, &*body)), beta.clone())
        }
        QuoteDeepen(body, pos) => QuoteMore(Box::new(parse_flimsy_ast(flimsy, &*body)), *pos),
        QuoteEscape(body, depth) => QuoteLess(Box::new(parse_flimsy_ast(flimsy, &*body)), *depth),

        Call(name) => {
            // HACK: don't descend into `Call(n("DefaultAtom"))
            if *name == n("DefaultAtom") || *name == n("AtomNotInPat") {
                match flimsy {
                    VariableReference(a) => Atom(*a),
                    non_atom => panic!("Needed an atom, got {}", non_atom),
                }
            } else {
                flimsy.clone()
            }
        }
        _ => unimplemented!("Can't handle {:?}", grammar),
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{convert::TryInto, fs::File, io::Write, path::Path};

use assert_cmd::Command;
use autocxx_integration_tests::build_from_folder;
use tempdir::TempDir;

static MAIN_RS: &str = include_str!("../../../demo/src/main.rs");
static INPUT_H: &str = include_str!("../../../demo/src/input.h");
static BLANK: &str = "// Blank autocxx placeholder";

const KEEP_TEMPDIRS: bool = false;

#[test]
fn test_help() -> Result<(), Box<dyn std::error::Error>> {
    let mut cmd = Command::cargo_bin("autocxx-gen")?;
    cmd.arg("-h").assert().success();
    Ok(())
}

fn base_test<F>(tmp_dir: &TempDir, arg_modifier: F) -> Result<(), Box<dyn std::error::Error>>
where
    F: FnOnce(&mut Command),
{
    let result = base_test_ex(tmp_dir, arg_modifier, false);
    assert_contentful(tmp_dir, "gen0.cc");
    result
}

fn base_test_ex<F>(
    tmp_dir: &TempDir,
    arg_modifier: F,
    use_complete_rs: bool,
) -> Result<(), Box<dyn std::error::Error>>
where
    F: FnOnce(&mut Command),
{
    let demo_code_dir = tmp_dir.path().join("demo");
    std::fs::create_dir(&demo_code_dir).unwrap();
    write_to_file(&demo_code_dir, "input.h", INPUT_H.as_bytes());
    write_to_file(&demo_code_dir, "main.rs", MAIN_RS.as_bytes());
    let demo_rs = demo_code_dir.join("main.rs");
    let mut cmd = Command::cargo_bin("autocxx-gen")?;
    arg_modifier(&mut cmd);
    cmd.arg("--inc")
        .arg(demo_code_dir.to_str().unwrap())
        .arg(demo_rs)
        .arg("--outdir")
        .arg(tmp_dir.path().to_str().unwrap())
        .arg("--gen-cpp");
    if use_complete_rs {
        cmd.arg("--gen-rs-complete");
    } else {
        cmd.arg("--gen-rs-include");
    }
    cmd.assert().success();
    Ok(())
}

#[test]
fn test_gen() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test(&tmp_dir, |_| {})?;
    File::create(tmp_dir.path().join("cxx.h"))
        .and_then(|mut cxx_h| cxx_h.write_all(&autocxx_engine::HEADER.as_bytes().to_vec()))?;
    std::env::set_var("OUT_DIR", tmp_dir.path().to_str().unwrap());
    let r = build_from_folder(
        tmp_dir.path(),
        &tmp_dir.path().join("demo/main.rs"),
        vec![tmp_dir
            .path()
            .join("autocxx-ffi-default-gen.rs")
            .to_path_buf()],
        &["gen0.cc"],
    );
    if KEEP_TEMPDIRS {
        println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
    }
    r.unwrap();
    Ok(())
}

#[test]
fn test_include_prefixes() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test(&tmp_dir, |cmd| {
        cmd.arg("--cxx-h-path")
            .arg("foo/")
            .arg("--cxxgen-h-path")
            .arg("bar/")
            .arg("--generate-exact")
            .arg("3");
    })?;
    assert_contains(&tmp_dir, "gen0.h", "foo/cxx.h");
    // Currently we don't test cxxgen-h-path because we build the demo code
    // which doesn't refer to generated cxx header code.
    Ok(())
}

#[test]
fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test_ex(
        &tmp_dir,
        |cmd| {
            cmd.arg("--generate-exact").arg("3");
        },
        true,
    )?;
    assert_contentful(&tmp_dir, "gen0.cc");
    assert_contentful(&tmp_dir, "gen0.h");
    // TODO: This file will actually try #including one of our .h files if there's anything to put
    // in it. Figure out how to make that happen to test that it works.
    assert_not_contentful(&tmp_dir, "gen1.cc");
    assert_not_contentful(&tmp_dir, "gen1.h");
    assert_not_contentful(&tmp_dir, "gen2.cc");
    assert_not_contentful(&tmp_dir, "gen2.h");
    assert_contentful(&tmp_dir, "cxxgen.h");
    assert_contentful(&tmp_dir, "gen.complete.rs");
    File::create(tmp_dir.path().join("cxx.h"))
        .and_then(|mut cxx_h| cxx_h.write_all(&autocxx_engine::HEADER.as_bytes().to_vec()))?;
    let r = build_from_folder(
        tmp_dir.path(),
        &tmp_dir.path().join("gen.complete.rs"),
        vec![],
        &["gen0.cc"],
    );
    if KEEP_TEMPDIRS {
        println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
    }
    r.unwrap();
    Ok(())
}

#[test]
fn test_gen_preprocess() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    let prepro_path = tmp_dir.path().join("preprocessed.h");
    base_test(&tmp_dir, |cmd| {
        cmd.env("AUTOCXX_PREPROCESS", prepro_path.to_str().unwrap());
    })?;
    assert_contentful(&tmp_dir, "preprocessed.h");
    // Check that a random thing from one of the headers in
    // `ALL_KNOWN_SYSTEM_HEADERS` is included.
    assert!(std::fs::read_to_string(prepro_path)?.contains("integer_sequence"));
    Ok(())
}

#[test]
fn test_gen_repro() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    let repro_path = tmp_dir.path().join("repro.json");
    base_test(&tmp_dir, |cmd| {
        cmd.env("AUTOCXX_REPRO_CASE", repro_path.to_str().unwrap());
    })?;
    assert_contentful(&tmp_dir, "repro.json");
    // Check that a random thing from one of the headers in
    // `ALL_KNOWN_SYSTEM_HEADERS` is included.
    assert!(std::fs::read_to_string(repro_path)?.contains("integer_sequence"));
    Ok(())
}

#[test]
fn test_skip_cxx_gen() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test_ex(
        &tmp_dir,
        |cmd| {
            cmd.arg("--generate-exact")
                .arg("3")
                .arg("--fix-rs-include-name")
                .arg("--skip-cxx-gen");
        },
        false,
    )?;
    assert_contentful(&tmp_dir, "gen0.h");
    assert_not_contentful(&tmp_dir, "gen0.cc");
    assert_exists(&tmp_dir, "gen1.cc");
    assert_exists(&tmp_dir, "gen1.h");
    assert_exists(&tmp_dir, "gen2.cc");
    assert_exists(&tmp_dir, "gen2.h");
    assert_contentful(&tmp_dir, "gen0.include.rs");
    assert_exists(&tmp_dir, "gen1.include.rs");
    assert_exists(&tmp_dir, "gen2.include.rs");
    Ok(())
}

fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
    let path = dir.join(filename);
    let mut f = File::create(&path).expect("Unable to create file");
    f.write_all(content).expect("Unable to write file");
}

fn assert_contentful(outdir: &TempDir, fname: &str) {
    let p = outdir.path().join(fname);
    if !p.exists() {
        panic!("File {} didn't exist", p.to_string_lossy());
    }
    assert!(
        p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(),
        "File {} is empty",
        fname
    );
}

fn assert_not_contentful(outdir: &TempDir, fname: &str) {
    let p = outdir.path().join(fname);
    if !p.exists() {
        panic!("File {} didn't exist", p.to_string_lossy());
    }
    assert!(
        p.metadata().unwrap().len() <= BLANK.len().try_into().unwrap(),
        "File {} is not empty",
        fname
    );
}

fn assert_exists(outdir: &TempDir, fname: &str) {
    let p = outdir.path().join(fname);
    if !p.exists() {
        panic!("File {} didn't exist", p.to_string_lossy());
    }
}

fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) {
    let p = outdir.path().join(fname);
    let content = std::fs::read_to_string(&p).expect(fname);
    eprintln!("content = {}", content);
    assert!(content.contains(pattern));
}
<commit_msg>More clippy fixes<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{convert::TryInto, fs::File, io::Write, path::Path};

use assert_cmd::Command;
use autocxx_integration_tests::build_from_folder;
use tempdir::TempDir;

static MAIN_RS: &str = include_str!("../../../demo/src/main.rs");
static INPUT_H: &str = include_str!("../../../demo/src/input.h");
static BLANK: &str = "// Blank autocxx placeholder";

const KEEP_TEMPDIRS: bool = false;

#[test]
fn test_help() -> Result<(), Box<dyn std::error::Error>> {
    let mut cmd = Command::cargo_bin("autocxx-gen")?;
    cmd.arg("-h").assert().success();
    Ok(())
}

fn base_test<F>(tmp_dir: &TempDir, arg_modifier: F) -> Result<(), Box<dyn std::error::Error>>
where
    F: FnOnce(&mut Command),
{
    let result = base_test_ex(tmp_dir, arg_modifier, false);
    assert_contentful(tmp_dir, "gen0.cc");
    result
}

fn base_test_ex<F>(
    tmp_dir: &TempDir,
    arg_modifier: F,
    use_complete_rs: bool,
) -> Result<(), Box<dyn std::error::Error>>
where
    F: FnOnce(&mut Command),
{
    let demo_code_dir = tmp_dir.path().join("demo");
    std::fs::create_dir(&demo_code_dir).unwrap();
    write_to_file(&demo_code_dir, "input.h", INPUT_H.as_bytes());
    write_to_file(&demo_code_dir, "main.rs", MAIN_RS.as_bytes());
    let demo_rs = demo_code_dir.join("main.rs");
    let mut cmd = Command::cargo_bin("autocxx-gen")?;
    arg_modifier(&mut cmd);
    cmd.arg("--inc")
        .arg(demo_code_dir.to_str().unwrap())
        .arg(demo_rs)
        .arg("--outdir")
        .arg(tmp_dir.path().to_str().unwrap())
        .arg("--gen-cpp");
    if use_complete_rs {
        cmd.arg("--gen-rs-complete");
    } else {
        cmd.arg("--gen-rs-include");
    }
    cmd.assert().success();
    Ok(())
}

#[test]
fn test_gen() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test(&tmp_dir, |_| {})?;
    File::create(tmp_dir.path().join("cxx.h"))
        .and_then(|mut cxx_h| cxx_h.write_all(&autocxx_engine::HEADER.as_bytes()))?;
    std::env::set_var("OUT_DIR", tmp_dir.path().to_str().unwrap());
    let r = build_from_folder(
        tmp_dir.path(),
        &tmp_dir.path().join("demo/main.rs"),
        vec![tmp_dir.path().join("autocxx-ffi-default-gen.rs")],
        &["gen0.cc"],
    );
    if KEEP_TEMPDIRS {
        println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
    }
    r.unwrap();
    Ok(())
}

#[test]
fn test_include_prefixes() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test(&tmp_dir, |cmd| {
        cmd.arg("--cxx-h-path")
            .arg("foo/")
            .arg("--cxxgen-h-path")
            .arg("bar/")
            .arg("--generate-exact")
            .arg("3");
    })?;
    assert_contains(&tmp_dir, "gen0.h", "foo/cxx.h");
    // Currently we don't test cxxgen-h-path because we build the demo code
    // which doesn't refer to generated cxx header code.
    Ok(())
}

#[test]
fn test_gen_fixed_num() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test_ex(
        &tmp_dir,
        |cmd| {
            cmd.arg("--generate-exact").arg("3");
        },
        true,
    )?;
    assert_contentful(&tmp_dir, "gen0.cc");
    assert_contentful(&tmp_dir, "gen0.h");
    // TODO: This file will actually try #including one of our .h files if there's anything to put
    // in it. Figure out how to make that happen to test that it works.
    assert_not_contentful(&tmp_dir, "gen1.cc");
    assert_not_contentful(&tmp_dir, "gen1.h");
    assert_not_contentful(&tmp_dir, "gen2.cc");
    assert_not_contentful(&tmp_dir, "gen2.h");
    assert_contentful(&tmp_dir, "cxxgen.h");
    assert_contentful(&tmp_dir, "gen.complete.rs");
    File::create(tmp_dir.path().join("cxx.h"))
        .and_then(|mut cxx_h| cxx_h.write_all(&autocxx_engine::HEADER.as_bytes()))?;
    let r = build_from_folder(
        tmp_dir.path(),
        &tmp_dir.path().join("gen.complete.rs"),
        vec![],
        &["gen0.cc"],
    );
    if KEEP_TEMPDIRS {
        println!("Tempdir: {:?}", tmp_dir.into_path().to_str());
    }
    r.unwrap();
    Ok(())
}

#[test]
fn test_gen_preprocess() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    let prepro_path = tmp_dir.path().join("preprocessed.h");
    base_test(&tmp_dir, |cmd| {
        cmd.env("AUTOCXX_PREPROCESS", prepro_path.to_str().unwrap());
    })?;
    assert_contentful(&tmp_dir, "preprocessed.h");
    // Check that a random thing from one of the headers in
    // `ALL_KNOWN_SYSTEM_HEADERS` is included.
    assert!(std::fs::read_to_string(prepro_path)?.contains("integer_sequence"));
    Ok(())
}

#[test]
fn test_gen_repro() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    let repro_path = tmp_dir.path().join("repro.json");
    base_test(&tmp_dir, |cmd| {
        cmd.env("AUTOCXX_REPRO_CASE", repro_path.to_str().unwrap());
    })?;
    assert_contentful(&tmp_dir, "repro.json");
    // Check that a random thing from one of the headers in
    // `ALL_KNOWN_SYSTEM_HEADERS` is included.
    assert!(std::fs::read_to_string(repro_path)?.contains("integer_sequence"));
    Ok(())
}

#[test]
fn test_skip_cxx_gen() -> Result<(), Box<dyn std::error::Error>> {
    let tmp_dir = TempDir::new("example")?;
    base_test_ex(
        &tmp_dir,
        |cmd| {
            cmd.arg("--generate-exact")
                .arg("3")
                .arg("--fix-rs-include-name")
                .arg("--skip-cxx-gen");
        },
        false,
    )?;
    assert_contentful(&tmp_dir, "gen0.h");
    assert_not_contentful(&tmp_dir, "gen0.cc");
    assert_exists(&tmp_dir, "gen1.cc");
    assert_exists(&tmp_dir, "gen1.h");
    assert_exists(&tmp_dir, "gen2.cc");
    assert_exists(&tmp_dir, "gen2.h");
    assert_contentful(&tmp_dir, "gen0.include.rs");
    assert_exists(&tmp_dir, "gen1.include.rs");
    assert_exists(&tmp_dir, "gen2.include.rs");
    Ok(())
}

fn write_to_file(dir: &Path, filename: &str, content: &[u8]) {
    let path = dir.join(filename);
    let mut f = File::create(&path).expect("Unable to create file");
    f.write_all(content).expect("Unable to write file");
}

fn assert_contentful(outdir: &TempDir, fname: &str) {
    let p = outdir.path().join(fname);
    if !p.exists() {
        panic!("File {} didn't exist", p.to_string_lossy());
    }
    assert!(
        p.metadata().unwrap().len() > BLANK.len().try_into().unwrap(),
        "File {} is empty",
        fname
    );
}

fn assert_not_contentful(outdir: &TempDir, fname: &str) {
    let p = outdir.path().join(fname);
    if !p.exists() {
        panic!("File {} didn't exist", p.to_string_lossy());
    }
    assert!(
        p.metadata().unwrap().len() <= BLANK.len().try_into().unwrap(),
        "File {} is not empty",
        fname
    );
}

fn assert_exists(outdir: &TempDir, fname: &str) {
    let p = outdir.path().join(fname);
    if !p.exists() {
        panic!("File {} didn't exist", p.to_string_lossy());
    }
}

fn assert_contains(outdir: &TempDir, fname: &str, pattern: &str) {
    let p = outdir.path().join(fname);
    let content = std::fs::read_to_string(&p).expect(fname);
    eprintln!("content = {}", content);
    assert!(content.contains(pattern));
}
<|endoftext|>


<commit_before>// Bitcoin secp256k1 bindings
// Written in 2019 by
//   Elichai Turkel
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # secp256k1 no-std test.
//! This binary is a short smallest rust code to produce a working binary *without libstd*.
//! This gives us 2 things:
//!     1. Test that the parts of the code that should work in a no-std enviroment actually work.
//!     2. Test that we don't accidentally import libstd into `secp256k1`.
//!
//! The first is tested using the following command `cargo run --release | grep -q "Verified Successfully"`.
//! (Making sure that it successfully printed that. i.e. it didn't abort before that).
//!
//! The second is tested by the fact that it compiles. if we accidentally link against libstd we should see the following error:
//! `error[E0152]: duplicate lang item found`.
//! Example:
//! ```
//! error[E0152]: duplicate lang item found: `eh_personality`.
//!   --> src/main.rs:37:1
//!    |
//! 37 | pub extern "C" fn rust_eh_personality() {}
//!    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//!    |
//!    = note: first defined in crate `panic_unwind` (which `std` depends on).
//! ```
//!
//! Notes:
//!     * Requires `panic=abort` and `--release` to not depend on libunwind(which is provided usually by libstd) https://github.com/rust-lang/rust/issues/47493
//!     * Requires linking with `libc` for calling `printf`.
//!

#![feature(lang_items)]
#![feature(start)]
#![feature(core_intrinsics)]
#![feature(panic_info_message)]
#![no_std]
extern crate libc;
extern crate secp256k1;
extern crate serde_cbor;

use core::fmt::{self, write, Write};
use core::intrinsics;
use core::panic::PanicInfo;

use secp256k1::rand::{self, RngCore};
use secp256k1::serde::Serialize;
use secp256k1::*;

use serde_cbor::de;
use serde_cbor::ser::SliceWrite;
use serde_cbor::Serializer;

struct FakeRng;
impl RngCore for FakeRng {
    fn next_u32(&mut self) -> u32 {
        57
    }
    fn next_u64(&mut self) -> u64 {
        57
    }
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
        for i in dest {
            *i = 57;
        }
        Ok(())
    }
    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.try_fill_bytes(dest).unwrap();
    }
}

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    let mut buf = [0u8; 600_000];
    let size = Secp256k1::preallocate_size();
    unsafe { libc::printf("needed size: %d\n\0".as_ptr() as _, size) };

    let mut secp = Secp256k1::preallocated_new(&mut buf).unwrap();
    secp.randomize(&mut FakeRng);
    let secret_key = SecretKey::new(&mut FakeRng);
    let public_key = PublicKey::from_secret_key(&secp, &secret_key);
    let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");

    let sig = secp.sign(&message, &secret_key);
    assert!(secp.verify(&message, &sig, &public_key).is_ok());

    let mut cbor_ser = [0u8; 100];
    let writer = SliceWrite::new(&mut cbor_ser[..]);
    let mut ser = Serializer::new(writer);
    sig.serialize(&mut ser).unwrap();
    let size = ser.into_inner().bytes_written();
    let new_sig: Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap();
    assert_eq!(sig, new_sig);

    unsafe { libc::printf("Verified Successfully!\n\0".as_ptr() as _) };
    0
}

// These functions are used by the compiler, but not
// for a bare-bones hello world. These are normally
// provided by libstd.
#[lang = "eh_personality"]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

// This function may be needed based on the compilation target.
#[lang = "eh_unwind_resume"]
#[no_mangle]
pub extern "C" fn rust_eh_unwind_resume() {}

const MAX_PRINT: usize = 511;
struct Print {
    loc: usize,
    buf: [u8; 512],
}

impl Print {
    pub fn new() -> Self {
        Self {
            loc: 0,
            buf: [0u8; 512],
        }
    }

    pub fn print(&self) {
        unsafe {
            let newline = "\n";
            libc::printf(self.buf.as_ptr() as _);
            libc::printf(newline.as_ptr() as _);
        }
    }
}

impl Write for Print {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        let curr = self.loc;
        if curr + s.len() > MAX_PRINT {
            unsafe {
                libc::printf("overflow\n\0".as_ptr() as _);
                intrinsics::abort();
            }
        }
        self.loc += s.len();
        self.buf[curr..self.loc].copy_from_slice(s.as_bytes());
        Ok(())
    }
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    unsafe { libc::printf("shi1\n\0".as_ptr() as _) };
    let msg = info.message().unwrap();
    let mut buf = Print::new();
    write(&mut buf, *msg).unwrap();
    buf.print();
    unsafe { intrinsics::abort() }
}
<commit_msg>Add ECDH to the no-std tests<commit_after>// Bitcoin secp256k1 bindings
// Written in 2019 by
//   Elichai Turkel
//
// To the extent possible under law, the author(s) have dedicated all
// copyright and related and neighboring rights to this software to
// the public domain worldwide. This software is distributed without
// any warranty.
//
// You should have received a copy of the CC0 Public Domain Dedication
// along with this software.
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//

//! # secp256k1 no-std test.
//! This binary is a short smallest rust code to produce a working binary *without libstd*.
//! This gives us 2 things:
//!     1. Test that the parts of the code that should work in a no-std enviroment actually work.
//!     2. Test that we don't accidentally import libstd into `secp256k1`.
//!
//! The first is tested using the following command `cargo run --release | grep -q "Verified Successfully"`.
//! (Making sure that it successfully printed that. i.e. it didn't abort before that).
//!
//! The second is tested by the fact that it compiles. if we accidentally link against libstd we should see the following error:
//! `error[E0152]: duplicate lang item found`.
//! Example:
//! ```
//! error[E0152]: duplicate lang item found: `eh_personality`.
//!   --> src/main.rs:37:1
//!    |
//! 37 | pub extern "C" fn rust_eh_personality() {}
//!    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//!    |
//!    = note: first defined in crate `panic_unwind` (which `std` depends on).
//! ```
//!
//! Notes:
//!     * Requires `panic=abort` and `--release` to not depend on libunwind(which is provided usually by libstd) https://github.com/rust-lang/rust/issues/47493
//!     * Requires linking with `libc` for calling `printf`.
//!

#![feature(lang_items)]
#![feature(start)]
#![feature(core_intrinsics)]
#![feature(panic_info_message)]
#![no_std]
extern crate libc;
extern crate secp256k1;
extern crate serde_cbor;

use core::fmt::{self, write, Write};
use core::intrinsics;
use core::panic::PanicInfo;

use secp256k1::rand::{self, RngCore};
use secp256k1::serde::Serialize;
use secp256k1::*;
use secp256k1::ecdh::SharedSecret;

use serde_cbor::de;
use serde_cbor::ser::SliceWrite;
use serde_cbor::Serializer;

struct FakeRng;
impl RngCore for FakeRng {
    fn next_u32(&mut self) -> u32 {
        57
    }
    fn next_u64(&mut self) -> u64 {
        57
    }
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
        for i in dest {
            *i = 57;
        }
        Ok(())
    }
    fn fill_bytes(&mut self, dest: &mut [u8]) {
        self.try_fill_bytes(dest).unwrap();
    }
}

#[start]
fn start(_argc: isize, _argv: *const *const u8) -> isize {
    let mut buf = [0u8; 600_000];
    let size = Secp256k1::preallocate_size();
    unsafe { libc::printf("needed size: %d\n\0".as_ptr() as _, size) };

    let mut secp = Secp256k1::preallocated_new(&mut buf).unwrap();
    secp.randomize(&mut FakeRng);
    let secret_key = SecretKey::new(&mut FakeRng);
    let public_key = PublicKey::from_secret_key(&secp, &secret_key);
    let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");

    let sig = secp.sign(&message, &secret_key);
    assert!(secp.verify(&message, &sig, &public_key).is_ok());

    let mut cbor_ser = [0u8; 100];
    let writer = SliceWrite::new(&mut cbor_ser[..]);
    let mut ser = Serializer::new(writer);
    sig.serialize(&mut ser).unwrap();
    let size = ser.into_inner().bytes_written();
    let new_sig: Signature = de::from_mut_slice(&mut cbor_ser[..size]).unwrap();
    assert_eq!(sig, new_sig);

    let _ = SharedSecret::new(&public_key, &secret_key);
    let mut x_arr = [0u8; 32];
    let y_arr = unsafe { SharedSecret::new_with_hash_no_panic(&public_key, &secret_key, |x,y| {
        x_arr = x;
        y.into()
    })}.unwrap();
    assert_ne!(x_arr, [0u8; 32]);
    assert_ne!(&y_arr[..], &[0u8; 32][..]);

    unsafe { libc::printf("Verified Successfully!\n\0".as_ptr() as _) };
    0
}

// These functions are used by the compiler, but not
// for a bare-bones hello world. These are normally
// provided by libstd.
#[lang = "eh_personality"]
#[no_mangle]
pub extern "C" fn rust_eh_personality() {}

// This function may be needed based on the compilation target.
#[lang = "eh_unwind_resume"]
#[no_mangle]
pub extern "C" fn rust_eh_unwind_resume() {}

const MAX_PRINT: usize = 511;
struct Print {
    loc: usize,
    buf: [u8; 512],
}

impl Print {
    pub fn new() -> Self {
        Self {
            loc: 0,
            buf: [0u8; 512],
        }
    }

    pub fn print(&self) {
        unsafe {
            let newline = "\n";
            libc::printf(self.buf.as_ptr() as _);
            libc::printf(newline.as_ptr() as _);
        }
    }
}

impl Write for Print {
    fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
        let curr = self.loc;
        if curr + s.len() > MAX_PRINT {
            unsafe {
                libc::printf("overflow\n\0".as_ptr() as _);
                intrinsics::abort();
            }
        }
        self.loc += s.len();
        self.buf[curr..self.loc].copy_from_slice(s.as_bytes());
        Ok(())
    }
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    unsafe { libc::printf("shi1\n\0".as_ptr() as _) };
    let msg = info.message().unwrap();
    let mut buf = Print::new();
    write(&mut buf, *msg).unwrap();
    buf.print();
    unsafe { intrinsics::abort() }
}
<|endoftext|>


<commit_before>// Copyright (c) 2015, The Radare Project. All rights reserved.
// See the COPYING file at the top-level directory of this distribution.
// Licensed under the BSD 3-Clause License:
// <http://opensource.org/licenses/BSD-3-Clause>
// This file may not be copied, modified, or distributed
// except according to those terms.

//! # The Radare2 Decompilation Library
//! radeco-lib is the crate that powers the
//! [radare2 decompiler](https://github.com/radare/radeco).
//!
//! Apart from decompilation, this library is designed to allow users to
//! perform static analysis on binaries in a easy and intuitive way.
//! __Reusablility__ and __Interactivity__ are the most important
//! design principles of this library.
//!
//! # Design
//! radeco-lib is built on top of r2pipe.rs, a simple library that provides
//! methods
//! to communicate with radare2 (using pipes). To know more about r2pipe or
//! radare2 in general, please head over to the
//! [repo](https://github.com/radare/radare2).
//!
//! radeco-lib works on analyzing ESIL (Evaluable Strings Intermediate
//! Language), an intermediate
//! representation (IR) used by radare2 for emulation. ESIL is converted into
//! an internal SSA IR
//! and used for subsequent analysis and optimizations.
//!
//! __NOTE__: This library is still under heavy developement.
//! Some API's have been stabilized, please check the docs before using
//! radeco-lib
//! in your projects as changes may not be backwards compatible. Contributions,
//! suggestions
//! and bug reports are always welcome at:
//! [tracker](https://github.com/radare/radeco-lib/issues)
//!

#![doc(html_root_url = "https://radare.github.io/radeco-lib/")]
#![doc(html_logo_url = "http://rada.re/r/img/r2logo3.png")]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(slice_patterns)]
#![feature(try_trait)]
#![cfg_attr(test, feature(plugin))]
#![cfg_attr(test, plugin(quickcheck_macros))]

extern crate petgraph;
extern crate regex;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate bit_set;
extern crate either;
extern crate fixedbitset;
extern crate linear_map;
extern crate num;
extern crate typed_arena;
extern crate vec_map;

#[cfg(test)]
extern crate quickcheck;

#[cfg(feature = "trace_log")]
#[macro_use]
extern crate log;
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate r2api;
extern crate r2pipe;

extern crate esil;
// extern crate capstone_rust;
extern crate rayon;

#[cfg(feature = "profile")]
extern crate cpuprofiler;

extern crate lalrpop_util;

#[macro_use]
pub mod utils;
#[macro_use]
pub mod middle;
#[macro_use]
pub mod analysis;

pub mod backend;
pub mod frontend;
<commit_msg>Build fixes<commit_after>// Copyright (c) 2015, The Radare Project. All rights reserved.
// See the COPYING file at the top-level directory of this distribution.
// Licensed under the BSD 3-Clause License:
// <http://opensource.org/licenses/BSD-3-Clause>
// This file may not be copied, modified, or distributed
// except according to those terms.

//! # The Radare2 Decompilation Library
//! radeco-lib is the crate that powers the
//! [radare2 decompiler](https://github.com/radare/radeco).
//!
//! Apart from decompilation, this library is designed to allow users to
//! perform static analysis on binaries in a easy and intuitive way.
//! __Reusablility__ and __Interactivity__ are the most important
//! design principles of this library.
//!
//! # Design
//! radeco-lib is built on top of r2pipe.rs, a simple library that provides
//! methods
//! to communicate with radare2 (using pipes). To know more about r2pipe or
//! radare2 in general, please head over to the
//! [repo](https://github.com/radare/radare2).
//!
//! radeco-lib works on analyzing ESIL (Evaluable Strings Intermediate
//! Language), an intermediate
//! representation (IR) used by radare2 for emulation. ESIL is converted into
//! an internal SSA IR
//! and used for subsequent analysis and optimizations.
//!
//! __NOTE__: This library is still under heavy developement.
//! Some API's have been stabilized, please check the docs before using
//! radeco-lib
//! in your projects as changes may not be backwards compatible. Contributions,
//! suggestions
//! and bug reports are always welcome at:
//! [tracker](https://github.com/radare/radeco-lib/issues)
//!

#![doc(html_root_url = "https://radare.github.io/radeco-lib/")]
#![doc(html_logo_url = "http://rada.re/r/img/r2logo3.png")]
#![feature(box_patterns)]
#![feature(box_syntax)]
#![feature(slice_patterns)]
#![feature(try_trait)]
//#![cfg_attr(test, feature(plugin))]
#![cfg(test)] #![macro_use] extern crate quickcheck_macros;

extern crate petgraph;
extern crate regex;
extern crate serde_json;

#[macro_use]
extern crate lazy_static;
extern crate bit_set;
extern crate either;
extern crate fixedbitset;
extern crate linear_map;
extern crate num;
extern crate typed_arena;
extern crate vec_map;

#[cfg(test)]
extern crate quickcheck;
//#[macro_use(quickcheck)]
//extern crate quickcheck_macros;

#[cfg(feature = "trace_log")]
#[macro_use]
extern crate log;
#[cfg(feature = "trace_log")]
extern crate env_logger;

extern crate r2api;
extern crate r2pipe;

extern crate esil;
// extern crate capstone_rust;
extern crate rayon;

#[cfg(feature = "profile")]
extern crate cpuprofiler;

extern crate lalrpop_util;

#[macro_use]
pub mod utils;
#[macro_use]
pub mod middle;
#[macro_use]
pub mod analysis;

pub mod backend;
pub mod frontend;
<|endoftext|>