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

//! Feature gating
//!
//! This modules implements the gating necessary for preventing certain compiler
//! features from being used by default. This module will crawl a pre-expanded
//! AST to ensure that there are no features which are used that are not
//! enabled.
//!
//! Features are enabled in programs via the crate-level attributes of
//! `#![feature(...)]` with a comma-separated list of features.

use abi::RustIntrinsic;
use ast::NodeId;
use ast;
use attr;
use attr::AttrMetaMethods;
use codemap::Span;
use diagnostic::SpanHandler;
use visit;
use visit::Visitor;
use parse::token;

use std::slice;

/// This is a list of all known features since the beginning of time. This list
/// can never shrink, it may only be expanded (in order to prevent old programs
/// from failing to compile). The status of each feature may change, however.
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
    ("globs", Active),
    ("macro_rules", Active),
    ("struct_variant", Active),
    ("once_fns", Active),
    ("asm", Active),
    ("managed_boxes", Removed),
    ("non_ascii_idents", Active),
    ("thread_local", Active),
    ("link_args", Active),
    ("phase", Active),
    ("plugin_registrar", Active),
    ("log_syntax", Active),
    ("trace_macros", Active),
    ("concat_idents", Active),
    ("unsafe_destructor", Active),
    ("intrinsics", Active),
    ("lang_items", Active),

    ("simd", Active),
    ("default_type_params", Active),
    ("quote", Active),
    ("linkage", Active),
    ("struct_inherit", Active),
    ("overloaded_calls", Active),
    ("unboxed_closure_sugar", Active),

    ("quad_precision_float", Removed),

    ("rustc_diagnostic_macros", Active),
    ("unboxed_closures", Active),
    ("import_shadowing", Active),
    ("advanced_slice_patterns", Active),
    ("tuple_indexing", Active),
    ("associated_types", Active),
    ("visible_private_types", Active),
    ("slicing_syntax", Active),

    ("if_let", Active),
    ("while_let", Active),

    // if you change this list without updating src/doc/reference.md, cmr will be sad

    // A temporary feature gate used to enable parser extensions needed
    // to bootstrap fix for #5723.
    ("issue_5723_bootstrap", Accepted),

    // These are used to test this portion of the compiler, they don't actually
    // mean anything
    ("test_accepted_feature", Accepted),
    ("test_removed_feature", Removed),
];

enum Status {
    /// Represents an active feature that is currently being implemented or
    /// currently being considered for addition/removal.
    Active,

    /// Represents a feature which has since been removed (it was once Active)
    Removed,

    /// This language feature has since been Accepted (it was once Active)
    Accepted,
}

/// A set of features to be used by later passes.
pub struct Features {
    pub default_type_params: bool,
    pub overloaded_calls: bool,
    pub rustc_diagnostic_macros: bool,
    pub import_shadowing: bool,
    pub visible_private_types: bool,
    pub quote: bool,
}

impl Features {
    pub fn new() -> Features {
        Features {
            default_type_params: false,
            overloaded_calls: false,
            rustc_diagnostic_macros: false,
            import_shadowing: false,
            visible_private_types: false,
            quote: false,
        }
    }
}

struct Context<'a> {
    features: Vec<&'static str>,
    span_handler: &'a SpanHandler,
}

impl<'a> Context<'a> {
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
        if !self.has_feature(feature) {
            self.span_handler.span_err(span, explain);
            self.span_handler.span_note(span, format!("add #![feature({})] to the \
                                                       crate attributes to enable",
                                                      feature).as_slice());
        }
    }

    fn has_feature(&self, feature: &str) -> bool {
        self.features.iter().any(|n| n.as_slice() == feature)
    }
}

impl<'a, 'v> Visitor<'v> for Context<'a> {
    fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
        if !token::get_ident(id).get().is_ascii() {
            self.gate_feature("non_ascii_idents", sp,
                              "non-ascii idents are not fully supported.");
        }
    }

    fn visit_view_item(&mut self, i: &ast::ViewItem) {
        match i.node {
            ast::ViewItemUse(ref path) => {
                match path.node {
                    ast::ViewPathGlob(..) => {
                        self.gate_feature("globs", path.span,
                                          "glob import statements are \
                                           experimental and possibly buggy");
                    }
                    _ => {}
                }
            }
            ast::ViewItemExternCrate(..) => {
                for attr in i.attrs.iter() {
                    if attr.name().get() == "phase"{
                        self.gate_feature("phase", attr.span,
                                          "compile time crate loading is \
                                           experimental and possibly buggy");
                    }
                }
            }
        }
        visit::walk_view_item(self, i)
    }

    fn visit_item(&mut self, i: &ast::Item) {
        for attr in i.attrs.iter() {
            if attr.name().equiv(&("thread_local")) {
                self.gate_feature("thread_local", i.span,
                                  "`#[thread_local]` is an experimental feature, and does not \
                                  currently handle destructors. There is no corresponding \
                                  `#[task_local]` mapping to the task model");
            }
        }
        match i.node {
            ast::ItemEnum(ref def, _) => {
                for variant in def.variants.iter() {
                    match variant.node.kind {
                        ast::StructVariantKind(..) => {
                            self.gate_feature("struct_variant", variant.span,
                                              "enum struct variants are \
                                               experimental and possibly buggy");
                        }
                        _ => {}
                    }
                }
            }

            ast::ItemForeignMod(ref foreign_module) => {
                if attr::contains_name(i.attrs.as_slice(), "link_args") {
                    self.gate_feature("link_args", i.span,
                                      "the `link_args` attribute is not portable \
                                       across platforms, it is recommended to \
                                       use `#[link(name = \"foo\")]` instead")
                }
                if foreign_module.abi == RustIntrinsic {
                    self.gate_feature("intrinsics",
                                      i.span,
                                      "intrinsics are subject to change")
                }
            }

            ast::ItemFn(..) => {
                if attr::contains_name(i.attrs.as_slice(), "plugin_registrar") {
                    self.gate_feature("plugin_registrar", i.span,
                                      "compiler plugins are experimental and possibly buggy");
                }
            }

            ast::ItemStruct(..) => {
                if attr::contains_name(i.attrs.as_slice(), "simd") {
                    self.gate_feature("simd", i.span,
                                      "SIMD types are experimental and possibly buggy");
                }
            }

            ast::ItemImpl(_, _, _, ref items) => {
                if attr::contains_name(i.attrs.as_slice(),
                                       "unsafe_destructor") {
                    self.gate_feature("unsafe_destructor",
                                      i.span,
                                      "`#[unsafe_destructor]` allows too \
                                       many unsafe patterns and may be \
                                       removed in the future");
                }

                for item in items.iter() {
                    match *item {
                        ast::MethodImplItem(_) => {}
                        ast::TypeImplItem(ref typedef) => {
                            self.gate_feature("associated_types",
                                              typedef.span,
                                              "associated types are \
                                               experimental")
                        }
                    }
                }
            }

            _ => {}
        }

        visit::walk_item(self, i);
    }

    fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
        match *trait_item {
            ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {}
            ast::TypeTraitItem(ref ti) => {
                self.gate_feature("associated_types",
                                  ti.span,
                                  "associated types are experimental")
            }
        }
    }

    fn visit_mac(&mut self, macro: &ast::Mac) {
        let ast::MacInvocTT(ref path, _, _) = macro.node;
        let id = path.segments.last().unwrap().identifier;

        if id == token::str_to_ident("macro_rules") {
            self.gate_feature("macro_rules", path.span, "macro definitions are \
                not stable enough for use and are subject to change");
        }

        else if id == token::str_to_ident("asm") {
            self.gate_feature("asm", path.span, "inline assembly is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("log_syntax") {
            self.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("trace_macros") {
            self.gate_feature("trace_macros", path.span, "`trace_macros` is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("concat_idents") {
            self.gate_feature("concat_idents", path.span, "`concat_idents` is not \
                stable enough for use and is subject to change");
        }
    }

    fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
        if attr::contains_name(i.attrs.as_slice(), "linkage") {
            self.gate_feature("linkage", i.span,
                              "the `linkage` attribute is experimental \
                               and not portable across platforms")
        }
        visit::walk_foreign_item(self, i)
    }

    fn visit_ty(&mut self, t: &ast::Ty) {
        match t.node {
            ast::TyClosure(ref closure) if closure.onceness == ast::Once => {
                self.gate_feature("once_fns", t.span,
                                  "once functions are \
                                   experimental and likely to be removed");

            },
            ast::TyUnboxedFn(..) => {
                self.gate_feature("unboxed_closure_sugar",
                                  t.span,
                                  "unboxed closure trait sugar is experimental");
            }
            _ => {}
        }

        visit::walk_ty(self, t);
    }

    fn visit_expr(&mut self, e: &ast::Expr) {
        match e.node {
            ast::ExprUnboxedFn(..) => {
                self.gate_feature("unboxed_closures",
                                  e.span,
                                  "unboxed closures are a work-in-progress \
                                   feature with known bugs");
            }
            ast::ExprTupField(..) => {
                self.gate_feature("tuple_indexing",
                                  e.span,
                                  "tuple indexing is experimental");
            }
            ast::ExprIfLet(..) => {
                self.gate_feature("if_let", e.span,
                                  "`if let` syntax is experimental");
            }
            ast::ExprSlice(..) => {
                self.gate_feature("slicing_syntax",
                                  e.span,
                                  "slicing syntax is experimental");
            }
            ast::ExprWhileLet(..) => {
                self.gate_feature("while_let", e.span,
                                  "`while let` syntax is experimental");
            }
            _ => {}
        }
        visit::walk_expr(self, e);
    }

    fn visit_generics(&mut self, generics: &ast::Generics) {
        for type_parameter in generics.ty_params.iter() {
            match type_parameter.default {
                Some(ref ty) => {
                    self.gate_feature("default_type_params", ty.span,
                                      "default type parameters are \
                                       experimental and possibly buggy");
                }
                None => {}
            }
        }
        visit::walk_generics(self, generics);
    }

    fn visit_attribute(&mut self, attr: &ast::Attribute) {
        if attr::contains_name(slice::ref_slice(attr), "lang") {
            self.gate_feature("lang_items",
                              attr.span,
                              "language items are subject to change");
        }
    }

    fn visit_pat(&mut self, pattern: &ast::Pat) {
        match pattern.node {
            ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
                self.gate_feature("advanced_slice_patterns",
                                  pattern.span,
                                  "multiple-element slice matches anywhere \
                                   but at the end of a slice (e.g. \
                                   `[0, ..xs, 0]` are experimental")
            }
            _ => {}
        }
        visit::walk_pat(self, pattern)
    }

    fn visit_fn(&mut self,
                fn_kind: visit::FnKind<'v>,
                fn_decl: &'v ast::FnDecl,
                block: &'v ast::Block,
                span: Span,
                _: NodeId) {
        match fn_kind {
            visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
                self.gate_feature("intrinsics",
                                  span,
                                  "intrinsics are subject to change")
            }
            _ => {}
        }
        visit::walk_fn(self, fn_kind, fn_decl, block, span);
    }
}

pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
    let mut cx = Context {
        features: Vec::new(),
        span_handler: span_handler,
    };

    let mut unknown_features = Vec::new();

    for attr in krate.attrs.iter() {
        if !attr.check_name("feature") {
            continue
        }

        match attr.meta_item_list() {
            None => {
                span_handler.span_err(attr.span, "malformed feature attribute, \
                                                  expected #![feature(...)]");
            }
            Some(list) => {
                for mi in list.iter() {
                    let name = match mi.node {
                        ast::MetaWord(ref word) => (*word).clone(),
                        _ => {
                            span_handler.span_err(mi.span,
                                                  "malformed feature, expected just \
                                                   one word");
                            continue
                        }
                    };
                    match KNOWN_FEATURES.iter()
                                        .find(|& &(n, _)| name.equiv(&n)) {
                        Some(&(name, Active)) => { cx.features.push(name); }
                        Some(&(_, Removed)) => {
                            span_handler.span_err(mi.span, "feature has been removed");
                        }
                        Some(&(_, Accepted)) => {
                            span_handler.span_warn(mi.span, "feature has been added to Rust, \
                                                             directive not necessary");
                        }
                        None => {
                            unknown_features.push(mi.span);
                        }
                    }
                }
            }
        }
    }

    visit::walk_crate(&mut cx, krate);

    (Features {
        default_type_params: cx.has_feature("default_type_params"),
        overloaded_calls: cx.has_feature("overloaded_calls"),
        rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
        import_shadowing: cx.has_feature("import_shadowing"),
        visible_private_types: cx.has_feature("visible_private_types"),
        quote: cx.has_feature("quote"),
    },
    unknown_features)
}

<commit_msg>rollup merge of #18542 : jakub-/struct-inherit-feature-gate<commit_after>// Copyright 2013 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.

//! Feature gating
//!
//! This modules implements the gating necessary for preventing certain compiler
//! features from being used by default. This module will crawl a pre-expanded
//! AST to ensure that there are no features which are used that are not
//! enabled.
//!
//! Features are enabled in programs via the crate-level attributes of
//! `#![feature(...)]` with a comma-separated list of features.

use abi::RustIntrinsic;
use ast::NodeId;
use ast;
use attr;
use attr::AttrMetaMethods;
use codemap::Span;
use diagnostic::SpanHandler;
use visit;
use visit::Visitor;
use parse::token;

use std::slice;

/// This is a list of all known features since the beginning of time. This list
/// can never shrink, it may only be expanded (in order to prevent old programs
/// from failing to compile). The status of each feature may change, however.
static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[
    ("globs", Active),
    ("macro_rules", Active),
    ("struct_variant", Active),
    ("once_fns", Active),
    ("asm", Active),
    ("managed_boxes", Removed),
    ("non_ascii_idents", Active),
    ("thread_local", Active),
    ("link_args", Active),
    ("phase", Active),
    ("plugin_registrar", Active),
    ("log_syntax", Active),
    ("trace_macros", Active),
    ("concat_idents", Active),
    ("unsafe_destructor", Active),
    ("intrinsics", Active),
    ("lang_items", Active),

    ("simd", Active),
    ("default_type_params", Active),
    ("quote", Active),
    ("linkage", Active),
    ("struct_inherit", Removed),
    ("overloaded_calls", Active),
    ("unboxed_closure_sugar", Active),

    ("quad_precision_float", Removed),

    ("rustc_diagnostic_macros", Active),
    ("unboxed_closures", Active),
    ("import_shadowing", Active),
    ("advanced_slice_patterns", Active),
    ("tuple_indexing", Active),
    ("associated_types", Active),
    ("visible_private_types", Active),
    ("slicing_syntax", Active),

    ("if_let", Active),
    ("while_let", Active),

    // if you change this list without updating src/doc/reference.md, cmr will be sad

    // A temporary feature gate used to enable parser extensions needed
    // to bootstrap fix for #5723.
    ("issue_5723_bootstrap", Accepted),

    // These are used to test this portion of the compiler, they don't actually
    // mean anything
    ("test_accepted_feature", Accepted),
    ("test_removed_feature", Removed),
];

enum Status {
    /// Represents an active feature that is currently being implemented or
    /// currently being considered for addition/removal.
    Active,

    /// Represents a feature which has since been removed (it was once Active)
    Removed,

    /// This language feature has since been Accepted (it was once Active)
    Accepted,
}

/// A set of features to be used by later passes.
pub struct Features {
    pub default_type_params: bool,
    pub overloaded_calls: bool,
    pub rustc_diagnostic_macros: bool,
    pub import_shadowing: bool,
    pub visible_private_types: bool,
    pub quote: bool,
}

impl Features {
    pub fn new() -> Features {
        Features {
            default_type_params: false,
            overloaded_calls: false,
            rustc_diagnostic_macros: false,
            import_shadowing: false,
            visible_private_types: false,
            quote: false,
        }
    }
}

struct Context<'a> {
    features: Vec<&'static str>,
    span_handler: &'a SpanHandler,
}

impl<'a> Context<'a> {
    fn gate_feature(&self, feature: &str, span: Span, explain: &str) {
        if !self.has_feature(feature) {
            self.span_handler.span_err(span, explain);
            self.span_handler.span_note(span, format!("add #![feature({})] to the \
                                                       crate attributes to enable",
                                                      feature).as_slice());
        }
    }

    fn has_feature(&self, feature: &str) -> bool {
        self.features.iter().any(|n| n.as_slice() == feature)
    }
}

impl<'a, 'v> Visitor<'v> for Context<'a> {
    fn visit_ident(&mut self, sp: Span, id: ast::Ident) {
        if !token::get_ident(id).get().is_ascii() {
            self.gate_feature("non_ascii_idents", sp,
                              "non-ascii idents are not fully supported.");
        }
    }

    fn visit_view_item(&mut self, i: &ast::ViewItem) {
        match i.node {
            ast::ViewItemUse(ref path) => {
                match path.node {
                    ast::ViewPathGlob(..) => {
                        self.gate_feature("globs", path.span,
                                          "glob import statements are \
                                           experimental and possibly buggy");
                    }
                    _ => {}
                }
            }
            ast::ViewItemExternCrate(..) => {
                for attr in i.attrs.iter() {
                    if attr.name().get() == "phase"{
                        self.gate_feature("phase", attr.span,
                                          "compile time crate loading is \
                                           experimental and possibly buggy");
                    }
                }
            }
        }
        visit::walk_view_item(self, i)
    }

    fn visit_item(&mut self, i: &ast::Item) {
        for attr in i.attrs.iter() {
            if attr.name().equiv(&("thread_local")) {
                self.gate_feature("thread_local", i.span,
                                  "`#[thread_local]` is an experimental feature, and does not \
                                  currently handle destructors. There is no corresponding \
                                  `#[task_local]` mapping to the task model");
            }
        }
        match i.node {
            ast::ItemEnum(ref def, _) => {
                for variant in def.variants.iter() {
                    match variant.node.kind {
                        ast::StructVariantKind(..) => {
                            self.gate_feature("struct_variant", variant.span,
                                              "enum struct variants are \
                                               experimental and possibly buggy");
                        }
                        _ => {}
                    }
                }
            }

            ast::ItemForeignMod(ref foreign_module) => {
                if attr::contains_name(i.attrs.as_slice(), "link_args") {
                    self.gate_feature("link_args", i.span,
                                      "the `link_args` attribute is not portable \
                                       across platforms, it is recommended to \
                                       use `#[link(name = \"foo\")]` instead")
                }
                if foreign_module.abi == RustIntrinsic {
                    self.gate_feature("intrinsics",
                                      i.span,
                                      "intrinsics are subject to change")
                }
            }

            ast::ItemFn(..) => {
                if attr::contains_name(i.attrs.as_slice(), "plugin_registrar") {
                    self.gate_feature("plugin_registrar", i.span,
                                      "compiler plugins are experimental and possibly buggy");
                }
            }

            ast::ItemStruct(..) => {
                if attr::contains_name(i.attrs.as_slice(), "simd") {
                    self.gate_feature("simd", i.span,
                                      "SIMD types are experimental and possibly buggy");
                }
            }

            ast::ItemImpl(_, _, _, ref items) => {
                if attr::contains_name(i.attrs.as_slice(),
                                       "unsafe_destructor") {
                    self.gate_feature("unsafe_destructor",
                                      i.span,
                                      "`#[unsafe_destructor]` allows too \
                                       many unsafe patterns and may be \
                                       removed in the future");
                }

                for item in items.iter() {
                    match *item {
                        ast::MethodImplItem(_) => {}
                        ast::TypeImplItem(ref typedef) => {
                            self.gate_feature("associated_types",
                                              typedef.span,
                                              "associated types are \
                                               experimental")
                        }
                    }
                }
            }

            _ => {}
        }

        visit::walk_item(self, i);
    }

    fn visit_trait_item(&mut self, trait_item: &ast::TraitItem) {
        match *trait_item {
            ast::RequiredMethod(_) | ast::ProvidedMethod(_) => {}
            ast::TypeTraitItem(ref ti) => {
                self.gate_feature("associated_types",
                                  ti.span,
                                  "associated types are experimental")
            }
        }
    }

    fn visit_mac(&mut self, macro: &ast::Mac) {
        let ast::MacInvocTT(ref path, _, _) = macro.node;
        let id = path.segments.last().unwrap().identifier;

        if id == token::str_to_ident("macro_rules") {
            self.gate_feature("macro_rules", path.span, "macro definitions are \
                not stable enough for use and are subject to change");
        }

        else if id == token::str_to_ident("asm") {
            self.gate_feature("asm", path.span, "inline assembly is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("log_syntax") {
            self.gate_feature("log_syntax", path.span, "`log_syntax!` is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("trace_macros") {
            self.gate_feature("trace_macros", path.span, "`trace_macros` is not \
                stable enough for use and is subject to change");
        }

        else if id == token::str_to_ident("concat_idents") {
            self.gate_feature("concat_idents", path.span, "`concat_idents` is not \
                stable enough for use and is subject to change");
        }
    }

    fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
        if attr::contains_name(i.attrs.as_slice(), "linkage") {
            self.gate_feature("linkage", i.span,
                              "the `linkage` attribute is experimental \
                               and not portable across platforms")
        }
        visit::walk_foreign_item(self, i)
    }

    fn visit_ty(&mut self, t: &ast::Ty) {
        match t.node {
            ast::TyClosure(ref closure) if closure.onceness == ast::Once => {
                self.gate_feature("once_fns", t.span,
                                  "once functions are \
                                   experimental and likely to be removed");

            },
            ast::TyUnboxedFn(..) => {
                self.gate_feature("unboxed_closure_sugar",
                                  t.span,
                                  "unboxed closure trait sugar is experimental");
            }
            _ => {}
        }

        visit::walk_ty(self, t);
    }

    fn visit_expr(&mut self, e: &ast::Expr) {
        match e.node {
            ast::ExprUnboxedFn(..) => {
                self.gate_feature("unboxed_closures",
                                  e.span,
                                  "unboxed closures are a work-in-progress \
                                   feature with known bugs");
            }
            ast::ExprTupField(..) => {
                self.gate_feature("tuple_indexing",
                                  e.span,
                                  "tuple indexing is experimental");
            }
            ast::ExprIfLet(..) => {
                self.gate_feature("if_let", e.span,
                                  "`if let` syntax is experimental");
            }
            ast::ExprSlice(..) => {
                self.gate_feature("slicing_syntax",
                                  e.span,
                                  "slicing syntax is experimental");
            }
            ast::ExprWhileLet(..) => {
                self.gate_feature("while_let", e.span,
                                  "`while let` syntax is experimental");
            }
            _ => {}
        }
        visit::walk_expr(self, e);
    }

    fn visit_generics(&mut self, generics: &ast::Generics) {
        for type_parameter in generics.ty_params.iter() {
            match type_parameter.default {
                Some(ref ty) => {
                    self.gate_feature("default_type_params", ty.span,
                                      "default type parameters are \
                                       experimental and possibly buggy");
                }
                None => {}
            }
        }
        visit::walk_generics(self, generics);
    }

    fn visit_attribute(&mut self, attr: &ast::Attribute) {
        if attr::contains_name(slice::ref_slice(attr), "lang") {
            self.gate_feature("lang_items",
                              attr.span,
                              "language items are subject to change");
        }
    }

    fn visit_pat(&mut self, pattern: &ast::Pat) {
        match pattern.node {
            ast::PatVec(_, Some(_), ref last) if !last.is_empty() => {
                self.gate_feature("advanced_slice_patterns",
                                  pattern.span,
                                  "multiple-element slice matches anywhere \
                                   but at the end of a slice (e.g. \
                                   `[0, ..xs, 0]` are experimental")
            }
            _ => {}
        }
        visit::walk_pat(self, pattern)
    }

    fn visit_fn(&mut self,
                fn_kind: visit::FnKind<'v>,
                fn_decl: &'v ast::FnDecl,
                block: &'v ast::Block,
                span: Span,
                _: NodeId) {
        match fn_kind {
            visit::FkItemFn(_, _, _, abi) if abi == RustIntrinsic => {
                self.gate_feature("intrinsics",
                                  span,
                                  "intrinsics are subject to change")
            }
            _ => {}
        }
        visit::walk_fn(self, fn_kind, fn_decl, block, span);
    }
}

pub fn check_crate(span_handler: &SpanHandler, krate: &ast::Crate) -> (Features, Vec<Span>) {
    let mut cx = Context {
        features: Vec::new(),
        span_handler: span_handler,
    };

    let mut unknown_features = Vec::new();

    for attr in krate.attrs.iter() {
        if !attr.check_name("feature") {
            continue
        }

        match attr.meta_item_list() {
            None => {
                span_handler.span_err(attr.span, "malformed feature attribute, \
                                                  expected #![feature(...)]");
            }
            Some(list) => {
                for mi in list.iter() {
                    let name = match mi.node {
                        ast::MetaWord(ref word) => (*word).clone(),
                        _ => {
                            span_handler.span_err(mi.span,
                                                  "malformed feature, expected just \
                                                   one word");
                            continue
                        }
                    };
                    match KNOWN_FEATURES.iter()
                                        .find(|& &(n, _)| name.equiv(&n)) {
                        Some(&(name, Active)) => { cx.features.push(name); }
                        Some(&(_, Removed)) => {
                            span_handler.span_err(mi.span, "feature has been removed");
                        }
                        Some(&(_, Accepted)) => {
                            span_handler.span_warn(mi.span, "feature has been added to Rust, \
                                                             directive not necessary");
                        }
                        None => {
                            unknown_features.push(mi.span);
                        }
                    }
                }
            }
        }
    }

    visit::walk_crate(&mut cx, krate);

    (Features {
        default_type_params: cx.has_feature("default_type_params"),
        overloaded_calls: cx.has_feature("overloaded_calls"),
        rustc_diagnostic_macros: cx.has_feature("rustc_diagnostic_macros"),
        import_shadowing: cx.has_feature("import_shadowing"),
        visible_private_types: cx.has_feature("visible_private_types"),
        quote: cx.has_feature("quote"),
    },
    unknown_features)
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate stdweb;

use stdweb::unstable::TryInto;
use stdweb::web::{
    IEventTarget,
    IParentNode,
    IHtmlElement,
    document,
    window,
    CanvasRenderingContext2d
};

use stdweb::web::event::{
    IMouseEvent,
    MouseMoveEvent,
    ResizeEvent,
};

use stdweb::web::html_element::CanvasElement;

// Shamelessly stolen from webplatform's TodoMVC example.
macro_rules! enclose {
    ( ($( $x:ident ),*) $y:expr ) => {
        {
            $(let $x = $x.clone();)*
            $y
        }
    };
}

fn main() {
    stdweb::initialize();

    let canvas: CanvasElement = document().query_selector( "#canvas" ).unwrap().unwrap().try_into().unwrap();
    let context: CanvasRenderingContext2d = canvas.get_context().unwrap();

    canvas.set_width(canvas.offset_width() as u32);
    canvas.set_height(canvas.offset_height() as u32);

    window().add_event_listener( enclose!( (canvas) move |_: ResizeEvent| {
        canvas.set_width(canvas.offset_width() as u32);
        canvas.set_height(canvas.offset_height() as u32);
    }));

    canvas.add_event_listener( enclose!( (context) move |event: MouseMoveEvent| {
        context.fill_rect(event.client_x() - 5.0, event.client_y() - 5.0, 10.0, 10.0);
    }));

    stdweb::event_loop();
}
<commit_msg>fix compile error in canvas example<commit_after>#[macro_use]
extern crate stdweb;

use stdweb::unstable::TryInto;
use stdweb::web::{
    IEventTarget,
    IParentNode,
    IHtmlElement,
    document,
    window,
    CanvasRenderingContext2d
};

use stdweb::web::event::{
    IMouseEvent,
    MouseMoveEvent,
    ResizeEvent,
};

use stdweb::web::html_element::CanvasElement;

// Shamelessly stolen from webplatform's TodoMVC example.
macro_rules! enclose {
    ( ($( $x:ident ),*) $y:expr ) => {
        {
            $(let $x = $x.clone();)*
            $y
        }
    };
}

fn main() {
    stdweb::initialize();

    let canvas: CanvasElement = document().query_selector( "#canvas" ).unwrap().unwrap().try_into().unwrap();
    let context: CanvasRenderingContext2d = canvas.get_context().unwrap();

    canvas.set_width(canvas.offset_width() as u32);
    canvas.set_height(canvas.offset_height() as u32);

    window().add_event_listener( enclose!( (canvas) move |_: ResizeEvent| {
        canvas.set_width(canvas.offset_width() as u32);
        canvas.set_height(canvas.offset_height() as u32);
    }));

    canvas.add_event_listener( enclose!( (context) move |event: MouseMoveEvent| {
        context.fill_rect(f64::from(event.client_x() - 5), f64::from(event.client_y() - 5)
                          , 10.0, 10.0);
    }));

    stdweb::event_loop();
}
<|endoftext|>


<commit_before>extern crate rocks;

use rocks::prelude::*;
use std::str;

const DB_PATH: &str = "/tmp/rocksdb_column_families_example";

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // open DB
    let options = Options::default().map_db_options(|db_opt| db_opt.create_if_missing(true));

    let db = DB::open(&options, DB_PATH).map_err(|err| {
        eprintln!(
            "You should delete the {:?} directory before running this example.",
            DB_PATH
        );
        err
    })?;

    // create column family
    let cf = db.create_column_family(&ColumnFamilyOptions::default(), "new_cf")?;

    // close DB
    drop(cf);
    drop(db);

    // open DB with two column families
    let column_families = &[DEFAULT_COLUMN_FAMILY_NAME, "new_cf"];

    // open the new one, too
    let (db, handles) = DB::open_with_column_families(&DBOptions::default(), DB_PATH, column_families)?;

    println!("db: {:?}", db);
    println!("cf: {:?}", handles);

    // put and get from non-default column family
    db.put_cf(WriteOptions::default_instance(), &handles[1], b"key", b"value")?;
    let _ = db.get_cf(ReadOptions::default_instance(), &handles[1], b"key")?;

    // put and get from non-default column family, rustic style
    handles[1].put(WriteOptions::default_instance(), b"key0", b"value0")?;
    let _ = handles[1].get(ReadOptions::default_instance(), b"key0")?;

    // atomic write
    let mut batch = WriteBatch::new();
    batch
        .put_cf(&handles[0], b"key2", b"value2")
        .put_cf(&handles[1], b"key3", b"value3")
        .delete_cf(&handles[0], b"key");
    db.write(WriteOptions::default_instance(), &batch)?;

    // drop column family
    db.drop_column_family(&handles[1])?;

    // close db
    Ok(())
}
<commit_msg>fix(examples): rm unsed imports<commit_after>extern crate rocks;

use rocks::prelude::*;

const DB_PATH: &str = "/tmp/rocksdb_column_families_example";

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // open DB
    let options = Options::default().map_db_options(|db_opt| db_opt.create_if_missing(true));

    let db = DB::open(&options, DB_PATH).map_err(|err| {
        eprintln!(
            "You should delete the {:?} directory before running this example.",
            DB_PATH
        );
        err
    })?;

    // create column family
    let cf = db.create_column_family(&ColumnFamilyOptions::default(), "new_cf")?;

    // close DB
    drop(cf);
    drop(db);

    // open DB with two column families
    let column_families = &[DEFAULT_COLUMN_FAMILY_NAME, "new_cf"];

    // open the new one, too
    let (db, handles) = DB::open_with_column_families(&DBOptions::default(), DB_PATH, column_families)?;

    println!("db: {:?}", db);
    println!("cf: {:?}", handles);

    // put and get from non-default column family
    db.put_cf(WriteOptions::default_instance(), &handles[1], b"key", b"value")?;
    let _ = db.get_cf(ReadOptions::default_instance(), &handles[1], b"key")?;

    // put and get from non-default column family, rustic style
    handles[1].put(WriteOptions::default_instance(), b"key0", b"value0")?;
    let _ = handles[1].get(ReadOptions::default_instance(), b"key0")?;

    // atomic write
    let mut batch = WriteBatch::new();
    batch
        .put_cf(&handles[0], b"key2", b"value2")
        .put_cf(&handles[1], b"key3", b"value3")
        .delete_cf(&handles[0], b"key");
    db.write(WriteOptions::default_instance(), &batch)?;

    // drop column family
    db.drop_column_family(&handles[1])?;

    // close db
    Ok(())
}
<|endoftext|>


<commit_before>#![cfg(feature = "integration")]

use std::env;
use std::process::Command;

#[cfg_attr(feature = "integration", test)]
fn integration_test() {
    let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set");
    let repo_url = format!("https://github.com/{}", repo_name);
    let crate_name = repo_name
        .split('/')
        .nth(1)
        .expect("repo name should have format `<org>/<name>`");

    let repo_dir = tempfile::tempdir()
        .expect("couldn't create temp dir")
        .path()
        .join(crate_name);

    let st = Command::new("git")
        .args(&["clone", "--depth=1", &repo_url, repo_dir.to_str().unwrap()])
        .status()
        .expect("unable to run git");
    assert!(st.success());

    let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let target_dir = std::path::Path::new(&root_dir).join("target");
    let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy");

    let output = Command::new(clippy_binary)
        .current_dir(repo_dir)
        .env("RUST_BACKTRACE", "full")
        .env("CARGO_TARGET_DIR", target_dir)
        .args(&[
            "clippy",
            "--all-targets",
            "--all-features",
            "--",
            "--cap-lints",
            "warn",
            "-Wclippy::pedantic",
            "-Wclippy::nursery",
        ])
        .output()
        .expect("unable to run clippy");

    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr.contains("internal compiler error") {
        let backtrace_start = stderr
            .find("thread 'rustc' panicked at")
            .expect("start of backtrace not found");
        let backtrace_end = stderr
            .rfind("error: internal compiler error")
            .expect("end of backtrace not found");

        panic!(
            "internal compiler error\nBacktrace:\n\n{}",
            &stderr[backtrace_start..backtrace_end]
        );
    } else if stderr.contains("query stack during panic") {
        panic!("query stack during panic in the output");
    } else if stderr.contains("E0463") {
        panic!("error: E0463");
    } else if stderr.contains("E0514") {
        panic!("incompatible crate versions");
    } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
        panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
    }

    match output.status.code() {
        Some(code) => {
            if code == 0 {
                println!("Compilation successful");
            } else {
                eprintln!("Compilation failed. Exit code: {}", code);
            }
        },
        None => panic!("Process terminated by signal"),
    }
}
<commit_msg>Auto merge of #5316 - lzutao:osstr, r=phansch<commit_after>#![cfg(feature = "integration")]

use std::env;
use std::ffi::OsStr;
use std::process::Command;

#[cfg_attr(feature = "integration", test)]
fn integration_test() {
    let repo_name = env::var("INTEGRATION").expect("`INTEGRATION` var not set");
    let repo_url = format!("https://github.com/{}", repo_name);
    let crate_name = repo_name
        .split('/')
        .nth(1)
        .expect("repo name should have format `<org>/<name>`");

    let mut repo_dir = tempfile::tempdir().expect("couldn't create temp dir").into_path();
    repo_dir.push(crate_name);

    let st = Command::new("git")
        .args(&[
            OsStr::new("clone"),
            OsStr::new("--depth=1"),
            OsStr::new(&repo_url),
            OsStr::new(&repo_dir),
        ])
        .status()
        .expect("unable to run git");
    assert!(st.success());

    let root_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    let target_dir = std::path::Path::new(&root_dir).join("target");
    let clippy_binary = target_dir.join(env!("PROFILE")).join("cargo-clippy");

    let output = Command::new(clippy_binary)
        .current_dir(repo_dir)
        .env("RUST_BACKTRACE", "full")
        .env("CARGO_TARGET_DIR", target_dir)
        .args(&[
            "clippy",
            "--all-targets",
            "--all-features",
            "--",
            "--cap-lints",
            "warn",
            "-Wclippy::pedantic",
            "-Wclippy::nursery",
        ])
        .output()
        .expect("unable to run clippy");

    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr.contains("internal compiler error") {
        let backtrace_start = stderr
            .find("thread 'rustc' panicked at")
            .expect("start of backtrace not found");
        let backtrace_end = stderr
            .rfind("error: internal compiler error")
            .expect("end of backtrace not found");

        panic!(
            "internal compiler error\nBacktrace:\n\n{}",
            &stderr[backtrace_start..backtrace_end]
        );
    } else if stderr.contains("query stack during panic") {
        panic!("query stack during panic in the output");
    } else if stderr.contains("E0463") {
        panic!("error: E0463");
    } else if stderr.contains("E0514") {
        panic!("incompatible crate versions");
    } else if stderr.contains("failed to run `rustc` to learn about target-specific information") {
        panic!("couldn't find librustc_driver, consider setting `LD_LIBRARY_PATH`");
    }

    match output.status.code() {
        Some(0) => println!("Compilation successful"),
        Some(code) => eprintln!("Compilation failed. Exit code: {}", code),
        None => panic!("Process terminated by signal"),
    }
}
<|endoftext|>


<commit_before>/*
 * Rust BareBones OS
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * main.rs
 * - Top-level file for kernel
 *
 * This code has been put into the public domain, there are no restrictions on
 * its use, and the author takes no liability.
 */
#![no_std]	//< Kernels can't use std
#![feature(lang_items)]	//< unwind needs to define lang items
#![feature(asm)]	//< As a kernel, we need inline assembly

use prelude::*;

// Load libcore (it's nice and freestanding)
// - We want the macros from libcore
#[macro_use]
extern crate core;

/// A dummy 'std' module to work around a set of issues in rustc
mod std {
	// #18491 - write!() expands to std::fmt::Arguments::new
	pub use core::fmt;
}

/// Macros, need to be loaded before everything else due to how rust parses
#[macro_use]
mod macros;

// Achitecture-specific modules
#[cfg(arch__amd64)] #[path="arch/amd64/mod.rs"]
pub mod arch;
#[cfg(arch__x86)] #[path="arch/x86/mod.rs"]
pub mod arch;

// Prelude
mod prelude;

/// Exception handling (panic)
pub mod unwind;

/// Logging code
mod logging;

// Kernel entrypoint
#[lang="start"]
#[no_mangle]
fn kmain()
{
	log!("Hello world!");
	loop {}
}

<commit_msg>Solve warnings about libcore<commit_after>/*
 * Rust BareBones OS
 * - By John Hodge (Mutabah/thePowersGang)
 *
 * main.rs
 * - Top-level file for kernel
 *
 * This code has been put into the public domain, there are no restrictions on
 * its use, and the author takes no liability.
 */
#![no_std]	//< Kernels can't use std
#![feature(lang_items)]	//< unwind needs to define lang items
#![feature(asm)]	//< As a kernel, we need inline assembly
#![feature(core)]	//< libcore (see below) is not yet stablized

use prelude::*;

// Load libcore (it's nice and freestanding)
// - We want the macros from libcore
#[macro_use]
extern crate core;

/// A dummy 'std' module to work around a set of issues in rustc
mod std {
	// #18491 - write!() expands to std::fmt::Arguments::new
	pub use core::fmt;
}

/// Macros, need to be loaded before everything else due to how rust parses
#[macro_use]
mod macros;

// Achitecture-specific modules
#[cfg(arch__amd64)] #[path="arch/amd64/mod.rs"]
pub mod arch;
#[cfg(arch__x86)] #[path="arch/x86/mod.rs"]
pub mod arch;

// Prelude
mod prelude;

/// Exception handling (panic)
pub mod unwind;

/// Logging code
mod logging;

// Kernel entrypoint
#[lang="start"]
#[no_mangle]
fn kmain()
{
	log!("Hello world!");
	loop {}
}

<|endoftext|>


<commit_before>#![cfg(target_os = "linux")]

pub mod ffi;

use std::ptr;
use std::ffi::CString;

use error::{RuntimeError, ErrorKind};

use super::xcb::Connection;

pub struct Display {
   pub ptr: *mut ffi::Display
}

impl Display {
   pub fn default() -> Result<Self, RuntimeError> {
      let display_ptr = unsafe {
         ffi::XOpenDisplay(ptr::null())
      };

      if display_ptr.is_null() {
         return Err(RuntimeError::new(
            ErrorKind::Xlib,
            "Opening default X display failed".to_string()
         ));
      }

      Ok(
         Display {
            ptr: display_ptr
         }
      )
   }

   pub fn new(name: &str) -> Result<Self, RuntimeError> {
      let c_name = CString::new(name).unwrap();

      let display_ptr = unsafe {
         ffi::XOpenDisplay(c_name.as_ptr())
      };

      if display_ptr.is_null() {
         let description = format!(
            "Opening X display '{}' failed", name
         );
         return Err(RuntimeError::new(
            ErrorKind::Xlib,
            description
         ));
      }

      Ok(
         Display {
            ptr: display_ptr
         }
      )
   }

   pub fn xcb_connection(&self) -> Result<Connection, RuntimeError> {
      let connection = unsafe {
         ffi::XGetXCBConnection(self.ptr)
      };

      if connection.is_null() {
         return Err(RuntimeError::new(
            ErrorKind::Xlib,
            "Getting XCB connection failed".to_string()
         ));
      }

      Ok(
         Connection::new(connection)
      )
   }

   pub fn xcb_own_event_queue(&self) {
      unsafe {
         ffi::XSetEventQueueOwner(
            self.ptr,
            ffi::XCBOwnsEventQueue
         )
      };
   }

   #[inline]
   pub fn default_screen(&self) -> ScreenID {
      let screen = unsafe {
         (*(self.ptr as ffi::_XPrivDisplay)).default_screen
      };

      ScreenID {
         screen: screen
      }
   }
}

impl Drop for Display {
   fn drop (&mut self) {
      unsafe {
         ffi::XCloseDisplay(self.ptr);
      }
   }
}

pub struct ScreenID {
   pub screen: ffi::c_int
}

#[cfg(test)]
mod test {
   use std::env;
   use super::Display;

   #[test]
   fn test_create_display() {
      assert!(Display::default().is_ok());
   }

   #[test]
   fn test_create_display_from_env() {
      let name = env::var("DISPLAY").unwrap();
      assert!(Display::new(name).is_ok());
   }

   #[test]
   fn test_create_display_error_str() {
      assert!(Display::new("ErrorStringHere").is_err());
   }

   #[test]
   fn test_create_display_nul_str() {
      assert!(Display::new(&b"one\0two"[..]).is_err());
   }

   #[test]
   fn test_create_get_xcb_connection() {
      assert!(Display::default().unwrap().xcb_connection().is_ok());
   }
}

<commit_msg>Fixing X11 tests<commit_after>#![cfg(target_os = "linux")]

pub mod ffi;

use std::ptr;
use std::ffi::CString;

use error::{RuntimeError, ErrorKind};

use super::xcb::Connection;

pub struct Display {
   pub ptr: *mut ffi::Display
}

impl Display {
   pub fn default() -> Result<Self, RuntimeError> {
      let display_ptr = unsafe {
         ffi::XOpenDisplay(ptr::null())
      };

      if display_ptr.is_null() {
         return Err(RuntimeError::new(
            ErrorKind::Xlib,
            "Opening default X display failed".to_string()
         ));
      }

      Ok(
         Display {
            ptr: display_ptr
         }
      )
   }

   pub fn new(name: &str) -> Result<Self, RuntimeError> {
      let c_name = CString::new(name).unwrap();

      let display_ptr = unsafe {
         ffi::XOpenDisplay(c_name.as_ptr())
      };

      if display_ptr.is_null() {
         let description = format!(
            "Opening X display '{}' failed", name
         );
         return Err(RuntimeError::new(
            ErrorKind::Xlib,
            description
         ));
      }

      Ok(
         Display {
            ptr: display_ptr
         }
      )
   }

   pub fn xcb_connection(&self) -> Result<Connection, RuntimeError> {
      let connection = unsafe {
         ffi::XGetXCBConnection(self.ptr)
      };

      if connection.is_null() {
         return Err(RuntimeError::new(
            ErrorKind::Xlib,
            "Getting XCB connection failed".to_string()
         ));
      }

      Ok(
         Connection::new(connection)
      )
   }

   pub fn xcb_own_event_queue(&self) {
      unsafe {
         ffi::XSetEventQueueOwner(
            self.ptr,
            ffi::XCBOwnsEventQueue
         )
      };
   }

   #[inline]
   pub fn default_screen(&self) -> ScreenID {
      let screen = unsafe {
         (*(self.ptr as ffi::_XPrivDisplay)).default_screen
      };

      ScreenID {
         screen: screen
      }
   }
}

impl Drop for Display {
   fn drop (&mut self) {
      unsafe {
         ffi::XCloseDisplay(self.ptr);
      }
   }
}

pub struct ScreenID {
   pub screen: ffi::c_int
}

#[cfg(test)]
mod test {
   use std::env;
   use super::Display;

   #[test]
   fn test_create_display() {
      assert!(Display::default().is_ok());
   }

   #[test]
   fn test_create_display_from_env() {
      let name = env::var("DISPLAY").unwrap();
      assert!(Display::new(name.as_ref()).is_ok());
   }

   #[test]
   fn test_create_display_error_str() {
      assert!(Display::new("ErrorStringHere").is_err());
   }

   #[test]
   fn test_create_display_nul_str() {
      assert!(Display::new("one\0two").is_err());
   }

   #[test]
   fn test_create_get_xcb_connection() {
      assert!(Display::default().unwrap().xcb_connection().is_ok());
   }
}

<|endoftext|>


<commit_before>//! A timer wheel implementation

use std::cmp;
use std::mem;
use std::time::{Instant, Duration};

use slab::Slab;

/// An implementation of a timer wheel where data can be associated with each
/// timer firing.
///
/// This structure implements a timer wheel data structure where each timeout
/// has a piece of associated data, `T`. A timer wheel supports O(1) insertion
/// and removal of timers, as well as quickly figuring out what needs to get
/// fired.
///
/// Note, though, that the resolution of a timer wheel means that timeouts will
/// not arrive promptly when they expire, but rather in certain increments of
/// each time. The time delta between each slot of a time wheel is of a fixed
/// length, meaning that if a timeout is scheduled between two slots it'll end
/// up getting scheduled into the later slot.
pub struct TimerWheel<T> {
    // Actual timer wheel itself.
    //
    // Each slot represents a fixed duration of time, and this wheel also
    // behaves like a ring buffer. All timeouts scheduled will correspond to one
    // slot and therefore each slot has a linked list of timeouts scheduled in
    // it. Right now linked lists are done through indices into the `slab`
    // below.
    //
    // Each slot also contains the next timeout associated with it (the minimum
    // of the entire linked list).
    wheel: Vec<Slot>,

    // A slab containing all the timeout entries themselves. This is the memory
    // backing the "linked lists" in the wheel above. Each entry has a prev/next
    // pointer (indices in this array) along with the data associated with the
    // timeout and the time the timeout will fire.
    slab: Slab<Entry<T>, usize>,

    // The instant that this timer was created, through which all other timeout
    // computations are relative to.
    start: Instant,

    // State used during `poll`. The `cur_wheel_tick` field is the current tick
    // we've poll'd to. That is, all events from `cur_wheel_tick` to the
    // actual current tick in time still need to be processed.
    //
    // The `cur_slab_idx` variable is basically just an iterator over the linked
    // list associated with a wheel slot. This will get incremented as we move
    // forward in `poll`
    cur_wheel_tick: u64,
    cur_slab_idx: usize,
}

#[derive(Clone)]
struct Slot {
    head: usize,
    next_timeout: Option<Instant>,
}

struct Entry<T> {
    data: T,
    when: Instant,
    prev: usize,
    next: usize,
}

/// A timeout which has been scheduled with a timer wheel.
///
/// This can be used to later cancel a timeout, if necessary.
pub struct Timeout {
    when: Instant,
    slab_idx: usize,
}

const EMPTY: usize = 0;
const LEN: usize = 256;
const MASK: usize = LEN - 1;
const TICK_MS: u64 = 100;

impl<T> TimerWheel<T> {
    /// Creates a new timer wheel configured with no timeouts and with the
    /// default parameters.
    ///
    /// Currently this is a timer wheel of length 256 with a 100ms time
    /// resolution.
    pub fn new() -> TimerWheel<T> {
        TimerWheel {
            wheel: vec![Slot { head: EMPTY, next_timeout: None }; LEN],
            slab: Slab::new_starting_at(1, 256),
            start: Instant::now(),
            cur_wheel_tick: 0,
            cur_slab_idx: EMPTY,
        }
    }

    /// Creates a new timeout to get fired at a particular point in the future.
    ///
    /// The timeout will be associated with the specified `data`, and this data
    /// will be returned from `poll` when it's ready.
    ///
    /// The returned `Timeout` can later get passesd to `cancel` to retrieve the
    /// data and ensure the timeout doesn't fire.
    ///
    /// This method completes in O(1) time.
    ///
    /// # Panics
    ///
    /// This method will panic if `at` is before the time that this timer wheel
    /// was created.
    pub fn insert(&mut self, at: Instant, data: T) -> Timeout {
        // First up, figure out where we're gonna go in the wheel. Note that if
        // we're being scheduled on or before the current wheel tick we just
        // make sure to defer ourselves to the next tick.
        let mut tick = self.time_to_ticks(at);
        if tick <= self.cur_wheel_tick {
            debug!("moving {} to {}", tick, self.cur_wheel_tick + 1);
            tick = self.cur_wheel_tick + 1;
        }
        let wheel_idx = self.ticks_to_wheel_idx(tick);
        trace!("inserting timeout at {} for {}", wheel_idx, tick);

        // Next, make sure there's enough space in the slab for the timeout.
        if self.slab.vacant_entry().is_none() {
            let amt = self.slab.count();
            self.slab.grow(amt);
        }

        // Insert ourselves at the head of the linked list in the wheel.
        let slot = &mut self.wheel[wheel_idx];
        let prev_head;
        {
            let entry = self.slab.vacant_entry().unwrap();
            prev_head = mem::replace(&mut slot.head, entry.index());

            entry.insert(Entry {
                data: data,
                when: at,
                prev: EMPTY,
                next: prev_head,
            });
        }
        if prev_head != EMPTY {
            self.slab[prev_head].prev = slot.head;
        }

        // Update the wheel slot's next timeout field.
        if at <= slot.next_timeout.unwrap_or(at) {
            let tick = tick as u32;
            let actual_tick = self.start + Duration::from_millis(TICK_MS) * tick;
            let at = cmp::max(actual_tick, at);
            slot.next_timeout = Some(at);
        }

        Timeout {
            when: at,
            slab_idx: slot.head,
        }
    }

    /// Queries this timer to see if any timeouts are ready to fire.
    ///
    /// This function will advance the internal wheel to the time specified by
    /// `at`, returning any timeout which has happened up to that point. This
    /// method should be called in a loop until it returns `None` to ensure that
    /// all timeouts are processed.
    ///
    /// # Panics
    ///
    /// This method will panic if `at` is before the instant that this timer
    /// wheel was created.
    pub fn poll(&mut self, at: Instant) -> Option<T> {
        let wheel_tick = self.time_to_ticks(at);

        trace!("polling {} => {}", self.cur_wheel_tick, wheel_tick);

        // Advance forward in time to the `wheel_tick` specified.
        //
        // TODO: don't visit slots in the wheel more than once
        while self.cur_wheel_tick <= wheel_tick {
            let head = self.cur_slab_idx;
            trace!("next head[{} => {}]: {}",
                   self.cur_wheel_tick, wheel_tick, head);

            // If the current slot has no entries or we're done iterating go to
            // the next tick.
            if head == EMPTY {
                self.cur_wheel_tick += 1;
                let idx = self.ticks_to_wheel_idx(self.cur_wheel_tick);
                self.cur_slab_idx = self.wheel[idx].head;
                continue
            }

            // If we're starting to iterate over a slot, clear its timeout as
            // we're probably going to remove entries. As we skip over each
            // element of this slot we'll restore the `next_timeout` field if
            // necessary.
            let idx = self.ticks_to_wheel_idx(self.cur_wheel_tick);
            if head == self.wheel[idx].head {
                self.wheel[idx].next_timeout = None;
            }

            // Otherwise, continue iterating over the linked list in the wheel
            // slot we're on and remove anything which has expired.
            self.cur_slab_idx = self.slab[head].next;
            let head_timeout = self.slab[head].when;
            if self.time_to_ticks(head_timeout) <= self.time_to_ticks(at) {
                return self.remove_slab(head).map(|e| e.data)
            } else {
                let next = self.wheel[idx].next_timeout.unwrap_or(head_timeout);
                if head_timeout <= next {
                    self.wheel[idx].next_timeout = Some(head_timeout);
                }
            }
        }

        None
    }

    /// Returns the instant in time that corresponds to the next timeout
    /// scheduled in this wheel.
    pub fn next_timeout(&self) -> Option<Instant> {
        // TODO: can this be optimized to not look at the whole array?
        let timeouts = self.wheel.iter().map(|slot| slot.next_timeout);
        let min = timeouts.fold(None, |prev, cur| {
            match (prev, cur) {
                (None, cur) => cur,
                (Some(time), None) => Some(time),
                (Some(a), Some(b)) => Some(cmp::min(a, b)),
            }
        });
        let time = min.map(|min| min + Duration::from_millis(TICK_MS / 2));
        if let Some(time) = time {
            debug!("next timeout {:?}", time);
            debug!("now          {:?}", Instant::now());
        }
        return time
    }

    /// Cancels the specified timeout.
    ///
    /// For timeouts previously registered via `insert` they can be passed back
    /// to this method to cancel the associated timeout, retrieving the value
    /// inserted if the timeout has not already fired.
    ///
    /// This method completes in O(1) time.
    ///
    /// # Panics
    ///
    /// This method may panic if `timeout` wasn't created by this timer wheel.
    pub fn cancel(&mut self, timeout: &Timeout) -> Option<T> {
        match self.slab.get(timeout.slab_idx) {
            Some(e) if e.when == timeout.when => {}
            _ => return None,
        }

        self.remove_slab(timeout.slab_idx).map(|e| e.data)
    }

    fn remove_slab(&mut self, slab_idx: usize) -> Option<Entry<T>> {
        let entry = match self.slab.remove(slab_idx) {
            Some(e) => e,
            None => return None,
        };

        // Remove the node from the linked list
        if entry.prev == EMPTY {
            let idx = self.ticks_to_wheel_idx(self.time_to_ticks(entry.when));
            self.wheel[idx].head = entry.next;
        } else {
            self.slab[entry.prev].next = entry.next;
        }
        if entry.next != EMPTY {
            self.slab[entry.next].prev = entry.prev;
        }

        return Some(entry)
    }

    fn time_to_ticks(&self, time: Instant) -> u64 {
        let dur = time - self.start;
        let ms = dur.subsec_nanos() as u64 / 1_000_000;
        let ms = dur.as_secs()
                    .checked_mul(1_000)
                    .and_then(|m| m.checked_add(ms))
                    .expect("overflow scheduling timeout");
        (ms + TICK_MS / 2) / TICK_MS
    }

    fn ticks_to_wheel_idx(&self, ticks: u64) -> usize {
        (ticks as usize) & MASK
    }
}
<commit_msg>Fix timer wheel tests<commit_after>//! A timer wheel implementation

use std::cmp;
use std::mem;
use std::time::{Instant, Duration};

use slab::Slab;

/// An implementation of a timer wheel where data can be associated with each
/// timer firing.
///
/// This structure implements a timer wheel data structure where each timeout
/// has a piece of associated data, `T`. A timer wheel supports O(1) insertion
/// and removal of timers, as well as quickly figuring out what needs to get
/// fired.
///
/// Note, though, that the resolution of a timer wheel means that timeouts will
/// not arrive promptly when they expire, but rather in certain increments of
/// each time. The time delta between each slot of a time wheel is of a fixed
/// length, meaning that if a timeout is scheduled between two slots it'll end
/// up getting scheduled into the later slot.
pub struct TimerWheel<T> {
    // Actual timer wheel itself.
    //
    // Each slot represents a fixed duration of time, and this wheel also
    // behaves like a ring buffer. All timeouts scheduled will correspond to one
    // slot and therefore each slot has a linked list of timeouts scheduled in
    // it. Right now linked lists are done through indices into the `slab`
    // below.
    //
    // Each slot also contains the next timeout associated with it (the minimum
    // of the entire linked list).
    wheel: Vec<Slot>,

    // A slab containing all the timeout entries themselves. This is the memory
    // backing the "linked lists" in the wheel above. Each entry has a prev/next
    // pointer (indices in this array) along with the data associated with the
    // timeout and the time the timeout will fire.
    slab: Slab<Entry<T>, usize>,

    // The instant that this timer was created, through which all other timeout
    // computations are relative to.
    start: Instant,

    // State used during `poll`. The `cur_wheel_tick` field is the current tick
    // we've poll'd to. That is, all events from `cur_wheel_tick` to the
    // actual current tick in time still need to be processed.
    //
    // The `cur_slab_idx` variable is basically just an iterator over the linked
    // list associated with a wheel slot. This will get incremented as we move
    // forward in `poll`
    cur_wheel_tick: u64,
    cur_slab_idx: usize,
}

#[derive(Clone)]
struct Slot {
    head: usize,
    next_timeout: Option<Instant>,
}

struct Entry<T> {
    data: T,
    when: Instant,
    prev: usize,
    next: usize,
}

/// A timeout which has been scheduled with a timer wheel.
///
/// This can be used to later cancel a timeout, if necessary.
pub struct Timeout {
    when: Instant,
    slab_idx: usize,
}

const EMPTY: usize = 0;
const LEN: usize = 256;
const MASK: usize = LEN - 1;
const TICK_MS: u64 = 100;

impl<T> TimerWheel<T> {
    /// Creates a new timer wheel configured with no timeouts and with the
    /// default parameters.
    ///
    /// Currently this is a timer wheel of length 256 with a 100ms time
    /// resolution.
    pub fn new() -> TimerWheel<T> {
        TimerWheel {
            wheel: vec![Slot { head: EMPTY, next_timeout: None }; LEN],
            slab: Slab::new_starting_at(1, 256),
            start: Instant::now(),
            cur_wheel_tick: 0,
            cur_slab_idx: EMPTY,
        }
    }

    /// Creates a new timeout to get fired at a particular point in the future.
    ///
    /// The timeout will be associated with the specified `data`, and this data
    /// will be returned from `poll` when it's ready.
    ///
    /// The returned `Timeout` can later get passesd to `cancel` to retrieve the
    /// data and ensure the timeout doesn't fire.
    ///
    /// This method completes in O(1) time.
    ///
    /// # Panics
    ///
    /// This method will panic if `at` is before the time that this timer wheel
    /// was created.
    pub fn insert(&mut self, at: Instant, data: T) -> Timeout {
        // First up, figure out where we're gonna go in the wheel. Note that if
        // we're being scheduled on or before the current wheel tick we just
        // make sure to defer ourselves to the next tick.
        let mut tick = self.time_to_ticks(at);
        if tick <= self.cur_wheel_tick {
            debug!("moving {} to {}", tick, self.cur_wheel_tick + 1);
            tick = self.cur_wheel_tick + 1;
        }
        let wheel_idx = self.ticks_to_wheel_idx(tick);
        trace!("inserting timeout at {} for {}", wheel_idx, tick);

        // Next, make sure there's enough space in the slab for the timeout.
        if self.slab.vacant_entry().is_none() {
            let amt = self.slab.count();
            self.slab.grow(amt);
        }

        // Insert ourselves at the head of the linked list in the wheel.
        let slot = &mut self.wheel[wheel_idx];
        let prev_head;
        {
            let entry = self.slab.vacant_entry().unwrap();
            prev_head = mem::replace(&mut slot.head, entry.index());

            entry.insert(Entry {
                data: data,
                when: at,
                prev: EMPTY,
                next: prev_head,
            });
        }
        if prev_head != EMPTY {
            self.slab[prev_head].prev = slot.head;
        }

        // Update the wheel slot's next timeout field.
        if at <= slot.next_timeout.unwrap_or(at) {
            let tick = tick as u32;
            let actual_tick = self.start + Duration::from_millis(TICK_MS) * tick;
            let at = cmp::max(actual_tick, at);
            slot.next_timeout = Some(at);
        }

        Timeout {
            when: at,
            slab_idx: slot.head,
        }
    }

    /// Queries this timer to see if any timeouts are ready to fire.
    ///
    /// This function will advance the internal wheel to the time specified by
    /// `at`, returning any timeout which has happened up to that point. This
    /// method should be called in a loop until it returns `None` to ensure that
    /// all timeouts are processed.
    ///
    /// # Panics
    ///
    /// This method will panic if `at` is before the instant that this timer
    /// wheel was created.
    pub fn poll(&mut self, at: Instant) -> Option<T> {
        let wheel_tick = self.time_to_ticks(at);

        trace!("polling {} => {}", self.cur_wheel_tick, wheel_tick);

        // Advance forward in time to the `wheel_tick` specified.
        //
        // TODO: don't visit slots in the wheel more than once
        while self.cur_wheel_tick <= wheel_tick {
            let head = self.cur_slab_idx;
            trace!("next head[{} => {}]: {}",
                   self.cur_wheel_tick, wheel_tick, head);

            // If the current slot has no entries or we're done iterating go to
            // the next tick.
            if head == EMPTY {
                self.cur_wheel_tick += 1;
                let idx = self.ticks_to_wheel_idx(self.cur_wheel_tick);
                self.cur_slab_idx = self.wheel[idx].head;
                continue
            }

            // If we're starting to iterate over a slot, clear its timeout as
            // we're probably going to remove entries. As we skip over each
            // element of this slot we'll restore the `next_timeout` field if
            // necessary.
            let idx = self.ticks_to_wheel_idx(self.cur_wheel_tick);
            if head == self.wheel[idx].head {
                self.wheel[idx].next_timeout = None;
            }

            // Otherwise, continue iterating over the linked list in the wheel
            // slot we're on and remove anything which has expired.
            self.cur_slab_idx = self.slab[head].next;
            let head_timeout = self.slab[head].when;
            if self.time_to_ticks(head_timeout) <= self.time_to_ticks(at) {
                return self.remove_slab(head).map(|e| e.data)
            } else {
                let next = self.wheel[idx].next_timeout.unwrap_or(head_timeout);
                if head_timeout <= next {
                    self.wheel[idx].next_timeout = Some(head_timeout);
                }
            }
        }

        None
    }

    /// Returns the instant in time that corresponds to the next timeout
    /// scheduled in this wheel.
    pub fn next_timeout(&self) -> Option<Instant> {
        // TODO: can this be optimized to not look at the whole array?
        let timeouts = self.wheel.iter().map(|slot| slot.next_timeout);
        let min = timeouts.fold(None, |prev, cur| {
            match (prev, cur) {
                (None, cur) => cur,
                (Some(time), None) => Some(time),
                (Some(a), Some(b)) => Some(cmp::min(a, b)),
            }
        });
        if let Some(min) = min {
            debug!("next timeout {:?}", min);
            debug!("now          {:?}", Instant::now());
        }
        return min
    }

    /// Cancels the specified timeout.
    ///
    /// For timeouts previously registered via `insert` they can be passed back
    /// to this method to cancel the associated timeout, retrieving the value
    /// inserted if the timeout has not already fired.
    ///
    /// This method completes in O(1) time.
    ///
    /// # Panics
    ///
    /// This method may panic if `timeout` wasn't created by this timer wheel.
    pub fn cancel(&mut self, timeout: &Timeout) -> Option<T> {
        match self.slab.get(timeout.slab_idx) {
            Some(e) if e.when == timeout.when => {}
            _ => return None,
        }

        self.remove_slab(timeout.slab_idx).map(|e| e.data)
    }

    fn remove_slab(&mut self, slab_idx: usize) -> Option<Entry<T>> {
        let entry = match self.slab.remove(slab_idx) {
            Some(e) => e,
            None => return None,
        };

        // Remove the node from the linked list
        if entry.prev == EMPTY {
            let idx = self.ticks_to_wheel_idx(self.time_to_ticks(entry.when));
            self.wheel[idx].head = entry.next;
        } else {
            self.slab[entry.prev].next = entry.next;
        }
        if entry.next != EMPTY {
            self.slab[entry.next].prev = entry.prev;
        }

        return Some(entry)
    }

    fn time_to_ticks(&self, time: Instant) -> u64 {
        let dur = time - self.start;
        let ms = dur.subsec_nanos() as u64 / 1_000_000;
        let ms = dur.as_secs()
                    .checked_mul(1_000)
                    .and_then(|m| m.checked_add(ms))
                    .expect("overflow scheduling timeout");
        (ms + TICK_MS / 2) / TICK_MS
    }

    fn ticks_to_wheel_idx(&self, ticks: u64) -> usize {
        (ticks as usize) & MASK
    }
}
<|endoftext|>


<commit_before>extern crate kernel32;
extern crate winapi;

use super::StandardStream;

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
fn is_a_tty(stream: StandardStream) -> bool {
    let handle = match stream {
            StandardStream::StdIn => winapi::winbase::STD_INPUT_HANDLE,
            StandardStream::Stdout => winapi::winbase::STD_OUTPUT_HANDLE,
    };

    unsafe {
            let handle = kernel32::GetStdHandle(handle);
            let mut out = 0;
            kernel32::GetConsoleMode(handle, &mut out) != 0
    }
}

/// Checking for an unsupported TERM in windows is a no-op
pub fn is_unsupported_term() -> bool {
    false
}
<commit_msg>Implement necessary functions for Windows Console support<commit_after>extern crate kernel32;
extern crate winapi;

use super::StandardStream;
use super::Terminal;
use ::Result;

/// Try to get the number of columns in the current terminal, or assume 80 if it fails.
pub fn get_columns() -> usize {
    // Get HANDLE to stdout
    let handle = unsafe { kernel32::GetStdHandle(winapi::STD_OUTPUT_HANDLE) };

    // Create CONSOLE_SCREEN_BUFFER_INFO with some default values
    let mut csbi = winapi::wincon::CONSOLE_SCREEN_BUFFER_INFO {
        dwSize: winapi::wincon::COORD { X: 0, Y: 0 },
        dwCursorPosition: winapi::wincon::COORD { X: 0, Y: 0 },
        wAttributes: 0,
        srWindow: winapi::wincon::SMALL_RECT {
            Left: 0,
            Top: 0,
            Right: 0,
            Bottom: 0,
        },
        dwMaximumWindowSize: winapi::wincon::COORD { X: 0, Y: 0 },
    };

    let success: bool = unsafe { kernel32::GetConsoleScreenBufferInfo(handle, &mut csbi) != 0 };

    // If we were not able to retrieve console info successfully,
    // we will default to a column size of 80
    if success && csbi.dwSize.X > 0 {
        csbi.dwSize.X as usize
    } else {
        80
    }
}

/// Get WindowsTerminal struct
pub fn get_terminal() -> WindowsTerminal {
    WindowsTerminal{ original_mode: None }
}

/// Checking for an unsupported TERM in windows is a no-op
pub fn is_unsupported_term() -> bool {
    false
}

/// Return whether or not STDIN, STDOUT or STDERR is a TTY
pub fn is_a_tty(stream: StandardStream) -> bool {
    let handle = match stream {
            StandardStream::StdIn => winapi::STD_INPUT_HANDLE,
            StandardStream::StdOut => winapi::STD_OUTPUT_HANDLE,
    };

    unsafe {
            let handle = kernel32::GetStdHandle(handle);
            let mut out = 0;
            kernel32::GetConsoleMode(handle, &mut out) != 0
    }
}

pub struct WindowsTerminal {
    original_mode: Option<winapi::minwindef::DWORD>
}

impl Terminal for WindowsTerminal {
    /// Enable raw mode for the TERM
    fn enable_raw_mode(&mut self) -> Result<()> {
        let mut original_mode: winapi::minwindef::DWORD = 0;
        unsafe {
            let handle = kernel32::GetStdHandle(winapi::STD_INPUT_HANDLE);
            kernel32::GetConsoleMode(handle, &mut original_mode);
            kernel32::SetConsoleMode(
                handle,
                original_mode & !(winapi::wincon::ENABLE_LINE_INPUT |
                            winapi::wincon::ENABLE_ECHO_INPUT |
                            winapi::wincon::ENABLE_PROCESSED_INPUT)
            );
        };
        self.original_mode = Some(original_mode);
        Ok(())
    }

    /// Disable Raw mode for the term
    fn disable_raw_mode(&self) -> Result<()> {
        unsafe {
            let handle = kernel32::GetStdHandle(winapi::STD_INPUT_HANDLE);
            kernel32::SetConsoleMode(handle,
                                    self.original_mode.expect("RAW MODE was not enabled previously"));
        }
        Ok(())
    }
}

/// Ensure that RAW mode is disabled even in the case of a panic!
#[allow(unused_must_use)]
impl Drop for WindowsTerminal {
    fn drop(&mut self) {
        self.disable_raw_mode();
    }
}
<|endoftext|>