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

use crate::rustc::hir;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::source_map::Span;
use crate::utils::span_lint;

/// **What it does:** Checks for plain integer arithmetic.
///
/// **Why is this bad?** This is only checked against overflow in debug builds.
/// In some applications one wants explicitly checked, wrapping or saturating
/// arithmetic.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// a + 1
/// ```
declare_clippy_lint! {
    pub INTEGER_ARITHMETIC,
    restriction,
    "any integer arithmetic statement"
}

/// **What it does:** Checks for float arithmetic.
///
/// **Why is this bad?** For some embedded systems or kernel development, it
/// can be useful to rule out floating-point numbers.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// a + 1.0
/// ```
declare_clippy_lint! {
    pub FLOAT_ARITHMETIC,
    restriction,
    "any floating-point arithmetic statement"
}

#[derive(Copy, Clone, Default)]
pub struct Arithmetic {
    expr_span: Option<Span>,
    /// This field is used to check whether expressions are constants, such as in enum discriminants
    /// and consts
    const_span: Option<Span>,
}

impl LintPass for Arithmetic {
    fn get_lints(&self) -> LintArray {
        lint_array!(INTEGER_ARITHMETIC, FLOAT_ARITHMETIC)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        if self.expr_span.is_some() {
            return;
        }

        if let Some(span) = self.const_span {
            if span.contains(expr.span) {
                return;
            }
        }
        match expr.node {
            hir::ExprKind::Binary(ref op, ref l, ref r) => {
                match op.node {
                    hir::BinOpKind::And
                    | hir::BinOpKind::Or
                    | hir::BinOpKind::BitAnd
                    | hir::BinOpKind::BitOr
                    | hir::BinOpKind::BitXor
                    | hir::BinOpKind::Shl
                    | hir::BinOpKind::Shr
                    | hir::BinOpKind::Eq
                    | hir::BinOpKind::Lt
                    | hir::BinOpKind::Le
                    | hir::BinOpKind::Ne
                    | hir::BinOpKind::Ge
                    | hir::BinOpKind::Gt => return,
                    _ => (),
                }
                let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
                if l_ty.is_integral() && r_ty.is_integral() {
                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
                    self.expr_span = Some(expr.span);
                } else if l_ty.is_floating_point() && r_ty.is_floating_point() {
                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
                    self.expr_span = Some(expr.span);
                }
            },
            hir::ExprKind::Unary(hir::UnOp::UnNeg, ref arg) => {
                let ty = cx.tables.expr_ty(arg);
                if ty.is_integral() {
                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
                    self.expr_span = Some(expr.span);
                } else if ty.is_floating_point() {
                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
                    self.expr_span = Some(expr.span);
                }
            },
            _ => (),
        }
    }

    fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        if Some(expr.span) == self.expr_span {
            self.expr_span = None;
        }
    }

    fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body) {
        let body_owner = cx.tcx.hir().body_owner(body.id());

        match cx.tcx.hir().body_owner_kind(body_owner) {
            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
                let body_span = cx.tcx.hir().span(body_owner);

                if let Some(span) = self.const_span {
                    if span.contains(body_span) {
                        return;
                    }
                }
                self.const_span = Some(body_span);
            },
            hir::BodyOwnerKind::Fn => (),
        }
    }

    fn check_body_post(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body) {
        let body_owner = cx.tcx.hir().body_owner(body.id());
        let body_span = cx.tcx.hir().span(body_owner);

        if let Some(span) = self.const_span {
            if span.contains(body_span) {
                return;
            }
        }
        self.const_span = None;
    }
}
<commit_msg>Use match ergonomics for artithmetic lint<commit_after>// Copyright 2014-2018 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use crate::rustc::hir;
use crate::rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
use crate::rustc::{declare_tool_lint, lint_array};
use crate::syntax::source_map::Span;
use crate::utils::span_lint;

/// **What it does:** Checks for plain integer arithmetic.
///
/// **Why is this bad?** This is only checked against overflow in debug builds.
/// In some applications one wants explicitly checked, wrapping or saturating
/// arithmetic.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// a + 1
/// ```
declare_clippy_lint! {
    pub INTEGER_ARITHMETIC,
    restriction,
    "any integer arithmetic statement"
}

/// **What it does:** Checks for float arithmetic.
///
/// **Why is this bad?** For some embedded systems or kernel development, it
/// can be useful to rule out floating-point numbers.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// a + 1.0
/// ```
declare_clippy_lint! {
    pub FLOAT_ARITHMETIC,
    restriction,
    "any floating-point arithmetic statement"
}

#[derive(Copy, Clone, Default)]
pub struct Arithmetic {
    expr_span: Option<Span>,
    /// This field is used to check whether expressions are constants, such as in enum discriminants
    /// and consts
    const_span: Option<Span>,
}

impl LintPass for Arithmetic {
    fn get_lints(&self) -> LintArray {
        lint_array!(INTEGER_ARITHMETIC, FLOAT_ARITHMETIC)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Arithmetic {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        if self.expr_span.is_some() {
            return;
        }

        if let Some(span) = self.const_span {
            if span.contains(expr.span) {
                return;
            }
        }
        match &expr.node {
            hir::ExprKind::Binary(op, l, r) => {
                match op.node {
                    hir::BinOpKind::And
                    | hir::BinOpKind::Or
                    | hir::BinOpKind::BitAnd
                    | hir::BinOpKind::BitOr
                    | hir::BinOpKind::BitXor
                    | hir::BinOpKind::Shl
                    | hir::BinOpKind::Shr
                    | hir::BinOpKind::Eq
                    | hir::BinOpKind::Lt
                    | hir::BinOpKind::Le
                    | hir::BinOpKind::Ne
                    | hir::BinOpKind::Ge
                    | hir::BinOpKind::Gt => return,
                    _ => (),
                }
                let (l_ty, r_ty) = (cx.tables.expr_ty(l), cx.tables.expr_ty(r));
                if l_ty.is_integral() && r_ty.is_integral() {
                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
                    self.expr_span = Some(expr.span);
                } else if l_ty.is_floating_point() && r_ty.is_floating_point() {
                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
                    self.expr_span = Some(expr.span);
                }
            },
            hir::ExprKind::Unary(hir::UnOp::UnNeg, arg) => {
                let ty = cx.tables.expr_ty(arg);
                if ty.is_integral() {
                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
                    self.expr_span = Some(expr.span);
                } else if ty.is_floating_point() {
                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
                    self.expr_span = Some(expr.span);
                }
            },
            _ => (),
        }
    }

    fn check_expr_post(&mut self, _: &LateContext<'a, 'tcx>, expr: &'tcx hir::Expr) {
        if Some(expr.span) == self.expr_span {
            self.expr_span = None;
        }
    }

    fn check_body(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body) {
        let body_owner = cx.tcx.hir().body_owner(body.id());

        match cx.tcx.hir().body_owner_kind(body_owner) {
            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
                let body_span = cx.tcx.hir().span(body_owner);

                if let Some(span) = self.const_span {
                    if span.contains(body_span) {
                        return;
                    }
                }
                self.const_span = Some(body_span);
            },
            hir::BodyOwnerKind::Fn => (),
        }
    }

    fn check_body_post(&mut self, cx: &LateContext<'_, '_>, body: &hir::Body) {
        let body_owner = cx.tcx.hir().body_owner(body.id());
        let body_span = cx.tcx.hir().span(body_owner);

        if let Some(span) = self.const_span {
            if span.contains(body_span) {
                return;
            }
        }
        self.const_span = None;
    }
}
<|endoftext|>


<commit_before>use crate::utils::{differing_macro_contexts, in_macro, snippet_opt, span_note_and_lint};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass, in_external_macro};
use rustc::{declare_tool_lint, lint_array};
use syntax::ast;
use syntax::ptr::P;

declare_clippy_lint! {
    /// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
    /// operators.
    ///
    /// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
    /// confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
    /// ```
    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
    style,
    "suspicious formatting of `*=`, `-=` or `!=`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for formatting of `else`. It lints if the `else`
    /// is followed immediately by a newline or the `else` seems to be missing.
    ///
    /// **Why is this bad?** This is probably some refactoring remnant, even if the
    /// code is correct, it might look confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if foo {
    /// } { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } if bar { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// { // this is the `else` block of the previous `if`, but should it be?
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// if bar { // this is the `else` block of the previous `if`, but should it be?
    /// }
    /// ```
    pub SUSPICIOUS_ELSE_FORMATTING,
    style,
    "suspicious formatting of `else`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for possible missing comma in an array. It lints if
    /// an array element is a binary operator expression and it lies on two lines.
    ///
    /// **Why is this bad?** This could lead to unexpected results.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// let a = &[
    ///     -1, -2, -3 // <= no comma here
    ///     -4, -5, -6
    /// ];
    /// ```
    pub POSSIBLE_MISSING_COMMA,
    correctness,
    "possible missing comma in array"
}

#[derive(Copy, Clone)]
pub struct Formatting;

impl LintPass for Formatting {
    fn get_lints(&self) -> LintArray {
        lint_array!(
            SUSPICIOUS_ASSIGNMENT_FORMATTING,
            SUSPICIOUS_ELSE_FORMATTING,
            POSSIBLE_MISSING_COMMA
        )
    }

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

impl EarlyLintPass for Formatting {
    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        for w in block.stmts.windows(2) {
            match (&w[0].node, &w[1].node) {
                (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second))
                | (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
                    check_missing_else(cx, first, second);
                },
                _ => (),
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
        check_assign(cx, expr);
        check_else(cx, expr);
        check_array(cx, expr);
    }
}

/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext<'_>, expr: &ast::Expr) {
    if let ast::ExprKind::Assign(ref lhs, ref rhs) = expr.node {
        if !differing_macro_contexts(lhs.span, rhs.span) && !in_macro(lhs.span) {
            let eq_span = lhs.span.between(rhs.span);
            if let ast::ExprKind::Unary(op, ref sub_rhs) = rhs.node {
                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
                    let op = ast::UnOp::to_string(op);
                    let eqop_span = lhs.span.between(sub_rhs.span);
                    if eq_snippet.ends_with('=') {
                        span_note_and_lint(
                            cx,
                            SUSPICIOUS_ASSIGNMENT_FORMATTING,
                            eqop_span,
                            &format!(
                                "this looks like you are trying to use `.. {op}= ..`, but you \
                                 really are doing `.. = ({op} ..)`",
                                op = op
                            ),
                            eqop_span,
                            &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
                        );
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
fn check_else(cx: &EarlyContext<'_>, expr: &ast::Expr) {
    if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
        if (is_block(else_) || unsugar_if(else_).is_some())
            && !differing_macro_contexts(then.span, else_.span)
            && !in_macro(then.span) && !in_external_macro(cx.sess, expr.span)
        {
            // workaround for rust-lang/rust#43081
            if expr.span.lo().0 == 0 && expr.span.hi().0 == 0 {
                return;
            }

            // this will be a span from the closing ‘}’ of the “then” block (excluding) to
            // the
            // “if” of the “else if” block (excluding)
            let else_span = then.span.between(else_.span);

            // the snippet should look like " else \n    " with maybe comments anywhere
            // it’s bad when there is a ‘\n’ after the “else”
            if let Some(else_snippet) = snippet_opt(cx, else_span) {
                let else_pos = else_snippet.find("else").expect("there must be a `else` here");

                if else_snippet[else_pos..].contains('\n') {
                    let else_desc = if unsugar_if(else_).is_some() { "if" } else { "{..}" };

                    span_note_and_lint(
                        cx,
                        SUSPICIOUS_ELSE_FORMATTING,
                        else_span,
                        &format!("this is an `else {}` but the formatting might hide it", else_desc),
                        else_span,
                        &format!(
                            "to remove this lint, remove the `else` or remove the new line between \
                             `else` and `{}`",
                            else_desc,
                        ),
                    );
                }
            }
        }
    }
}

fn has_unary_equivalent(bin_op: ast::BinOpKind) -> bool {
    // &, *, -
    bin_op == ast::BinOpKind::And || bin_op == ast::BinOpKind::Mul || bin_op == ast::BinOpKind::Sub
}

/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
fn check_array(cx: &EarlyContext<'_>, expr: &ast::Expr) {
    if let ast::ExprKind::Array(ref array) = expr.node {
        for element in array {
            if let ast::ExprKind::Binary(ref op, ref lhs, _) = element.node {
                if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span) {
                    let space_span = lhs.span.between(op.span);
                    if let Some(space_snippet) = snippet_opt(cx, space_span) {
                        let lint_span = lhs.span.with_lo(lhs.span.hi());
                        if space_snippet.contains('\n') {
                            span_note_and_lint(
                                cx,
                                POSSIBLE_MISSING_COMMA,
                                lint_span,
                                "possibly missing a comma here",
                                lint_span,
                                "to remove this lint, add a comma or write the expr in a single line",
                            );
                        }
                    }
                }
            }
        }
    }
}

fn check_missing_else(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
    if !differing_macro_contexts(first.span, second.span)
        && !in_macro(first.span)
        && unsugar_if(first).is_some()
        && (is_block(second) || unsugar_if(second).is_some())
    {
        // where the else would be
        let else_span = first.span.between(second.span);

        if let Some(else_snippet) = snippet_opt(cx, else_span) {
            if !else_snippet.contains('\n') {
                let (looks_like, next_thing) = if unsugar_if(second).is_some() {
                    ("an `else if`", "the second `if`")
                } else {
                    ("an `else {..}`", "the next block")
                };

                span_note_and_lint(
                    cx,
                    SUSPICIOUS_ELSE_FORMATTING,
                    else_span,
                    &format!("this looks like {} but the `else` is missing", looks_like),
                    else_span,
                    &format!(
                        "to remove this lint, add the missing `else` or add a new line before {}",
                        next_thing,
                    ),
                );
            }
        }
    }
}

fn is_block(expr: &ast::Expr) -> bool {
    if let ast::ExprKind::Block(..) = expr.node {
        true
    } else {
        false
    }
}

/// Match `if` or `if let` expressions and return the `then` and `else` block.
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
    match expr.node {
        ast::ExprKind::If(_, ref then, ref else_) | ast::ExprKind::IfLet(_, _, ref then, ref else_) => {
            Some((then, else_))
        },
        _ => None,
    }
}
<commit_msg>cargo fmt<commit_after>use crate::utils::{differing_macro_contexts, in_macro, snippet_opt, span_note_and_lint};
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, lint_array};
use syntax::ast;
use syntax::ptr::P;

declare_clippy_lint! {
    /// **What it does:** Checks for use of the non-existent `=*`, `=!` and `=-`
    /// operators.
    ///
    /// **Why is this bad?** This is either a typo of `*=`, `!=` or `-=` or
    /// confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// a =- 42; // confusing, should it be `a -= 42` or `a = -42`?
    /// ```
    pub SUSPICIOUS_ASSIGNMENT_FORMATTING,
    style,
    "suspicious formatting of `*=`, `-=` or `!=`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for formatting of `else`. It lints if the `else`
    /// is followed immediately by a newline or the `else` seems to be missing.
    ///
    /// **Why is this bad?** This is probably some refactoring remnant, even if the
    /// code is correct, it might look confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// if foo {
    /// } { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } if bar { // looks like an `else` is missing here
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// { // this is the `else` block of the previous `if`, but should it be?
    /// }
    ///
    /// if foo {
    /// } else
    ///
    /// if bar { // this is the `else` block of the previous `if`, but should it be?
    /// }
    /// ```
    pub SUSPICIOUS_ELSE_FORMATTING,
    style,
    "suspicious formatting of `else`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for possible missing comma in an array. It lints if
    /// an array element is a binary operator expression and it lies on two lines.
    ///
    /// **Why is this bad?** This could lead to unexpected results.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// let a = &[
    ///     -1, -2, -3 // <= no comma here
    ///     -4, -5, -6
    /// ];
    /// ```
    pub POSSIBLE_MISSING_COMMA,
    correctness,
    "possible missing comma in array"
}

#[derive(Copy, Clone)]
pub struct Formatting;

impl LintPass for Formatting {
    fn get_lints(&self) -> LintArray {
        lint_array!(
            SUSPICIOUS_ASSIGNMENT_FORMATTING,
            SUSPICIOUS_ELSE_FORMATTING,
            POSSIBLE_MISSING_COMMA
        )
    }

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

impl EarlyLintPass for Formatting {
    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &ast::Block) {
        for w in block.stmts.windows(2) {
            match (&w[0].node, &w[1].node) {
                (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Expr(ref second))
                | (&ast::StmtKind::Expr(ref first), &ast::StmtKind::Semi(ref second)) => {
                    check_missing_else(cx, first, second);
                },
                _ => (),
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
        check_assign(cx, expr);
        check_else(cx, expr);
        check_array(cx, expr);
    }
}

/// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
fn check_assign(cx: &EarlyContext<'_>, expr: &ast::Expr) {
    if let ast::ExprKind::Assign(ref lhs, ref rhs) = expr.node {
        if !differing_macro_contexts(lhs.span, rhs.span) && !in_macro(lhs.span) {
            let eq_span = lhs.span.between(rhs.span);
            if let ast::ExprKind::Unary(op, ref sub_rhs) = rhs.node {
                if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
                    let op = ast::UnOp::to_string(op);
                    let eqop_span = lhs.span.between(sub_rhs.span);
                    if eq_snippet.ends_with('=') {
                        span_note_and_lint(
                            cx,
                            SUSPICIOUS_ASSIGNMENT_FORMATTING,
                            eqop_span,
                            &format!(
                                "this looks like you are trying to use `.. {op}= ..`, but you \
                                 really are doing `.. = ({op} ..)`",
                                op = op
                            ),
                            eqop_span,
                            &format!("to remove this lint, use either `{op}=` or `= {op}`", op = op),
                        );
                    }
                }
            }
        }
    }
}

/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
fn check_else(cx: &EarlyContext<'_>, expr: &ast::Expr) {
    if let Some((then, &Some(ref else_))) = unsugar_if(expr) {
        if (is_block(else_) || unsugar_if(else_).is_some())
            && !differing_macro_contexts(then.span, else_.span)
            && !in_macro(then.span)
            && !in_external_macro(cx.sess, expr.span)
        {
            // workaround for rust-lang/rust#43081
            if expr.span.lo().0 == 0 && expr.span.hi().0 == 0 {
                return;
            }

            // this will be a span from the closing ‘}’ of the “then” block (excluding) to
            // the
            // “if” of the “else if” block (excluding)
            let else_span = then.span.between(else_.span);

            // the snippet should look like " else \n    " with maybe comments anywhere
            // it’s bad when there is a ‘\n’ after the “else”
            if let Some(else_snippet) = snippet_opt(cx, else_span) {
                let else_pos = else_snippet.find("else").expect("there must be a `else` here");

                if else_snippet[else_pos..].contains('\n') {
                    let else_desc = if unsugar_if(else_).is_some() { "if" } else { "{..}" };

                    span_note_and_lint(
                        cx,
                        SUSPICIOUS_ELSE_FORMATTING,
                        else_span,
                        &format!("this is an `else {}` but the formatting might hide it", else_desc),
                        else_span,
                        &format!(
                            "to remove this lint, remove the `else` or remove the new line between \
                             `else` and `{}`",
                            else_desc,
                        ),
                    );
                }
            }
        }
    }
}

fn has_unary_equivalent(bin_op: ast::BinOpKind) -> bool {
    // &, *, -
    bin_op == ast::BinOpKind::And || bin_op == ast::BinOpKind::Mul || bin_op == ast::BinOpKind::Sub
}

/// Implementation of the `POSSIBLE_MISSING_COMMA` lint for array
fn check_array(cx: &EarlyContext<'_>, expr: &ast::Expr) {
    if let ast::ExprKind::Array(ref array) = expr.node {
        for element in array {
            if let ast::ExprKind::Binary(ref op, ref lhs, _) = element.node {
                if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span) {
                    let space_span = lhs.span.between(op.span);
                    if let Some(space_snippet) = snippet_opt(cx, space_span) {
                        let lint_span = lhs.span.with_lo(lhs.span.hi());
                        if space_snippet.contains('\n') {
                            span_note_and_lint(
                                cx,
                                POSSIBLE_MISSING_COMMA,
                                lint_span,
                                "possibly missing a comma here",
                                lint_span,
                                "to remove this lint, add a comma or write the expr in a single line",
                            );
                        }
                    }
                }
            }
        }
    }
}

fn check_missing_else(cx: &EarlyContext<'_>, first: &ast::Expr, second: &ast::Expr) {
    if !differing_macro_contexts(first.span, second.span)
        && !in_macro(first.span)
        && unsugar_if(first).is_some()
        && (is_block(second) || unsugar_if(second).is_some())
    {
        // where the else would be
        let else_span = first.span.between(second.span);

        if let Some(else_snippet) = snippet_opt(cx, else_span) {
            if !else_snippet.contains('\n') {
                let (looks_like, next_thing) = if unsugar_if(second).is_some() {
                    ("an `else if`", "the second `if`")
                } else {
                    ("an `else {..}`", "the next block")
                };

                span_note_and_lint(
                    cx,
                    SUSPICIOUS_ELSE_FORMATTING,
                    else_span,
                    &format!("this looks like {} but the `else` is missing", looks_like),
                    else_span,
                    &format!(
                        "to remove this lint, add the missing `else` or add a new line before {}",
                        next_thing,
                    ),
                );
            }
        }
    }
}

fn is_block(expr: &ast::Expr) -> bool {
    if let ast::ExprKind::Block(..) = expr.node {
        true
    } else {
        false
    }
}

/// Match `if` or `if let` expressions and return the `then` and `else` block.
fn unsugar_if(expr: &ast::Expr) -> Option<(&P<ast::Block>, &Option<P<ast::Expr>>)> {
    match expr.node {
        ast::ExprKind::If(_, ref then, ref else_) | ast::ExprKind::IfLet(_, _, ref then, ref else_) => {
            Some((then, else_))
        },
        _ => None,
    }
}
<|endoftext|>


<commit_before>use crate::clean;
use crate::core::DocContext;
use crate::fold::{self, DocFolder};
use crate::html::markdown::{find_testable_code, ErrorCodes};
use crate::passes::doc_test_lints::{should_have_doc_example, Tests};
use crate::passes::Pass;
use rustc_lint::builtin::MISSING_DOCS;
use rustc_middle::lint::LintLevelSource;
use rustc_session::lint;
use rustc_span::FileName;
use serde::Serialize;

use std::collections::BTreeMap;
use std::ops;

crate const CALCULATE_DOC_COVERAGE: Pass = Pass {
    name: "calculate-doc-coverage",
    run: calculate_doc_coverage,
    description: "counts the number of items with and without documentation",
};

fn calculate_doc_coverage(krate: clean::Crate, ctx: &mut DocContext<'_>) -> clean::Crate {
    let mut calc = CoverageCalculator { items: Default::default(), ctx };
    let krate = calc.fold_crate(krate);

    calc.print_results();

    krate
}

#[derive(Default, Copy, Clone, Serialize, Debug)]
struct ItemCount {
    total: u64,
    with_docs: u64,
    total_examples: u64,
    with_examples: u64,
}

impl ItemCount {
    fn count_item(
        &mut self,
        has_docs: bool,
        has_doc_example: bool,
        should_have_doc_examples: bool,
        should_have_docs: bool,
    ) {
        if has_docs || should_have_docs {
            self.total += 1;
        }

        if has_docs {
            self.with_docs += 1;
        }
        if should_have_doc_examples || has_doc_example {
            self.total_examples += 1;
        }
        if has_doc_example {
            self.with_examples += 1;
        }
    }

    fn percentage(&self) -> Option<f64> {
        if self.total > 0 {
            Some((self.with_docs as f64 * 100.0) / self.total as f64)
        } else {
            None
        }
    }

    fn examples_percentage(&self) -> Option<f64> {
        if self.total_examples > 0 {
            Some((self.with_examples as f64 * 100.0) / self.total_examples as f64)
        } else {
            None
        }
    }
}

impl ops::Sub for ItemCount {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        ItemCount {
            total: self.total - rhs.total,
            with_docs: self.with_docs - rhs.with_docs,
            total_examples: self.total_examples - rhs.total_examples,
            with_examples: self.with_examples - rhs.with_examples,
        }
    }
}

impl ops::AddAssign for ItemCount {
    fn add_assign(&mut self, rhs: Self) {
        self.total += rhs.total;
        self.with_docs += rhs.with_docs;
        self.total_examples += rhs.total_examples;
        self.with_examples += rhs.with_examples;
    }
}

struct CoverageCalculator<'a, 'b> {
    items: BTreeMap<FileName, ItemCount>,
    ctx: &'a mut DocContext<'b>,
}

fn limit_filename_len(filename: String) -> String {
    let nb_chars = filename.chars().count();
    if nb_chars > 35 {
        "...".to_string()
            + &filename[filename.char_indices().nth(nb_chars - 32).map(|x| x.0).unwrap_or(0)..]
    } else {
        filename
    }
}

impl<'a, 'b> CoverageCalculator<'a, 'b> {
    fn to_json(&self) -> String {
        serde_json::to_string(
            &self
                .items
                .iter()
                .map(|(k, v)| (k.prefer_local().to_string(), v))
                .collect::<BTreeMap<String, &ItemCount>>(),
        )
        .expect("failed to convert JSON data to string")
    }

    fn print_results(&self) {
        let output_format = self.ctx.output_format;
        if output_format.is_json() {
            println!("{}", self.to_json());
            return;
        }
        let mut total = ItemCount::default();

        fn print_table_line() {
            println!("+-{0:->35}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+", "");
        }

        fn print_table_record(
            name: &str,
            count: ItemCount,
            percentage: f64,
            examples_percentage: f64,
        ) {
            println!(
                "| {:<35} | {:>10} | {:>9.1}% | {:>10} | {:>9.1}% |",
                name, count.with_docs, percentage, count.with_examples, examples_percentage,
            );
        }

        print_table_line();
        println!(
            "| {:<35} | {:>10} | {:>10} | {:>10} | {:>10} |",
            "File", "Documented", "Percentage", "Examples", "Percentage",
        );
        print_table_line();

        for (file, &count) in &self.items {
            if let Some(percentage) = count.percentage() {
                print_table_record(
                    &limit_filename_len(file.prefer_local().to_string_lossy().into()),
                    count,
                    percentage,
                    count.examples_percentage().unwrap_or(0.),
                );

                total += count;
            }
        }

        print_table_line();
        print_table_record(
            "Total",
            total,
            total.percentage().unwrap_or(0.0),
            total.examples_percentage().unwrap_or(0.0),
        );
        print_table_line();
    }
}

impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
    fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
        match *i.kind {
            _ if !i.def_id.is_local() => {
                // non-local items are skipped because they can be out of the users control,
                // especially in the case of trait impls, which rustdoc eagerly inlines
                return Some(i);
            }
            clean::StrippedItem(..) => {
                // don't count items in stripped modules
                return Some(i);
            }
            // docs on `use` and `extern crate` statements are not displayed, so they're not
            // worth counting
            clean::ImportItem(..) | clean::ExternCrateItem { .. } => {}
            // Don't count trait impls, the missing-docs lint doesn't so we shouldn't either.
            // Inherent impls *can* be documented, and those docs show up, but in most cases it
            // doesn't make sense, as all methods on a type are in one single impl block
            clean::ImplItem(_) => {}
            _ => {
                let has_docs = !i.attrs.doc_strings.is_empty();
                let mut tests = Tests { found_tests: 0 };

                find_testable_code(
                    &i.attrs.collapsed_doc_value().unwrap_or_default(),
                    &mut tests,
                    ErrorCodes::No,
                    false,
                    None,
                );

                let filename = i.span(self.ctx.tcx).filename(self.ctx.sess());
                let has_doc_example = tests.found_tests != 0;
                // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
                // would presumably panic if a fake `DefIndex` were passed.
                let hir_id = self
                    .ctx
                    .tcx
                    .hir()
                    .local_def_id_to_hir_id(i.def_id.expect_def_id().expect_local());
                let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
                // `missing_docs` is allow-by-default, so don't treat this as ignoring the item
                // unless the user had an explicit `allow`
                let should_have_docs =
                    level != lint::Level::Allow || matches!(source, LintLevelSource::Default);
                debug!("counting {:?} {:?} in {:?}", i.type_(), i.name, filename);
                self.items.entry(filename).or_default().count_item(
                    has_docs,
                    has_doc_example,
                    should_have_doc_example(self.ctx, &i),
                    should_have_docs,
                );
            }
        }

        Some(self.fold_item_recur(i))
    }
}
<commit_msg>Don't require documentation for fields in an enum tuple variant or for tuple struct fields.<commit_after>use crate::clean;
use crate::core::DocContext;
use crate::fold::{self, DocFolder};
use crate::html::markdown::{find_testable_code, ErrorCodes};
use crate::passes::doc_test_lints::{should_have_doc_example, Tests};
use crate::passes::Pass;
use rustc_hir as hir;
use rustc_lint::builtin::MISSING_DOCS;
use rustc_middle::lint::LintLevelSource;
use rustc_middle::ty::DefIdTree;
use rustc_session::lint;
use rustc_span::FileName;
use serde::Serialize;

use std::collections::BTreeMap;
use std::ops;

crate const CALCULATE_DOC_COVERAGE: Pass = Pass {
    name: "calculate-doc-coverage",
    run: calculate_doc_coverage,
    description: "counts the number of items with and without documentation",
};

fn calculate_doc_coverage(krate: clean::Crate, ctx: &mut DocContext<'_>) -> clean::Crate {
    let mut calc = CoverageCalculator { items: Default::default(), ctx };
    let krate = calc.fold_crate(krate);

    calc.print_results();

    krate
}

#[derive(Default, Copy, Clone, Serialize, Debug)]
struct ItemCount {
    total: u64,
    with_docs: u64,
    total_examples: u64,
    with_examples: u64,
}

impl ItemCount {
    fn count_item(
        &mut self,
        has_docs: bool,
        has_doc_example: bool,
        should_have_doc_examples: bool,
        should_have_docs: bool,
    ) {
        if has_docs || should_have_docs {
            self.total += 1;
        }

        if has_docs {
            self.with_docs += 1;
        }
        if should_have_doc_examples || has_doc_example {
            self.total_examples += 1;
        }
        if has_doc_example {
            self.with_examples += 1;
        }
    }

    fn percentage(&self) -> Option<f64> {
        if self.total > 0 {
            Some((self.with_docs as f64 * 100.0) / self.total as f64)
        } else {
            None
        }
    }

    fn examples_percentage(&self) -> Option<f64> {
        if self.total_examples > 0 {
            Some((self.with_examples as f64 * 100.0) / self.total_examples as f64)
        } else {
            None
        }
    }
}

impl ops::Sub for ItemCount {
    type Output = Self;

    fn sub(self, rhs: Self) -> Self {
        ItemCount {
            total: self.total - rhs.total,
            with_docs: self.with_docs - rhs.with_docs,
            total_examples: self.total_examples - rhs.total_examples,
            with_examples: self.with_examples - rhs.with_examples,
        }
    }
}

impl ops::AddAssign for ItemCount {
    fn add_assign(&mut self, rhs: Self) {
        self.total += rhs.total;
        self.with_docs += rhs.with_docs;
        self.total_examples += rhs.total_examples;
        self.with_examples += rhs.with_examples;
    }
}

struct CoverageCalculator<'a, 'b> {
    items: BTreeMap<FileName, ItemCount>,
    ctx: &'a mut DocContext<'b>,
}

fn limit_filename_len(filename: String) -> String {
    let nb_chars = filename.chars().count();
    if nb_chars > 35 {
        "...".to_string()
            + &filename[filename.char_indices().nth(nb_chars - 32).map(|x| x.0).unwrap_or(0)..]
    } else {
        filename
    }
}

impl<'a, 'b> CoverageCalculator<'a, 'b> {
    fn to_json(&self) -> String {
        serde_json::to_string(
            &self
                .items
                .iter()
                .map(|(k, v)| (k.prefer_local().to_string(), v))
                .collect::<BTreeMap<String, &ItemCount>>(),
        )
        .expect("failed to convert JSON data to string")
    }

    fn print_results(&self) {
        let output_format = self.ctx.output_format;
        if output_format.is_json() {
            println!("{}", self.to_json());
            return;
        }
        let mut total = ItemCount::default();

        fn print_table_line() {
            println!("+-{0:->35}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+-{0:->10}-+", "");
        }

        fn print_table_record(
            name: &str,
            count: ItemCount,
            percentage: f64,
            examples_percentage: f64,
        ) {
            println!(
                "| {:<35} | {:>10} | {:>9.1}% | {:>10} | {:>9.1}% |",
                name, count.with_docs, percentage, count.with_examples, examples_percentage,
            );
        }

        print_table_line();
        println!(
            "| {:<35} | {:>10} | {:>10} | {:>10} | {:>10} |",
            "File", "Documented", "Percentage", "Examples", "Percentage",
        );
        print_table_line();

        for (file, &count) in &self.items {
            if let Some(percentage) = count.percentage() {
                print_table_record(
                    &limit_filename_len(file.prefer_local().to_string_lossy().into()),
                    count,
                    percentage,
                    count.examples_percentage().unwrap_or(0.),
                );

                total += count;
            }
        }

        print_table_line();
        print_table_record(
            "Total",
            total,
            total.percentage().unwrap_or(0.0),
            total.examples_percentage().unwrap_or(0.0),
        );
        print_table_line();
    }
}

impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
    fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
        match *i.kind {
            _ if !i.def_id.is_local() => {
                // non-local items are skipped because they can be out of the users control,
                // especially in the case of trait impls, which rustdoc eagerly inlines
                return Some(i);
            }
            clean::StrippedItem(..) => {
                // don't count items in stripped modules
                return Some(i);
            }
            // docs on `use` and `extern crate` statements are not displayed, so they're not
            // worth counting
            clean::ImportItem(..) | clean::ExternCrateItem { .. } => {}
            // Don't count trait impls, the missing-docs lint doesn't so we shouldn't either.
            // Inherent impls *can* be documented, and those docs show up, but in most cases it
            // doesn't make sense, as all methods on a type are in one single impl block
            clean::ImplItem(_) => {}
            _ => {
                let has_docs = !i.attrs.doc_strings.is_empty();
                let mut tests = Tests { found_tests: 0 };

                find_testable_code(
                    &i.attrs.collapsed_doc_value().unwrap_or_default(),
                    &mut tests,
                    ErrorCodes::No,
                    false,
                    None,
                );

                let filename = i.span(self.ctx.tcx).filename(self.ctx.sess());
                let has_doc_example = tests.found_tests != 0;
                // The `expect_def_id()` should be okay because `local_def_id_to_hir_id`
                // would presumably panic if a fake `DefIndex` were passed.
                let hir_id = self
                    .ctx
                    .tcx
                    .hir()
                    .local_def_id_to_hir_id(i.def_id.expect_def_id().expect_local());
                let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);

                // In case we have:
                //
                // ```
                // enum Foo { Bar(u32) }
                // // or:
                // struct Bar(u32);
                // ```
                //
                // there is no need to require documentation on the fields of tuple variants and
                // tuple structs.
                let should_be_ignored = i
                    .def_id
                    .as_def_id()
                    .and_then(|def_id| self.ctx.tcx.parent(def_id))
                    .and_then(|def_id| self.ctx.tcx.hir().get_if_local(def_id))
                    .map(|node| {
                        matches!(
                            node,
                            hir::Node::Variant(hir::Variant {
                                data: hir::VariantData::Tuple(_, _),
                                ..
                            }) | hir::Node::Item(hir::Item {
                                kind: hir::ItemKind::Struct(hir::VariantData::Tuple(_, _), _),
                                ..
                            })
                        )
                    })
                    .unwrap_or(false);

                // `missing_docs` is allow-by-default, so don't treat this as ignoring the item
                // unless the user had an explicit `allow`.
                //
                let should_have_docs = !should_be_ignored
                    && (level != lint::Level::Allow || matches!(source, LintLevelSource::Default));

                debug!("counting {:?} {:?} in {:?}", i.type_(), i.name, filename);
                self.items.entry(filename).or_default().count_item(
                    has_docs,
                    has_doc_example,
                    should_have_doc_example(self.ctx, &i),
                    should_have_docs,
                );
            }
        }

        Some(self.fold_item_recur(i))
    }
}
<|endoftext|>


<commit_before>//! The server-side implementation of `multipart/form-data` requests.
//!
//! Use this when you are implementing a server on top of Hyper and want to
//! to parse and serve POST `multipart/form-data` requests.
//!
//! See the `Multipart` struct for more info.

use mime::Mime;

use std::borrow::Borrow;
use std::ops::Deref;

use std::collections::HashMap;

use std::fs::{self, File};
use std::io;
use std::io::prelude::*;

use std::path::{Path, PathBuf};

#[doc(inline)]
pub use self::boundary::BoundaryReader;

mod boundary;

#[cfg(feature = "hyper")]
pub mod hyper;

/// The server-side implementation of `multipart/form-data` requests.
///
/// Create this with `Multipart::from_request()` passing a `server::Request` object from Hyper,
/// or give Hyper a `handler::Switch` instance instead,
/// then read individual entries with `.read_entry()` or process them all at once with
/// `.foreach_entry()`.
///
/// Implements `Deref<Request>` to allow access to read-only fields on `Request` without copying.
pub struct Multipart<R> {
    source: BoundaryReader<R>,
    line_buf: String,
}

macro_rules! try_find(
    ($needle:expr, $haystack:expr, $err:expr) => (
        try!($haystack.find($needle).ok_or_else(|| line_error($err, $haystack)))
    )
);

impl<R> Multipart<R> where R: HttpRequest {
    /// If the given `HttpRequest` is a POST request of `Content-Type: multipart/form-data`,
    /// return the wrapped request as `Ok(Multipart)`, otherwise `Err(HttpRequest)`.
    pub fn from_request(req: R) -> Result<Multipart<R>, R> {
        if !req.is_multipart() { return Err(req); }

        if req.get_boundary().is_none() {
            return Err(req);
        }

        let boundary = req.get_boundary().unwrap().to_owned();

        debug!("Boundary: {}", boundary);

        Ok(
            Multipart {
                source: BoundaryReader::from_reader(req, boundary),
                line_buf: String::new(),
            }
        )
    }

    /// Read an entry from this multipart request, returning a pair with the field's name and
    /// contents. This will return an End of File error if there are no more entries.
    ///
    /// To get to the data, you will need to match on `MultipartField`.
    ///
    /// ##Warning
    /// If the last returned entry had contents of type `MultipartField::File`,
    /// calling this again will discard any unread contents of that entry!
    pub fn read_entry(&mut self) -> io::Result<(String, MultipartField<R>)> {
        try!(self.source.consume_boundary());
        let (disp_type, field_name, filename) = try!(self.read_content_disposition());

        if disp_type != "form-data" {
            return Err(io::Error::new(
                io::ErrorKind::Other,
                format!("Content-Disposition value: {:?} expected: \"form-data\"", disp_type),
            ));
        }

        if let Some(content_type) = try!(self.read_content_type()) {
            let _ = try!(self.read_line()); // Consume empty line
            Ok((field_name,
                MultipartField::File(
                    MultipartFile::from_reader(filename, &mut self.source, &content_type)
                )
            ))
        } else {
            // Empty line consumed by read_content_type()
            let text = try!(self.read_to_string());
            // The last two characters are "\r\n".
            // We can't do a simple trim because the content might be terminated
            // with line separators we want to preserve.
            Ok((field_name, MultipartField::Text(text[..text.len() - 2].into())))
        }
    }

    /// Call `f` for each entry in the multipart request.
    /// This is a substitute for `Multipart` implementing `Iterator`,
    /// since `Iterator::next()` can't use bound lifetimes.
    ///
    /// See https://www.reddit.com/r/rust/comments/2lkk\4\isize/concrete_lifetime_vs_bound_lifetime/
    pub fn foreach_entry<F>(&mut self, mut foreach: F) where F: FnMut(String, MultipartField<R>) {
        loop {
            match self.read_entry() {
                Ok((name, field)) => foreach(name, field),
                Err(err) => {
                    error!("Error reading Multipart: {}", err);
                    break;
                },
            }
        }
    }

    fn read_content_disposition(&mut self) -> io::Result<(String, String, Option<String>)> {
        let line = try!(self.read_line());

        // Find the end of CONT_DISP in the line
        let disp_type = {
            const CONT_DISP: &'static str = "Content-Disposition:";

            let disp_idx = try_find!(CONT_DISP, &line, "Content-Disposition subheader not found!")
                + CONT_DISP.len();

            let disp_type_end = try_find!(
                ';', &line[disp_idx..],
                "Error parsing Content-Disposition value!"
            );

            line[disp_idx .. disp_idx + disp_type_end].trim().to_owned()
        };

        let field_name = {
            const NAME: &'static str = "name=\"";

            let name_idx = try_find!(NAME, &line, "Error parsing field name!") + NAME.len();
            let name_end = try_find!('"', &line[name_idx ..], "Error parsing field name!");

            line[name_idx .. name_idx + name_end].to_owned() // No trim here since it's in quotes.
        };

        let filename = {
            const FILENAME: &'static str = "filename=\"";

            let filename_idx = line.find(FILENAME).map(|idx| idx + FILENAME.len());
            let filename_idxs = with(filename_idx, |&start| line[start ..].find('"'));

            filename_idxs.map(|(start, end)| line[start .. start + end].to_owned())
        };

        Ok((disp_type, field_name, filename))
    }

    fn read_content_type(&mut self) -> io::Result<Option<String>> {
        debug!("Read content type!");
        let line = try!(self.read_line());

        const CONTENT_TYPE: &'static str = "Content-Type:";

        let type_idx = line.find(CONTENT_TYPE);

        // FIXME Will not properly parse for multiple files!
        // Does not expect boundary=<boundary>
        Ok(type_idx.map(|start| line[(start + CONTENT_TYPE.len())..].trim().to_owned()))
    }

    /// Read the request fully, parsing all fields and saving all files
    /// to the given directory (if given) and return the result.
    ///
    /// If `dir` is none, uses `std::os::tmpdir()`.
    pub fn save_all(mut self, dir: Option<&Path>) -> io::Result<Entries> {
        let dir = dir.map_or_else(::std::env::temp_dir, |path| path.to_owned());

        let mut entries = Entries::with_path(dir);

        loop {
            match self.read_entry() {
                Ok((name, MultipartField::Text(text))) => { entries.fields.insert(name, text.to_owned()); },
                Ok((name, MultipartField::File(mut file))) => {
                    let path = try!(file.save_in(&entries.dir));
                    entries.files.insert(name, path);
                },
                Err(err) => {
                    error!("Error reading Multipart: {}", err);
                    break;
                },
            }
        }

        Ok(entries)
    }

    fn read_line(&mut self) -> io::Result<&str> {
        match self.source.read_line(&mut self.line_buf) {
            Ok(read) => Ok(&self.line_buf[..read]),
            Err(err) => Err(err),
        }
    }

    fn read_to_string(&mut self) -> io::Result<&str> {
        match self.source.read_to_string(&mut self.line_buf) {
            Ok(read) => Ok(&self.line_buf[..read]),
            Err(err) => Err(err),
        }
    }
}

impl<R> Deref for Multipart<R> where R: HttpRequest {
    type Target = R;
    fn deref(&self) -> &R {
        self.source.borrow()
    }
}

fn with<T, U, F: FnOnce(&T) -> Option<U>>(left: Option<T>, right: F) -> Option<(T, U)> {
    let temp = left.as_ref().and_then(right);
    match (left, temp) {
        (Some(lval), Some(rval)) => Some((lval, rval)),
        _ => None,
    }
}

fn line_error(msg: &str, line: &str) -> io::Error {
    io::Error::new(
        io::ErrorKind::Other,
        format!("Error: {:?} on line of request: {:?}", msg, line)
    )
}

/// A result of `Multipart::save_all()`.
pub struct Entries {
    pub fields: HashMap<String, String>,
    pub files: HashMap<String, PathBuf>,
    /// The directory the files were saved under.
    pub dir: PathBuf,
}

impl Entries {
    fn with_path<P: Into<PathBuf>>(path: P) -> Entries {
        Entries {
            fields: HashMap::new(),
            files: HashMap::new(),
            dir: path.into(),
        }
    }
}

pub trait HttpRequest: Read {
    fn is_multipart(&self) -> bool;
    fn get_boundary(&self) -> Option<&str>;
}

/// A field in a `multipart/form-data` request.
///
/// This enum does not include the names of the fields, as those are yielded separately
/// by `server::Multipart::read_entry()`.
#[derive(Debug)]
pub enum MultipartField<'a, R: 'a> {
    /// A text field.
    Text(&'a str),
    /// A file field, including the content type and optional filename
    /// along with a `Read` implementation for getting the contents.
    File(MultipartFile<'a, R>),
    // MultiFiles(Vec<MultipartFile>), /* TODO: Multiple files */
}

impl<'a, R> MultipartField<'a, R> {
    /// Borrow this field as a text field, if possible.
    pub fn as_text(&self) -> Option<&str> {
        match *self {
            MultipartField::Text(ref s) => Some(s),
            _ => None,
        }
    }

    /// Borrow this field as a file field, if possible
    /// Mutably borrows so the contents can be read.
    pub fn as_file(&mut self) -> Option<&mut MultipartFile<'a, R>> {
        match *self {
            MultipartField::File(ref mut file) => Some(file),
            _ => None,
        }
    }
}

/// A representation of a file in HTTP `multipart/form-data`.
///
/// Note that the file is not yet saved to the system;
/// instead, the struct implements a `Reader` that points
/// to the beginning of the file's contents in the HTTP stream.
/// You can read it to EOF, or use one of the `save_*()` methods here
/// to save it to disk.
#[derive(Debug)]
pub struct MultipartFile<'a, R: 'a> {
    filename: Option<String>,
    content_type: Mime,
    reader: &'a mut BoundaryReader<R>,
}

impl<'a, R: Read> MultipartFile<'a, R> {
    fn from_reader(
        filename: Option<String>, reader: &'a mut BoundaryReader<R>, cont_type: &str
    ) -> MultipartFile<'a, R> {
        MultipartFile {
            filename: filename,
            content_type: cont_type.parse::<Mime>().ok().unwrap_or_else(::mime_guess::octet_stream),
            reader: reader,
        }
    }

    /// Save this file to `path`, discarding the filename.
    ///
    /// If successful, the file can be found at `path`.
    pub fn save_as(&mut self, path: &Path) -> io::Result<()> {
        let mut file = try!(File::create(path));
        io::copy(self.reader, &mut file).and(Ok(()))
    }

    /// Save this file in the directory described by `dir`,
    /// appending `filename` if present, or a random string otherwise.
    ///
    /// Returns the created file's path on success.
    ///
    /// ###Panics
    /// If `dir` does not represent a directory.
    pub fn save_in(&mut self, dir: &Path) -> io::Result<PathBuf> {
        let meta = try!(fs::metadata(dir));
        assert!(meta.is_dir(), "Given path is not a directory!");

        let path = dir.join(::random_alphanumeric(8));

        try!(self.save_as(&path));

        Ok(path)
    }

    /// Save this file in the OS temp directory, returned from `std::env::temp_dir()`.
    ///
    /// Returns the created file's path on success.
    pub fn save_temp(&mut self) -> io::Result<PathBuf> {
        use std::env;

        self.save_in(&env::temp_dir())
    }

    pub fn filename(&self) -> Option<&str> {
        self.filename.as_ref().map(String::as_ref)
    }

    /// Get the content type of this file.
    /// On the client, it is guessed by the file extension.
    /// On the server, it is retrieved from the request or assumed to be
    /// `application/octet-stream`.
    pub fn content_type(&self) -> Mime {
        self.content_type.clone()
    }
}

impl<'a, R: Read> Read for MultipartFile<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>{
        self.reader.read(buf)
    }
}

<commit_msg>Server API rework (may not compile)<commit_after>//! The server-side implementation of `multipart/form-data` requests.
//!
//! Use this when you are implementing a server on top of Hyper and want to
//! to parse and serve POST `multipart/form-data` requests.
//!
//! See the `Multipart` struct for more info.

use mime::Mime;

use std::borrow::Borrow;
use std::ops::Deref;

use std::collections::HashMap;

use std::fs::{self, File};
use std::io;
use std::io::prelude::*;

use std::path::{Path, PathBuf};

#[doc(inline)]
pub use self::boundary::BoundaryReader;

mod boundary;

#[cfg(feature = "hyper")]
pub mod hyper;

macro_rules! try_opt (
    ($expr:expr) => (
        match $expr {
            Some(val) => Some(val),
            None => return None,
        }
    )
);

#[derive(Clone, Debug)]
pub enum MultipartError {
    EndOfStream,
    Io(io::Error),
}

impl From<io::Error> for MultipartError {
    fn from(err: io::Error) -> MultipartError {
        MultipartError::Io(err)
    }
}

impl MultipartError {
    fn is_end_of_stream(&self) -> bool {
        match *self {
            MultipartError::EndOfStream => true,
            _ => false,
        }
    }
}

/// The server-side implementation of `multipart/form-data` requests.
///
/// Create this with `Multipart::from_request()` passing a `server::Request` object from Hyper,
/// or give Hyper a `handler::Switch` instance instead,
/// then read individual entries with `.read_entry()` or process them all at once with
/// `.foreach_entry()`.
///
/// Implements `Deref<Request>` to allow access to read-only fields on `Request` without copying.
pub struct Multipart<R> {
    source: BoundaryReader<R>,
    line_buf: String,
}

impl<R> Multipart<R> where R: HttpRequest {
    /// If the given `HttpRequest` is a POST request of `Content-Type: multipart/form-data`,
    /// return the wrapped request as `Ok(Multipart)`, otherwise `Err(HttpRequest)`.
    pub fn from_request(req: R) -> Result<Multipart<R>, R> {
        if !req.is_multipart() { return Err(req); }

        if req.get_boundary().is_none() {
            return Err(req);
        }

        let boundary = req.get_boundary().unwrap().to_owned();

        debug!("Boundary: {}", boundary);

        Ok(
            Multipart {
                source: BoundaryReader::from_reader(req, boundary),
                line_buf: String::new(),
            }
        )
    }

    /// Read an entry from this multipart request, returning a pair with the field's name and
    /// contents. This will return an End of File error if there are no more entries.
    ///
    /// To get to the data, you will need to match on `MultipartField`.
    ///
    /// ##Warning
    /// If the last returned entry had contents of type `MultipartField::File`,
    /// calling this again will discard any unread contents of that entry!
    pub fn read_entry(&mut self) -> io::Result<Option<MultipartField<R>>> {

    }

    fn read_content_disposition(&mut self) -> io::Result<Option<ContentDisp>> {
        let line = try!(self.read_line());
        Ok(ContentDisp::read_from(line))
    }

    /// Call `f` for each entry in the multipart request.
    /// This is a substitute for `Multipart` implementing `Iterator`,
    /// since `Iterator::next()` can't use bound lifetimes.
    ///
    /// See https://www.reddit.com/r/rust/comments/2lkk\4\isize/concrete_lifetime_vs_bound_lifetime/
    ///
    /// Returns `Ok(())` when all fields have been read, or the first error.
    pub fn foreach_entry<F>(&mut self, mut foreach: F) -> io::Result<()> where F: FnMut(MultipartField<R>) {
        loop {
            match self.read_entry() {
                Ok(Some(field)) => foreach(field),
                Ok(None) => return Ok(()),
                Err(err) => return Err(err),
            }
        }
    }

    fn read_content_type<'a>(&'a mut self) -> io::Result<Option<ContentType<'a>> {
        debug!("Read content type!");
        let line = try!(self.read_line());
        Ok(ContentType::read_from(line))
    }

    /// Read the request fully, parsing all fields and saving all files
    /// to the given directory (if given) and return the result.
    ///
    /// If `dir` is none, uses `std::os::tmpdir()`.
    pub fn save_all(mut self, dir: Option<&Path>) -> io::Result<Entries> {
        let dir = dir.map_or_else(::std::env::temp_dir, |path| path.to_owned());

        let mut entries = Entries::with_path(dir);

        loop {
            match self.read_entry() {
                Ok((name, MultipartField::Text(text))) => { entries.fields.insert(name, text.to_owned()); },
                Ok((name, MultipartField::File(mut file))) => {
                    let path = try!(file.save_in(&entries.dir));
                    entries.files.insert(name, path);
                },
                Err(err) => {
                    error!("Error reading Multipart: {}", err);
                    break;
                },
            }
        }

        Ok(entries)
    }

    fn read_line(&mut self) -> io::Result<&str> {
        self.line_buf.clear();

        match self.source.read_line(&mut self.line_buf) {
            Ok(read) => Ok(&self.line_buf[..read]),
            Err(err) => Err(err),
        }
    }

    fn read_to_string(&mut self) -> io::Result<&str> {
        self.line_buf.clear();

        match self.source.read_to_string(&mut self.line_buf) {
            Ok(read) => Ok(&self.line_buf[..read]),
            Err(err) => Err(err),
        }
    }
}

impl<R> Deref for Multipart<R> where R: HttpRequest {
    type Target = R;
    fn deref(&self) -> &R {
        self.source.borrow()
    }
}

fn with<T, U, F: FnOnce(&T) -> Option<U>>(left: Option<T>, right: F) -> Option<(T, U)> {
    let temp = left.as_ref().and_then(right);
    match (left, temp) {
        (Some(lval), Some(rval)) => Some((lval, rval)),
        _ => None,
    }
}

fn line_error(msg: &str, line: &str) -> io::Error {
    io::Error::new(
        io::ErrorKind::Other,
        format!("Error: {:?} on line of request: {:?}", msg, line)
    )
}

struct ContentType<'a> {
    cont_type: &'a str,
    boundary: Option<&'a str>,
}

impl<'a> ContentType<'a> {
    fn read_from(line: &'a str) -> Option<ContentType<'a>> {
        const CONTENT_TYPE: &'static str = "Content-Type:";
        const BOUNDARY: &'static str = "boundary=\"";

        let cont_type = get_str_after(CONTENT_TYPE, ';', line);

        if let Some((cont_type, after_cont_type)) = get_str_after(CONTENT_TYPE, ';', line) {
            let boundary = get_str_after(BOUNDARY, '"', after_cont_type).map(|tup| tup.0);

            Some(ContentType {
                cont_type: cont_type,
                boundary: boundary,
            })
        } else {
            get_remainder_after(CONTENT_TYPE, line).map(|cont_type|
                ContentType { cont_type: cont_type, boundary: None }
            )
        }
    }
}

struct ContentDisp {
    field_name: String,
    filename: Option<String>,
}

impl ContentDisp {
    fn read_from(line: &str) -> Option<ContentDisp>> {
        debug!("Reading Content-Disposition from line: {:?}", line);

        if line.is_empty() {
            return None;
        }

        const CONT_DISP: &'static str = "Content-Disposition:";
        const NAME: &'static str = "name=\"";
        const FILENAME: &'static str = "filename=\"";

        let after_disp_type = {
            let (disp_type, after_disp_type) = get_str_after(CONT_DISP, ';', line);

            if disp_type.trim() != "form-data" {
                return None;
            }

            after_disp_type
        };

        let (field_name, after_field_name) = try_opt!(get_str_after(NAME, '"', after_disp_type));

        let filename = get_str_after(FILENAME, '"', after_field_name)
            .map(|(filename, _)| filename.to_owned());

        Some(ContentDisp { field_name: field_name.to_owned(), filename: filename })
    }
}

/// Get the string after `needle` in `haystack`, stopping before `end_val_delim`
fn get_str_after<'a>(needle: &str, end_val_delim: char, haystack: &'a str) -> Option<(&'a str, &'a str)> {
    let val_start_idx = try_opt!(haystack.find(needle)) + needle.len();
    let val_end_idx = try_opt!(haystack[val_start_idx..].find(end_val_delim));
    Some(&haystack[val_start_idx..val_end_idx], &haystack[val_end_idx..])
}

/// Get everything after `needle` in `haystack`
fn get_remainder_after<'a>(needle: &str, haystack: &'a str) -> Option<(&'a str)> {
    let val_start_idx = try_opt!(haystack.find(needle)) + needle.len();
    Some(&haystack[val_start_idx..])
}

pub trait HttpRequest: Read {
    fn is_multipart(&self) -> bool;
    fn get_boundary(&self) -> Option<&str>;
}

pub struct MultipartField<'a, R: 'a> {
    /// The field's name from the form
    pub name: String,
    /// The data of the field. Can be text or binary.
    pub data: MultipartData<'a, R>,
}

impl<'a, R: HttpRequest + 'a> MultipartField<'a, R> {
    fn read_from(multipart: &'a mut Multipart<R>) -> io::Result<Option<MultipartField<'a>>> {
        try!(multipart.source.consume_boundary());

        let cont_disp = match multipart.read_content_disposition() {
            Ok(Some(cont_disp)) => cont_disp,
            Ok(None) => return None,
            Err(err) => return Err(err),
        };

        let data = match try!(multipart.read_content_type()) {
            Some(content_type) => {
                let _ = try!(multipart.read_line()); // Consume empty line
                MultipartData::File(
                    MultipartFile::from_reader(
                        cont_disp.filename,
                        &mut multipart.source,
                        &content_type,
                    )
                 )
            },
            None => {
                // Empty line consumed by read_content_type()
                let text = try!(multipart.read_to_string());
                // The last two characters are "\r\n".
                // We can't do a simple trim because the content might be terminated
                // with line separators we want to preserve.
                MultipartData::Text(&text[..text.len() - 2])
            },
        };

        Ok(Some(
            MultipartField {
                name: cont_disp.name,
                data: data,
            }
        ))
    }
}

/// The data of a field in a `multipart/form-data` request.
#[derive(Debug)]
pub enum MultipartData<'a, R: 'a> {
    /// A text field.
    Text(&'a str),
    /// A file field, including the content type and optional filename
    /// along with a `Read` implementation for getting the contents.
    File(MultipartFile<'a, R>),
    // MultiFiles(Vec<MultipartFile>), /* TODO: Multiple files */
}

impl<'a, R> MultipartData<'a, R> {
    /// Borrow this field as a text field, if possible.
    pub fn as_text(&self) -> Option<&str> {
        match *self {
            MultipartData::Text(ref s) => Some(s),
            _ => None,
        }
    }

    /// Borrow this field as a file field, if possible
    /// Mutably borrows so the contents can be read.
    pub fn as_file(&mut self) -> Option<&mut MultipartFile<'a, R>> {
        match *self {
            MultipartData::File(ref mut file) => Some(file),
            _ => None,
        }
    }
}

/// A representation of a file in HTTP `multipart/form-data`.
///
/// Note that the file is not yet saved to the system;
/// instead, the struct implements a `Reader` that points
/// to the beginning of the file's contents in the HTTP stream.
/// You can read it to EOF, or use one of the `save_*()` methods here
/// to save it to disk.
#[derive(Debug)]
pub struct MultipartFile<'a, R: 'a> {
    pub filename: Option<String>,
    pub content_type: Mime,
    reader: &'a mut BoundaryReader<R>,
}

impl<'a, R: Read> MultipartFile<'a, R> {
    fn from_reader(
        filename: Option<String>, reader: &'a mut BoundaryReader<R>, cont_type: &str
    ) -> MultipartFile<'a, R> {
        MultipartFile {
            filename: filename,
            content_type: cont_type.parse::<Mime>().ok().unwrap_or_else(::mime_guess::octet_stream),
            reader: reader,
        }
    }

    /// Save this file to `path`, discarding the filename.
    ///
    /// If successful, the file can be found at `path`.
    pub fn save_as(&mut self, path: &Path) -> io::Result<()> {
        let mut file = try!(File::create(path));
        io::copy(self.reader, &mut file).and(Ok(()))
    }

    /// Save this file in the directory described by `dir`,
    /// appending `filename` if present, or a random string otherwise.
    ///
    /// Returns the created file's path on success.
    ///
    /// ###Panics
    /// If `dir` does not represent a directory.
    pub fn save_in(&mut self, dir: &Path) -> io::Result<PathBuf> {
        let meta = try!(fs::metadata(dir));
        assert!(meta.is_dir(), "Given path is not a directory!");

        let path = dir.join(::random_alphanumeric(8));

        try!(self.save_as(&path));

        Ok(path)
    }

    /// Save this file in the OS temp directory, returned from `std::env::temp_dir()`.
    ///
    /// Returns the created file's path on success.
    pub fn save_temp(&mut self) -> io::Result<PathBuf> {
        use std::env;

        self.save_in(&env::temp_dir())
    }

    pub fn filename(&self) -> Option<&str> {
        self.filename.as_ref().map(String::as_ref)
    }

    /// Get the content type of this file.
    /// On the client, it is guessed by the file extension.
    /// On the server, it is retrieved from the request or assumed to be
    /// `application/octet-stream`.
    pub fn content_type(&self) -> Mime {
        self.content_type.clone()
    }
}

impl<'a, R: Read> Read for MultipartFile<'a, R> {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>{
        self.reader.read(buf)
    }
}

/// A result of `Multipart::save_all()`.
pub struct Entries {
    pub fields: HashMap<String, String>,
    pub files: HashMap<String, io::Result<PathBuf>>,
    /// The directory the files were saved under.
    pub dir: PathBuf,
}

impl Entries {
    fn with_path<P: Into<PathBuf>>(path: P) -> Entries {
        Entries {
            fields: HashMap::new(),
            files: HashMap::new(),
            dir: path.into(),
        }
    }
}

<|endoftext|>


<commit_before>//! [POST /_matrix/client/r0/keys/device_signing/upload](https://13301-24998719-gh.circle-artifacts.com/0/scripts/gen/client_server/unstable.html#post-matrix-client-r0-keys-device-signing-upload)

use ruma_api::ruma_api;

use super::CrossSigningKey;
use crate::r0::uiaa::{AuthData, IncomingAuthData};

ruma_api! {
    metadata: {
        description: "Publishes cross signing keys for the user.",
        method: POST,
        name: "upload_signing_keys",
        path: "/_matrix/client/r0/keys/device_signing/upload",
        rate_limited: false,
        authentication: AccessToken,
    }

    #[derive(Default)]
    request: {
        /// Additional authentication information for the user-interactive authentication API.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub auth: Option<AuthData<'a>>,

        /// The user's master key.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub master_key: Option<CrossSigningKey>,

        /// The user's self-signing key. Must be signed with the accompanied master, or by the
        /// user's most recently uploaded master key if no master key is included in the request.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub self_signing_key: Option<CrossSigningKey>,

        /// The user's user-signing key. Must be signed with the accompanied master, or by the
        /// user's most recently uploaded master key if no master key is included in the request.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub user_signing_key: Option<CrossSigningKey>,
    }

    #[derive(Default)]
    response: {}

    error: crate::Error
}

impl Request<'_> {
    /// Creates an empty `Request`.
    pub fn new() -> Self {
        Default::default()
    }
}

impl Response {
    /// Creates an empty `Response`.
    pub fn new() -> Self {
        Self
    }
}
<commit_msg>client-api: Fix the signing key uploading endpoint<commit_after>//! [POST /_matrix/client/r0/keys/device_signing/upload](https://13301-24998719-gh.circle-artifacts.com/0/scripts/gen/client_server/unstable.html#post-matrix-client-r0-keys-device-signing-upload)

use ruma_api::ruma_api;

use super::CrossSigningKey;
use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse};

ruma_api! {
    metadata: {
        description: "Publishes cross signing keys for the user.",
        method: POST,
        name: "upload_signing_keys",
        path: "/_matrix/client/unstable/keys/device_signing/upload",
        rate_limited: false,
        authentication: AccessToken,
    }

    #[derive(Default)]
    request: {
        /// Additional authentication information for the user-interactive authentication API.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub auth: Option<AuthData<'a>>,

        /// The user's master key.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub master_key: Option<CrossSigningKey>,

        /// The user's self-signing key. Must be signed with the accompanied master, or by the
        /// user's most recently uploaded master key if no master key is included in the request.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub self_signing_key: Option<CrossSigningKey>,

        /// The user's user-signing key. Must be signed with the accompanied master, or by the
        /// user's most recently uploaded master key if no master key is included in the request.
        #[serde(skip_serializing_if = "Option::is_none")]
        pub user_signing_key: Option<CrossSigningKey>,
    }

    #[derive(Default)]
    response: {}

    error: UiaaResponse
}

impl Request<'_> {
    /// Creates an empty `Request`.
    pub fn new() -> Self {
        Default::default()
    }
}

impl Response {
    /// Creates an empty `Response`.
    pub fn new() -> Self {
        Self
    }
}
<|endoftext|>


<commit_before>#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    let _: asn1::ParseResult<()> = asn1::parse(data, |d| {
        d.read_element::<()>()?;
        d.read_element::<bool>()?;

        d.read_element::<i8>()?;
        d.read_element::<u8>()?;
        d.read_element::<i64>()?;
        d.read_element::<u64>()?;
        d.read_element::<asn1::BigUint>()?;

        d.read_element::<&[u8]>()?;
        d.read_element::<asn1::PrintableString>()?;
        d.read_element::<asn1::BMPString>()?;
        d.read_element::<asn1::UniversalString>()?;
        d.read_element::<asn1::ObjectIdentifier>()?;
        d.read_element::<asn1::BitString>()?;
        d.read_element::<asn1::UtcTime>()?;
        d.read_element::<asn1::GeneralizedTime>()?;

        d.read_element::<Option<()>>()?;
        d.read_element::<asn1::Choice2<bool, i64>>()?;

        d.read_element::<asn1::SequenceOf<i64>>()?.collect::<Vec<_>>();
        d.read_element::<asn1::SetOf<i64>>()?.collect::<Vec<_>>();

        Ok(())
    });
});
<commit_msg>Rewrite the fuzzer to a more modern style (#294)<commit_after>#![no_main]
use libfuzzer_sys::fuzz_target;

#[derive(asn1::Asn1Read, asn1::Asn1Write)]
struct Data<'a> {
    f1: (),
    f2: bool,

    f3: i8,
    f4: u8,
    f5: i64,
    f6: u64,
    f7: asn1::BigUint<'a>,
    f8: asn1::BigInt<'a>,

    f9: &'a [u8],

    f10: asn1::PrintableString<'a>,
    f11: asn1::BMPString<'a>,
    f12: asn1::UniversalString<'a>,
    f13: asn1::BitString<'a>,

    f14: asn1::ObjectIdentifier,

    f15: asn1::UtcTime,
    f16: asn1::GeneralizedTime,

    f17: Option<()>,
    f18: asn1::Choice2<bool, i64>,

    f19: asn1::SequenceOf<'a, i64>,
    f20: asn1::SetOf<'a, i64>,
}

fuzz_target!(|data: &[u8]| {
    match asn1::parse_single::<Data>(data) {
        Ok(parsed) => {
            // I'd like to test that the result of `write_single` is the same
            // as `data`, which should hold in general... but it doesn't hold
            // for our UtcTime/GeneralizedTime types. Those types can parse
            // several formats, but always serialize to the same one.
            asn1::write_single(&parsed);
        }
        Err(_) => {}
    };
});
<|endoftext|>


<commit_before>//! Algorithm citation:
//! A Simple, Fast Dominance Algorithm.
//! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
//! Rice Computer Science TS-06-33870
//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>

use rustc_index::vec::{Idx, IndexVec};
use super::iterate::reverse_post_order;
use super::ControlFlowGraph;

#[cfg(test)]
mod tests;

pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    let rpo = reverse_post_order(graph, start_node);
    dominators_given_rpo(graph, &rpo)
}

pub fn dominators_given_rpo<G: ControlFlowGraph>(
    graph: &G,
    rpo: &[G::Node],
) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    assert_eq!(rpo[0], start_node);

    // compute the post order index (rank) for each node
    let mut post_order_rank: IndexVec<G::Node, usize> =
        (0..graph.num_nodes()).map(|_| 0).collect();
    for (index, node) in rpo.iter().rev().cloned().enumerate() {
        post_order_rank[node] = index;
    }

    let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
        (0..graph.num_nodes()).map(|_| None).collect();
    immediate_dominators[start_node] = Some(start_node);

    let mut changed = true;
    while changed {
        changed = false;

        for &node in &rpo[1..] {
            let mut new_idom = None;
            for pred in graph.predecessors(node) {
                if immediate_dominators[pred].is_some() {
                    // (*)
                    // (*) dominators for `pred` have been calculated
                    new_idom = intersect_opt(
                        &post_order_rank,
                        &immediate_dominators,
                        new_idom,
                        Some(pred),
                    );
                }
            }

            if new_idom != immediate_dominators[node] {
                immediate_dominators[node] = new_idom;
                changed = true;
            }
        }
    }

    Dominators {
        post_order_rank,
        immediate_dominators,
    }
}

fn intersect_opt<Node: Idx>(
    post_order_rank: &IndexVec<Node, usize>,
    immediate_dominators: &IndexVec<Node, Option<Node>>,
    node1: Option<Node>,
    node2: Option<Node>,
) -> Option<Node> {
    match (node1, node2) {
        (None, None) => None,
        (Some(n), None) | (None, Some(n)) => Some(n),
        (Some(n1), Some(n2)) => Some(intersect(post_order_rank, immediate_dominators, n1, n2)),
    }
}

fn intersect<Node: Idx>(
    post_order_rank: &IndexVec<Node, usize>,
    immediate_dominators: &IndexVec<Node, Option<Node>>,
    mut node1: Node,
    mut node2: Node,
) -> Node {
    while node1 != node2 {
        while post_order_rank[node1] < post_order_rank[node2] {
            node1 = immediate_dominators[node1].unwrap();
        }

        while post_order_rank[node2] < post_order_rank[node1] {
            node2 = immediate_dominators[node2].unwrap();
        }
    }

    node1
}

#[derive(Clone, Debug)]
pub struct Dominators<N: Idx> {
    post_order_rank: IndexVec<N, usize>,
    immediate_dominators: IndexVec<N, Option<N>>,
}

impl<Node: Idx> Dominators<Node> {
    pub fn is_reachable(&self, node: Node) -> bool {
        self.immediate_dominators[node].is_some()
    }

    pub fn immediate_dominator(&self, node: Node) -> Node {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        self.immediate_dominators[node].unwrap()
    }

    pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        Iter {
            dominators: self,
            node: Some(node),
        }
    }

    pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
        // FIXME -- could be optimized by using post-order-rank
        self.dominators(node).any(|n| n == dom)
    }
}

pub struct Iter<'dom, Node: Idx> {
    dominators: &'dom Dominators<Node>,
    node: Option<Node>,
}

impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
    type Item = Node;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(node) = self.node {
            let dom = self.dominators.immediate_dominator(node);
            if dom == node {
                self.node = None; // reached the root
            } else {
                self.node = Some(dom);
            }
            return Some(node);
        } else {
            return None;
        }
    }
}
<commit_msg>Eliminate `intersect_opt`.<commit_after>//! Algorithm citation:
//! A Simple, Fast Dominance Algorithm.
//! Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
//! Rice Computer Science TS-06-33870
//! <https://www.cs.rice.edu/~keith/EMBED/dom.pdf>

use rustc_index::vec::{Idx, IndexVec};
use super::iterate::reverse_post_order;
use super::ControlFlowGraph;

#[cfg(test)]
mod tests;

pub fn dominators<G: ControlFlowGraph>(graph: &G) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    let rpo = reverse_post_order(graph, start_node);
    dominators_given_rpo(graph, &rpo)
}

fn dominators_given_rpo<G: ControlFlowGraph>(
    graph: &G,
    rpo: &[G::Node],
) -> Dominators<G::Node> {
    let start_node = graph.start_node();
    assert_eq!(rpo[0], start_node);

    // compute the post order index (rank) for each node
    let mut post_order_rank: IndexVec<G::Node, usize> =
        (0..graph.num_nodes()).map(|_| 0).collect();
    for (index, node) in rpo.iter().rev().cloned().enumerate() {
        post_order_rank[node] = index;
    }

    let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
        (0..graph.num_nodes()).map(|_| None).collect();
    immediate_dominators[start_node] = Some(start_node);

    let mut changed = true;
    while changed {
        changed = false;

        for &node in &rpo[1..] {
            let mut new_idom = None;
            for pred in graph.predecessors(node) {
                if immediate_dominators[pred].is_some() {
                    // (*) dominators for `pred` have been calculated
                    new_idom = Some(if let Some(new_idom) = new_idom {
                        intersect(&post_order_rank, &immediate_dominators, new_idom, pred)
                    } else {
                        pred
                    });
                }
            }

            if new_idom != immediate_dominators[node] {
                immediate_dominators[node] = new_idom;
                changed = true;
            }
        }
    }

    Dominators {
        post_order_rank,
        immediate_dominators,
    }
}

fn intersect<Node: Idx>(
    post_order_rank: &IndexVec<Node, usize>,
    immediate_dominators: &IndexVec<Node, Option<Node>>,
    mut node1: Node,
    mut node2: Node,
) -> Node {
    while node1 != node2 {
        while post_order_rank[node1] < post_order_rank[node2] {
            node1 = immediate_dominators[node1].unwrap();
        }

        while post_order_rank[node2] < post_order_rank[node1] {
            node2 = immediate_dominators[node2].unwrap();
        }
    }

    node1
}

#[derive(Clone, Debug)]
pub struct Dominators<N: Idx> {
    post_order_rank: IndexVec<N, usize>,
    immediate_dominators: IndexVec<N, Option<N>>,
}

impl<Node: Idx> Dominators<Node> {
    pub fn is_reachable(&self, node: Node) -> bool {
        self.immediate_dominators[node].is_some()
    }

    pub fn immediate_dominator(&self, node: Node) -> Node {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        self.immediate_dominators[node].unwrap()
    }

    pub fn dominators(&self, node: Node) -> Iter<'_, Node> {
        assert!(self.is_reachable(node), "node {:?} is not reachable", node);
        Iter {
            dominators: self,
            node: Some(node),
        }
    }

    pub fn is_dominated_by(&self, node: Node, dom: Node) -> bool {
        // FIXME -- could be optimized by using post-order-rank
        self.dominators(node).any(|n| n == dom)
    }
}

pub struct Iter<'dom, Node: Idx> {
    dominators: &'dom Dominators<Node>,
    node: Option<Node>,
}

impl<'dom, Node: Idx> Iterator for Iter<'dom, Node> {
    type Item = Node;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some(node) = self.node {
            let dom = self.dominators.immediate_dominator(node);
            if dom == node {
                self.node = None; // reached the root
            } else {
                self.node = Some(dom);
            }
            return Some(node);
        } else {
            return None;
        }
    }
}
<|endoftext|>