<commit_before>// Copyright 2012 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.

// Detecting language items.
//
// Language items are items that represent concepts intrinsic to the language
// itself. Examples are:
//
// * Traits that specify "kinds"; e.g. "Sync", "Send".
//
// * Traits that represent operators; e.g. "Add", "Sub", "Index".
//
// * Functions called by the compiler itself.

pub use self::LangItem::*;

use hir::def_id::DefId;
use ty::{self, TyCtxt};
use middle::weak_lang_items;
use util::nodemap::FxHashMap;

use syntax::ast;
use syntax::symbol::Symbol;
use hir::itemlikevisit::ItemLikeVisitor;
use hir;

// The actual lang items defined come at the end of this file in one handy table.
// So you probably just want to nip down to the end.
macro_rules! language_item_table {
    (
        $( $variant:ident, $name:expr, $method:ident; )*
    ) => {

enum_from_u32! {
    #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
    pub enum LangItem {
        $($variant,)*
    }
}

pub struct LanguageItems {
    pub items: Vec<Option<DefId>>,
    pub missing: Vec<LangItem>,
}

impl LanguageItems {
    pub fn new() -> LanguageItems {
        fn foo(_: LangItem) -> Option<DefId> { None }

        LanguageItems {
            items: vec![$(foo($variant)),*],
            missing: Vec::new(),
        }
    }

    pub fn items(&self) -> &[Option<DefId>] {
        &*self.items
    }

    pub fn item_name(index: usize) -> &'static str {
        let item: Option<LangItem> = LangItem::from_u32(index as u32);
        match item {
            $( Some($variant) => $name, )*
            None => "???"
        }
    }

    pub fn require(&self, it: LangItem) -> Result<DefId, String> {
        match self.items[it as usize] {
            Some(id) => Ok(id),
            None => {
                Err(format!("requires `{}` lang_item",
                            LanguageItems::item_name(it as usize)))
            }
        }
    }

    pub fn fn_trait_kind(&self, id: DefId) -> Option<ty::ClosureKind> {
        let def_id_kinds = [
            (self.fn_trait(), ty::ClosureKind::Fn),
            (self.fn_mut_trait(), ty::ClosureKind::FnMut),
            (self.fn_once_trait(), ty::ClosureKind::FnOnce),
            ];

        for &(opt_def_id, kind) in &def_id_kinds {
            if Some(id) == opt_def_id {
                return Some(kind);
            }
        }

        None
    }

    $(
        #[allow(dead_code)]
        pub fn $method(&self) -> Option<DefId> {
            self.items[$variant as usize]
        }
    )*
}

struct LanguageItemCollector<'a, 'tcx: 'a> {
    items: LanguageItems,

    tcx: TyCtxt<'a, 'tcx, 'tcx>,

    item_refs: FxHashMap<&'static str, usize>,
}

impl<'a, 'v, 'tcx> ItemLikeVisitor<'v> for LanguageItemCollector<'a, 'tcx> {
    fn visit_item(&mut self, item: &hir::Item) {
        if let Some(value) = extract(&item.attrs) {
            let item_index = self.item_refs.get(&*value.as_str()).cloned();

            if let Some(item_index) = item_index {
                let def_id = self.tcx.hir.local_def_id(item.id);
                self.collect_item(item_index, def_id);
            } else {
                let span = self.tcx.hir.span(item.id);
                span_err!(self.tcx.sess, span, E0522,
                          "definition of an unknown language item: `{}`.",
                          value);
            }
        }
    }

    fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
        // at present, lang items are always items, not trait items
    }

    fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
        // at present, lang items are always items, not impl items
    }
}

impl<'a, 'tcx> LanguageItemCollector<'a, 'tcx> {
    fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LanguageItemCollector<'a, 'tcx> {
        let mut item_refs = FxHashMap();

        $( item_refs.insert($name, $variant as usize); )*

        LanguageItemCollector {
            tcx,
            items: LanguageItems::new(),
            item_refs,
        }
    }

    fn collect_item(&mut self, item_index: usize, item_def_id: DefId) {
        // Check for duplicates.
        match self.items.items[item_index] {
            Some(original_def_id) if original_def_id != item_def_id => {
                let name = LanguageItems::item_name(item_index);
                let mut err = match self.tcx.hir.span_if_local(item_def_id) {
                    Some(span) => struct_span_err!(
                        self.tcx.sess,
                        span,
                        E0152,
                        "duplicate lang item found: `{}`.",
                        name),
                    None => self.tcx.sess.struct_err(&format!(
                            "duplicate lang item in crate `{}`: `{}`.",
                            self.tcx.crate_name(item_def_id.krate),
                            name)),
                };
                if let Some(span) = self.tcx.hir.span_if_local(original_def_id) {
                    span_note!(&mut err, span,
                               "first defined here.");
                } else {
                    err.note(&format!("first defined in crate `{}`.",
                                      self.tcx.crate_name(original_def_id.krate)));
                }
                err.emit();
            }
            _ => {
                // OK.
            }
        }

        // Matched.
        self.items.items[item_index] = Some(item_def_id);
    }
}

pub fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
    for attribute in attrs {
        if attribute.check_name("lang") {
            if let Some(value) = attribute.value_str() {
                return Some(value)
            }
        }
    }

    return None;
}

pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LanguageItems {
    let mut collector = LanguageItemCollector::new(tcx);
    for &cnum in tcx.crates().iter() {
        for &(index, item_index) in tcx.defined_lang_items(cnum).iter() {
            let def_id = DefId { krate: cnum, index: index };
            collector.collect_item(item_index, def_id);
        }
    }
    tcx.hir.krate().visit_all_item_likes(&mut collector);
    let LanguageItemCollector { mut items, .. } = collector;
    weak_lang_items::check_crate(tcx, &mut items);
    items
}

// End of the macro
    }
}

language_item_table! {
//  Variant name,                    Name,                      Method name;
    CharImplItem,                    "char",                    char_impl;
    StrImplItem,                     "str",                     str_impl;
    SliceImplItem,                   "slice",                   slice_impl;
    ConstPtrImplItem,                "const_ptr",               const_ptr_impl;
    MutPtrImplItem,                  "mut_ptr",                 mut_ptr_impl;
    I8ImplItem,                      "i8",                      i8_impl;
    I16ImplItem,                     "i16",                     i16_impl;
    I32ImplItem,                     "i32",                     i32_impl;
    I64ImplItem,                     "i64",                     i64_impl;
    I128ImplItem,                     "i128",                   i128_impl;
    IsizeImplItem,                   "isize",                   isize_impl;
    U8ImplItem,                      "u8",                      u8_impl;
    U16ImplItem,                     "u16",                     u16_impl;
    U32ImplItem,                     "u32",                     u32_impl;
    U64ImplItem,                     "u64",                     u64_impl;
    U128ImplItem,                    "u128",                    u128_impl;
    UsizeImplItem,                   "usize",                   usize_impl;
    F32ImplItem,                     "f32",                     f32_impl;
    F64ImplItem,                     "f64",                     f64_impl;

    SendTraitLangItem,               "send",                    send_trait;
    SizedTraitLangItem,              "sized",                   sized_trait;
    UnsizeTraitLangItem,             "unsize",                  unsize_trait;
    CopyTraitLangItem,               "copy",                    copy_trait;
    CloneTraitLangItem,              "clone",                   clone_trait;
    SyncTraitLangItem,               "sync",                    sync_trait;
    FreezeTraitLangItem,             "freeze",                  freeze_trait;

    DropTraitLangItem,               "drop",                    drop_trait;

    CoerceUnsizedTraitLangItem,      "coerce_unsized",          coerce_unsized_trait;

    AddTraitLangItem,                "add",                     add_trait;
    SubTraitLangItem,                "sub",                     sub_trait;
    MulTraitLangItem,                "mul",                     mul_trait;
    DivTraitLangItem,                "div",                     div_trait;
    RemTraitLangItem,                "rem",                     rem_trait;
    NegTraitLangItem,                "neg",                     neg_trait;
    NotTraitLangItem,                "not",                     not_trait;
    BitXorTraitLangItem,             "bitxor",                  bitxor_trait;
    BitAndTraitLangItem,             "bitand",                  bitand_trait;
    BitOrTraitLangItem,              "bitor",                   bitor_trait;
    ShlTraitLangItem,                "shl",                     shl_trait;
    ShrTraitLangItem,                "shr",                     shr_trait;
    AddAssignTraitLangItem,          "add_assign",              add_assign_trait;
    SubAssignTraitLangItem,          "sub_assign",              sub_assign_trait;
    MulAssignTraitLangItem,          "mul_assign",              mul_assign_trait;
    DivAssignTraitLangItem,          "div_assign",              div_assign_trait;
    RemAssignTraitLangItem,          "rem_assign",              rem_assign_trait;
    BitXorAssignTraitLangItem,       "bitxor_assign",           bitxor_assign_trait;
    BitAndAssignTraitLangItem,       "bitand_assign",           bitand_assign_trait;
    BitOrAssignTraitLangItem,        "bitor_assign",            bitor_assign_trait;
    ShlAssignTraitLangItem,          "shl_assign",              shl_assign_trait;
    ShrAssignTraitLangItem,          "shr_assign",              shr_assign_trait;
    IndexTraitLangItem,              "index",                   index_trait;
    IndexMutTraitLangItem,           "index_mut",               index_mut_trait;

    UnsafeCellTypeLangItem,          "unsafe_cell",             unsafe_cell_type;

    DerefTraitLangItem,              "deref",                   deref_trait;
    DerefMutTraitLangItem,           "deref_mut",               deref_mut_trait;

    FnTraitLangItem,                 "fn",                      fn_trait;
    FnMutTraitLangItem,              "fn_mut",                  fn_mut_trait;
    FnOnceTraitLangItem,             "fn_once",                 fn_once_trait;

    GeneratorStateLangItem,          "generator_state",         gen_state;
    GeneratorTraitLangItem,          "generator",               gen_trait;

    EqTraitLangItem,                 "eq",                      eq_trait;
    OrdTraitLangItem,                "ord",                     ord_trait;

    StrEqFnLangItem,                 "str_eq",                  str_eq_fn;

    // A number of panic-related lang items. The `panic` item corresponds to
    // divide-by-zero and various panic cases with `match`. The
    // `panic_bounds_check` item is for indexing arrays.
    //
    // The `begin_unwind` lang item has a predefined symbol name and is sort of
    // a "weak lang item" in the sense that a crate is not required to have it
    // defined to use it, but a final product is required to define it
    // somewhere. Additionally, there are restrictions on crates that use a weak
    // lang item, but do not have it defined.
    PanicFnLangItem,                 "panic",                   panic_fn;
    PanicBoundsCheckFnLangItem,      "panic_bounds_check",      panic_bounds_check_fn;
    PanicFmtLangItem,                "panic_fmt",               panic_fmt;

    ExchangeMallocFnLangItem,        "exchange_malloc",         exchange_malloc_fn;
    BoxFreeFnLangItem,               "box_free",                box_free_fn;
    DropInPlaceFnLangItem,             "drop_in_place",           drop_in_place_fn;

    StartFnLangItem,                 "start",                   start_fn;

    EhPersonalityLangItem,           "eh_personality",          eh_personality;
    EhUnwindResumeLangItem,          "eh_unwind_resume",        eh_unwind_resume;
    MSVCTryFilterLangItem,           "msvc_try_filter",         msvc_try_filter;

    OwnedBoxLangItem,                "owned_box",               owned_box;

    PhantomDataItem,                 "phantom_data",            phantom_data;

    NonZeroItem,                     "non_zero",                non_zero;

    DebugTraitLangItem,              "debug_trait",             debug_trait;
}

impl<'a, 'tcx, 'gcx> ty::TyCtxt<'a, 'tcx, 'gcx> {
    pub fn require_lang_item(&self, lang_item: LangItem) -> DefId {
        self.lang_items().require(lang_item).unwrap_or_else(|msg| {
            self.sess.fatal(&msg)
        })
    }
}
<commit_msg>Refactor how to get a lang item's name<commit_after>// Copyright 2012 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.

// Detecting language items.
//
// Language items are items that represent concepts intrinsic to the language
// itself. Examples are:
//
// * Traits that specify "kinds"; e.g. "Sync", "Send".
//
// * Traits that represent operators; e.g. "Add", "Sub", "Index".
//
// * Functions called by the compiler itself.

pub use self::LangItem::*;

use hir::def_id::DefId;
use ty::{self, TyCtxt};
use middle::weak_lang_items;
use util::nodemap::FxHashMap;

use syntax::ast;
use syntax::symbol::Symbol;
use hir::itemlikevisit::ItemLikeVisitor;
use hir;

// The actual lang items defined come at the end of this file in one handy table.
// So you probably just want to nip down to the end.
macro_rules! language_item_table {
    (
        $( $variant:ident, $name:expr, $method:ident; )*
    ) => {

enum_from_u32! {
    #[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
    pub enum LangItem {
        $($variant,)*
    }
}

impl LangItem {
    fn name(self) -> &'static str {
        match self {
            $( $variant => $name, )*
        }
    }
}

pub struct LanguageItems {
    pub items: Vec<Option<DefId>>,
    pub missing: Vec<LangItem>,
}

impl LanguageItems {
    pub fn new() -> LanguageItems {
        fn foo(_: LangItem) -> Option<DefId> { None }

        LanguageItems {
            items: vec![$(foo($variant)),*],
            missing: Vec::new(),
        }
    }

    pub fn items(&self) -> &[Option<DefId>] {
        &*self.items
    }

    pub fn require(&self, it: LangItem) -> Result<DefId, String> {
        self.items[it as usize].ok_or(format!("requires `{}` lang_item", it.name()))
    }

    pub fn fn_trait_kind(&self, id: DefId) -> Option<ty::ClosureKind> {
        let def_id_kinds = [
            (self.fn_trait(), ty::ClosureKind::Fn),
            (self.fn_mut_trait(), ty::ClosureKind::FnMut),
            (self.fn_once_trait(), ty::ClosureKind::FnOnce),
            ];

        for &(opt_def_id, kind) in &def_id_kinds {
            if Some(id) == opt_def_id {
                return Some(kind);
            }
        }

        None
    }

    $(
        #[allow(dead_code)]
        pub fn $method(&self) -> Option<DefId> {
            self.items[$variant as usize]
        }
    )*
}

struct LanguageItemCollector<'a, 'tcx: 'a> {
    items: LanguageItems,

    tcx: TyCtxt<'a, 'tcx, 'tcx>,

    item_refs: FxHashMap<&'static str, usize>,
}

impl<'a, 'v, 'tcx> ItemLikeVisitor<'v> for LanguageItemCollector<'a, 'tcx> {
    fn visit_item(&mut self, item: &hir::Item) {
        if let Some(value) = extract(&item.attrs) {
            let item_index = self.item_refs.get(&*value.as_str()).cloned();

            if let Some(item_index) = item_index {
                let def_id = self.tcx.hir.local_def_id(item.id);
                self.collect_item(item_index, def_id);
            } else {
                let span = self.tcx.hir.span(item.id);
                span_err!(self.tcx.sess, span, E0522,
                          "definition of an unknown language item: `{}`.",
                          value);
            }
        }
    }

    fn visit_trait_item(&mut self, _trait_item: &hir::TraitItem) {
        // at present, lang items are always items, not trait items
    }

    fn visit_impl_item(&mut self, _impl_item: &hir::ImplItem) {
        // at present, lang items are always items, not impl items
    }
}

impl<'a, 'tcx> LanguageItemCollector<'a, 'tcx> {
    fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LanguageItemCollector<'a, 'tcx> {
        let mut item_refs = FxHashMap();

        $( item_refs.insert($name, $variant as usize); )*

        LanguageItemCollector {
            tcx,
            items: LanguageItems::new(),
            item_refs,
        }
    }

    fn collect_item(&mut self, item_index: usize, item_def_id: DefId) {
        // Check for duplicates.
        match self.items.items[item_index] {
            Some(original_def_id) if original_def_id != item_def_id => {
                let name = LangItem::from_u32(item_index as u32).unwrap().name();
                let mut err = match self.tcx.hir.span_if_local(item_def_id) {
                    Some(span) => struct_span_err!(
                        self.tcx.sess,
                        span,
                        E0152,
                        "duplicate lang item found: `{}`.",
                        name),
                    None => self.tcx.sess.struct_err(&format!(
                            "duplicate lang item in crate `{}`: `{}`.",
                            self.tcx.crate_name(item_def_id.krate),
                            name)),
                };
                if let Some(span) = self.tcx.hir.span_if_local(original_def_id) {
                    span_note!(&mut err, span,
                               "first defined here.");
                } else {
                    err.note(&format!("first defined in crate `{}`.",
                                      self.tcx.crate_name(original_def_id.krate)));
                }
                err.emit();
            }
            _ => {
                // OK.
            }
        }

        // Matched.
        self.items.items[item_index] = Some(item_def_id);
    }
}

pub fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
    for attribute in attrs {
        if attribute.check_name("lang") {
            if let Some(value) = attribute.value_str() {
                return Some(value)
            }
        }
    }

    return None;
}

pub fn collect<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) -> LanguageItems {
    let mut collector = LanguageItemCollector::new(tcx);
    for &cnum in tcx.crates().iter() {
        for &(index, item_index) in tcx.defined_lang_items(cnum).iter() {
            let def_id = DefId { krate: cnum, index: index };
            collector.collect_item(item_index, def_id);
        }
    }
    tcx.hir.krate().visit_all_item_likes(&mut collector);
    let LanguageItemCollector { mut items, .. } = collector;
    weak_lang_items::check_crate(tcx, &mut items);
    items
}

// End of the macro
    }
}

language_item_table! {
//  Variant name,                    Name,                      Method name;
    CharImplItem,                    "char",                    char_impl;
    StrImplItem,                     "str",                     str_impl;
    SliceImplItem,                   "slice",                   slice_impl;
    ConstPtrImplItem,                "const_ptr",               const_ptr_impl;
    MutPtrImplItem,                  "mut_ptr",                 mut_ptr_impl;
    I8ImplItem,                      "i8",                      i8_impl;
    I16ImplItem,                     "i16",                     i16_impl;
    I32ImplItem,                     "i32",                     i32_impl;
    I64ImplItem,                     "i64",                     i64_impl;
    I128ImplItem,                     "i128",                   i128_impl;
    IsizeImplItem,                   "isize",                   isize_impl;
    U8ImplItem,                      "u8",                      u8_impl;
    U16ImplItem,                     "u16",                     u16_impl;
    U32ImplItem,                     "u32",                     u32_impl;
    U64ImplItem,                     "u64",                     u64_impl;
    U128ImplItem,                    "u128",                    u128_impl;
    UsizeImplItem,                   "usize",                   usize_impl;
    F32ImplItem,                     "f32",                     f32_impl;
    F64ImplItem,                     "f64",                     f64_impl;

    SendTraitLangItem,               "send",                    send_trait;
    SizedTraitLangItem,              "sized",                   sized_trait;
    UnsizeTraitLangItem,             "unsize",                  unsize_trait;
    CopyTraitLangItem,               "copy",                    copy_trait;
    CloneTraitLangItem,              "clone",                   clone_trait;
    SyncTraitLangItem,               "sync",                    sync_trait;
    FreezeTraitLangItem,             "freeze",                  freeze_trait;

    DropTraitLangItem,               "drop",                    drop_trait;

    CoerceUnsizedTraitLangItem,      "coerce_unsized",          coerce_unsized_trait;

    AddTraitLangItem,                "add",                     add_trait;
    SubTraitLangItem,                "sub",                     sub_trait;
    MulTraitLangItem,                "mul",                     mul_trait;
    DivTraitLangItem,                "div",                     div_trait;
    RemTraitLangItem,                "rem",                     rem_trait;
    NegTraitLangItem,                "neg",                     neg_trait;
    NotTraitLangItem,                "not",                     not_trait;
    BitXorTraitLangItem,             "bitxor",                  bitxor_trait;
    BitAndTraitLangItem,             "bitand",                  bitand_trait;
    BitOrTraitLangItem,              "bitor",                   bitor_trait;
    ShlTraitLangItem,                "shl",                     shl_trait;
    ShrTraitLangItem,                "shr",                     shr_trait;
    AddAssignTraitLangItem,          "add_assign",              add_assign_trait;
    SubAssignTraitLangItem,          "sub_assign",              sub_assign_trait;
    MulAssignTraitLangItem,          "mul_assign",              mul_assign_trait;
    DivAssignTraitLangItem,          "div_assign",              div_assign_trait;
    RemAssignTraitLangItem,          "rem_assign",              rem_assign_trait;
    BitXorAssignTraitLangItem,       "bitxor_assign",           bitxor_assign_trait;
    BitAndAssignTraitLangItem,       "bitand_assign",           bitand_assign_trait;
    BitOrAssignTraitLangItem,        "bitor_assign",            bitor_assign_trait;
    ShlAssignTraitLangItem,          "shl_assign",              shl_assign_trait;
    ShrAssignTraitLangItem,          "shr_assign",              shr_assign_trait;
    IndexTraitLangItem,              "index",                   index_trait;
    IndexMutTraitLangItem,           "index_mut",               index_mut_trait;

    UnsafeCellTypeLangItem,          "unsafe_cell",             unsafe_cell_type;

    DerefTraitLangItem,              "deref",                   deref_trait;
    DerefMutTraitLangItem,           "deref_mut",               deref_mut_trait;

    FnTraitLangItem,                 "fn",                      fn_trait;
    FnMutTraitLangItem,              "fn_mut",                  fn_mut_trait;
    FnOnceTraitLangItem,             "fn_once",                 fn_once_trait;

    GeneratorStateLangItem,          "generator_state",         gen_state;
    GeneratorTraitLangItem,          "generator",               gen_trait;

    EqTraitLangItem,                 "eq",                      eq_trait;
    OrdTraitLangItem,                "ord",                     ord_trait;

    StrEqFnLangItem,                 "str_eq",                  str_eq_fn;

    // A number of panic-related lang items. The `panic` item corresponds to
    // divide-by-zero and various panic cases with `match`. The
    // `panic_bounds_check` item is for indexing arrays.
    //
    // The `begin_unwind` lang item has a predefined symbol name and is sort of
    // a "weak lang item" in the sense that a crate is not required to have it
    // defined to use it, but a final product is required to define it
    // somewhere. Additionally, there are restrictions on crates that use a weak
    // lang item, but do not have it defined.
    PanicFnLangItem,                 "panic",                   panic_fn;
    PanicBoundsCheckFnLangItem,      "panic_bounds_check",      panic_bounds_check_fn;
    PanicFmtLangItem,                "panic_fmt",               panic_fmt;

    ExchangeMallocFnLangItem,        "exchange_malloc",         exchange_malloc_fn;
    BoxFreeFnLangItem,               "box_free",                box_free_fn;
    DropInPlaceFnLangItem,             "drop_in_place",           drop_in_place_fn;

    StartFnLangItem,                 "start",                   start_fn;

    EhPersonalityLangItem,           "eh_personality",          eh_personality;
    EhUnwindResumeLangItem,          "eh_unwind_resume",        eh_unwind_resume;
    MSVCTryFilterLangItem,           "msvc_try_filter",         msvc_try_filter;

    OwnedBoxLangItem,                "owned_box",               owned_box;

    PhantomDataItem,                 "phantom_data",            phantom_data;

    NonZeroItem,                     "non_zero",                non_zero;

    DebugTraitLangItem,              "debug_trait",             debug_trait;
}

impl<'a, 'tcx, 'gcx> ty::TyCtxt<'a, 'tcx, 'gcx> {
    pub fn require_lang_item(&self, lang_item: LangItem) -> DefId {
        self.lang_items().require(lang_item).unwrap_or_else(|msg| {
            self.sess.fatal(&msg)
        })
    }
}
<|endoftext|>


<commit_before>use LalafellBot;
use tasks::{RunsTask, FromConfig};
use config::Task;
use discord::GetMessages;
use discord::model::ChannelId;
use chrono::prelude::*;
use chrono::Duration;

use xivdb::error::*;
use serde_json;

use std::sync::Arc;
use std::thread;

#[derive(Debug)]
pub struct DeleteAllMessagesTask {
  config: DeleteAllMessagesTaskConfig,
  next_sleep: i64
}

impl RunsTask for DeleteAllMessagesTask {
  fn start(mut self, s: Arc<LalafellBot>) {
    loop {
      thread::sleep(Duration::seconds(self.next_sleep).to_std().unwrap());
      info!("Delete messages task running");
      let channel = ChannelId(self.config.channel);
      let messages = match s.discord.get_messages(channel, GetMessages::MostRecent, None) {
        Ok(m) => m,
        Err(e) => {
          warn!("Could not get messages for channel {}: {}", channel, e);
          self.next_sleep = 30;
          continue;
        }
      };
      for message in messages {
        if self.config.except.contains(&message.id.0) {
          continue;
        }
        let timestamp: DateTime<UTC> = match message.timestamp.parse() {
          Ok(t) => t,
          Err(e) => {
            warn!("Could not parse message timestamp: {}", e);
            continue;
          }
        };
        if timestamp + Duration::seconds(self.config.after) > UTC::now() {
          continue;
        }
        if let Err(e) = s.discord.delete_message(message.channel_id, message.id) {
          warn!("Could not delete message {}: {}", message.id, e);
        }
      }
      self.next_sleep = 60;
      info!("Delete messages task done");
    }
  }
}

impl FromConfig for DeleteAllMessagesTask {
  fn from_config(task: &Task) -> Result<Self> {
    let val = match task.config {
      None => return Err("delete_all_messages task is missing configuration".into()),
      Some(ref val) => val
    };
    let config: DeleteAllMessagesTaskConfig = serde_json::from_value(val.clone()).chain_err(|| "could not parse delete_all_messages configuration")?;
    Ok(DeleteAllMessagesTask {
      config: config,
      next_sleep: Default::default()
    })
  }
}

#[derive(Debug, Deserialize)]
pub struct DeleteAllMessagesTaskConfig {
  pub channel: u64,
  pub after: i64,
  #[serde(default)]
  pub except: Vec<u64>
}
<commit_msg>fix: use delete_messages when possible<commit_after>use LalafellBot;
use tasks::{RunsTask, FromConfig};
use config::Task;
use discord::GetMessages;
use discord::model::ChannelId;
use chrono::prelude::*;
use chrono::Duration;

use xivdb::error::*;
use serde_json;

use std::sync::Arc;
use std::thread;

#[derive(Debug)]
pub struct DeleteAllMessagesTask {
  config: DeleteAllMessagesTaskConfig,
  next_sleep: i64
}

impl RunsTask for DeleteAllMessagesTask {
  fn start(mut self, s: Arc<LalafellBot>) {
    loop {
      thread::sleep(Duration::seconds(self.next_sleep).to_std().unwrap());
      info!("Delete messages task running");
      let channel = ChannelId(self.config.channel);
      let messages = match s.discord.get_messages(channel, GetMessages::MostRecent, None) {
        Ok(m) => m,
        Err(e) => {
          warn!("Could not get messages for channel {}: {}", channel, e);
          self.next_sleep = 30;
          continue;
        }
      };
      let mut to_delete = Vec::new();
      for message in messages {
        if self.config.except.contains(&message.id.0) {
          continue;
        }
        let timestamp: DateTime<UTC> = match message.timestamp.parse() {
          Ok(t) => t,
          Err(e) => {
            warn!("Could not parse message timestamp: {}", e);
            continue;
          }
        };
        if timestamp + Duration::seconds(self.config.after) > UTC::now() {
          continue;
        }
        to_delete.push(message);
      }
      for chunk in to_delete.chunks(100) {
        let result = if chunk.len() == 1 {
          s.discord.delete_message(channel, chunk[0].id)
        } else {
          let ids: Vec<_> = chunk.iter().map(|m| m.id).collect();
          s.discord.delete_messages(channel, &ids)
        };
        if let Err(e) = result {
          warn!("Could not delete messages: {}", e);
        }
      }
      self.next_sleep = 60;
      info!("Delete messages task done");
    }
  }
}

impl FromConfig for DeleteAllMessagesTask {
  fn from_config(task: &Task) -> Result<Self> {
    let val = match task.config {
      None => return Err("delete_all_messages task is missing configuration".into()),
      Some(ref val) => val
    };
    let config: DeleteAllMessagesTaskConfig = serde_json::from_value(val.clone()).chain_err(|| "could not parse delete_all_messages configuration")?;
    Ok(DeleteAllMessagesTask {
      config: config,
      next_sleep: Default::default()
    })
  }
}

#[derive(Debug, Deserialize)]
pub struct DeleteAllMessagesTaskConfig {
  pub channel: u64,
  pub after: i64,
  #[serde(default)]
  pub except: Vec<u64>
}
<|endoftext|>


<commit_before>#[cfg(not(target_os = "macos"))]
use std::arch::asm;

use dora_asm::arm64::Cond;
use lazy_static::lazy_static;

use crate::masm::CondCode;

#[cfg(target_os = "macos")]
pub fn flush_icache(start: *const u8, len: usize) {
    extern "C" {
        fn sys_icache_invalidate(data: *const u8, len: usize);
    }

    unsafe {
        sys_icache_invalidate(start, len);
    }
}

#[cfg(not(target_os = "macos"))]
pub fn flush_icache(start: *const u8, len: usize) {
    let start = start as usize;
    let end = start + len;

    let (icacheline_size, dcacheline_size) = cacheline_sizes();

    let istart = start & !(icacheline_size - 1);
    let dstart = start & !(dcacheline_size - 1);

    let mut ptr = dstart;

    while ptr < end {
        unsafe {
            asm!("dc civac, {}", in(reg) ptr);
        }

        ptr += dcacheline_size;
    }

    unsafe {
        asm!("dsb ish");
    }

    ptr = istart;

    while ptr < end {
        unsafe {
            asm!("ic ivau, {}", in(reg) ptr);
        }

        ptr += icacheline_size;
    }

    unsafe {
        asm!("dsb ish", "isb");
    }
}

pub fn has_lse_atomics() -> bool {
    *HAS_LSE_ATOMICS
}

lazy_static! {
    static ref HAS_LSE_ATOMICS: bool = check_support_for_lse_atomics();
}

#[cfg(target_os = "macos")]
fn check_support_for_lse_atomics() -> bool {
    use std::ffi::CString;

    unsafe {
        let mut oldbuffer: usize = 0;
        let mut oldlenp: libc::size_t = 4;
        let name = CString::new("hw.optional.armv8_1_atomics").unwrap();
        let result = libc::sysctlbyname(
            name.as_ptr(),
            &mut oldbuffer as *mut usize as *mut libc::c_void,
            &mut oldlenp,
            std::ptr::null_mut(),
            0,
        );
        assert_eq!(result, 0);
        oldbuffer != 0
    }
}

#[cfg(not(target_os = "macos"))]
fn check_support_for_lse_atomics() -> bool {
    let value: usize;
    unsafe {
        asm!("mrs {}, ID_AA64DFR1_EL1", out(reg) value);
    }
    (value >> 20) & 0xF == 0b0010
}

#[cfg(not(target_os = "macos"))]
pub fn cacheline_sizes() -> (usize, usize) {
    let value: usize;

    unsafe {
        asm!("mrs {}, ctr_el0", out(reg) value);
    }

    let insn = 4 << (value & 0xF);
    let data = 4 << ((value >> 16) & 0xF);

    (insn, data)
}

pub fn has_round() -> bool {
    true
}

pub fn has_popcnt() -> bool {
    true
}

pub fn has_lzcnt() -> bool {
    true
}

pub fn has_tzcnt() -> bool {
    true
}

impl From<CondCode> for Cond {
    fn from(c: CondCode) -> Cond {
        match c {
            CondCode::Zero => Cond::EQ,
            CondCode::NonZero => Cond::NE,
            CondCode::Equal => Cond::EQ,
            CondCode::NotEqual => Cond::NE,
            CondCode::Greater => Cond::GT,
            CondCode::GreaterEq => Cond::GE,
            CondCode::Less => Cond::LT,
            CondCode::LessEq => Cond::LE,
            CondCode::UnsignedGreater => Cond::HI,
            CondCode::UnsignedGreaterEq => Cond::HS,
            CondCode::UnsignedLess => Cond::LO,
            CondCode::UnsignedLessEq => Cond::LS,
        }
    }
}

use crate::cpu::{FReg, Reg};
use crate::language::ty::SourceType;
use dora_asm::arm64 as arm64_asm;
use dora_asm::arm64::Register;

pub static REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static CCALL_REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static CCALL_FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static SCRATCH: [Reg; 5] = [R9, R12, R13, R14, R15];

pub const REG_RESULT: Reg = R0;
pub const REG_TMP1: Reg = R10;
pub const REG_TMP2: Reg = R11;
pub const REG_FP: Reg = R29;
pub const REG_LR: Reg = R30;
pub const REG_THREAD: Reg = R28;

pub const REG_SP: Reg = Reg(32);
pub const REG_ZERO: Reg = Reg(33);

pub const FREG_RESULT: FReg = F0;

// shall not overlap with param registers
pub const FREG_TMP1: FReg = F16;

pub const STACK_FRAME_ALIGNMENT: usize = 16;

pub const R0: Reg = Reg(0);
pub const R1: Reg = Reg(1);
pub const R2: Reg = Reg(2);
pub const R3: Reg = Reg(3);
pub const R4: Reg = Reg(4);
pub const R5: Reg = Reg(5);
pub const R6: Reg = Reg(6);
pub const R7: Reg = Reg(7);
pub const R8: Reg = Reg(8);
pub const R9: Reg = Reg(9);
pub const R10: Reg = Reg(10);
pub const R11: Reg = Reg(11);
pub const R12: Reg = Reg(12);
pub const R13: Reg = Reg(13);
pub const R14: Reg = Reg(14);
pub const R15: Reg = Reg(15);
pub const R16: Reg = Reg(16);
pub const R17: Reg = Reg(17);
pub const R18: Reg = Reg(18);
pub const R19: Reg = Reg(19);
pub const R20: Reg = Reg(20);
pub const R21: Reg = Reg(21);
pub const R22: Reg = Reg(22);
pub const R23: Reg = Reg(23);
pub const R24: Reg = Reg(24);
pub const R25: Reg = Reg(25);
pub const R26: Reg = Reg(26);
pub const R27: Reg = Reg(27);
pub const R28: Reg = Reg(28);
pub const R29: Reg = Reg(29);
pub const R30: Reg = Reg(30);

pub const F0: FReg = FReg(0);
pub const F1: FReg = FReg(1);
pub const F2: FReg = FReg(2);
pub const F3: FReg = FReg(3);
pub const F4: FReg = FReg(4);
pub const F5: FReg = FReg(5);
pub const F6: FReg = FReg(6);
pub const F7: FReg = FReg(7);
pub const F8: FReg = FReg(8);
pub const F9: FReg = FReg(9);
pub const F10: FReg = FReg(10);
pub const F11: FReg = FReg(11);
pub const F12: FReg = FReg(12);
pub const F13: FReg = FReg(13);
pub const F14: FReg = FReg(14);
pub const F15: FReg = FReg(15);
pub const F16: FReg = FReg(16);
pub const F17: FReg = FReg(17);
pub const F18: FReg = FReg(18);
pub const F19: FReg = FReg(19);
pub const F20: FReg = FReg(20);
pub const F21: FReg = FReg(21);
pub const F22: FReg = FReg(22);
pub const F23: FReg = FReg(23);
pub const F24: FReg = FReg(24);
pub const F25: FReg = FReg(25);
pub const F26: FReg = FReg(26);
pub const F27: FReg = FReg(27);
pub const F28: FReg = FReg(28);
pub const F29: FReg = FReg(29);
pub const F30: FReg = FReg(30);
pub const F31: FReg = FReg(31);

impl Reg {
    pub fn asm(self) -> u32 {
        match self {
            REG_SP => 31,
            REG_ZERO => 31,
            _ => self.0 as u32,
        }
    }

    pub fn is_gpr(self) -> bool {
        self.0 <= 30
    }

    pub fn is_gpr_or_zero(self) -> bool {
        self.is_gpr() || self == REG_ZERO
    }

    pub fn is_gpr_or_sp(self) -> bool {
        self.is_gpr() || self == REG_SP
    }
}

impl From<Reg> for Register {
    fn from(reg: Reg) -> Register {
        if reg.0 < 31 {
            Register::new(reg.0)
        } else if reg == REG_ZERO {
            arm64_asm::REG_ZERO
        } else {
            assert_eq!(reg, REG_SP);
            arm64_asm::REG_SP
        }
    }
}

impl FReg {
    pub fn asm(self) -> u32 {
        assert!(self.0 < 32);
        self.0 as u32
    }
}

pub static PARAM_OFFSET: i32 = 16;

pub fn next_param_offset(param_offset: i32, _: SourceType) -> i32 {
    param_offset + 8
}
<commit_msg>arm64: use macro for detecting lse atomics support on linux<commit_after>#[cfg(not(target_os = "macos"))]
use std::arch::asm;

use dora_asm::arm64::Cond;
use lazy_static::lazy_static;

use crate::masm::CondCode;

#[cfg(target_os = "macos")]
pub fn flush_icache(start: *const u8, len: usize) {
    extern "C" {
        fn sys_icache_invalidate(data: *const u8, len: usize);
    }

    unsafe {
        sys_icache_invalidate(start, len);
    }
}

#[cfg(not(target_os = "macos"))]
pub fn flush_icache(start: *const u8, len: usize) {
    let start = start as usize;
    let end = start + len;

    let (icacheline_size, dcacheline_size) = cacheline_sizes();

    let istart = start & !(icacheline_size - 1);
    let dstart = start & !(dcacheline_size - 1);

    let mut ptr = dstart;

    while ptr < end {
        unsafe {
            asm!("dc civac, {}", in(reg) ptr);
        }

        ptr += dcacheline_size;
    }

    unsafe {
        asm!("dsb ish");
    }

    ptr = istart;

    while ptr < end {
        unsafe {
            asm!("ic ivau, {}", in(reg) ptr);
        }

        ptr += icacheline_size;
    }

    unsafe {
        asm!("dsb ish", "isb");
    }
}

pub fn has_lse_atomics() -> bool {
    *HAS_LSE_ATOMICS
}

lazy_static! {
    static ref HAS_LSE_ATOMICS: bool = check_support_for_lse_atomics();
}

#[cfg(target_os = "macos")]
fn check_support_for_lse_atomics() -> bool {
    use std::ffi::CString;

    unsafe {
        let mut oldbuffer: usize = 0;
        let mut oldlenp: libc::size_t = 4;
        let name = CString::new("hw.optional.armv8_1_atomics").unwrap();
        let result = libc::sysctlbyname(
            name.as_ptr(),
            &mut oldbuffer as *mut usize as *mut libc::c_void,
            &mut oldlenp,
            std::ptr::null_mut(),
            0,
        );
        assert_eq!(result, 0);
        oldbuffer != 0
    }
}

#[cfg(not(target_os = "macos"))]
fn check_support_for_lse_atomics() -> bool {
    std::arch::is_aarch64_feature_detected!("lse")
}

#[cfg(not(target_os = "macos"))]
pub fn cacheline_sizes() -> (usize, usize) {
    let value: usize;

    unsafe {
        asm!("mrs {}, ctr_el0", out(reg) value);
    }

    let insn = 4 << (value & 0xF);
    let data = 4 << ((value >> 16) & 0xF);

    (insn, data)
}

pub fn has_round() -> bool {
    true
}

pub fn has_popcnt() -> bool {
    true
}

pub fn has_lzcnt() -> bool {
    true
}

pub fn has_tzcnt() -> bool {
    true
}

impl From<CondCode> for Cond {
    fn from(c: CondCode) -> Cond {
        match c {
            CondCode::Zero => Cond::EQ,
            CondCode::NonZero => Cond::NE,
            CondCode::Equal => Cond::EQ,
            CondCode::NotEqual => Cond::NE,
            CondCode::Greater => Cond::GT,
            CondCode::GreaterEq => Cond::GE,
            CondCode::Less => Cond::LT,
            CondCode::LessEq => Cond::LE,
            CondCode::UnsignedGreater => Cond::HI,
            CondCode::UnsignedGreaterEq => Cond::HS,
            CondCode::UnsignedLess => Cond::LO,
            CondCode::UnsignedLessEq => Cond::LS,
        }
    }
}

use crate::cpu::{FReg, Reg};
use crate::language::ty::SourceType;
use dora_asm::arm64 as arm64_asm;
use dora_asm::arm64::Register;

pub static REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static CCALL_REG_PARAMS: [Reg; 8] = [R0, R1, R2, R3, R4, R5, R6, R7];
pub static CCALL_FREG_PARAMS: [FReg; 8] = [F0, F1, F2, F3, F4, F5, F6, F7];

pub static SCRATCH: [Reg; 5] = [R9, R12, R13, R14, R15];

pub const REG_RESULT: Reg = R0;
pub const REG_TMP1: Reg = R10;
pub const REG_TMP2: Reg = R11;
pub const REG_FP: Reg = R29;
pub const REG_LR: Reg = R30;
pub const REG_THREAD: Reg = R28;

pub const REG_SP: Reg = Reg(32);
pub const REG_ZERO: Reg = Reg(33);

pub const FREG_RESULT: FReg = F0;

// shall not overlap with param registers
pub const FREG_TMP1: FReg = F16;

pub const STACK_FRAME_ALIGNMENT: usize = 16;

pub const R0: Reg = Reg(0);
pub const R1: Reg = Reg(1);
pub const R2: Reg = Reg(2);
pub const R3: Reg = Reg(3);
pub const R4: Reg = Reg(4);
pub const R5: Reg = Reg(5);
pub const R6: Reg = Reg(6);
pub const R7: Reg = Reg(7);
pub const R8: Reg = Reg(8);
pub const R9: Reg = Reg(9);
pub const R10: Reg = Reg(10);
pub const R11: Reg = Reg(11);
pub const R12: Reg = Reg(12);
pub const R13: Reg = Reg(13);
pub const R14: Reg = Reg(14);
pub const R15: Reg = Reg(15);
pub const R16: Reg = Reg(16);
pub const R17: Reg = Reg(17);
pub const R18: Reg = Reg(18);
pub const R19: Reg = Reg(19);
pub const R20: Reg = Reg(20);
pub const R21: Reg = Reg(21);
pub const R22: Reg = Reg(22);
pub const R23: Reg = Reg(23);
pub const R24: Reg = Reg(24);
pub const R25: Reg = Reg(25);
pub const R26: Reg = Reg(26);
pub const R27: Reg = Reg(27);
pub const R28: Reg = Reg(28);
pub const R29: Reg = Reg(29);
pub const R30: Reg = Reg(30);

pub const F0: FReg = FReg(0);
pub const F1: FReg = FReg(1);
pub const F2: FReg = FReg(2);
pub const F3: FReg = FReg(3);
pub const F4: FReg = FReg(4);
pub const F5: FReg = FReg(5);
pub const F6: FReg = FReg(6);
pub const F7: FReg = FReg(7);
pub const F8: FReg = FReg(8);
pub const F9: FReg = FReg(9);
pub const F10: FReg = FReg(10);
pub const F11: FReg = FReg(11);
pub const F12: FReg = FReg(12);
pub const F13: FReg = FReg(13);
pub const F14: FReg = FReg(14);
pub const F15: FReg = FReg(15);
pub const F16: FReg = FReg(16);
pub const F17: FReg = FReg(17);
pub const F18: FReg = FReg(18);
pub const F19: FReg = FReg(19);
pub const F20: FReg = FReg(20);
pub const F21: FReg = FReg(21);
pub const F22: FReg = FReg(22);
pub const F23: FReg = FReg(23);
pub const F24: FReg = FReg(24);
pub const F25: FReg = FReg(25);
pub const F26: FReg = FReg(26);
pub const F27: FReg = FReg(27);
pub const F28: FReg = FReg(28);
pub const F29: FReg = FReg(29);
pub const F30: FReg = FReg(30);
pub const F31: FReg = FReg(31);

impl Reg {
    pub fn asm(self) -> u32 {
        match self {
            REG_SP => 31,
            REG_ZERO => 31,
            _ => self.0 as u32,
        }
    }

    pub fn is_gpr(self) -> bool {
        self.0 <= 30
    }

    pub fn is_gpr_or_zero(self) -> bool {
        self.is_gpr() || self == REG_ZERO
    }

    pub fn is_gpr_or_sp(self) -> bool {
        self.is_gpr() || self == REG_SP
    }
}

impl From<Reg> for Register {
    fn from(reg: Reg) -> Register {
        if reg.0 < 31 {
            Register::new(reg.0)
        } else if reg == REG_ZERO {
            arm64_asm::REG_ZERO
        } else {
            assert_eq!(reg, REG_SP);
            arm64_asm::REG_SP
        }
    }
}

impl FReg {
    pub fn asm(self) -> u32 {
        assert!(self.0 < 32);
        self.0 as u32
    }
}

pub static PARAM_OFFSET: i32 = 16;

pub fn next_param_offset(param_offset: i32, _: SourceType) -> i32 {
    param_offset + 8
}
<|endoftext|>


<commit_before>use raytracer::animator::CameraKeyframe;
use raytracer::Renderer;
use scene::{Camera, Scene};
use std::sync::{Arc, Semaphore};
use vec3::Vec3;

pub struct Animator {
    pub fps: f64,
    pub animate_from: f64, // Number of frames is rounded down to nearest frame
    pub animate_to: f64,
    pub starting_frame_number: uint, // For filename
    pub renderer: Renderer
}

// TODO: Non-linear interpolation
// TODO: Improve keyframes (sort/order them as we don't need dynamic keyframe insertion)
impl Animator {
    // TODO: make this a Surface iterator so both single frame and animation
    // process flows are similar
    pub fn animate(&self, camera: Camera, shared_scene: Arc<Scene>, filename: &str) {
        let animate_start = ::time::get_time();
        let length = self.animate_to - self.animate_from;
        let total_frames = (self.fps * length).floor() as uint;

        let sema = Arc::new(Semaphore::new(::std::os::num_cpus() as int));

        for frame_number in range(0, total_frames) {
            let time = self.animate_from + frame_number as f64 / self.fps;
            let lerped_camera = Animator::lerp_camera(&camera, time);
            let frame_data = self.renderer.render(lerped_camera, shared_scene.clone());

            let file_frame_number = self.starting_frame_number + frame_number;
            let shared_name = format!("{}{:06u}.ppm", filename, file_frame_number);

            let child_sema = sema.clone();
            sema.acquire();
            spawn(proc() {
                ::util::export::to_ppm(frame_data, shared_name.as_slice());
                child_sema.release();
            });

            ::util::print_progress("*** Frame", animate_start, frame_number + 1 as uint, total_frames);
            println!("");
        }

        sema.acquire();
    }

    fn get_neighbour_keyframes(keyframes: Vec<CameraKeyframe>, time: f64)
                               -> (CameraKeyframe, CameraKeyframe, f64) {

        if keyframes.len() <= 1 {
            fail!("Not enough keyframes to interpolate: got: {} expected: >= 2", keyframes.len());
        }

        // Get the two keyframes inbetween current time
        let mut first = &keyframes[0];
        let mut second = &keyframes[1];

        for keyframe in keyframes.iter() {
            if keyframe.time <= time && time - keyframe.time >= first.time - time {
                first = keyframe;
            }

            if keyframe.time > time &&
               (keyframe.time - time < second.time - time || second.time < time) {
                second = keyframe;
            }
        }

        let keyframe_length = second.time - first.time;
        let alpha = (time - first.time) / keyframe_length;

        (first.clone(), second.clone(), alpha)
    }

    fn lerp_camera(camera: &Camera, time: f64) -> Camera {
        let keyframes = match camera.keyframes.clone() {
            Some(k) => k,
            None => fail!("Cannot lerp a camera with no keyframes!")
        };

        let (first, second, alpha) = Animator::get_neighbour_keyframes(keyframes, time);

        let lerped_position = Vec3::lerp(&first.position, &second.position, alpha);
        let lerped_look_at  = Vec3::lerp(&first.look_at, &second.look_at, alpha);
        let lerped_up       = Vec3::lerp(&first.up, &second.up, alpha);

        let mut lerped_camera = Camera::new(
            lerped_position,
            lerped_look_at,
            lerped_up,
            camera.fov_deg,
            camera.image_width,
            camera.image_height,
        );

        lerped_camera.keyframes = camera.keyframes.clone();
        lerped_camera
    }
}
<commit_msg>Have only one frame being written to disk at a time<commit_after>use raytracer::animator::CameraKeyframe;
use raytracer::Renderer;
use scene::{Camera, Scene};
use std::sync::{Arc, Semaphore};
use vec3::Vec3;

pub struct Animator {
    pub fps: f64,
    pub animate_from: f64, // Number of frames is rounded down to nearest frame
    pub animate_to: f64,
    pub starting_frame_number: uint, // For filename
    pub renderer: Renderer
}

// TODO: Non-linear interpolation
// TODO: Improve keyframes (sort/order them as we don't need dynamic keyframe insertion)
impl Animator {
    // TODO: make this a Surface iterator so both single frame and animation
    // process flows are similar
    pub fn animate(&self, camera: Camera, shared_scene: Arc<Scene>, filename: &str) {
        let animate_start = ::time::get_time();
        let length = self.animate_to - self.animate_from;
        let total_frames = (self.fps * length).floor() as uint;

        let sema = Arc::new(Semaphore::new(1));

        for frame_number in range(0, total_frames) {
            let time = self.animate_from + frame_number as f64 / self.fps;
            let lerped_camera = Animator::lerp_camera(&camera, time);
            let frame_data = self.renderer.render(lerped_camera, shared_scene.clone());

            let file_frame_number = self.starting_frame_number + frame_number;
            let shared_name = format!("{}{:06u}.ppm", filename, file_frame_number);

            let child_sema = sema.clone();
            sema.acquire();
            spawn(proc() {
                ::util::export::to_ppm(frame_data, shared_name.as_slice());
                child_sema.release();
            });

            ::util::print_progress("*** Frame", animate_start, frame_number + 1 as uint, total_frames);
            println!("");
        }

        sema.acquire();
    }

    fn get_neighbour_keyframes(keyframes: Vec<CameraKeyframe>, time: f64)
                               -> (CameraKeyframe, CameraKeyframe, f64) {

        if keyframes.len() <= 1 {
            fail!("Not enough keyframes to interpolate: got: {} expected: >= 2", keyframes.len());
        }

        // Get the two keyframes inbetween current time
        let mut first = &keyframes[0];
        let mut second = &keyframes[1];

        for keyframe in keyframes.iter() {
            if keyframe.time <= time && time - keyframe.time >= first.time - time {
                first = keyframe;
            }

            if keyframe.time > time &&
               (keyframe.time - time < second.time - time || second.time < time) {
                second = keyframe;
            }
        }

        let keyframe_length = second.time - first.time;
        let alpha = (time - first.time) / keyframe_length;

        (first.clone(), second.clone(), alpha)
    }

    fn lerp_camera(camera: &Camera, time: f64) -> Camera {
        let keyframes = match camera.keyframes.clone() {
            Some(k) => k,
            None => fail!("Cannot lerp a camera with no keyframes!")
        };

        let (first, second, alpha) = Animator::get_neighbour_keyframes(keyframes, time);

        let lerped_position = Vec3::lerp(&first.position, &second.position, alpha);
        let lerped_look_at  = Vec3::lerp(&first.look_at, &second.look_at, alpha);
        let lerped_up       = Vec3::lerp(&first.up, &second.up, alpha);

        let mut lerped_camera = Camera::new(
            lerped_position,
            lerped_look_at,
            lerped_up,
            camera.fov_deg,
            camera.image_width,
            camera.image_height,
        );

        lerped_camera.keyframes = camera.keyframes.clone();
        lerped_camera
    }
}
<|endoftext|>


<commit_before>use std::ffi::OsStr;
use std::path::PathBuf;

use regex::RegexSet;

#[derive(Debug)]
struct Message {
    path: PathBuf,
    bad_lines: Vec<String>,
}

impl Message {
    fn new(path: PathBuf) -> Self {
        let content: String = std::fs::read_to_string(&path).unwrap();
        // we don't want the first letter after "error: ", "help: " ... to be capitalized
        // also no puncutation (except for "?" ?) at the end of a line
        let regex_set: RegexSet = RegexSet::new(&[
            r"error: [A-Z]",
            r"help: [A-Z]",
            r"warning: [A-Z]",
            r"note: [A-Z]",
            r"try this: [A-Z]",
            r"error: .*[.!]$",
            r"help: .*[.!]$",
            r"warning: .*[.!]$",
            r"note: .*[.!]$",
            r"try this: .*[.!]$",
        ])
        .unwrap();

        // sometimes the first character is capitalized and it is legal (like in "Iterator...") or
        // we want to ask a question ending in "?"
        let exceptions_set: RegexSet = RegexSet::new(&[
            r".*C-like enum variant discriminant is not portable to 32-bit targets",
            r".*did you mean `unix`?",
            r".*the arguments may be inverted...",
            r".*Intel x86 assembly syntax used",
            r".*AT&T x86 assembly syntax used",
            r".*remove .*the return type...",
            r"note: Clippy version: .*",
            r"the compiler unexpectedly panicked. this is a bug.",
        ])
        .unwrap();

        let bad_lines = content
            .lines()
            .filter(|line| regex_set.matches(line).matched_any())
            // ignore exceptions
            .filter(|line| !exceptions_set.matches(line).matched_any())
            .map(ToOwned::to_owned)
            .collect::<Vec<String>>();

        Message { path, bad_lines }
    }
}

#[test]
fn lint_message_convention() {
    // disable the test inside the rustc test suite
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }

    // make sure that lint messages:
    // * are not capitalized
    // * don't have puncuation at the end of the last sentence

    // these directories have interesting tests
    let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"]
        .iter()
        .map(PathBuf::from)
        .map(|p| {
            let base = PathBuf::from("tests");
            base.join(p)
        });

    // gather all .stderr files
    let tests = test_dirs
        .flat_map(|dir| {
            std::fs::read_dir(dir)
                .expect("failed to read dir")
                .map(|direntry| direntry.unwrap().path())
        })
        .filter(|file| matches!(file.extension().map(OsStr::to_str), Some(Some("stderr"))));

    // get all files that have any "bad lines" in them
    let bad_tests: Vec<Message> = tests
        .map(Message::new)
        .filter(|message| !message.bad_lines.is_empty())
        .collect();

    bad_tests.iter().for_each(|message| {
        eprintln!(
            "error: the test '{}' contained the following nonconforming lines :",
            message.path.display()
        );
        message.bad_lines.iter().for_each(|line| eprintln!("{}", line));
        eprintln!("\n\n");
    });

    eprintln!("\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed.");
    eprintln!("Check out the rustc-dev-guide for more information:");
    eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure\n\n\n");

    assert!(bad_tests.is_empty());
}
<commit_msg>use different example (C-like) for valid capitalized start of lint message<commit_after>use std::ffi::OsStr;
use std::path::PathBuf;

use regex::RegexSet;

#[derive(Debug)]
struct Message {
    path: PathBuf,
    bad_lines: Vec<String>,
}

impl Message {
    fn new(path: PathBuf) -> Self {
        let content: String = std::fs::read_to_string(&path).unwrap();
        // we don't want the first letter after "error: ", "help: " ... to be capitalized
        // also no puncutation (except for "?" ?) at the end of a line
        let regex_set: RegexSet = RegexSet::new(&[
            r"error: [A-Z]",
            r"help: [A-Z]",
            r"warning: [A-Z]",
            r"note: [A-Z]",
            r"try this: [A-Z]",
            r"error: .*[.!]$",
            r"help: .*[.!]$",
            r"warning: .*[.!]$",
            r"note: .*[.!]$",
            r"try this: .*[.!]$",
        ])
        .unwrap();

        // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
        // we want to ask a question ending in "?"
        let exceptions_set: RegexSet = RegexSet::new(&[
            r".*C-like enum variant discriminant is not portable to 32-bit targets",
            r".*did you mean `unix`?",
            r".*the arguments may be inverted...",
            r".*Intel x86 assembly syntax used",
            r".*AT&T x86 assembly syntax used",
            r".*remove .*the return type...",
            r"note: Clippy version: .*",
            r"the compiler unexpectedly panicked. this is a bug.",
        ])
        .unwrap();

        let bad_lines = content
            .lines()
            .filter(|line| regex_set.matches(line).matched_any())
            // ignore exceptions
            .filter(|line| !exceptions_set.matches(line).matched_any())
            .map(ToOwned::to_owned)
            .collect::<Vec<String>>();

        Message { path, bad_lines }
    }
}

#[test]
fn lint_message_convention() {
    // disable the test inside the rustc test suite
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }

    // make sure that lint messages:
    // * are not capitalized
    // * don't have puncuation at the end of the last sentence

    // these directories have interesting tests
    let test_dirs = ["ui", "ui-cargo", "ui-internal", "ui-toml"]
        .iter()
        .map(PathBuf::from)
        .map(|p| {
            let base = PathBuf::from("tests");
            base.join(p)
        });

    // gather all .stderr files
    let tests = test_dirs
        .flat_map(|dir| {
            std::fs::read_dir(dir)
                .expect("failed to read dir")
                .map(|direntry| direntry.unwrap().path())
        })
        .filter(|file| matches!(file.extension().map(OsStr::to_str), Some(Some("stderr"))));

    // get all files that have any "bad lines" in them
    let bad_tests: Vec<Message> = tests
        .map(Message::new)
        .filter(|message| !message.bad_lines.is_empty())
        .collect();

    bad_tests.iter().for_each(|message| {
        eprintln!(
            "error: the test '{}' contained the following nonconforming lines :",
            message.path.display()
        );
        message.bad_lines.iter().for_each(|line| eprintln!("{}", line));
        eprintln!("\n\n");
    });

    eprintln!("\n\n\nLint message should not start with a capital letter and should not have punctuation at the end of the message unless multiple sentences are needed.");
    eprintln!("Check out the rustc-dev-guide for more information:");
    eprintln!("https://rustc-dev-guide.rust-lang.org/diagnostics.html#diagnostic-structure\n\n\n");

    assert!(bad_tests.is_empty());
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use autocxx_parser::file_locations::FileLocationStrategy;
use miette::Diagnostic;
use proc_macro2::TokenStream;
use thiserror::Error;

use crate::{strip_system_headers, CppCodegenOptions, ParseError, RebuildDependencyRecorder};
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};

/// Errors returned during creation of a [`cc::Build`] from an include_cxx
/// macro.
#[derive(Error, Diagnostic, Debug)]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub enum BuilderError {
    #[error("cxx couldn't handle our generated bindings - could be a bug in autocxx: {0}")]
    InvalidCxx(cxx_gen::Error),
    #[error(transparent)]
    #[diagnostic(transparent)]
    ParseError(ParseError),
    #[error("we couldn't write the generated code to disk at {1}: {0}")]
    FileWriteFail(std::io::Error, PathBuf),
    #[error("no include_cpp! macro was found")]
    NoIncludeCxxMacrosFound,
    #[error("could not create a directory {1}: {0}")]
    UnableToCreateDirectory(std::io::Error, PathBuf),
}

#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub type BuilderBuild = cc::Build;

/// For test purposes only, a [`cc::Build`] and lists of Rust and C++
/// files generated.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub struct BuilderSuccess(pub BuilderBuild, pub Vec<PathBuf>, pub Vec<PathBuf>);

/// Results of a build.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub type BuilderResult = Result<BuilderSuccess, BuilderError>;

/// The context in which a builder object lives. Callbacks for various
/// purposes.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub trait BuilderContext {
    /// Perform any initialization specific to the context in which this
    /// builder lives.
    fn setup() {}

    /// Create a dependency recorder, if any.
    fn get_dependency_recorder() -> Option<Box<dyn RebuildDependencyRecorder>>;
}

/// An object to allow building of bindings from a `build.rs` file.
///
/// It would be unusual to use this directly - see the `autocxx_build` or
/// `autocxx_gen` crates.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub struct Builder<'a, BuilderContext> {
    rs_file: PathBuf,
    autocxx_incs: Vec<OsString>,
    extra_clang_args: Vec<String>,
    dependency_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
    custom_gendir: Option<PathBuf>,
    auto_allowlist: bool,
    cpp_codegen_options: CppCodegenOptions<'a>,
    // This member is to ensure that this type is parameterized
    // by a BuilderContext. The goal is to balance three needs:
    // (1) have most of the functionality over in autocxx_engine,
    // (2) expose this type to users of autocxx_build and to
    //     make it easy for callers simply to call Builder::new,
    // (3) ensure that such a Builder does a few tasks specific to its use
    // in a cargo environment.
    ctx: PhantomData<BuilderContext>,
}

impl<CTX: BuilderContext> Builder<'_, CTX> {
    #[doc(hidden)]
    pub fn new(
        rs_file: impl AsRef<Path>,
        autocxx_incs: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> Self {
        CTX::setup();
        Self {
            rs_file: rs_file.as_ref().to_path_buf(),
            autocxx_incs: autocxx_incs
                .into_iter()
                .map(|s| s.as_ref().to_os_string())
                .collect(),
            extra_clang_args: Vec::new(),
            dependency_recorder: CTX::get_dependency_recorder(),
            custom_gendir: None,
            auto_allowlist: false,
            cpp_codegen_options: CppCodegenOptions::default(),
            ctx: PhantomData,
        }
    }

    /// Specify extra arguments for clang.
    pub fn extra_clang_args(mut self, extra_clang_args: &[&str]) -> Self {
        self.extra_clang_args = extra_clang_args.iter().map(|s| s.to_string()).collect();
        self
    }

    /// Where to generate the code.
    pub fn custom_gendir(mut self, custom_gendir: PathBuf) -> Self {
        self.custom_gendir = Some(custom_gendir);
        self
    }

    /// Update C++ code generation options. See [`CppCodegenOptions`] for details.
    pub fn cpp_codegen_options<F>(mut self, modifier: F) -> Self
    where
        F: FnOnce(&mut CppCodegenOptions),
    {
        modifier(&mut self.cpp_codegen_options);
        self
    }

    /// Automatically discover uses of the C++ `ffi` mod and generate the allowlist
    /// from that.
    /// This is a highly experimental option, not currently recommended.
    /// It doesn't work in the following cases:
    /// * Static function calls on types within the FFI mod.
    /// * Anything inside a macro invocation.
    /// * You're using a different name for your `ffi` mod
    /// * You're using multiple FFI mods
    /// * You've got usages scattered across files beyond that with the
    ///   `include_cpp` invocation
    /// * You're using `use` statements to rename mods or items. If this
    /// proves to be a promising or helpful direction, autocxx would be happy
    /// to accept pull requests to remove some of these limitations.
    pub fn auto_allowlist(mut self, do_it: bool) -> Self {
        self.auto_allowlist = do_it;
        self
    }

    /// Whether to suppress inclusion of system headers (`memory`, `string` etc.)
    /// from generated C++ bindings code. This should not normally be used,
    /// but can occasionally be useful if you're reducing a test case and you
    /// have a preprocessed header file which already contains absolutely everything
    /// that the bindings could ever need.
    pub fn suppress_system_headers(mut self, do_it: bool) -> Self {
        self.cpp_codegen_options.suppress_system_headers = do_it;
        self
    }

    /// An annotation optionally to include on each C++ function.
    /// For example to export the symbol from a library.
    pub fn cxx_impl_annotations(mut self, cxx_impl_annotations: Option<String>) -> Self {
        self.cpp_codegen_options.cxx_impl_annotations = cxx_impl_annotations;
        self
    }

    /// Whether to skip using [`cxx_gen`] to generate the C++ code,
    /// so that some other process can handle that.
    pub fn skip_cxx_gen(mut self, skip_cxx_gen: bool) -> Self {
        self.cpp_codegen_options.skip_cxx_gen = skip_cxx_gen;
        self
    }

    /// Build autocxx C++ files and return a [`cc::Build`] you can use to build
    /// more from a build.rs file.
    ///
    /// The error type returned by this function supports [`miette::Diagnostic`],
    /// so if you use the `miette` crate and its `fancy` feature, then simply
    /// return a `miette::Result` from your main function, you should get nicely
    /// printed diagnostics.
    pub fn build(self) -> Result<BuilderBuild, BuilderError> {
        self.build_listing_files().map(|r| r.0)
    }

    /// For use in tests only, this does the build and returns additional information
    /// about the files generated which can subsequently be examined for correctness.
    pub fn build_listing_files(self) -> Result<BuilderSuccess, BuilderError> {
        let clang_args = &self
            .extra_clang_args
            .iter()
            .map(|s| &s[..])
            .collect::<Vec<_>>();
        rust_version_check();
        let gen_location_strategy = match self.custom_gendir {
            None => FileLocationStrategy::new(),
            Some(custom_dir) => FileLocationStrategy::Custom(custom_dir),
        };
        let incdir = gen_location_strategy.get_include_dir();
        ensure_created(&incdir)?;
        let cxxdir = gen_location_strategy.get_cxx_dir();
        ensure_created(&cxxdir)?;
        let rsdir = gen_location_strategy.get_rs_dir();
        ensure_created(&rsdir)?;
        // We are incredibly unsophisticated in our directory arrangement here
        // compared to cxx. I have no doubt that we will need to replicate just
        // about everything cxx does, in due course...
        // Write cxx.h to that location, as it may be needed by
        // some of our generated code.
        write_to_file(
            &incdir,
            "cxx.h",
            &Self::get_cxx_header_bytes(self.cpp_codegen_options.suppress_system_headers),
        )?;

        let autocxx_inc = build_autocxx_inc(self.autocxx_incs, &incdir);
        gen_location_strategy.set_cargo_env_vars_for_build();

        let mut parsed_file = crate::parse_file(self.rs_file, self.auto_allowlist)
            .map_err(BuilderError::ParseError)?;
        parsed_file
            .resolve_all(
                autocxx_inc,
                clang_args,
                self.dependency_recorder,
                &self.cpp_codegen_options,
            )
            .map_err(BuilderError::ParseError)?;
        let mut counter = 0;
        let mut builder = cc::Build::new();
        builder.cpp(true);
        if std::env::var_os("AUTOCXX_ASAN").is_some() {
            builder.flag_if_supported("-fsanitize=address");
        }
        let mut generated_rs = Vec::new();
        let mut generated_cpp = Vec::new();
        builder.includes(parsed_file.include_dirs());
        for include_cpp in parsed_file.get_cpp_buildables() {
            let generated_code = include_cpp
                .generate_h_and_cxx(&self.cpp_codegen_options)
                .map_err(BuilderError::InvalidCxx)?;
            for filepair in generated_code.0 {
                let fname = format!("gen{}.cxx", counter);
                counter += 1;
                if let Some(implementation) = &filepair.implementation {
                    let gen_cxx_path = write_to_file(&cxxdir, &fname, implementation)?;
                    builder.file(&gen_cxx_path);
                    generated_cpp.push(gen_cxx_path);
                }
                write_to_file(&incdir, &filepair.header_name, &filepair.header)?;
                generated_cpp.push(incdir.join(filepair.header_name));
            }
        }

        for include_cpp in parsed_file.get_rs_buildables() {
            let rs = include_cpp.generate_rs();
            generated_rs.push(write_rs_to_file(
                &rsdir,
                &include_cpp.config.get_rs_filename(),
                rs,
            )?);
        }
        if counter == 0 {
            Err(BuilderError::NoIncludeCxxMacrosFound)
        } else {
            Ok(BuilderSuccess(builder, generated_rs, generated_cpp))
        }
    }

    fn get_cxx_header_bytes(suppress_system_headers: bool) -> Vec<u8> {
        strip_system_headers(crate::HEADER.as_bytes().to_vec(), suppress_system_headers)
    }
}

fn ensure_created(dir: &Path) -> Result<(), BuilderError> {
    std::fs::create_dir_all(dir)
        .map_err(|e| BuilderError::UnableToCreateDirectory(e, dir.to_path_buf()))
}

fn build_autocxx_inc<I, T>(paths: I, extra_path: &Path) -> Vec<PathBuf>
where
    I: IntoIterator<Item = T>,
    T: AsRef<OsStr>,
{
    paths
        .into_iter()
        .map(|p| PathBuf::from(p.as_ref()))
        .chain(std::iter::once(extra_path.to_path_buf()))
        .collect()
}

fn write_to_file(dir: &Path, filename: &str, content: &[u8]) -> Result<PathBuf, BuilderError> {
    let path = dir.join(filename);
    try_write_to_file(&path, content).map_err(|e| BuilderError::FileWriteFail(e, path.clone()))?;
    Ok(path)
}

fn try_write_to_file(path: &Path, content: &[u8]) -> std::io::Result<()> {
    let mut f = File::create(path)?;
    f.write_all(content)
}

fn write_rs_to_file(
    dir: &Path,
    filename: &str,
    content: TokenStream,
) -> Result<PathBuf, BuilderError> {
    write_to_file(dir, filename, content.to_string().as_bytes())
}

fn rust_version_check() {
    if !version_check::is_min_version("1.54.0").unwrap_or(false) {
        panic!("Rust 1.54 or later is required.")
    }
}
<commit_msg>Show `autocxx::Builder::new` in docs<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use autocxx_parser::file_locations::FileLocationStrategy;
use miette::Diagnostic;
use proc_macro2::TokenStream;
use thiserror::Error;

use crate::{strip_system_headers, CppCodegenOptions, ParseError, RebuildDependencyRecorder};
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs::File;
use std::io::Write;
use std::marker::PhantomData;
use std::path::{Path, PathBuf};

/// Errors returned during creation of a [`cc::Build`] from an include_cxx
/// macro.
#[derive(Error, Diagnostic, Debug)]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub enum BuilderError {
    #[error("cxx couldn't handle our generated bindings - could be a bug in autocxx: {0}")]
    InvalidCxx(cxx_gen::Error),
    #[error(transparent)]
    #[diagnostic(transparent)]
    ParseError(ParseError),
    #[error("we couldn't write the generated code to disk at {1}: {0}")]
    FileWriteFail(std::io::Error, PathBuf),
    #[error("no include_cpp! macro was found")]
    NoIncludeCxxMacrosFound,
    #[error("could not create a directory {1}: {0}")]
    UnableToCreateDirectory(std::io::Error, PathBuf),
}

#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub type BuilderBuild = cc::Build;

/// For test purposes only, a [`cc::Build`] and lists of Rust and C++
/// files generated.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub struct BuilderSuccess(pub BuilderBuild, pub Vec<PathBuf>, pub Vec<PathBuf>);

/// Results of a build.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub type BuilderResult = Result<BuilderSuccess, BuilderError>;

/// The context in which a builder object lives. Callbacks for various
/// purposes.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub trait BuilderContext {
    /// Perform any initialization specific to the context in which this
    /// builder lives.
    fn setup() {}

    /// Create a dependency recorder, if any.
    fn get_dependency_recorder() -> Option<Box<dyn RebuildDependencyRecorder>>;
}

/// An object to allow building of bindings from a `build.rs` file.
///
/// It would be unusual to use this directly - see the `autocxx_build` or
/// `autocxx_gen` crates.
#[cfg_attr(feature = "nightly", doc(cfg(feature = "build")))]
pub struct Builder<'a, BuilderContext> {
    rs_file: PathBuf,
    autocxx_incs: Vec<OsString>,
    extra_clang_args: Vec<String>,
    dependency_recorder: Option<Box<dyn RebuildDependencyRecorder>>,
    custom_gendir: Option<PathBuf>,
    auto_allowlist: bool,
    cpp_codegen_options: CppCodegenOptions<'a>,
    // This member is to ensure that this type is parameterized
    // by a BuilderContext. The goal is to balance three needs:
    // (1) have most of the functionality over in autocxx_engine,
    // (2) expose this type to users of autocxx_build and to
    //     make it easy for callers simply to call Builder::new,
    // (3) ensure that such a Builder does a few tasks specific to its use
    // in a cargo environment.
    ctx: PhantomData<BuilderContext>,
}

impl<CTX: BuilderContext> Builder<'_, CTX> {
    pub fn new(
        rs_file: impl AsRef<Path>,
        autocxx_incs: impl IntoIterator<Item = impl AsRef<OsStr>>,
    ) -> Self {
        CTX::setup();
        Self {
            rs_file: rs_file.as_ref().to_path_buf(),
            autocxx_incs: autocxx_incs
                .into_iter()
                .map(|s| s.as_ref().to_os_string())
                .collect(),
            extra_clang_args: Vec::new(),
            dependency_recorder: CTX::get_dependency_recorder(),
            custom_gendir: None,
            auto_allowlist: false,
            cpp_codegen_options: CppCodegenOptions::default(),
            ctx: PhantomData,
        }
    }

    /// Specify extra arguments for clang.
    pub fn extra_clang_args(mut self, extra_clang_args: &[&str]) -> Self {
        self.extra_clang_args = extra_clang_args.iter().map(|s| s.to_string()).collect();
        self
    }

    /// Where to generate the code.
    pub fn custom_gendir(mut self, custom_gendir: PathBuf) -> Self {
        self.custom_gendir = Some(custom_gendir);
        self
    }

    /// Update C++ code generation options. See [`CppCodegenOptions`] for details.
    pub fn cpp_codegen_options<F>(mut self, modifier: F) -> Self
    where
        F: FnOnce(&mut CppCodegenOptions),
    {
        modifier(&mut self.cpp_codegen_options);
        self
    }

    /// Automatically discover uses of the C++ `ffi` mod and generate the allowlist
    /// from that.
    /// This is a highly experimental option, not currently recommended.
    /// It doesn't work in the following cases:
    /// * Static function calls on types within the FFI mod.
    /// * Anything inside a macro invocation.
    /// * You're using a different name for your `ffi` mod
    /// * You're using multiple FFI mods
    /// * You've got usages scattered across files beyond that with the
    ///   `include_cpp` invocation
    /// * You're using `use` statements to rename mods or items. If this
    /// proves to be a promising or helpful direction, autocxx would be happy
    /// to accept pull requests to remove some of these limitations.
    pub fn auto_allowlist(mut self, do_it: bool) -> Self {
        self.auto_allowlist = do_it;
        self
    }

    /// Whether to suppress inclusion of system headers (`memory`, `string` etc.)
    /// from generated C++ bindings code. This should not normally be used,
    /// but can occasionally be useful if you're reducing a test case and you
    /// have a preprocessed header file which already contains absolutely everything
    /// that the bindings could ever need.
    pub fn suppress_system_headers(mut self, do_it: bool) -> Self {
        self.cpp_codegen_options.suppress_system_headers = do_it;
        self
    }

    /// An annotation optionally to include on each C++ function.
    /// For example to export the symbol from a library.
    pub fn cxx_impl_annotations(mut self, cxx_impl_annotations: Option<String>) -> Self {
        self.cpp_codegen_options.cxx_impl_annotations = cxx_impl_annotations;
        self
    }

    /// Whether to skip using [`cxx_gen`] to generate the C++ code,
    /// so that some other process can handle that.
    pub fn skip_cxx_gen(mut self, skip_cxx_gen: bool) -> Self {
        self.cpp_codegen_options.skip_cxx_gen = skip_cxx_gen;
        self
    }

    /// Build autocxx C++ files and return a [`cc::Build`] you can use to build
    /// more from a build.rs file.
    ///
    /// The error type returned by this function supports [`miette::Diagnostic`],
    /// so if you use the `miette` crate and its `fancy` feature, then simply
    /// return a `miette::Result` from your main function, you should get nicely
    /// printed diagnostics.
    pub fn build(self) -> Result<BuilderBuild, BuilderError> {
        self.build_listing_files().map(|r| r.0)
    }

    /// For use in tests only, this does the build and returns additional information
    /// about the files generated which can subsequently be examined for correctness.
    pub fn build_listing_files(self) -> Result<BuilderSuccess, BuilderError> {
        let clang_args = &self
            .extra_clang_args
            .iter()
            .map(|s| &s[..])
            .collect::<Vec<_>>();
        rust_version_check();
        let gen_location_strategy = match self.custom_gendir {
            None => FileLocationStrategy::new(),
            Some(custom_dir) => FileLocationStrategy::Custom(custom_dir),
        };
        let incdir = gen_location_strategy.get_include_dir();
        ensure_created(&incdir)?;
        let cxxdir = gen_location_strategy.get_cxx_dir();
        ensure_created(&cxxdir)?;
        let rsdir = gen_location_strategy.get_rs_dir();
        ensure_created(&rsdir)?;
        // We are incredibly unsophisticated in our directory arrangement here
        // compared to cxx. I have no doubt that we will need to replicate just
        // about everything cxx does, in due course...
        // Write cxx.h to that location, as it may be needed by
        // some of our generated code.
        write_to_file(
            &incdir,
            "cxx.h",
            &Self::get_cxx_header_bytes(self.cpp_codegen_options.suppress_system_headers),
        )?;

        let autocxx_inc = build_autocxx_inc(self.autocxx_incs, &incdir);
        gen_location_strategy.set_cargo_env_vars_for_build();

        let mut parsed_file = crate::parse_file(self.rs_file, self.auto_allowlist)
            .map_err(BuilderError::ParseError)?;
        parsed_file
            .resolve_all(
                autocxx_inc,
                clang_args,
                self.dependency_recorder,
                &self.cpp_codegen_options,
            )
            .map_err(BuilderError::ParseError)?;
        let mut counter = 0;
        let mut builder = cc::Build::new();
        builder.cpp(true);
        if std::env::var_os("AUTOCXX_ASAN").is_some() {
            builder.flag_if_supported("-fsanitize=address");
        }
        let mut generated_rs = Vec::new();
        let mut generated_cpp = Vec::new();
        builder.includes(parsed_file.include_dirs());
        for include_cpp in parsed_file.get_cpp_buildables() {
            let generated_code = include_cpp
                .generate_h_and_cxx(&self.cpp_codegen_options)
                .map_err(BuilderError::InvalidCxx)?;
            for filepair in generated_code.0 {
                let fname = format!("gen{}.cxx", counter);
                counter += 1;
                if let Some(implementation) = &filepair.implementation {
                    let gen_cxx_path = write_to_file(&cxxdir, &fname, implementation)?;
                    builder.file(&gen_cxx_path);
                    generated_cpp.push(gen_cxx_path);
                }
                write_to_file(&incdir, &filepair.header_name, &filepair.header)?;
                generated_cpp.push(incdir.join(filepair.header_name));
            }
        }

        for include_cpp in parsed_file.get_rs_buildables() {
            let rs = include_cpp.generate_rs();
            generated_rs.push(write_rs_to_file(
                &rsdir,
                &include_cpp.config.get_rs_filename(),
                rs,
            )?);
        }
        if counter == 0 {
            Err(BuilderError::NoIncludeCxxMacrosFound)
        } else {
            Ok(BuilderSuccess(builder, generated_rs, generated_cpp))
        }
    }

    fn get_cxx_header_bytes(suppress_system_headers: bool) -> Vec<u8> {
        strip_system_headers(crate::HEADER.as_bytes().to_vec(), suppress_system_headers)
    }
}

fn ensure_created(dir: &Path) -> Result<(), BuilderError> {
    std::fs::create_dir_all(dir)
        .map_err(|e| BuilderError::UnableToCreateDirectory(e, dir.to_path_buf()))
}

fn build_autocxx_inc<I, T>(paths: I, extra_path: &Path) -> Vec<PathBuf>
where
    I: IntoIterator<Item = T>,
    T: AsRef<OsStr>,
{
    paths
        .into_iter()
        .map(|p| PathBuf::from(p.as_ref()))
        .chain(std::iter::once(extra_path.to_path_buf()))
        .collect()
}

fn write_to_file(dir: &Path, filename: &str, content: &[u8]) -> Result<PathBuf, BuilderError> {
    let path = dir.join(filename);
    try_write_to_file(&path, content).map_err(|e| BuilderError::FileWriteFail(e, path.clone()))?;
    Ok(path)
}

fn try_write_to_file(path: &Path, content: &[u8]) -> std::io::Result<()> {
    let mut f = File::create(path)?;
    f.write_all(content)
}

fn write_rs_to_file(
    dir: &Path,
    filename: &str,
    content: TokenStream,
) -> Result<PathBuf, BuilderError> {
    write_to_file(dir, filename, content.to_string().as_bytes())
}

fn rust_version_check() {
    if !version_check::is_min_version("1.54.0").unwrap_or(false) {
        panic!("Rust 1.54 or later is required.")
    }
}
<|endoftext|>


<commit_before>use super::scope::CowScope;
use super::CompileError;
use super::lexer::{Token, Symbol, Operator};
use super::function::Function;
use super::parser::{self, Parser, TokenStream};
use super::identifier::{IdMap, Identifier};
use super::expr::Expression;
use std::collections::VecMap;

/// Represents a function definition written in synthizer
#[derive(Show)]
pub struct FunctionDef {
	statement: Statement,
	args: VecMap<Option<f32>>, // Default arguments
	pub ident: Identifier,
}
impl<'a> Parser<'a> for FunctionDef {
	/// Parse a function definition from a token stream. Scope is used to find function definitions
	fn parse(tokens: TokenStream<'a>, scope: CowScope<'a>) -> Result<FunctionDef, CompileError> {
		let mut tokens = tokens;
		let mut args = VecMap::new();

		try!(expect!(tokens.next(), Token::Symbol(Symbol::LeftBracket), "expected `[`, got `{}`"));

		let fn_ident = try!(expect_value!(tokens.next(), Token::Ident, "expected function name, got `{}`"));

		loop {
			let next = tokens.next();
			if expect!(next, Token::Symbol(Symbol::RightBracket)).is_ok() {
				break;
			}
			let pos = next.map(|x| x.pos);
			let arg_ident = try!(expect_value!(next, Token::Ident, "expected argument name, got `{}`"));
			if args.contains_key(&arg_ident) {
				return Err(CompileError::new(format!("argument {} defined twice", arg_ident)).with_pos(pos.unwrap()));
			}

			let next = tokens.next();
			let (pos, mut token) = (next.map(|x| x.pos), next.map(|x| x.token));
			if let Some(Token::Symbol(Symbol::Equals)) = token {
				let nexttoken = tokens.next();
				let value = if let Ok(_) = expect!(nexttoken, Token::Operator(Operator::Sub)) {
					-try!(expect_value!(tokens.next(), Token::Const, "expected numerical constant, got `{}`"))
				} else {
					try!(expect_value!(nexttoken, Token::Const, "expected numerical constant, got `{}`"))
				};
				args.insert(arg_ident, Some(value));
				token = tokens.next().map(|x| x.token);
			}
			match token {
				Some(Token::Symbol(Symbol::Comma)) => {
					if !args.contains_key(&arg_ident) {
						args.insert(arg_ident, None);
					}
				}
				Some(Token::Symbol(Symbol::RightBracket)) => {
					if !args.contains_key(&arg_ident) {
						args.insert(arg_ident, None);
					}
					break;
				}
				Some(token) => return Err(CompileError::new(format!("expected `=`, `,` or `]`, got `{}`", token)).with_pos(pos.unwrap())),
				None => return Err(CompileError::new_static("expected `=`, `,` or `]`, got EOF")),
			}
		}
		//try!(expect!(iter.next().map(|(_, x)| x), Token::Symbol(Symbol::Colon), "expected `:` following function argument declaration, got `{}`"));

		let statement = if !tokens.is_empty() {
			try!(Parser::parse(tokens, scope))
		} else {
			return Err(CompileError::new_static("expected block, got EOF"));
		};

		Ok(FunctionDef {
			statement: statement,
			args: args,
			ident: fn_ident,
		})
	}
}
impl Function for FunctionDef {
	fn call(&self, _: CowScope, _: &IdMap) -> Result<f32, CompileError> {
		unimplemented!();
	}
}

#[derive(Show)]
enum Statement {
	Block(Vec<(Option<Expression>, Operator, Statement)>),
	Expr(Expression),
}

impl<'a> Parser<'a> for Statement {
	fn parse(tokens: TokenStream<'a>, scope: CowScope<'a>) -> Result<Statement, CompileError> {
		let mut tokens = tokens;
		let mut statements = Vec::new();
		match expect!(tokens.peek(0), Token::Symbol(Symbol::LeftBrace)) {
			// If it starts with `{` it's a block
			Ok(_) => {
				tokens.next(); // consume brace
				loop {
					let operator = match tokens.next().map(|x| x.token) {
						Some(Token::Operator(op)) => op,
						Some(Token::Newline) => continue,
						Some(Token::Symbol(Symbol::RightBrace)) => break,
						Some(x) =>
							return Err(CompileError::new(format!("expected operator, newline or }}, got {}", x)).with_pos(tokens.peek(0).map(|x| x.pos).unwrap())),
						None =>
							return Err(CompileError::new_static("expected operator, newline or `}`, got EOF"))
					};
					let mut condition = None;
					let start_pos = tokens.pos();
					let mut pos;
					if let Some(Token::Symbol(Symbol::LeftBrace)) = tokens.peek(0).map(|x| x.token) {
						tokens = try!(parser::match_paren(tokens, Token::Symbol(Symbol::LeftBrace), Token::Symbol(Symbol::RightBrace)));
					}
					loop {
						pos = tokens.pos();
						match tokens.next().map(|x| x.token) {
							Some(Token::Newline) => break,
							Some(Token::Symbol(Symbol::QuestionMark)) => {
								// parse condition
								let start_pos = pos;
								let mut pos;
								loop {
									pos = tokens.pos();
									match tokens.next().map(|x| x.token) {
										Some(Token::Newline) => break,
										Some(_) => { },
										None => return Err(CompileError::new_static("expected newline to end condition, got EOF")),
									}
								}
								condition = Some(try!(Parser::parse(tokens.slice(start_pos+1, pos), scope.clone())));
								break;
							}
							Some(_) => { }
							None =>
								return Err(CompileError::new_static("expected newline or `?` to end expression, got EOF")),
						}
					}
					let statement = try!(Parser::parse(tokens.slice(start_pos, pos), scope.clone()));
					statements.push((condition, operator, statement));
				}
			}

			// otherwise it's an expression
			Err(_) => {
				return Ok(Statement::Expr(try!(Parser::parse(tokens, scope))));
			}
		}
		Ok(Statement::Block(statements))
	}
}
impl Function for Statement {
	fn call(&self, scope: CowScope, idmap: &IdMap) -> Result<f32, CompileError> {
		match self {
			&Statement::Expr(ref x) =>
				x.call(scope, idmap),
			_ => unimplemented!(),
		}
	}
}
<commit_msg>remove old commented out line<commit_after>use super::scope::CowScope;
use super::CompileError;
use super::lexer::{Token, Symbol, Operator};
use super::function::Function;
use super::parser::{self, Parser, TokenStream};
use super::identifier::{IdMap, Identifier};
use super::expr::Expression;
use std::collections::VecMap;

/// Represents a function definition written in synthizer
#[derive(Show)]
pub struct FunctionDef {
	statement: Statement,
	args: VecMap<Option<f32>>, // Default arguments
	pub ident: Identifier,
}
impl<'a> Parser<'a> for FunctionDef {
	/// Parse a function definition from a token stream. Scope is used to find function definitions
	fn parse(tokens: TokenStream<'a>, scope: CowScope<'a>) -> Result<FunctionDef, CompileError> {
		let mut tokens = tokens;
		let mut args = VecMap::new();

		try!(expect!(tokens.next(), Token::Symbol(Symbol::LeftBracket), "expected `[`, got `{}`"));

		let fn_ident = try!(expect_value!(tokens.next(), Token::Ident, "expected function name, got `{}`"));

		loop {
			let next = tokens.next();
			if expect!(next, Token::Symbol(Symbol::RightBracket)).is_ok() {
				break;
			}
			let pos = next.map(|x| x.pos);
			let arg_ident = try!(expect_value!(next, Token::Ident, "expected argument name, got `{}`"));
			if args.contains_key(&arg_ident) {
				return Err(CompileError::new(format!("argument {} defined twice", arg_ident)).with_pos(pos.unwrap()));
			}

			let next = tokens.next();
			let (pos, mut token) = (next.map(|x| x.pos), next.map(|x| x.token));
			if let Some(Token::Symbol(Symbol::Equals)) = token {
				let nexttoken = tokens.next();
				let value = if let Ok(_) = expect!(nexttoken, Token::Operator(Operator::Sub)) {
					-try!(expect_value!(tokens.next(), Token::Const, "expected numerical constant, got `{}`"))
				} else {
					try!(expect_value!(nexttoken, Token::Const, "expected numerical constant, got `{}`"))
				};
				args.insert(arg_ident, Some(value));
				token = tokens.next().map(|x| x.token);
			}
			match token {
				Some(Token::Symbol(Symbol::Comma)) => {
					if !args.contains_key(&arg_ident) {
						args.insert(arg_ident, None);
					}
				}
				Some(Token::Symbol(Symbol::RightBracket)) => {
					if !args.contains_key(&arg_ident) {
						args.insert(arg_ident, None);
					}
					break;
				}
				Some(token) => return Err(CompileError::new(format!("expected `=`, `,` or `]`, got `{}`", token)).with_pos(pos.unwrap())),
				None => return Err(CompileError::new_static("expected `=`, `,` or `]`, got EOF")),
			}
		}

		let statement = if !tokens.is_empty() {
			try!(Parser::parse(tokens, scope))
		} else {
			return Err(CompileError::new_static("expected block, got EOF"));
		};

		Ok(FunctionDef {
			statement: statement,
			args: args,
			ident: fn_ident,
		})
	}
}
impl Function for FunctionDef {
	fn call(&self, _: CowScope, _: &IdMap) -> Result<f32, CompileError> {
		unimplemented!();
	}
}

#[derive(Show)]
enum Statement {
	Block(Vec<(Option<Expression>, Operator, Statement)>),
	Expr(Expression),
}

impl<'a> Parser<'a> for Statement {
	fn parse(tokens: TokenStream<'a>, scope: CowScope<'a>) -> Result<Statement, CompileError> {
		let mut tokens = tokens;
		let mut statements = Vec::new();
		match expect!(tokens.peek(0), Token::Symbol(Symbol::LeftBrace)) {
			// If it starts with `{` it's a block
			Ok(_) => {
				tokens.next(); // consume brace
				loop {
					let operator = match tokens.next().map(|x| x.token) {
						Some(Token::Operator(op)) => op,
						Some(Token::Newline) => continue,
						Some(Token::Symbol(Symbol::RightBrace)) => break,
						Some(x) =>
							return Err(CompileError::new(format!("expected operator, newline or }}, got {}", x)).with_pos(tokens.peek(0).map(|x| x.pos).unwrap())),
						None =>
							return Err(CompileError::new_static("expected operator, newline or `}`, got EOF"))
					};
					let mut condition = None;
					let start_pos = tokens.pos();
					let mut pos;
					if let Some(Token::Symbol(Symbol::LeftBrace)) = tokens.peek(0).map(|x| x.token) {
						tokens = try!(parser::match_paren(tokens, Token::Symbol(Symbol::LeftBrace), Token::Symbol(Symbol::RightBrace)));
					}
					loop {
						pos = tokens.pos();
						match tokens.next().map(|x| x.token) {
							Some(Token::Newline) => break,
							Some(Token::Symbol(Symbol::QuestionMark)) => {
								// parse condition
								let start_pos = pos;
								let mut pos;
								loop {
									pos = tokens.pos();
									match tokens.next().map(|x| x.token) {
										Some(Token::Newline) => break,
										Some(_) => { },
										None => return Err(CompileError::new_static("expected newline to end condition, got EOF")),
									}
								}
								condition = Some(try!(Parser::parse(tokens.slice(start_pos+1, pos), scope.clone())));
								break;
							}
							Some(_) => { }
							None =>
								return Err(CompileError::new_static("expected newline or `?` to end expression, got EOF")),
						}
					}
					let statement = try!(Parser::parse(tokens.slice(start_pos, pos), scope.clone()));
					statements.push((condition, operator, statement));
				}
			}

			// otherwise it's an expression
			Err(_) => {
				return Ok(Statement::Expr(try!(Parser::parse(tokens, scope))));
			}
		}
		Ok(Statement::Block(statements))
	}
}
impl Function for Statement {
	fn call(&self, scope: CowScope, idmap: &IdMap) -> Result<f32, CompileError> {
		match self {
			&Statement::Expr(ref x) =>
				x.call(scope, idmap),
			_ => unimplemented!(),
		}
	}
}
<|endoftext|>