<commit_before>//! lint on blocks unnecessarily using >= with a + 1 or - 1

use rustc::lint::*;
use syntax::ast::*;

use utils::{span_lint_and_then, snippet_opt};

/// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
///
///
/// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// x >= y + 1
/// ```
///
/// Could be written:
///
/// ```rust
/// x > y
/// ```
declare_lint! {
    pub INT_PLUS_ONE,
    Allow,
    "instead of using x >= y + 1, use x > y"
}

pub struct IntPlusOne;

impl LintPass for IntPlusOne {
    fn get_lints(&self) -> LintArray {
        lint_array!(INT_PLUS_ONE)
    }
}

// cases:
// BinOpKind::Ge
// x >= y + 1
// x - 1 >= y
//
// BinOpKind::Le
// x + 1 <= y
// x <= y - 1

enum Side {
    LHS,
    RHS,
}

impl IntPlusOne {
    #[allow(cast_sign_loss)]
    fn check_lit(&self, lit: &Lit, target_value: i128) -> bool {
        if let LitKind::Int(value, ..) = lit.node {
            return value == (target_value as u128)
        }
        false
    }

    fn check_binop(&self, cx: &EarlyContext, block: &Expr, binop: BinOpKind, lhs: &Expr, rhs: &Expr) -> Option<(bool, Option<String>)> {
        match (binop, &lhs.node, &rhs.node) {
            // case where `x - 1 >= ...` or `-1 + x >= ...`
            (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
                match (lhskind.node, &lhslhs.node, &lhsrhs.node) {
                    // `-1 + x`
                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS);
                        if self.check_lit(lit, -1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    // `x - 1`
                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    }
                    _ => ()
                }
            },
            // case where `... >= y + 1` or `... >= 1 + y`
            (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) if rhskind.node == BinOpKind::Add => {
                match (&rhslhs.node, &rhsrhs.node) {
                    // `y + 1` and `1 + y`
                    (&ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    (_, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    _ => ()
                }
            },
            // case where `x + 1 <= ...` or `1 + x <= ...`
            (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) if lhskind.node == BinOpKind::Add => {
                match (&lhslhs.node, &lhsrhs.node) {
                    // `1 + x` and `x + 1`
                    (&ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    (_, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    _ => ()
                }
            },
            // case where `... >= y - 1` or `... >= -1 + y`
            (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
                match (rhskind.node, &rhslhs.node, &rhsrhs.node) {
                    // `-1 + y`
                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS);
                        if self.check_lit(lit, -1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    // `y - 1`
                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    _ => ()
                }
            },
            _ => ()
        }
    }

    fn generate_recommendation(&self, cx: &EarlyContext, binop: BinOpKind, node: &Expr, other_side: &Expr, side: Side) -> Option<String> {
        let binop_string = match binop {
            BinOpKind::Ge => ">",
            BinOpKind::Le => "<",
            _ => return None
        };
        if let Some(snippet) = snippet_opt(cx, node.span) {
            if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
                let rec = match side {
                    Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
                    Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
                };
                return rec;
            }
        }
        None
    }

    fn emit_warning(&self, cx: &EarlyContext, block: &Expr, recommendation: Option<String>) {
        if let Some(rec) = recommendation {
            span_lint_and_then(cx,
                               INT_PLUS_ONE,
                               block.span,
                               "Unnecessary `>= y + 1` or `x - 1 >=`",
                               |db| {
                db.span_suggestion(block.span, "change `>= y + 1` to `> y` as shown", rec);
            });
        }
    }
}

impl EarlyLintPass for IntPlusOne {
    fn check_expr(&mut self, cx: &EarlyContext, item: &Expr) {
        if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.node {
            self.check_binop(cx, item, kind.node, lhs, rhs);
        }
    }
}
<commit_msg>Remove old return-value<commit_after>//! lint on blocks unnecessarily using >= with a + 1 or - 1

use rustc::lint::*;
use syntax::ast::*;

use utils::{span_lint_and_then, snippet_opt};

/// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
///
///
/// **Why is this bad?** Readability -- better to use `> y` instead of `>= y + 1`.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// x >= y + 1
/// ```
///
/// Could be written:
///
/// ```rust
/// x > y
/// ```
declare_lint! {
    pub INT_PLUS_ONE,
    Allow,
    "instead of using x >= y + 1, use x > y"
}

pub struct IntPlusOne;

impl LintPass for IntPlusOne {
    fn get_lints(&self) -> LintArray {
        lint_array!(INT_PLUS_ONE)
    }
}

// cases:
// BinOpKind::Ge
// x >= y + 1
// x - 1 >= y
//
// BinOpKind::Le
// x + 1 <= y
// x <= y - 1

enum Side {
    LHS,
    RHS,
}

impl IntPlusOne {
    #[allow(cast_sign_loss)]
    fn check_lit(&self, lit: &Lit, target_value: i128) -> bool {
        if let LitKind::Int(value, ..) = lit.node {
            return value == (target_value as u128)
        }
        false
    }

    fn check_binop(&self, cx: &EarlyContext, block: &Expr, binop: BinOpKind, lhs: &Expr, rhs: &Expr) {
        match (binop, &lhs.node, &rhs.node) {
            // case where `x - 1 >= ...` or `-1 + x >= ...`
            (BinOpKind::Ge, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) => {
                match (lhskind.node, &lhslhs.node, &lhsrhs.node) {
                    // `-1 + x`
                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS);
                        if self.check_lit(lit, -1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    // `x - 1`
                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    }
                    _ => ()
                }
            },
            // case where `... >= y + 1` or `... >= 1 + y`
            (BinOpKind::Ge, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) if rhskind.node == BinOpKind::Add => {
                match (&rhslhs.node, &rhsrhs.node) {
                    // `y + 1` and `1 + y`
                    (&ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    (_, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    _ => ()
                }
            },
            // case where `x + 1 <= ...` or `1 + x <= ...`
            (BinOpKind::Le, &ExprKind::Binary(ref lhskind, ref lhslhs, ref lhsrhs), _) if lhskind.node == BinOpKind::Add => {
                match (&lhslhs.node, &lhsrhs.node) {
                    // `1 + x` and `x + 1`
                    (&ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhsrhs, rhs, Side::LHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    (_, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, lhslhs, rhs, Side::LHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    _ => ()
                }
            },
            // case where `... >= y - 1` or `... >= -1 + y`
            (BinOpKind::Le, _, &ExprKind::Binary(ref rhskind, ref rhslhs, ref rhsrhs)) => {
                match (rhskind.node, &rhslhs.node, &rhsrhs.node) {
                    // `-1 + y`
                    (BinOpKind::Add, &ExprKind::Lit(ref lit), _) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhsrhs, lhs, Side::RHS);
                        if self.check_lit(lit, -1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    // `y - 1`
                    (BinOpKind::Sub, _, &ExprKind::Lit(ref lit)) => {
                        let recommendation = self.generate_recommendation(cx, binop, rhslhs, lhs, Side::RHS);
                        if self.check_lit(lit, 1) {
                            self.emit_warning(cx, block, recommendation)
                        }
                    },
                    _ => ()
                }
            },
            _ => ()
        }
    }

    fn generate_recommendation(&self, cx: &EarlyContext, binop: BinOpKind, node: &Expr, other_side: &Expr, side: Side) -> Option<String> {
        let binop_string = match binop {
            BinOpKind::Ge => ">",
            BinOpKind::Le => "<",
            _ => return None
        };
        if let Some(snippet) = snippet_opt(cx, node.span) {
            if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
                let rec = match side {
                    Side::LHS => Some(format!("{} {} {}", snippet, binop_string, other_side_snippet)),
                    Side::RHS => Some(format!("{} {} {}", other_side_snippet, binop_string, snippet)),
                };
                return rec;
            }
        }
        None
    }

    fn emit_warning(&self, cx: &EarlyContext, block: &Expr, recommendation: Option<String>) {
        if let Some(rec) = recommendation {
            span_lint_and_then(cx,
                               INT_PLUS_ONE,
                               block.span,
                               "Unnecessary `>= y + 1` or `x - 1 >=`",
                               |db| {
                db.span_suggestion(block.span, "change `>= y + 1` to `> y` as shown", rec);
            });
        }
    }
}

impl EarlyLintPass for IntPlusOne {
    fn check_expr(&mut self, cx: &EarlyContext, item: &Expr) {
        if let ExprKind::Binary(ref kind, ref lhs, ref rhs) = item.node {
            self.check_binop(cx, item, kind.node, lhs, rhs);
        }
    }
}
<|endoftext|>


<commit_before>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            match tokens.next() {
                                Some(next) => resolve(sub_document, next),
                                None => Ok(sub_document),
                            }
                        },
                    }
                },

                &Token::Index { idx: i, .. } => {
                    let kind = ErrorKind::NoIndexInTable(i);
                    Err(Error::from(kind))
                },
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => {
                    match tokens.next() {
                        Some(next) => resolve(ary.get_mut(i).unwrap(), next),
                        None       => Ok(ary.index_mut(i)),
                    }
                },
                &Token::Identifier { ident: ref ident, .. } => {
                    let kind = ErrorKind::NoIdentifierInArray(ident.clone());
                    Err(Error::from(kind))
                },
            }
        },

        _ => match tokens {
            &Token::Identifier { ident: ref ident, .. } => {
                Err(Error::from(ErrorKind::QueryingValueAsTable(ident.clone())))
            },

            &Token::Index { idx: i, .. } => {
                Err(Error::from(ErrorKind::QueryingValueAsArray(i)))
            },
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_member_of_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_query_index_in_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIndexInTable { .. }));
    }

    #[test]
    fn test_resolve_query_identifier_in_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = [ 1, 2, 3 ]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIdentifierInArray { .. }));
    }

    #[test]
    fn test_resolve_query_value_as_table() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = 1
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::QueryingValueAsTable { .. }));
    }

    #[test]
    fn test_resolve_query_value_as_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = 1
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::QueryingValueAsArray { .. }));
    }

}
<commit_msg>Make resolve() pub<commit_after>/// The query resolver that operates on the AST and the TOML object

use std::ops::Deref;
use std::ops::IndexMut;

use toml::Value;
use tokenizer::Token;
use error::*;

pub fn resolve<'doc>(toml: &'doc mut Value, tokens: &Token) -> Result<&'doc mut Value> {
    match toml {
        &mut Value::Table(ref mut t) => {
            match tokens {
                &Token::Identifier { ident: ref ident, .. } => {
                    match t.get_mut(ident) {
                        None    => {
                            let ek = ErrorKind::IdentifierNotFoundInDocument(ident.clone());
                            Err(Error::from(ek))
                        },
                        Some(sub_document) => {
                            match tokens.next() {
                                Some(next) => resolve(sub_document, next),
                                None => Ok(sub_document),
                            }
                        },
                    }
                },

                &Token::Index { idx: i, .. } => {
                    let kind = ErrorKind::NoIndexInTable(i);
                    Err(Error::from(kind))
                },
            }
        },

        &mut Value::Array(ref mut ary) => {
            match tokens {
                &Token::Index { idx: i, .. } => {
                    match tokens.next() {
                        Some(next) => resolve(ary.get_mut(i).unwrap(), next),
                        None       => Ok(ary.index_mut(i)),
                    }
                },
                &Token::Identifier { ident: ref ident, .. } => {
                    let kind = ErrorKind::NoIdentifierInArray(ident.clone());
                    Err(Error::from(kind))
                },
            }
        },

        _ => match tokens {
            &Token::Identifier { ident: ref ident, .. } => {
                Err(Error::from(ErrorKind::QueryingValueAsTable(ident.clone())))
            },

            &Token::Index { idx: i, .. } => {
                Err(Error::from(ErrorKind::QueryingValueAsArray(i)))
            },
        }
    }
}

#[cfg(test)]
mod test {
    use toml::from_str as toml_from_str;
    use toml::Value;
    use tokenizer::*;
    use error::*;
    use super::resolve;

    macro_rules! do_resolve {
        ( $toml:ident => $query:expr ) => {
            resolve(&mut $toml, &tokenize_with_seperator(&String::from($query), '.').unwrap())
        }
    }

    #[test]
    fn test_resolve_empty_toml_simple_query() {
        let mut toml = toml_from_str("").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_present_bool() {
        let mut toml = toml_from_str("example = true").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Boolean(true)));
    }

    #[test]
    fn test_resolve_present_integer() {
        let mut toml = toml_from_str("example = 1").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_present_float() {
        let mut toml = toml_from_str("example = 1.0").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Float(1.0)));
    }

    #[test]
    fn test_resolve_present_string() {
        let mut toml = toml_from_str("example = 'string'").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("string", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_bools() {
        let mut toml = toml_from_str("example = [ true, false ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Boolean(true));
                assert_eq!(ary[1], Value::Boolean(false));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_integers() {
        let mut toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Integer(1));
                assert_eq!(ary[1], Value::Integer(1337));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_present_array_floats() {
        let mut toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(1.0));
                assert_eq!(ary[1], Value::Float(133.25));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_index_query_1() {
        let mut toml = toml_from_str("example = [ 1 ]").unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(1)));
    }

    #[test]
    fn test_resolve_array_index_query_2() {
        let mut toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
        let result = do_resolve!(toml => "example.[4]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(5)));
    }

    #[test]
    fn test_resolve_table_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value = 42
        "#).unwrap();
        let result = do_resolve!(toml => "table.value");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_with_many_elements_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = 42
        value2 = 43
        value3 = 44
        value4 = 45
        value5 = 46
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_table_array_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42.0, 50.0 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Array(_)));
        match result {
            &mut Value::Array(ref ary) => {
                assert_eq!(ary[0], Value::Float(42.0));
                assert_eq!(ary[1], Value::Float(50.0));
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_table_array_element_query() {
        let mut toml = toml_from_str(r#"
        [table]
        value1 = [ 42 ]
        "#).unwrap();
        let result = do_resolve!(toml => "table.value1.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Integer(42)));
    }

    #[test]
    fn test_resolve_multi_table_query() {
        let mut toml = toml_from_str(r#"
        [table0]
        value = [ 1 ]
        [table1]
        value = [ "Foo" ]
        [table2]
        value = [ 42.0 ]
        [table3]
        value = [ true ]
        "#).unwrap();
        let result = do_resolve!(toml => "table1.value.[0]");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("Foo", s),
            _ => panic!("What just happened?"),
        }
    }

    static FRUIT_TABLE : &'static str = r#"
    [[fruit.blah]]
      name = "apple"

      [fruit.blah.physical]
        color = "red"
        shape = "round"

    [[fruit.blah]]
      name = "banana"

      [fruit.blah.physical]
        color = "yellow"
        shape = "bent"
    "#;

    #[test]
    fn test_resolve_array_table_query_1() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].name");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("apple", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_array_table_query_2() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[0].physical");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref tab) => {
                match tab.get("color") {
                    Some(&Value::String(ref s)) => assert_eq!("red", s),
                    _ => assert!(false),
                }
                match tab.get("shape") {
                    Some(&Value::String(ref s)) => assert_eq!("round", s),
                    _ => assert!(false),
                }
            },
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_on_result() {
        let mut toml = toml_from_str(FRUIT_TABLE).unwrap();
        let result = do_resolve!(toml => "fruit.blah.[1].physical");

        assert!(result.is_ok());
        let mut result = result.unwrap();

        let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
        let result = resolve(result, &tokens);

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::String(_)));
        match result {
            &mut Value::String(ref s) => assert_eq!("yellow", s),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example");

        assert!(result.is_ok());
        let result = result.unwrap();

        assert!(is_match!(result, &mut Value::Table(_)));
        match result {
            &mut Value::Table(ref t) => assert!(t.is_empty()),
            _ => panic!("What just happened?"),
        }
    }

    #[test]
    fn test_resolve_query_member_of_empty_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::IdentifierNotFoundInDocument { .. }));
    }

    #[test]
    fn test_resolve_query_index_in_table() {
        let mut toml = toml_from_str(r#"
        [example]
        "#).unwrap();
        let result = do_resolve!(toml => "example.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIndexInTable { .. }));
    }

    #[test]
    fn test_resolve_query_identifier_in_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = [ 1, 2, 3 ]
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::NoIdentifierInArray { .. }));
    }

    #[test]
    fn test_resolve_query_value_as_table() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = 1
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.bar");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::QueryingValueAsTable { .. }));
    }

    #[test]
    fn test_resolve_query_value_as_array() {
        let mut toml = toml_from_str(r#"
        [example]
        foo = 1
        "#).unwrap();
        let result = do_resolve!(toml => "example.foo.[0]");

        assert!(result.is_err());
        let result = result.unwrap_err();

        let errkind = result.kind();
        assert!(is_match!(errkind, &ErrorKind::QueryingValueAsArray { .. }));
    }

}
<|endoftext|>


<commit_before>use crate::utils::{iter_input_pats, snippet, span_lint, type_is_unsafe_function};
use matches::matches;
use rustc::hir;
use rustc::hir::def::Def;
use rustc::hir::intravisit;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_data_structures::fx::FxHashSet;
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::source_map::Span;

/// **What it does:** Checks for functions with too many parameters.
///
/// **Why is this bad?** Functions with lots of parameters are considered bad
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
/// grouping some parameters into a new type.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
///     ..
/// }
/// ```
declare_clippy_lint! {
    pub TOO_MANY_ARGUMENTS,
    complexity,
    "functions with too many arguments"
}

/// **What it does:** Checks for functions with a large amount of lines.
///
/// **Why is this bad?** Functions with a lot of lines are harder to understand
/// due to having to look at a larger amount of code to understand what the
/// function is doing. Consider splitting the body of the function into
/// multiple functions.
///
/// **Known problems:** None.
///
/// ```
declare_clippy_lint! {
    pub TOO_MANY_LINES,
    pedantic,
    "functions with too many lines"
}

/// **What it does:** Checks for public functions that dereferences raw pointer
/// arguments but are not marked unsafe.
///
/// **Why is this bad?** The function should probably be marked `unsafe`, since
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
/// valid.
///
/// **Known problems:**
///
/// * It does not check functions recursively so if the pointer is passed to a
/// private non-`unsafe` function which does the dereferencing, the lint won't
/// trigger.
/// * It only checks for arguments whose type are raw pointers, not raw pointers
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
/// `some_argument.get_raw_ptr()`).
///
/// **Example:**
/// ```rust
/// pub fn foo(x: *const u8) {
///     println!("{}", unsafe { *x });
/// }
/// ```
declare_clippy_lint! {
    pub NOT_UNSAFE_PTR_ARG_DEREF,
    correctness,
    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
}

#[derive(Copy, Clone)]
pub struct Functions {
    threshold: u64,
    max_lines: u64
}

impl Functions {
    pub fn new(threshold: u64, max_lines: u64) -> Self {
        Self {
            threshold,
            max_lines
        }
    }
}

impl LintPass for Functions {
    fn get_lints(&self) -> LintArray {
        lint_array!(TOO_MANY_ARGUMENTS, TOO_MANY_LINES, NOT_UNSAFE_PTR_ARG_DEREF)
    }

    fn name(&self) -> &'static str {
        "Functions"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: intravisit::FnKind<'tcx>,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        span: Span,
        nodeid: ast::NodeId,
    ) {
        let is_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(nodeid)) {
            matches!(item.node, hir::ItemKind::Impl(_, _, _, _, Some(_), _, _))
        } else {
            false
        };

        let unsafety = match kind {
            hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety,
            hir::intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety,
            hir::intravisit::FnKind::Closure(_) => return,
        };

        // don't warn for implementations, it's not their fault
        if !is_impl {
            // don't lint extern functions decls, it's not their fault either
            match kind {
                hir::intravisit::FnKind::Method(
                    _,
                    &hir::MethodSig {
                        header: hir::FnHeader { abi: Abi::Rust, .. },
                        ..
                    },
                    _,
                    _,
                )
                | hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => {
                    self.check_arg_number(cx, decl, span)
                },
                _ => {},
            }
        }

        self.check_raw_ptr(cx, unsafety, decl, body, nodeid);
        self.check_line_number(cx, span);
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
        if let hir::TraitItemKind::Method(ref sig, ref eid) = item.node {
            // don't lint extern functions decls, it's not their fault
            if sig.header.abi == Abi::Rust {
                self.check_arg_number(cx, &sig.decl, item.span);
            }

            if let hir::TraitMethod::Provided(eid) = *eid {
                let body = cx.tcx.hir().body(eid);
                self.check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.id);
            }
        }
    }
}

impl<'a, 'tcx> Functions {
    fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl, span: Span) {
        let args = decl.inputs.len() as u64;
        if args > self.threshold {
            span_lint(
                cx,
                TOO_MANY_ARGUMENTS,
                span,
                &format!("this function has too many arguments ({}/{})", args, self.threshold),
            );
        }
    }

    fn check_line_number(self, cx: &LateContext, span: Span) {
        let code_snippet = snippet(cx, span, "..");
        let mut line_count = 0;
        let mut in_comment = false;
        for mut line in code_snippet.lines() {
            if in_comment {
                let end_comment_loc = match line.find("*/") {
                    Some(i) => i,
                    None => continue
                };
                in_comment = false;
                line = &line[end_comment_loc..];
            }
            line = line.trim_left();
            if line.is_empty() || line.starts_with("//") { continue; }
            if line.contains("/*") {
                let mut count_line: bool = !line.starts_with("/*");
                let close_counts = line.match_indices("*/").count();
                let open_counts = line.match_indices("/*").count();

                if close_counts > 1 || open_counts > 1 {
                    line_count += 1;
                } else if close_counts == 1 {
                    match line.find("*/") {
                        Some(i) => {
                            line = line[i..].trim_left();
                            if !line.is_empty() && !line.starts_with("//") {
                                count_line = true;
                            }
                        },
                        None => continue
                    }
                } else {
                    in_comment = true;
                }
                if count_line { line_count += 1; }
            } else {
                // No multipart comment, no single comment, non-empty string.
                line_count += 1;
            }
        }

        if line_count > self.max_lines {
            span_lint(cx, TOO_MANY_LINES, span,
                      "This function has a large number of lines.")
        }
    }

    fn check_raw_ptr(
        self,
        cx: &LateContext<'a, 'tcx>,
        unsafety: hir::Unsafety,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        nodeid: ast::NodeId,
    ) {
        let expr = &body.value;
        if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
            let raw_ptrs = iter_input_pats(decl, body)
                .zip(decl.inputs.iter())
                .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
                .collect::<FxHashSet<_>>();

            if !raw_ptrs.is_empty() {
                let tables = cx.tcx.body_tables(body.id());
                let mut v = DerefVisitor {
                    cx,
                    ptrs: raw_ptrs,
                    tables,
                };

                hir::intravisit::walk_expr(&mut v, expr);
            }
        }
    }
}

fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<ast::NodeId> {
    if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.node, &ty.node) {
        Some(id)
    } else {
        None
    }
}

struct DerefVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    ptrs: FxHashSet<ast::NodeId>,
    tables: &'a ty::TypeckTables<'tcx>,
}

impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        match expr.node {
            hir::ExprKind::Call(ref f, ref args) => {
                let ty = self.tables.expr_ty(f);

                if type_is_unsafe_function(self.cx, ty) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::MethodCall(_, _, ref args) => {
                let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id();
                let base_type = self.cx.tcx.type_of(def_id);

                if type_is_unsafe_function(self.cx, base_type) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::Unary(hir::UnDeref, ref ptr) => self.check_arg(ptr),
            _ => (),
        }

        hir::intravisit::walk_expr(self, expr);
    }
    fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
        intravisit::NestedVisitorMap::None
    }
}

impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
    fn check_arg(&self, ptr: &hir::Expr) {
        if let hir::ExprKind::Path(ref qpath) = ptr.node {
            if let Def::Local(id) = self.cx.tables.qpath_def(qpath, ptr.hir_id) {
                if self.ptrs.contains(&id) {
                    span_lint(
                        self.cx,
                        NOT_UNSAFE_PTR_ARG_DEREF,
                        ptr.span,
                        "this public function dereferences a raw pointer but is not marked `unsafe`",
                    );
                }
            }
        }
    }
}
<commit_msg>Reworking function logic, and adding doc example.<commit_after>use crate::utils::{iter_input_pats, snippet, span_lint, type_is_unsafe_function};
use matches::matches;
use rustc::hir;
use rustc::hir::def::Def;
use rustc::hir::intravisit;
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use rustc::ty;
use rustc::{declare_tool_lint, lint_array};
use rustc_data_structures::fx::FxHashSet;
use rustc_target::spec::abi::Abi;
use syntax::ast;
use syntax::source_map::Span;

/// **What it does:** Checks for functions with too many parameters.
///
/// **Why is this bad?** Functions with lots of parameters are considered bad
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
/// grouping some parameters into a new type.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
///     ..
/// }
/// ```
declare_clippy_lint! {
    pub TOO_MANY_ARGUMENTS,
    complexity,
    "functions with too many arguments"
}

/// **What it does:** Checks for functions with a large amount of lines.
///
/// **Why is this bad?** Functions with a lot of lines are harder to understand
/// due to having to look at a larger amount of code to understand what the
/// function is doing. Consider splitting the body of the function into
/// multiple functions.
///
/// **Known problems:** None.
///
/// **Example:**
/// ``` rust
/// fn im_too_long() {
/// println!("");
/// // ... 100 more LoC
/// println!("");
/// }
/// ```
declare_clippy_lint! {
    pub TOO_MANY_LINES,
    pedantic,
    "functions with too many lines"
}

/// **What it does:** Checks for public functions that dereferences raw pointer
/// arguments but are not marked unsafe.
///
/// **Why is this bad?** The function should probably be marked `unsafe`, since
/// for an arbitrary raw pointer, there is no way of telling for sure if it is
/// valid.
///
/// **Known problems:**
///
/// * It does not check functions recursively so if the pointer is passed to a
/// private non-`unsafe` function which does the dereferencing, the lint won't
/// trigger.
/// * It only checks for arguments whose type are raw pointers, not raw pointers
/// got from an argument in some other way (`fn foo(bar: &[*const u8])` or
/// `some_argument.get_raw_ptr()`).
///
/// **Example:**
/// ```rust
/// pub fn foo(x: *const u8) {
///     println!("{}", unsafe { *x });
/// }
/// ```
declare_clippy_lint! {
    pub NOT_UNSAFE_PTR_ARG_DEREF,
    correctness,
    "public functions dereferencing raw pointer arguments but not marked `unsafe`"
}

#[derive(Copy, Clone)]
pub struct Functions {
    threshold: u64,
    max_lines: u64
}

impl Functions {
    pub fn new(threshold: u64, max_lines: u64) -> Self {
        Self {
            threshold,
            max_lines
        }
    }
}

impl LintPass for Functions {
    fn get_lints(&self) -> LintArray {
        lint_array!(TOO_MANY_ARGUMENTS, TOO_MANY_LINES, NOT_UNSAFE_PTR_ARG_DEREF)
    }

    fn name(&self) -> &'static str {
        "Functions"
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Functions {
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: intravisit::FnKind<'tcx>,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        span: Span,
        nodeid: ast::NodeId,
    ) {
        let is_impl = if let Some(hir::Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(nodeid)) {
            matches!(item.node, hir::ItemKind::Impl(_, _, _, _, Some(_), _, _))
        } else {
            false
        };

        let unsafety = match kind {
            hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _, _) => unsafety,
            hir::intravisit::FnKind::Method(_, sig, _, _) => sig.header.unsafety,
            hir::intravisit::FnKind::Closure(_) => return,
        };

        // don't warn for implementations, it's not their fault
        if !is_impl {
            // don't lint extern functions decls, it's not their fault either
            match kind {
                hir::intravisit::FnKind::Method(
                    _,
                    &hir::MethodSig {
                        header: hir::FnHeader { abi: Abi::Rust, .. },
                        ..
                    },
                    _,
                    _,
                )
                | hir::intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _, _) => {
                    self.check_arg_number(cx, decl, span)
                },
                _ => {},
            }
        }

        self.check_raw_ptr(cx, unsafety, decl, body, nodeid);
        self.check_line_number(cx, span);
    }

    fn check_trait_item(&mut self, cx: &LateContext<'a, 'tcx>, item: &'tcx hir::TraitItem) {
        if let hir::TraitItemKind::Method(ref sig, ref eid) = item.node {
            // don't lint extern functions decls, it's not their fault
            if sig.header.abi == Abi::Rust {
                self.check_arg_number(cx, &sig.decl, item.span);
            }

            if let hir::TraitMethod::Provided(eid) = *eid {
                let body = cx.tcx.hir().body(eid);
                self.check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.id);
            }
        }
    }
}

impl<'a, 'tcx> Functions {
    fn check_arg_number(self, cx: &LateContext<'_, '_>, decl: &hir::FnDecl, span: Span) {
        let args = decl.inputs.len() as u64;
        if args > self.threshold {
            span_lint(
                cx,
                TOO_MANY_ARGUMENTS,
                span,
                &format!("this function has too many arguments ({}/{})", args, self.threshold),
            );
        }
    }

    fn check_line_number(self, cx: &LateContext<'_, '_>, span: Span) {
        let code_snippet = snippet(cx, span, "..");
        let mut line_count: u64 = 0;
        let mut in_comment = false;
        let mut code_in_line;

        // Skip the surrounding function decl.
        let start_brace_idx = match code_snippet.find("{") {
            Some(i) => i + 1,
            None => 0
        };
        let end_brace_idx = match code_snippet.find("}") {
            Some(i) => i,
            None => code_snippet.len()
        };
        let function_lines = code_snippet[start_brace_idx..end_brace_idx].lines();

        for mut line in function_lines {
            code_in_line = false;
            loop {
                line = line.trim_start();
                if line.is_empty() { break; }
                if in_comment {
                    match line.find("*/") {
                        Some(i) => {
                            line = &line[i + 2..];
                            in_comment = false;
                            continue;
                        },
                        None => break
                    }
                } else {
                    let multi_idx = match line.find("/*") {
                        Some(i) => i,
                        None => line.len()
                    };
                    let single_idx = match line.find("//") {
                        Some(i) => i,
                        None => line.len()
                    };
                    code_in_line |= multi_idx > 0 && single_idx > 0;
                    // Implies multi_idx is below line.len()
                    if multi_idx < single_idx {
                        line = &line[multi_idx + 2..];
                        in_comment = true;
                        continue;
                    }
                    break;
                }
            }
            if code_in_line { line_count += 1; }
        }

        if line_count > self.max_lines {
            span_lint(cx, TOO_MANY_LINES, span,
                      "This function has a large number of lines.")
        }
    }

    fn check_raw_ptr(
        self,
        cx: &LateContext<'a, 'tcx>,
        unsafety: hir::Unsafety,
        decl: &'tcx hir::FnDecl,
        body: &'tcx hir::Body,
        nodeid: ast::NodeId,
    ) {
        let expr = &body.value;
        if unsafety == hir::Unsafety::Normal && cx.access_levels.is_exported(nodeid) {
            let raw_ptrs = iter_input_pats(decl, body)
                .zip(decl.inputs.iter())
                .filter_map(|(arg, ty)| raw_ptr_arg(arg, ty))
                .collect::<FxHashSet<_>>();

            if !raw_ptrs.is_empty() {
                let tables = cx.tcx.body_tables(body.id());
                let mut v = DerefVisitor {
                    cx,
                    ptrs: raw_ptrs,
                    tables,
                };

                hir::intravisit::walk_expr(&mut v, expr);
            }
        }
    }
}

fn raw_ptr_arg(arg: &hir::Arg, ty: &hir::Ty) -> Option<ast::NodeId> {
    if let (&hir::PatKind::Binding(_, id, _, _), &hir::TyKind::Ptr(_)) = (&arg.pat.node, &ty.node) {
        Some(id)
    } else {
        None
    }
}

struct DerefVisitor<'a, 'tcx: 'a> {
    cx: &'a LateContext<'a, 'tcx>,
    ptrs: FxHashSet<ast::NodeId>,
    tables: &'a ty::TypeckTables<'tcx>,
}

impl<'a, 'tcx> hir::intravisit::Visitor<'tcx> for DerefVisitor<'a, 'tcx> {
    fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
        match expr.node {
            hir::ExprKind::Call(ref f, ref args) => {
                let ty = self.tables.expr_ty(f);

                if type_is_unsafe_function(self.cx, ty) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::MethodCall(_, _, ref args) => {
                let def_id = self.tables.type_dependent_defs()[expr.hir_id].def_id();
                let base_type = self.cx.tcx.type_of(def_id);

                if type_is_unsafe_function(self.cx, base_type) {
                    for arg in args {
                        self.check_arg(arg);
                    }
                }
            },
            hir::ExprKind::Unary(hir::UnDeref, ref ptr) => self.check_arg(ptr),
            _ => (),
        }

        hir::intravisit::walk_expr(self, expr);
    }
    fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
        intravisit::NestedVisitorMap::None
    }
}

impl<'a, 'tcx: 'a> DerefVisitor<'a, 'tcx> {
    fn check_arg(&self, ptr: &hir::Expr) {
        if let hir::ExprKind::Path(ref qpath) = ptr.node {
            if let Def::Local(id) = self.cx.tables.qpath_def(qpath, ptr.hir_id) {
                if self.ptrs.contains(&id) {
                    span_lint(
                        self.cx,
                        NOT_UNSAFE_PTR_ARG_DEREF,
                        ptr.span,
                        "this public function dereferences a raw pointer but is not marked `unsafe`",
                    );
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::collections::{BinaryHeap};
use std::error::Error;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use super::{
    MerkleTree,
    RemoteNode,
};
use super::rust_base58::ToBase58;
use super::types::*;
use utils::json::JsonEncodable;

pub struct CatchupHandler {
    pub f: usize,
    pub ledger_status_same: usize,
    pub merkle_tree: MerkleTree,
    pub new_mt_size: usize,
    pub new_mt_vote: usize,
    pub nodes: Vec<RemoteNode>,
    pub initiate_cmd_id: i32,
    pub is_refresh: bool,
    pub pending_catchup: Option<CatchUpProcess>,
    pub pool_id: i32,
}

impl Default for CatchupHandler {
    fn default() -> Self {
        CatchupHandler {
            f: 0,
            ledger_status_same: 0,
            merkle_tree: MerkleTree::from_vec(Vec::new()).unwrap(),
            nodes: Vec::new(),
            new_mt_size: 0,
            new_mt_vote: 0,
            pending_catchup: None,
            initiate_cmd_id: 0,
            is_refresh: false,
            pool_id: 0,
        }
    }
}

impl CatchupHandler {
    pub fn process_msg(&mut self, msg: Message, raw_msg: &String, src_ind: usize) -> Result<Option<MerkleTree>, PoolError> {
        match msg {
            Message::Pong => {
                //sending ledger status
                //TODO not send ledger status directly as response on ping, wait pongs from all nodes?
                let ls: LedgerStatus = LedgerStatus {
                    txnSeqNo: self.nodes.len(),
                    merkleRoot: self.merkle_tree.root_hash().as_slice().to_base58(),
                    ledgerId: 0,
                    ppSeqNo: None,
                    viewNo: None,
                };
                let resp_msg: Message = Message::LedgerStatus(ls);
                self.nodes[src_ind].send_msg(&resp_msg)?;
            }
            Message::LedgerStatus(ledger_status) => {
                if self.merkle_tree.root_hash().as_slice().to_base58().ne(ledger_status.merkleRoot.as_str()) {
                    return Err(PoolError::CommonError(
                        CommonError::InvalidState(
                            "Ledger merkle tree doesn't acceptable for current tree.".to_string())));
                }
                self.ledger_status_same += 1;
                if self.ledger_status_same == self.f + 1 {
                    return Ok(Some(self.merkle_tree.clone()));
                }
            }
            Message::ConsistencyProof(cons_proof) => {
                trace!("{:?}", cons_proof);
                if cons_proof.seqNoStart == self.merkle_tree.count()
                    && cons_proof.seqNoEnd > self.merkle_tree.count() {
                    self.new_mt_size = cmp::max(cons_proof.seqNoEnd, self.new_mt_size);
                    self.new_mt_vote += 1;
                    debug!("merkle tree expected size now {}", self.new_mt_size);
                }
                if self.new_mt_vote == self.f + 1 {
                    self.start_catchup()?;
                }
            }
            Message::CatchupRep(catchup) => {
                if let Some(new_mt) = self.process_catchup_rep(catchup)? {
                    return Ok(Some(new_mt));
                }
            }
            _ => {
                warn!("unhandled msg {:?}", msg);
            }
        };
        Ok(None)
    }

    pub fn start_catchup(&mut self) -> Result<(), PoolError> {
        trace!("start_catchup");
        if self.pending_catchup.is_some() {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "CatchUp already started for the pool".to_string())));
        }
        let node_cnt = self.nodes.len();
        if self.merkle_tree.count() != node_cnt {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "Merkle tree doesn't equal nodes count".to_string())));
        }
        let cnt_to_catchup = self.new_mt_size - self.merkle_tree.count();
        if cnt_to_catchup <= 0 {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Nothing to CatchUp, but started".to_string())));
        }

        self.pending_catchup = Some(CatchUpProcess {
            merkle_tree: self.merkle_tree.clone(),
            pending_reps: BinaryHeap::new(),
        });

        let portion = (cnt_to_catchup + node_cnt - 1) / node_cnt; //TODO check standard round up div
        let mut catchup_req = CatchupReq {
            ledgerId: 0,
            seqNoStart: node_cnt + 1,
            seqNoEnd: node_cnt + 1 + portion - 1,
            catchupTill: self.new_mt_size,
        };
        for node in &self.nodes {
            node.send_msg(&Message::CatchupReq(catchup_req.clone()))?;
            catchup_req.seqNoStart += portion;
            catchup_req.seqNoEnd = cmp::min(catchup_req.seqNoStart + portion - 1,
                                            catchup_req.catchupTill);
        }
        Ok(())
    }

    pub fn process_catchup_rep(&mut self, catchup: CatchupRep) -> Result<Option<MerkleTree>, PoolError> {
        trace!("append {:?}", catchup);
        let catchup_finished = {
            let mut process = self.pending_catchup.as_mut()
                .ok_or(CommonError::InvalidState("Process non-existing CatchUp".to_string()))?;
            process.pending_reps.push(catchup);
            while !process.pending_reps.is_empty()
                && process.pending_reps.peek().unwrap().min_tx() - 1 == process.merkle_tree.count() {
                let mut first_resp = process.pending_reps.pop().unwrap();
                while !first_resp.txns.is_empty() {
                    let key = first_resp.min_tx().to_string();
                    let new_gen_tx = first_resp.txns
                        .remove(&key)
                        .unwrap()
                        .to_json()
                        .map_err(|err|
                            CommonError::InvalidState(
                                format!("Can't serialize gen-tx json: {}", err.description())))?;
                    trace!("append to tree {}", new_gen_tx);
                    process.merkle_tree.append(
                        new_gen_tx
                    )?;
                }
            }
            trace!("updated mt hash {}, tree {:?}", process.merkle_tree.root_hash().as_slice().to_base58(), process.merkle_tree);
            if &process.merkle_tree.count() == &self.new_mt_size {
                //TODO check also root hash?
                true
            } else {
                false
            }
        };
        if catchup_finished {
            return Ok(Some(self.finish_catchup()?));
        }
        Ok(None)
    }

    pub fn finish_catchup(&mut self) -> Result<MerkleTree, PoolError> {
        Ok(self.pending_catchup.take().
            ok_or(CommonError::InvalidState("Try to finish non-existing CatchUp".to_string()))?
            .merkle_tree)
    }

    pub fn flush_requests(&mut self, status: Result<(), PoolError>) -> Result<(), PoolError> {
        let cmd = if self.is_refresh {
            PoolCommand::RefreshAck(self.initiate_cmd_id, status)
        } else {
            PoolCommand::OpenAck(self.initiate_cmd_id, status.map(|()| self.pool_id))
        };
        CommandExecutor::instance()
            .send(Command::Pool(cmd))
            .map_err(|err|
                PoolError::CommonError(
                    CommonError::InvalidState("Can't send ACK cmd".to_string())))
    }
}
<commit_msg>Draft implementation for checking proofs while CatchUp.<commit_after>use std::cmp;
use std::collections::{BinaryHeap};
use std::error::Error;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::common::CommonError;
use errors::pool::PoolError;
use super::{
    MerkleTree,
    RemoteNode,
};
use super::rust_base58::{FromBase58, ToBase58};
use super::types::*;
use utils::json::JsonEncodable;

pub struct CatchupHandler {
    pub f: usize,
    pub ledger_status_same: usize,
    pub merkle_tree: MerkleTree,
    pub new_mt_size: usize,
    pub new_mt_root: Vec<u8>,
    pub new_mt_vote: usize,
    pub nodes: Vec<RemoteNode>,
    pub initiate_cmd_id: i32,
    pub is_refresh: bool,
    pub pending_catchup: Option<CatchUpProcess>,
    pub pool_id: i32,
}

impl Default for CatchupHandler {
    fn default() -> Self {
        CatchupHandler {
            f: 0,
            ledger_status_same: 0,
            merkle_tree: MerkleTree::from_vec(Vec::new()).unwrap(),
            nodes: Vec::new(),
            new_mt_size: 0,
            new_mt_vote: 0,
            new_mt_root: Vec::new(),
            pending_catchup: None,
            initiate_cmd_id: 0,
            is_refresh: false,
            pool_id: 0,
        }
    }
}

impl CatchupHandler {
    pub fn process_msg(&mut self, msg: Message, raw_msg: &String, src_ind: usize) -> Result<Option<MerkleTree>, PoolError> {
        match msg {
            Message::Pong => {
                //sending ledger status
                //TODO not send ledger status directly as response on ping, wait pongs from all nodes?
                let ls: LedgerStatus = LedgerStatus {
                    txnSeqNo: self.nodes.len(),
                    merkleRoot: self.merkle_tree.root_hash().as_slice().to_base58(),
                    ledgerId: 0,
                    ppSeqNo: None,
                    viewNo: None,
                };
                let resp_msg: Message = Message::LedgerStatus(ls);
                self.nodes[src_ind].send_msg(&resp_msg)?;
            }
            Message::LedgerStatus(ledger_status) => {
                if self.merkle_tree.root_hash().as_slice().to_base58().ne(ledger_status.merkleRoot.as_str()) {
                    return Err(PoolError::CommonError(
                        CommonError::InvalidState(
                            "Ledger merkle tree doesn't acceptable for current tree.".to_string())));
                }
                self.ledger_status_same += 1;
                if self.ledger_status_same == self.f + 1 {
                    return Ok(Some(self.merkle_tree.clone()));
                }
            }
            Message::ConsistencyProof(cons_proof) => {
                trace!("{:?}", cons_proof);
                if cons_proof.seqNoStart == self.merkle_tree.count()
                    && cons_proof.seqNoEnd > self.merkle_tree.count() {
                    self.new_mt_size = cmp::max(cons_proof.seqNoEnd, self.new_mt_size);
                    self.new_mt_vote += 1;
                    self.new_mt_root = cons_proof.newMerkleRoot.from_base58().unwrap();
                    debug!("merkle tree expected size now {}", self.new_mt_size);
                }
                if self.new_mt_vote == self.f + 1 {
                    self.start_catchup()?;
                }
            }
            Message::CatchupRep(catchup) => {
                if let Some(new_mt) = self.process_catchup_rep(catchup)? {
                    return Ok(Some(new_mt));
                }
            }
            _ => {
                warn!("unhandled msg {:?}", msg);
            }
        };
        Ok(None)
    }

    pub fn start_catchup(&mut self) -> Result<(), PoolError> {
        trace!("start_catchup");
        if self.pending_catchup.is_some() {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "CatchUp already started for the pool".to_string())));
        }
        let node_cnt = self.nodes.len();
        if self.merkle_tree.count() != node_cnt {
            return Err(PoolError::CommonError(
                CommonError::InvalidState(
                    "Merkle tree doesn't equal nodes count".to_string())));
        }
        let cnt_to_catchup = self.new_mt_size - self.merkle_tree.count();
        if cnt_to_catchup <= 0 {
            return Err(PoolError::CommonError(CommonError::InvalidState(
                "Nothing to CatchUp, but started".to_string())));
        }

        self.pending_catchup = Some(CatchUpProcess {
            merkle_tree: self.merkle_tree.clone(),
            pending_reps: BinaryHeap::new(),
        });

        let portion = (cnt_to_catchup + node_cnt - 1) / node_cnt; //TODO check standard round up div
        let mut catchup_req = CatchupReq {
            ledgerId: 0,
            seqNoStart: node_cnt + 1,
            seqNoEnd: node_cnt + 1 + portion - 1,
            catchupTill: self.new_mt_size,
        };
        for node in &self.nodes {
            node.send_msg(&Message::CatchupReq(catchup_req.clone()))?;
            catchup_req.seqNoStart += portion;
            catchup_req.seqNoEnd = cmp::min(catchup_req.seqNoStart + portion - 1,
                                            catchup_req.catchupTill);
        }
        Ok(())
    }

    pub fn process_catchup_rep(&mut self, catchup: CatchupRep) -> Result<Option<MerkleTree>, PoolError> {
        trace!("append {:?}", catchup);
        let catchup_finished = {
            let mut process = self.pending_catchup.as_mut()
                .ok_or(CommonError::InvalidState("Process non-existing CatchUp".to_string()))?;
            process.pending_reps.push(catchup);
            while !process.pending_reps.is_empty()
                && process.pending_reps.peek().unwrap().min_tx() - 1 == process.merkle_tree.count() {
                let mut first_resp = process.pending_reps.pop().unwrap();
                while !first_resp.txns.is_empty() {
                    let key = first_resp.min_tx().to_string();
                    let new_gen_tx = first_resp.txns
                        .remove(&key)
                        .unwrap()
                        .to_json()
                        .map_err(|err|
                            CommonError::InvalidState(
                                format!("Can't serialize gen-tx json: {}", err.description())))?;
                    trace!("append to tree {}", new_gen_tx);
                    process.merkle_tree.append(
                        new_gen_tx
                    )?;
                    assert!(process.merkle_tree
                        .consistency_proof(&self.new_mt_root, self.new_mt_size,
                                           &first_resp.consProof.iter().map(|x| x.from_base58().unwrap()).collect())
                        .unwrap());
                }
            }
            trace!("updated mt hash {}, tree {:?}", process.merkle_tree.root_hash().as_slice().to_base58(), process.merkle_tree);
            if &process.merkle_tree.count() == &self.new_mt_size {
                if process.merkle_tree.root_hash().ne(&self.new_mt_root) {
                    return Err(PoolError::CommonError(CommonError::InvalidState(
                        "CatchUp failed: all transactions added, proofs checked, but root hash differ with target".to_string())));
                }
                //TODO check also root hash?
                true
            } else {
                false
            }
        };
        if catchup_finished {
            return Ok(Some(self.finish_catchup()?));
        }
        Ok(None)
    }

    pub fn finish_catchup(&mut self) -> Result<MerkleTree, PoolError> {
        Ok(self.pending_catchup.take().
            ok_or(CommonError::InvalidState("Try to finish non-existing CatchUp".to_string()))?
            .merkle_tree)
    }

    pub fn flush_requests(&mut self, status: Result<(), PoolError>) -> Result<(), PoolError> {
        let cmd = if self.is_refresh {
            PoolCommand::RefreshAck(self.initiate_cmd_id, status)
        } else {
            PoolCommand::OpenAck(self.initiate_cmd_id, status.map(|()| self.pool_id))
        };
        CommandExecutor::instance()
            .send(Command::Pool(cmd))
            .map_err(|err|
                PoolError::CommonError(
                    CommonError::InvalidState("Can't send ACK cmd".to_string())))
    }
}
<|endoftext|>


<commit_before>#![allow(non_camel_case_types)]

extern crate libc;
use libc::{
    size_t
};

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum xz_mode {
    /// Single-call mode.
    ///
    /// This uses less RAM than than multi-call modes, because the LZMA2 dictionary doesn't need to
    /// be allocated as part of the decoder state. All required data structures are allocated at
    /// initialization, so xz_dec_run() cannot return XZ_MEM_ERROR.
	XZ_SINGLE,

    /// Multi-call mode with preallocated LZMA2 dictionary buffer.
    ///
    /// All data structures are allocated at initialization, so xz_dec_run() cannot return
    /// XZ_MEM_ERROR.
	XZ_PREALLOC,

    /// Multi-call mode.
    ///
    /// The LZMA2 dictionary is allocated once the required size has been parsed from the stream
    /// headers. If the allocation fails, xz_dec_run() will return XZ_MEM_ERROR.
	XZ_DYNALLOC
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum xz_ret {
	XZ_OK,
	XZ_STREAM_END,
	XZ_UNSUPPORTED_CHECK,
	XZ_MEM_ERROR,
	XZ_MEMLIMIT_ERROR,
	XZ_FORMAT_ERROR,
	XZ_OPTIONS_ERROR,
	XZ_DATA_ERROR,
	XZ_BUF_ERROR
}

#[repr(C)]
pub struct xz_buf {
    pub _in: *const u8,
    pub in_pos: size_t,
    pub in_size: size_t,

    pub out: *mut u8,
    pub out_pos: size_t,
    pub out_size: size_t
}

/// Opaque type to hold the XZ decoder state
pub enum xz_dec {}

extern "C" {
    pub fn xz_dec_init(mode: xz_mode, dict_max: u32) -> *mut xz_dec;
    pub fn xz_dec_run(s: *mut xz_dec, b: *mut xz_buf) -> xz_ret;
    pub fn xz_dec_reset(s: *mut xz_dec);
    pub fn xz_dec_end(s: *mut xz_dec);

    pub fn xz_crc32_init();
    pub fn xz_crc32(buf: *const u8, size: size_t, crc: u32) -> u32;

    pub fn xz_crc64_init();
    pub fn xz_crc64(buf: *const u8, size: size_t, crc: u64) -> u64;

}

#[test]
fn it_works() {
}
<commit_msg>Added docs for xz-embedded-sys<commit_after>#![allow(non_camel_case_types)]

//! xz-embedded-sys
//!
//! FFI Bindings to the xz-embedded library, which is a simple xz decompression library
//!
//! The documentation in this crate is copied almost verbatim from the xz-embedded header file, and
//! so there might be some C-isms that aren't applicable to this rust crate.  Please read
//! carefully.
//!

extern crate libc;
use libc::{
    size_t
};

/// Operation mode
///
/// It is possible to enable support only for a subset of the above
/// modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
/// or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
/// with support for all operation modes, but the preboot code may
/// be built with fewer features to minimize code size.
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum xz_mode {
    /// Single-call mode.
    ///
    /// This uses less RAM than than multi-call modes, because the LZMA2 dictionary doesn't need to
    /// be allocated as part of the decoder state. All required data structures are allocated at
    /// initialization, so xz_dec_run() cannot return XZ_MEM_ERROR.
	XZ_SINGLE,

    /// Multi-call mode with preallocated LZMA2 dictionary buffer.
    ///
    /// All data structures are allocated at initialization, so xz_dec_run() cannot return
    /// XZ_MEM_ERROR.
	XZ_PREALLOC,

    /// Multi-call mode.
    ///
    /// The LZMA2 dictionary is allocated once the required size has been parsed from the stream
    /// headers. If the allocation fails, xz_dec_run() will return XZ_MEM_ERROR.
	XZ_DYNALLOC
}

/// Return codes
///
/// In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
/// to XZ code cannot consume any input and cannot produce any new output.
/// This happens when there is no new input available, or the output buffer
/// is full while at least one output byte is still pending. Assuming your
/// code is not buggy, you can get this error only when decoding a compressed
/// stream that is truncated or otherwise corrupt.
///
/// In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
/// is too small or the compressed input is corrupt in a way that makes the
/// decoder produce more output than the caller expected. When it is
/// (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
/// is used instead of XZ_BUF_ERROR.
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub enum xz_ret {

    /// Everything is OK so far. More input or more
    /// output space is required to continue. This
    /// return code is possible only in multi-call mode
    /// (XZ_PREALLOC or XZ_DYNALLOC).
	XZ_OK,

    /// Operation finished successfully.
	XZ_STREAM_END,

    /// Integrity check type is not supported. Decoding
    /// is still possible in multi-call mode by simply
    /// calling xz_dec_run() again.
    /// Note that this return value is used only if
    /// XZ_DEC_ANY_CHECK was defined at build time,
    /// which is not used in the kernel. Unsupported
    /// check types return XZ_OPTIONS_ERROR if
    /// XZ_DEC_ANY_CHECK was not defined at build time.
	XZ_UNSUPPORTED_CHECK,

    /// Allocating memory failed. This return code is
    /// possible only if the decoder was initialized
    /// with XZ_DYNALLOC. The amount of memory that was
    /// tried to be allocated was no more than the
    /// dict_max argument given to xz_dec_init().
	XZ_MEM_ERROR,

    /// A bigger LZMA2 dictionary would be needed than
    /// allowed by the dict_max argument given to
    /// xz_dec_init(). This return value is possible
    /// only in multi-call mode (XZ_PREALLOC or
    /// XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
    /// ignores the dict_max argument.
	XZ_MEMLIMIT_ERROR,

    /// File format was not recognized (wrong magic
    /// bytes).
	XZ_FORMAT_ERROR,

    /// This implementation doesn't support the requested
    /// compression options. In the decoder this means
    /// that the header CRC32 matches, but the header
    /// itself specifies something that we don't support.
	XZ_OPTIONS_ERROR,

    /// Compressed data is corrupt.
	XZ_DATA_ERROR,

    /// Cannot make any progress. Details are slightly
    /// different between multi-call and single-call
    /// mode; more information below.
	XZ_BUF_ERROR
}

///  Passing input and output buffers to XZ code
///
///
#[repr(C)]
pub struct xz_buf {
    /// Beginning of the input buffer. This may be NULL if and only
    /// if in_pos is equal to in_size.
    pub _in: *const u8,
    /// Current position in the input buffer. This must not exceed
    /// in_size.
    pub in_pos: size_t,
    /// Size of the input buffer
    ///
    pub in_size: size_t,

    /// Beginning of the output buffer. This may be NULL if and only
    /// if out_pos is equal to out_size.
    pub out: *mut u8,
    /// Current position in the output buffer. This must not exceed
    /// out_size.
    pub out_pos: size_t,
    /// Size of the output buffer
    pub out_size: size_t
}

/// Opaque type to hold the XZ decoder state
pub enum xz_dec {}

extern "C" {
    /// Allocate and initialize a XZ decoder state
    ///
    /// @mode: Operation mode
    ///
    /// @dict_max: Maximum size of the LZMA2 dictionary (history buffer) for
    /// multi-call decoding. This is ignored in single-call mode
    /// (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
    /// or 2^n + 2^(n-1) bytes (the latter sizes are less common
    /// in practice), so other values for dict_max don't make sense.
    /// In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
    /// 512 KiB, and 1 MiB are probably the only reasonable values,
    /// except for kernel and initramfs images where a bigger
    /// dictionary can be fine and useful.
    ///
    /// Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
    /// once. The caller must provide enough output space or the decoding will
    /// fail. The output space is used as the dictionary buffer, which is why
    /// there is no need to allocate the dictionary as part of the decoder's
    /// internal state.
    ///
    /// Because the output buffer is used as the workspace, streams encoded using
    /// a big dictionary are not a problem in single-call mode. It is enough that
    /// the output buffer is big enough to hold the actual uncompressed data; it
    /// can be smaller than the dictionary size stored in the stream headers.
    ///
    /// Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
    /// of memory is preallocated for the LZMA2 dictionary. This way there is no
    /// risk that xz_dec_run() could run out of memory, since xz_dec_run() will
    /// never allocate any memory. Instead, if the preallocated dictionary is too
    /// small for decoding the given input stream, xz_dec_run() will return
    /// XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
    /// decoded to avoid allocating excessive amount of memory for the dictionary.
    ///
    /// Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
    /// dict_max specifies the maximum allowed dictionary size that xz_dec_run()
    /// may allocate once it has parsed the dictionary size from the stream
    /// headers. This way excessive allocations can be avoided while still
    /// limiting the maximum memory usage to a sane value to prevent running the
    /// system out of memory when decompressing streams from untrusted sources.
    ///
    /// On success, xz_dec_init() returns a pointer to struct xz_dec, which is
    /// ready to be used with xz_dec_run(). If memory allocation fails,
    /// xz_dec_init() returns NULL.
    pub fn xz_dec_init(mode: xz_mode, dict_max: u32) -> *mut xz_dec;

    /// Run the XZ decoder
    ///
    /// @s:          Decoder state allocated using xz_dec_init()
    ///
    /// @b:          Input and output buffers
    ///
    ///
    /// The possible return values depend on build options and operation mode.
    /// See enum xz_ret for details.
    ///
    /// Note that if an error occurs in single-call mode (return value is not
    /// XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
    /// contents of the output buffer from b->out[b->out_pos] onward are
    /// undefined. This is true even after XZ_BUF_ERROR, because with some filter
    /// chains, there may be a second pass over the output buffer, and this pass
    /// cannot be properly done if the output buffer is truncated. Thus, you
    /// cannot give the single-call decoder a too small buffer and then expect to
    /// get that amount valid data from the beginning of the stream. You must use
    /// the multi-call decoder if you don't want to uncompress the whole stream.
    pub fn xz_dec_run(s: *mut xz_dec, b: *mut xz_buf) -> xz_ret;

    /// Reset an already allocated decoder state
    ///
    /// @s:          Decoder state allocated using xz_dec_init()
    ///
    /// This function can be used to reset the multi-call decoder state without
    /// freeing and reallocating memory with xz_dec_end() and xz_dec_init().
    ///
    /// In single-call mode, xz_dec_reset() is always called in the beginning of
    /// xz_dec_run().  Thus, explicit call to xz_dec_reset() is useful only in
    /// multi-call mode.
    pub fn xz_dec_reset(s: *mut xz_dec);

    /// Free the memory allocated for the decoder state
    ///
    /// @s: Decoder state allocated using xz_dec_init(). If s is NULL, this function does nothing.
    pub fn xz_dec_end(s: *mut xz_dec);

    /// Initialize the CRC32 lookup table
    ///
    /// This must be called before any other xz_* function to initialize
    /// the CRC32 lookup table.
    pub fn xz_crc32_init();

    /// Update CRC32 value using the polynomial from IEEE-802.3.
    ///
    /// To start a new calculation, the third argument must be zero. To continue the calculation,
    /// the previously returned value is passed as the third argument.
    pub fn xz_crc32(buf: *const u8, size: size_t, crc: u32) -> u32;

    /// Initialize the CRC64 lookup table
    ///
    /// This must be called before any other xz_* function (except xz_crc32_init())
    /// to initialize the CRC64 lookup table.
    pub fn xz_crc64_init();

    /// Update CRC64 value using the polynomial from ECMA-182.
    ///
    /// To start a new calculation, the third argument must be zero. To continue the calculation,
    /// the previously returned value is passed as the third argument.
    pub fn xz_crc64(buf: *const u8, size: size_t, crc: u64) -> u64;

}

#[test]
fn it_works() {
}
<|endoftext|>


<commit_before>use action::Action;
use config;
use context::base::BaseContext;
use dispatcher::Response;
use dispatcher::response::ResponseSender;
use message::{
    Message,
    MessageBuilder
};

use handlebars::{
    Context,
    Handlebars,
};
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
use state::State;
use self::error::Error;
use self::renderer_context::RendererContext;

mod error;
mod renderer_context;
#[cfg(test)]
mod test;

pub const CONTEXT_UUID: &'static str = "context.uuid";
pub const CONTEXT_NAME: &'static str = "context.name";
pub const CONTEXT_LEN: &'static str = "context.len";
pub const MESSAGES: &'static str = "messages";
const MESSAGE_KEY: &'static str = "MESSAGE";

pub struct MessageAction {
    sender: Rc<RefCell<Box<ResponseSender<Response>>>>,
    uuid: String,
    name: Option<String>,
    values: Handlebars
}

impl MessageAction {
    pub fn new(sender: Rc<RefCell<Box<ResponseSender<Response>>>>, action: config::action::MessageAction) -> MessageAction {
        let config::action::MessageAction { uuid, name, message, values } = action;
        let mut handlebars = Handlebars::new();
        for (name, template) in values.into_iter() {
            handlebars.register_template(&name, template);
        }
        handlebars.register_template(MESSAGE_KEY, message);

        MessageAction {
            sender: sender,
            uuid: uuid,
            name: name,
            values: handlebars
        }
    }

    fn render_value(&self, key: &String, template_context: &Context) -> Result<String, Error> {
        let mut writer = Vec::new();
        {
            try!(self.values.renderw(key, &template_context, &mut writer));
        }
        let string = try!(String::from_utf8(writer));
        Ok(string)
    }

    fn render_values(&self, template_context: &Context) -> Result<BTreeMap<String, String>, Error> {
        let mut rendered_values = BTreeMap::new();
        for (key, _) in self.values.get_templates() {
            let rendered_value = try!(self.render_value(key, &template_context));
            rendered_values.insert(key.to_string(), rendered_value);
        }
        Ok(rendered_values)
    }

    fn render_message(&self, state: &State, context: &BaseContext) -> Result<Message, Error> {
        let template_context = {
            use handlebars::Context;
            let context = RendererContext::new(state, context);
            Context::wraps(&context)
        };

        let mut rendered_values = try!(self.render_values(&template_context));
        let message = rendered_values.remove(MESSAGE_KEY).expect(&format!("There is no '{}' key in the renderer key-value pairs", MESSAGE_KEY));
        let name = self.name.as_ref().map(|name| name.borrow());
        let message = MessageBuilder::new(&self.uuid, message)
                        .name(name)
                        .values(rendered_values)
                        .build();
        Ok(message)
    }
}

#[derive(Debug)]
pub struct MessageResponse {
    message: Message,
}

impl MessageResponse {
    pub fn message(&self) -> &Message {
        &self.message
    }
}

impl Action for MessageAction {
    fn execute(&self, _state: &State, _context: &BaseContext) {
        trace!("MessageAction: executed");
        match self.render_message(_state, _context) {
            Ok(message) => {
                let response = MessageResponse {
                    message: message,
                };
                self.sender.borrow_mut().send_response(Response::Message(response));
            },
            Err(error) => {
                error!("{}", error);
            }
        }
    }
}
<commit_msg>action::message: rename MESSAGE_KEY constant to MESSAGE<commit_after>use action::Action;
use config;
use context::base::BaseContext;
use dispatcher::Response;
use dispatcher::response::ResponseSender;
use message::{
    Message,
    MessageBuilder
};

use handlebars::{
    Context,
    Handlebars,
};
use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::rc::Rc;
use state::State;
use self::error::Error;
use self::renderer_context::RendererContext;

mod error;
mod renderer_context;
#[cfg(test)]
mod test;

pub const CONTEXT_UUID: &'static str = "context.uuid";
pub const CONTEXT_NAME: &'static str = "context.name";
pub const CONTEXT_LEN: &'static str = "context.len";
pub const MESSAGES: &'static str = "messages";
const MESSAGE: &'static str = "MESSAGE";

pub struct MessageAction {
    sender: Rc<RefCell<Box<ResponseSender<Response>>>>,
    uuid: String,
    name: Option<String>,
    values: Handlebars
}

impl MessageAction {
    pub fn new(sender: Rc<RefCell<Box<ResponseSender<Response>>>>, action: config::action::MessageAction) -> MessageAction {
        let config::action::MessageAction { uuid, name, message, values } = action;
        let mut handlebars = Handlebars::new();
        for (name, template) in values.into_iter() {
            handlebars.register_template(&name, template);
        }
        handlebars.register_template(MESSAGE, message);

        MessageAction {
            sender: sender,
            uuid: uuid,
            name: name,
            values: handlebars
        }
    }

    fn render_value(&self, key: &String, template_context: &Context) -> Result<String, Error> {
        let mut writer = Vec::new();
        {
            try!(self.values.renderw(key, &template_context, &mut writer));
        }
        let string = try!(String::from_utf8(writer));
        Ok(string)
    }

    fn render_values(&self, template_context: &Context) -> Result<BTreeMap<String, String>, Error> {
        let mut rendered_values = BTreeMap::new();
        for (key, _) in self.values.get_templates() {
            let rendered_value = try!(self.render_value(key, &template_context));
            rendered_values.insert(key.to_string(), rendered_value);
        }
        Ok(rendered_values)
    }

    fn render_message(&self, state: &State, context: &BaseContext) -> Result<Message, Error> {
        let template_context = {
            use handlebars::Context;
            let context = RendererContext::new(state, context);
            Context::wraps(&context)
        };

        let mut rendered_values = try!(self.render_values(&template_context));
        let message = rendered_values.remove(MESSAGE).expect(&format!("There is no '{}' key in the renderer key-value pairs", MESSAGE));
        let name = self.name.as_ref().map(|name| name.borrow());
        let message = MessageBuilder::new(&self.uuid, message)
                        .name(name)
                        .values(rendered_values)
                        .build();
        Ok(message)
    }
}

#[derive(Debug)]
pub struct MessageResponse {
    message: Message,
}

impl MessageResponse {
    pub fn message(&self) -> &Message {
        &self.message
    }
}

impl Action for MessageAction {
    fn execute(&self, _state: &State, _context: &BaseContext) {
        trace!("MessageAction: executed");
        match self.render_message(_state, _context) {
            Ok(message) => {
                let response = MessageResponse {
                    message: message,
                };
                self.sender.borrow_mut().send_response(Response::Message(response));
            },
            Err(error) => {
                error!("{}", error);
            }
        }
    }
}
<|endoftext|>