<commit_before>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{eq_expr_value, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Checks for expressions that could be replaced by the question mark operator.
    ///
    /// ### Why is this bad?
    /// Question mark usage is more idiomatic.
    ///
    /// ### Example
    /// ```ignore
    /// if option.is_none() {
    ///     return None;
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```ignore
    /// option?;
    /// ```
    pub QUESTION_MARK,
    style,
    "checks for expressions that could be replaced by the question mark operator"
}

declare_lint_pass!(QuestionMark => [QUESTION_MARK]);

impl QuestionMark {
    /// Checks if the given expression on the given context matches the following structure:
    ///
    /// ```ignore
    /// if option.is_none() {
    ///    return None;
    /// }
    /// ```
    ///
    /// If it matches, it will suggest to use the question mark operator instead
    fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
        if_chain! {
            if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
            if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind;
            if segment.ident.name == sym!(is_none);
            if Self::expression_returns_none(cx, then);
            if let Some(subject) = args.get(0);
            if Self::is_option(cx, subject);

            then {
                let mut applicability = Applicability::MachineApplicable;
                let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
                let mut replacement: Option<String> = None;
                if let Some(else_inner) = r#else {
                    if_chain! {
                        if let ExprKind::Block(block, None) = &else_inner.kind;
                        if block.stmts.is_empty();
                        if let Some(block_expr) = &block.expr;
                        if eq_expr_value(cx, subject, block_expr);
                        then {
                            replacement = Some(format!("Some({}?)", receiver_str));
                        }
                    }
                } else if Self::moves_by_default(cx, subject)
                    && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
                {
                    replacement = Some(format!("{}.as_ref()?;", receiver_str));
                } else {
                    replacement = Some(format!("{}?;", receiver_str));
                }

                if let Some(replacement_str) = replacement {
                    span_lint_and_sugg(
                        cx,
                        QUESTION_MARK,
                        expr.span,
                        "this block may be rewritten with the `?` operator",
                        "replace it with",
                        replacement_str,
                        applicability,
                    );
                }
            }
        }
    }

    fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
        if_chain! {
            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
                = higher::IfLet::hir(cx, expr);
            if Self::is_option(cx, let_expr);

            if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
            if is_lang_ctor(cx, path1, OptionSome);
            if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
            let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);

            if let ExprKind::Block(block, None) = if_then.kind;
            if block.stmts.is_empty();
            if let Some(trailing_expr) = &block.expr;
            if path_to_local_id(trailing_expr, bind_id);

            if Self::expression_returns_none(cx, if_else);
            then {
                let mut applicability = Applicability::MachineApplicable;
                let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
                let replacement = format!(
                    "{}{}?",
                    receiver_str,
                    if by_ref { ".as_ref()" } else { "" },
                );

                span_lint_and_sugg(
                    cx,
                    QUESTION_MARK,
                    expr.span,
                    "this if-let-else may be rewritten with the `?` operator",
                    "replace it with",
                    replacement,
                    applicability,
                );
            }
        }
    }

    fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
        let expr_ty = cx.typeck_results().expr_ty(expression);

        !expr_ty.is_copy_modulo_regions(cx.tcx.at(expression.span), cx.param_env)
    }

    fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
        let expr_ty = cx.typeck_results().expr_ty(expression);

        is_type_diagnostic_item(cx, expr_ty, sym::Option)
    }

    fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
        match expression.kind {
            ExprKind::Block(block, _) => {
                if let Some(return_expression) = Self::return_expression(block) {
                    return Self::expression_returns_none(cx, return_expression);
                }

                false
            },
            ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
            ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
            _ => false,
        }
    }

    fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
        // Check if last expression is a return statement. Then, return the expression
        if_chain! {
            if block.stmts.len() == 1;
            if let Some(expr) = block.stmts.iter().last();
            if let StmtKind::Semi(expr) = expr.kind;
            if let ExprKind::Ret(Some(ret_expr)) = expr.kind;

            then {
                return Some(ret_expr);
            }
        }

        // Check for `return` without a semicolon.
        if_chain! {
            if block.stmts.is_empty();
            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
            then {
                return Some(ret_expr);
            }
        }

        None
    }
}

impl<'tcx> LateLintPass<'tcx> for QuestionMark {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        Self::check_is_none_and_early_return_none(cx, expr);
        Self::check_if_let_some_and_early_return_none(cx, expr);
    }
}
<commit_msg>Cover `Result` for `question_mark`<commit_after>use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::higher;
use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{eq_expr_value, path_to_local, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Checks for expressions that could be replaced by the question mark operator.
    ///
    /// ### Why is this bad?
    /// Question mark usage is more idiomatic.
    ///
    /// ### Example
    /// ```ignore
    /// if option.is_none() {
    ///     return None;
    /// }
    /// ```
    ///
    /// Could be written:
    ///
    /// ```ignore
    /// option?;
    /// ```
    pub QUESTION_MARK,
    style,
    "checks for expressions that could be replaced by the question mark operator"
}

declare_lint_pass!(QuestionMark => [QUESTION_MARK]);

impl QuestionMark {
    /// Checks if the given expression on the given context matches the following structure:
    ///
    /// ```ignore
    /// if option.is_none() {
    ///    return None;
    /// }
    /// ```
    ///
    /// ```ignore
    /// if result.is_err() {
    ///     return result;
    /// }
    /// ```
    ///
    /// If it matches, it will suggest to use the question mark operator instead
    fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
        if_chain! {
            if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
            if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind;
            if let Some(subject) = args.get(0);
            if (Self::is_option(cx, subject)
                && Self::expression_returns_none(cx, then)
                && segment.ident.name == sym!(is_none))
                ||
                (Self::is_result(cx, subject)
                && Self::expression_returns_unmodified_err(cx, then, subject)
                && segment.ident.name == sym!(is_err));
            then {
                let mut applicability = Applicability::MachineApplicable;
                let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
                let mut replacement: Option<String> = None;
                if let Some(else_inner) = r#else {
                    if_chain! {
                        if let ExprKind::Block(block, None) = &else_inner.kind;
                        if block.stmts.is_empty();
                        if let Some(block_expr) = &block.expr;
                        if eq_expr_value(cx, subject, block_expr);
                        then {
                            replacement = Some(format!("Some({}?)", receiver_str));
                        }
                    }
                } else if Self::moves_by_default(cx, subject)
                    && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
                {
                    replacement = Some(format!("{}.as_ref()?;", receiver_str));
                } else {
                    replacement = Some(format!("{}?;", receiver_str));
                }

                if let Some(replacement_str) = replacement {
                    span_lint_and_sugg(
                        cx,
                        QUESTION_MARK,
                        expr.span,
                        "this block may be rewritten with the `?` operator",
                        "replace it with",
                        replacement_str,
                        applicability,
                    );
                }
            }
        }
    }

    fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
        if_chain! {
            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
                = higher::IfLet::hir(cx, expr);
            if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
            if (Self::is_option(cx, let_expr)
                && Self::expression_returns_none(cx, if_else)
                && is_lang_ctor(cx, path1, OptionSome))
                ||
                (Self::is_result(cx, let_expr)
                 && Self::expression_returns_unmodified_err(cx, if_else, let_expr)
                 && is_lang_ctor(cx, path1, ResultOk));

            if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
            let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
            if let ExprKind::Block(block, None) = if_then.kind;
            if block.stmts.is_empty();
            if let Some(trailing_expr) = &block.expr;
            if path_to_local_id(trailing_expr, bind_id);
            then {
                let mut applicability = Applicability::MachineApplicable;
                let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
                let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },);

                span_lint_and_sugg(
                    cx,
                    QUESTION_MARK,
                    expr.span,
                    "this if-let-else may be rewritten with the `?` operator",
                    "replace it with",
                    replacement,
                    applicability,
                );
            }
        }
    }

    fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
        let expr_ty = cx.typeck_results().expr_ty(expression);

        !expr_ty.is_copy_modulo_regions(cx.tcx.at(expression.span), cx.param_env)
    }

    fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
        let expr_ty = cx.typeck_results().expr_ty(expression);

        is_type_diagnostic_item(cx, expr_ty, sym::Option)
    }

    fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
        let expr_ty = cx.typeck_results().expr_ty(expression);

        is_type_diagnostic_item(cx, expr_ty, sym::Result)
    }

    fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
        match expression.kind {
            ExprKind::Block(block, _) => {
                if let Some(return_expression) = Self::return_expression(block) {
                    return Self::expression_returns_none(cx, return_expression);
                }

                false
            },
            ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
            ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
            _ => false,
        }
    }

    fn expression_returns_unmodified_err(
        cx: &LateContext<'_>,
        expression: &Expr<'_>,
        origin_hir_id: &Expr<'_>,
    ) -> bool {
        match expression.kind {
            ExprKind::Block(block, _) => {
                if let Some(return_expression) = Self::return_expression(block) {
                    return Self::expression_returns_unmodified_err(cx, return_expression, origin_hir_id);
                }

                false
            },
            ExprKind::Ret(Some(expr)) | ExprKind::Call(expr, _) => {
                Self::expression_returns_unmodified_err(cx, expr, origin_hir_id)
            },
            ExprKind::Path(_) => path_to_local(expression) == path_to_local(origin_hir_id),
            _ => false,
        }
    }

    fn return_expression<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
        // Check if last expression is a return statement. Then, return the expression
        if_chain! {
            if block.stmts.len() == 1;
            if let Some(expr) = block.stmts.iter().last();
            if let StmtKind::Semi(expr) = expr.kind;
            if let ExprKind::Ret(Some(ret_expr)) = expr.kind;

            then {
                return Some(ret_expr);
            }
        }

        // Check for `return` without a semicolon.
        if_chain! {
            if block.stmts.is_empty();
            if let Some(ExprKind::Ret(Some(ret_expr))) = block.expr.as_ref().map(|e| &e.kind);
            then {
                return Some(ret_expr);
            }
        }

        None
    }
}

impl<'tcx> LateLintPass<'tcx> for QuestionMark {
    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        Self::check_is_none_or_err_and_early_return(cx, expr);
        Self::check_if_let_some_or_err_and_early_return(cx, expr);
    }
}
<|endoftext|>


<commit_before>mod crosspointer_transmute;
mod transmute_float_to_int;
mod transmute_int_to_bool;
mod transmute_int_to_char;
mod transmute_int_to_float;
mod transmute_num_to_bytes;
mod transmute_ptr_to_ptr;
mod transmute_ptr_to_ref;
mod transmute_ref_to_ref;
mod transmutes_expressible_as_ptr_casts;
mod unsound_collection_transmute;
mod useless_transmute;
mod utils;
mod wrong_transmute;

use clippy_utils::in_constant;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes that can't ever be correct on any
    /// architecture.
    ///
    /// ### Why is this bad?
    /// It's basically guaranteed to be undefined behaviour.
    ///
    /// ### Known problems
    /// When accessing C, users might want to store pointer
    /// sized objects in `extradata` arguments to save an allocation.
    ///
    /// ### Example
    /// ```ignore
    /// let ptr: *const T = core::intrinsics::transmute('x')
    /// ```
    pub WRONG_TRANSMUTE,
    correctness,
    "transmutes that are confusing at best, undefined behaviour at worst and always useless"
}

// FIXME: Move this to `complexity` again, after #5343 is fixed
declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes to the original type of the object
    /// and transmutes that could be a cast.
    ///
    /// ### Why is this bad?
    /// Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// ### Example
    /// ```rust,ignore
    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
    /// ```
    pub USELESS_TRANSMUTE,
    nursery,
    "transmutes that have the same to and from types or could be a cast/coercion"
}

// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
declare_clippy_lint! {
    /// ### What it does
    ///Checks for transmutes that could be a pointer cast.
    ///
    /// ### Why is this bad?
    /// Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// ### Example
    ///
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
    /// ```
    /// Use instead:
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// p as *const [u16];
    /// ```
    pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
    complexity,
    "transmutes that could be a pointer cast"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes between a type `T` and `*T`.
    ///
    /// ### Why is this bad?
    /// It's easy to mistakenly transmute between a type and a
    /// pointer to that type.
    ///
    /// ### Example
    /// ```rust,ignore
    /// core::intrinsics::transmute(t) // where the result type is the same as
    ///                                // `*t` or `&t`'s
    /// ```
    pub CROSSPOINTER_TRANSMUTE,
    complexity,
    "transmutes that have to or from types that are a pointer to the other"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a pointer to a reference.
    ///
    /// ### Why is this bad?
    /// This can always be rewritten with `&` and `*`.
    ///
    /// ### Known problems
    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
    /// while dereferencing raw pointer is not stable yet.
    /// If you need to do this in those places,
    /// you would have to use `transmute` instead.
    ///
    /// ### Example
    /// ```rust,ignore
    /// unsafe {
    ///     let _: &T = std::mem::transmute(p); // where p: *const T
    /// }
    ///
    /// // can be written:
    /// let _: &T = &*p;
    /// ```
    pub TRANSMUTE_PTR_TO_REF,
    complexity,
    "transmutes from a pointer to a reference type"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from an integer to a `char`.
    ///
    /// ### Why is this bad?
    /// Not every integer is a Unicode scalar value.
    ///
    /// ### Known problems
    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid Unicode scalar value,
    /// use [`from_u32_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
    ///
    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
    ///
    /// ### Example
    /// ```rust
    /// let x = 1_u32;
    /// unsafe {
    ///     let _: char = std::mem::transmute(x); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _ = std::char::from_u32(x).unwrap();
    /// ```
    pub TRANSMUTE_INT_TO_CHAR,
    complexity,
    "transmutes from an integer to a `char`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a `&[u8]` to a `&str`.
    ///
    /// ### Why is this bad?
    /// Not every byte slice is a valid UTF-8 string.
    ///
    /// ### Known problems
    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid UTF-8,
    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
    ///
    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
    ///
    /// ### Example
    /// ```rust
    /// let b: &[u8] = &[1_u8, 2_u8];
    /// unsafe {
    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
    /// }
    ///
    /// // should be:
    /// let _ = std::str::from_utf8(b).unwrap();
    /// ```
    pub TRANSMUTE_BYTES_TO_STR,
    complexity,
    "transmutes from a `&[u8]` to a `&str`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from an integer to a `bool`.
    ///
    /// ### Why is this bad?
    /// This might result in an invalid in-memory representation of a `bool`.
    ///
    /// ### Example
    /// ```rust
    /// let x = 1_u8;
    /// unsafe {
    ///     let _: bool = std::mem::transmute(x); // where x: u8
    /// }
    ///
    /// // should be:
    /// let _: bool = x != 0;
    /// ```
    pub TRANSMUTE_INT_TO_BOOL,
    complexity,
    "transmutes from an integer to a `bool`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from an integer to a float.
    ///
    /// ### Why is this bad?
    /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
    /// and safe.
    ///
    /// ### Example
    /// ```rust
    /// unsafe {
    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _: f32 = f32::from_bits(1_u32);
    /// ```
    pub TRANSMUTE_INT_TO_FLOAT,
    complexity,
    "transmutes from an integer to a float"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a float to an integer.
    ///
    /// ### Why is this bad?
    /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
    /// and safe.
    ///
    /// ### Example
    /// ```rust
    /// unsafe {
    ///     let _: u32 = std::mem::transmute(1f32);
    /// }
    ///
    /// // should be:
    /// let _: u32 = 1f32.to_bits();
    /// ```
    pub TRANSMUTE_FLOAT_TO_INT,
    complexity,
    "transmutes from a float to an integer"
}

declare_clippy_lint! {
    /// # What it does
    /// Checks for transmutes from a number to an array of `u8`
    ///
    /// ### Why this is bad?
    /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
    /// is intuitive and safe.
    ///
    /// ### Example
    /// ```rust
    /// unsafe {
    ///     let x: [u8; 8] = std::mem::transmute(1i64);
    /// }
    ///
    /// // should be
    /// let x: [u8; 8] = 0i64.to_ne_bytes();
    /// ```
    pub TRANSMUTE_NUM_TO_BYTES,
    complexity,
    "transmutes from a number to an array of `u8`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a pointer to a pointer, or
    /// from a reference to a reference.
    ///
    /// ### Why is this bad?
    /// Transmutes are dangerous, and these can instead be
    /// written as casts.
    ///
    /// ### Example
    /// ```rust
    /// let ptr = &1u32 as *const u32;
    /// unsafe {
    ///     // pointer-to-pointer transmute
    ///     let _: *const f32 = std::mem::transmute(ptr);
    ///     // ref-ref transmute
    ///     let _: &f32 = std::mem::transmute(&1u32);
    /// }
    /// // These can be respectively written:
    /// let _ = ptr as *const f32;
    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
    /// ```
    pub TRANSMUTE_PTR_TO_PTR,
    pedantic,
    "transmutes from a pointer to a pointer / a reference to a reference"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes between collections whose
    /// types have different ABI, size or alignment.
    ///
    /// ### Why is this bad?
    /// This is undefined behavior.
    ///
    /// ### Known problems
    /// Currently, we cannot know whether a type is a
    /// collection, so we just lint the ones that come with `std`.
    ///
    /// ### Example
    /// ```rust
    /// // different size, therefore likely out-of-bounds memory access
    /// // You absolutely do not want this in your code!
    /// unsafe {
    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
    /// };
    /// ```
    ///
    /// You must always iterate, map and collect the values:
    ///
    /// ```rust
    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
    /// ```
    pub UNSOUND_COLLECTION_TRANSMUTE,
    correctness,
    "transmute between collections of layout-incompatible types"
}

declare_lint_pass!(Transmute => [
    CROSSPOINTER_TRANSMUTE,
    TRANSMUTE_PTR_TO_REF,
    TRANSMUTE_PTR_TO_PTR,
    USELESS_TRANSMUTE,
    WRONG_TRANSMUTE,
    TRANSMUTE_INT_TO_CHAR,
    TRANSMUTE_BYTES_TO_STR,
    TRANSMUTE_INT_TO_BOOL,
    TRANSMUTE_INT_TO_FLOAT,
    TRANSMUTE_FLOAT_TO_INT,
    TRANSMUTE_NUM_TO_BYTES,
    UNSOUND_COLLECTION_TRANSMUTE,
    TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
]);

impl<'tcx> LateLintPass<'tcx> for Transmute {
    #[allow(clippy::similar_names, clippy::too_many_lines)]
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        if_chain! {
            if let ExprKind::Call(path_expr, args) = e.kind;
            if let ExprKind::Path(ref qpath) = path_expr.kind;
            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
            if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
            then {
                // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts.
                // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
                // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
                let const_context = in_constant(cx, e.hir_id);

                let from_ty = cx.typeck_results().expr_ty(&args[0]);
                let to_ty = cx.typeck_results().expr_ty(e);

                // If useless_transmute is triggered, the other lints can be skipped.
                if useless_transmute::check(cx, e, from_ty, to_ty, args) {
                    return;
                }

                let mut linted = wrong_transmute::check(cx, e, from_ty, to_ty);
                linted |= crosspointer_transmute::check(cx, e, from_ty, to_ty);
                linted |= transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath);
                linted |= transmute_int_to_char::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_num_to_bytes::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty);

                if !linted {
                    transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args);
                }
            }
        }
    }
}
<commit_msg>Auto merge of #7848 - Veykril:patch-1, r=xFrednet<commit_after>mod crosspointer_transmute;
mod transmute_float_to_int;
mod transmute_int_to_bool;
mod transmute_int_to_char;
mod transmute_int_to_float;
mod transmute_num_to_bytes;
mod transmute_ptr_to_ptr;
mod transmute_ptr_to_ref;
mod transmute_ref_to_ref;
mod transmutes_expressible_as_ptr_casts;
mod unsound_collection_transmute;
mod useless_transmute;
mod utils;
mod wrong_transmute;

use clippy_utils::in_constant;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes that can't ever be correct on any
    /// architecture.
    ///
    /// ### Why is this bad?
    /// It's basically guaranteed to be undefined behaviour.
    ///
    /// ### Known problems
    /// When accessing C, users might want to store pointer
    /// sized objects in `extradata` arguments to save an allocation.
    ///
    /// ### Example
    /// ```ignore
    /// let ptr: *const T = core::intrinsics::transmute('x')
    /// ```
    pub WRONG_TRANSMUTE,
    correctness,
    "transmutes that are confusing at best, undefined behaviour at worst and always useless"
}

// FIXME: Move this to `complexity` again, after #5343 is fixed
declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes to the original type of the object
    /// and transmutes that could be a cast.
    ///
    /// ### Why is this bad?
    /// Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// ### Example
    /// ```rust,ignore
    /// core::intrinsics::transmute(t); // where the result type is the same as `t`'s
    /// ```
    pub USELESS_TRANSMUTE,
    nursery,
    "transmutes that have the same to and from types or could be a cast/coercion"
}

// FIXME: Merge this lint with USELESS_TRANSMUTE once that is out of the nursery.
declare_clippy_lint! {
    /// ### What it does
    ///Checks for transmutes that could be a pointer cast.
    ///
    /// ### Why is this bad?
    /// Readability. The code tricks people into thinking that
    /// something complex is going on.
    ///
    /// ### Example
    ///
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// unsafe { std::mem::transmute::<*const [i32], *const [u16]>(p) };
    /// ```
    /// Use instead:
    /// ```rust
    /// # let p: *const [i32] = &[];
    /// p as *const [u16];
    /// ```
    pub TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
    complexity,
    "transmutes that could be a pointer cast"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes between a type `T` and `*T`.
    ///
    /// ### Why is this bad?
    /// It's easy to mistakenly transmute between a type and a
    /// pointer to that type.
    ///
    /// ### Example
    /// ```rust,ignore
    /// core::intrinsics::transmute(t) // where the result type is the same as
    ///                                // `*t` or `&t`'s
    /// ```
    pub CROSSPOINTER_TRANSMUTE,
    complexity,
    "transmutes that have to or from types that are a pointer to the other"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a pointer to a reference.
    ///
    /// ### Why is this bad?
    /// This can always be rewritten with `&` and `*`.
    ///
    /// ### Known problems
    /// - `mem::transmute` in statics and constants is stable from Rust 1.46.0,
    /// while dereferencing raw pointer is not stable yet.
    /// If you need to do this in those places,
    /// you would have to use `transmute` instead.
    ///
    /// ### Example
    /// ```rust,ignore
    /// unsafe {
    ///     let _: &T = std::mem::transmute(p); // where p: *const T
    /// }
    ///
    /// // can be written:
    /// let _: &T = &*p;
    /// ```
    pub TRANSMUTE_PTR_TO_REF,
    complexity,
    "transmutes from a pointer to a reference type"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from an integer to a `char`.
    ///
    /// ### Why is this bad?
    /// Not every integer is a Unicode scalar value.
    ///
    /// ### Known problems
    /// - [`from_u32`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid Unicode scalar value,
    /// use [`from_u32_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
    ///
    /// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
    /// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
    ///
    /// ### Example
    /// ```rust
    /// let x = 1_u32;
    /// unsafe {
    ///     let _: char = std::mem::transmute(x); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _ = std::char::from_u32(x).unwrap();
    /// ```
    pub TRANSMUTE_INT_TO_CHAR,
    complexity,
    "transmutes from an integer to a `char`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a `&[u8]` to a `&str`.
    ///
    /// ### Why is this bad?
    /// Not every byte slice is a valid UTF-8 string.
    ///
    /// ### Known problems
    /// - [`from_utf8`] which this lint suggests using is slower than `transmute`
    /// as it needs to validate the input.
    /// If you are certain that the input is always a valid UTF-8,
    /// use [`from_utf8_unchecked`] which is as fast as `transmute`
    /// but has a semantically meaningful name.
    /// - You might want to handle errors returned from [`from_utf8`] instead of calling `unwrap`.
    ///
    /// [`from_utf8`]: https://doc.rust-lang.org/std/str/fn.from_utf8.html
    /// [`from_utf8_unchecked`]: https://doc.rust-lang.org/std/str/fn.from_utf8_unchecked.html
    ///
    /// ### Example
    /// ```rust
    /// let b: &[u8] = &[1_u8, 2_u8];
    /// unsafe {
    ///     let _: &str = std::mem::transmute(b); // where b: &[u8]
    /// }
    ///
    /// // should be:
    /// let _ = std::str::from_utf8(b).unwrap();
    /// ```
    pub TRANSMUTE_BYTES_TO_STR,
    complexity,
    "transmutes from a `&[u8]` to a `&str`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from an integer to a `bool`.
    ///
    /// ### Why is this bad?
    /// This might result in an invalid in-memory representation of a `bool`.
    ///
    /// ### Example
    /// ```rust
    /// let x = 1_u8;
    /// unsafe {
    ///     let _: bool = std::mem::transmute(x); // where x: u8
    /// }
    ///
    /// // should be:
    /// let _: bool = x != 0;
    /// ```
    pub TRANSMUTE_INT_TO_BOOL,
    complexity,
    "transmutes from an integer to a `bool`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from an integer to a float.
    ///
    /// ### Why is this bad?
    /// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
    /// and safe.
    ///
    /// ### Example
    /// ```rust
    /// unsafe {
    ///     let _: f32 = std::mem::transmute(1_u32); // where x: u32
    /// }
    ///
    /// // should be:
    /// let _: f32 = f32::from_bits(1_u32);
    /// ```
    pub TRANSMUTE_INT_TO_FLOAT,
    complexity,
    "transmutes from an integer to a float"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a float to an integer.
    ///
    /// ### Why is this bad?
    /// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
    /// and safe.
    ///
    /// ### Example
    /// ```rust
    /// unsafe {
    ///     let _: u32 = std::mem::transmute(1f32);
    /// }
    ///
    /// // should be:
    /// let _: u32 = 1f32.to_bits();
    /// ```
    pub TRANSMUTE_FLOAT_TO_INT,
    complexity,
    "transmutes from a float to an integer"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a number to an array of `u8`
    ///
    /// ### Why this is bad?
    /// Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
    /// is intuitive and safe.
    ///
    /// ### Example
    /// ```rust
    /// unsafe {
    ///     let x: [u8; 8] = std::mem::transmute(1i64);
    /// }
    ///
    /// // should be
    /// let x: [u8; 8] = 0i64.to_ne_bytes();
    /// ```
    pub TRANSMUTE_NUM_TO_BYTES,
    complexity,
    "transmutes from a number to an array of `u8`"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes from a pointer to a pointer, or
    /// from a reference to a reference.
    ///
    /// ### Why is this bad?
    /// Transmutes are dangerous, and these can instead be
    /// written as casts.
    ///
    /// ### Example
    /// ```rust
    /// let ptr = &1u32 as *const u32;
    /// unsafe {
    ///     // pointer-to-pointer transmute
    ///     let _: *const f32 = std::mem::transmute(ptr);
    ///     // ref-ref transmute
    ///     let _: &f32 = std::mem::transmute(&1u32);
    /// }
    /// // These can be respectively written:
    /// let _ = ptr as *const f32;
    /// let _ = unsafe{ &*(&1u32 as *const u32 as *const f32) };
    /// ```
    pub TRANSMUTE_PTR_TO_PTR,
    pedantic,
    "transmutes from a pointer to a pointer / a reference to a reference"
}

declare_clippy_lint! {
    /// ### What it does
    /// Checks for transmutes between collections whose
    /// types have different ABI, size or alignment.
    ///
    /// ### Why is this bad?
    /// This is undefined behavior.
    ///
    /// ### Known problems
    /// Currently, we cannot know whether a type is a
    /// collection, so we just lint the ones that come with `std`.
    ///
    /// ### Example
    /// ```rust
    /// // different size, therefore likely out-of-bounds memory access
    /// // You absolutely do not want this in your code!
    /// unsafe {
    ///     std::mem::transmute::<_, Vec<u32>>(vec![2_u16])
    /// };
    /// ```
    ///
    /// You must always iterate, map and collect the values:
    ///
    /// ```rust
    /// vec![2_u16].into_iter().map(u32::from).collect::<Vec<_>>();
    /// ```
    pub UNSOUND_COLLECTION_TRANSMUTE,
    correctness,
    "transmute between collections of layout-incompatible types"
}

declare_lint_pass!(Transmute => [
    CROSSPOINTER_TRANSMUTE,
    TRANSMUTE_PTR_TO_REF,
    TRANSMUTE_PTR_TO_PTR,
    USELESS_TRANSMUTE,
    WRONG_TRANSMUTE,
    TRANSMUTE_INT_TO_CHAR,
    TRANSMUTE_BYTES_TO_STR,
    TRANSMUTE_INT_TO_BOOL,
    TRANSMUTE_INT_TO_FLOAT,
    TRANSMUTE_FLOAT_TO_INT,
    TRANSMUTE_NUM_TO_BYTES,
    UNSOUND_COLLECTION_TRANSMUTE,
    TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
]);

impl<'tcx> LateLintPass<'tcx> for Transmute {
    #[allow(clippy::similar_names, clippy::too_many_lines)]
    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
        if_chain! {
            if let ExprKind::Call(path_expr, args) = e.kind;
            if let ExprKind::Path(ref qpath) = path_expr.kind;
            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
            if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
            then {
                // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts.
                // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
                // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
                let const_context = in_constant(cx, e.hir_id);

                let from_ty = cx.typeck_results().expr_ty(&args[0]);
                let to_ty = cx.typeck_results().expr_ty(e);

                // If useless_transmute is triggered, the other lints can be skipped.
                if useless_transmute::check(cx, e, from_ty, to_ty, args) {
                    return;
                }

                let mut linted = wrong_transmute::check(cx, e, from_ty, to_ty);
                linted |= crosspointer_transmute::check(cx, e, from_ty, to_ty);
                linted |= transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, args, qpath);
                linted |= transmute_int_to_char::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_ref_to_ref::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_int_to_bool::check(cx, e, from_ty, to_ty, args);
                linted |= transmute_int_to_float::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_float_to_int::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= transmute_num_to_bytes::check(cx, e, from_ty, to_ty, args, const_context);
                linted |= unsound_collection_transmute::check(cx, e, from_ty, to_ty);

                if !linted {
                    transmutes_expressible_as_ptr_casts::check(cx, e, from_ty, to_ty, args);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/* diosix heap management
 *
 * Simple heap manager. A CPU can allocate only from its own
 * heap pool, though it can share these pointers with any CPU.
 * Any CPU can free them back to the owner's heap pool when
 * they are done with these allocations.
 *
 * Thus this code is *single threaded* per individual CPU core
 *
 * Each CPU heap is primed with a small amount of fixed
 * physical RAM, defined by the platform code. When this
 * fixed pool runs low, the heap code requests a temporary
 * block of memory from the physical memory manager.
 * this block is added as a free block to the heap and
 * subsequently allocated from.
 *
 * We use Rust's memory safety features to prevent any
 * use-after-free(). Blocks are free()'d atomically
 * preventing any races.
 *
 * This code interfaces with Rust's global allocator API
 * so things like vec! and Box just work. Heap is
 * the underlying engine for HVallocator.
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use core::alloc::{GlobalAlloc, Layout};
use core::ptr::null_mut;
use core::mem;
use core::fmt;
use core::result::Result;
use platform::physmem::{barrier, PhysMemSize, PhysMemBase};
use super::physmem::{self, alloc_region, RegionHygiene};
use super::error::Cause;

/* different states each recognized heap block can be in */
#[derive(PartialEq, Debug, Clone, Copy)]
enum HeapMagic
{
    Free   = 0x0deadded,
    InUse  = 0x0d10c0de
}

/* source of a heap block */
#[derive(PartialEq, Debug, Clone, Copy)]
enum HeapSource
{
    Fixed,      /* allocated during startup by platform code */
    Temporary   /* allocated dynamically from physical memory pool */
}

/* to avoid fragmentation, allocate in block sizes of this multiple, including header */
const HEAP_BLOCK_SIZE: usize = 128;

/* follow Rust's heap allocator API so we can drop our per-CPU allocator in and use things
like Box. We allow the Rust toolchain to track and check pointers and object lifetimes,
while we'll manage the underlying physical memory used by the heap. */
pub struct HVallocator;

unsafe impl GlobalAlloc for HVallocator
{
    unsafe fn alloc(&self, layout: Layout) -> *mut u8
    {
        let bytes = layout.size();
        match (*<super::pcore::PhysicalCore>::this()).heap.alloc::<u8>(bytes)
        {
            Ok(p) => p,
            Err(e) =>
            {
                hvalert!("HVallocator: request for {} bytes failed ({:?})", bytes, e);
                null_mut() /* yeesh */
            }
        }
    }

    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout)
    {
        match (*<super::pcore::PhysicalCore>::this()).heap.free::<u8>(ptr)
        {
            Err(e) =>
            {
                hvalert!("HVallocator: request to free {:p} failed ({:?})", ptr, e)
            },
            _ => ()
        }
    }
}

/* describe the layout of a per-CPU heap block */
#[repr(C)]
pub struct HeapBlock
{
    /* heap is a single-link-list to keep it simple and safe */
    next: Option<*mut HeapBlock>,
    /* size of this block *including* header */
    size: PhysMemSize,
    /* define block state using magic words */
    magic: HeapMagic,
    /* define the source of the memory */
    source: HeapSource
    /* block contents follows... */
}

/* this is our own internal API for the per-CPU hypervisor heap. use high-level abstractions, such as Box,
rather than this directly, so we get all the safety measures and lifetime checking. think of kallocator
as the API and Heap as the engine. kallocator is built on top of Heap, and each CPU core has its own Heap. */
#[repr(C)]
pub struct Heap
{
    /* pointer to list of in-use and freed blocks */
    block_list_head: *mut HeapBlock,
    /* stash a copy of the block header size here */
    block_header_size: PhysMemSize,
}

impl fmt::Debug for Heap
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        let mut free_total = 0;
        let mut alloc_total = 0;
        let mut largest_free = 0;
        let mut largest_alloc = 0;

        let mut done = false;
        let mut block = self.block_list_head;
        unsafe
        {
            while !done
            {
                let size = (*block).size;
                match (*block).magic
                {
                    HeapMagic::InUse =>
                    {
                        alloc_total = alloc_total + size;
                        if size > largest_alloc
                        {
                            largest_alloc = size;
                        }
                    },
                    HeapMagic::Free =>
                    {
                        free_total = free_total + size;
                        if size > largest_free
                        {
                            largest_free = size;
                        }
                    }
                };

                match (*block).next
                {
                    None => done = true,
                    Some(b) => block = b
                };
            }
        }

        write!(f, "size: {} alloc'd {} free {} largest alloc'd {} largest free {}",
            alloc_total + free_total, alloc_total, free_total, largest_alloc, largest_free)
    }
}

/* clean up heap list by returning chunks of free temporary physical RAM */
macro_rules! heaphousekeeper
{
    () => ((*<super::pcore::PhysicalCore>::this()).heap.return_unused();)
}

impl Heap
{
    /* initialize this heap area. start off with one giant block
    covering all of free space, from which other blocks will be carved.
    this initial block is assuemd to be a fixed platform-allocated area
    of physical memory.
    => start = pointer to start of heap area
       size = number of available bytes in heap */
    pub fn init(&mut self, start: *mut HeapBlock, size: PhysMemSize)
    {
        /* here's our enormo free block */
        unsafe
        {
            let block = start;
            (*block).size = size;
            (*block).next = None;
            (*block).magic = HeapMagic::Free;
            (*block).source = HeapSource::Fixed;

            self.block_header_size = mem::size_of::<HeapBlock>();
            self.block_list_head = block;
        }
    }

    /* insert a free temporary physical memory block at the head of the list
    => base = base address of the memory block to add
       size = total size of the block, including header that will be automatically added
    <= OK or error code */
    pub fn insert_free(&mut self, base: PhysMemBase, size: PhysMemSize) -> Result<(), Cause>
    {
        unsafe
        {
            /* craft free block from scratch */
            let block = base as *mut HeapBlock;
            (*block).size = size;
            (*block).next = Some(self.block_list_head);
            (*block).magic = HeapMagic::Free;
            (*block).source = HeapSource::Temporary;

            /* add the free block to the start of the list */
            self.block_list_head = block;
        }

        Ok(())
    }

    /* free a previously allocated block
    => to_free = pointer previously returned by alloc()
    <= OK or failure code */
    pub fn free<T>(&mut self, to_free: *mut T) -> Result<(), Cause>
    {
        /* convert this into a raw pointer so we can find the heap block header */
        let mut ptr = to_free as usize;
        ptr = ptr - self.block_header_size;
        let block = ptr as *mut HeapBlock;

        unsafe
        {
            match (*block).magic
            {
                HeapMagic::InUse =>
                {
                    /* assume writes are atomic. place a memory barrier to avoid any release ordering issues */
                    barrier();
                    (*block).magic = HeapMagic::Free;
                    Ok(())
                },
                /* if it's not in use, or bad magic, then bail out */
                HeapMagic::Free => Err(Cause::HeapNotInUse)
            }
        }
    }

    /* allocate memory for the given object type. the returned pointer skips
    the heap block header, pointing to the available space,
    just like malloc() on other platforms.
    => T = type of object to allocate memory for
       num = number of objects to allocate for
    <= pointer to memory, or error code */
    pub fn alloc<T>(&mut self, num: usize) -> Result<*mut T, Cause>
    {
        if num == 0
        {
            return Err(Cause::HeapBadSize);
        }

        let mut done = false;
        let mut extended = false;

        /* calculate size of block required, including header, rounded up to
        nearest whole heap block multiple */
        let mut size_req = (mem::size_of::<T>() * num) + self.block_header_size;
        size_req = ((size_req / HEAP_BLOCK_SIZE) + 1) * HEAP_BLOCK_SIZE;

        /* scan all blocks for first free fit */
        let mut search_block = self.block_list_head;
        unsafe
        {
            while !done
            {
                if (*search_block).magic == HeapMagic::Free && (*search_block).size >= size_req
                {
                    /* we've got a winner. if the found block is equal size, or only a few bytes
                    larger than the required size, then take the whole block */
                    if ((*search_block).size - size_req) < HEAP_BLOCK_SIZE
                    {
                        (*search_block).magic = HeapMagic::InUse;
                        let found_ptr = (search_block as usize) + self.block_header_size;
                        return Result::Ok(found_ptr as *mut T);
                    }
                    else
                    {
                        /* carve the end of a large-enough free block off to make a new block.
                        then add this new block to the start of the list */
                        (*search_block).size = (*search_block).size - size_req;

                        /* skip to the new (shorter) end of the free block */
                        let mut found_ptr = (search_block as usize) + (*search_block).size;

                        /* set metadata for newly allocated block */
                        let alloc_block = found_ptr as *mut HeapBlock;
                        (*alloc_block).next  = Some(self.block_list_head);
                        (*alloc_block).magic = HeapMagic::InUse;
                        (*alloc_block).size  = size_req;

                        /* point the head of the list at new block */
                        self.block_list_head = alloc_block;

                        /* adjust pointer to skip the header of our new block, and we're done */
                        found_ptr = found_ptr + self.block_header_size;
                        return Result::Ok(found_ptr as *mut T);
                    }
                }

                /* make sure we don't run off the end of the list.
                also, attempt to consolidate neighboring blocks to make
                more bytes available and reduce fragmentation. do this
                after we've tried searching for available blocks */
                match (*search_block).next
                {
                    None => if self.consolidate() < HEAP_BLOCK_SIZE
                    {
                        if extended == false
                        {
                            /* if we can't squeeze any more bytes out of the list
                            then grab a chunk of available RAM from the physical
                            memory manager and add it to the free list */
                            let region = match alloc_region(size_req)
                            {
                                Ok(r) => r,
                                Err(_e) =>
                                {
                                    /* give up and bail out if there's no more physical memory */
                                    hvdebug!("Failed to extend heap by {} bytes: {:?}", size_req, _e);
                                    return Result::Err(Cause::HeapNoFreeMem);
                                }
                            };

                            hvdebug!("Extending heap by {} bytes (needed {}), base: 0x{:x}",
                            region.size(), size_req, region.base());

                            if self.insert_free(region.base(), region.size()).is_ok()
                            {
                                extended = true;

                                /* start the search over, starting with the new block */
                                search_block = self.block_list_head;
                            }
                            else
                            {
                                /* if we couldn't insert free block, give up */
                                done = true;
                            }
                        }
                        else
                        {
                            /* can't squeeze any more out of list and we've tried allocating more
                            physical memory. give up at this point, though we shouldn't really
                            end up here */
                            hvdebug!("Giving up allocating {} bytes", size_req);
                            done = true;
                        }
                    }
                    else
                    {
                        /* start the search over */
                        search_block = self.block_list_head;
                    },
                    Some(n) => search_block = n
                };
            }
        }

        return Result::Err(Cause::HeapNoFreeMem);
    }

    /* deallocate any free temporary physical memory regions that are no longer needed */
    pub fn return_unused(&mut self)
    {
        /* ensure all blocks are gathered up */
        loop
        {
            if self.consolidate() < HEAP_BLOCK_SIZE
            {
                break;
            }
        }

        /* search for unused physical memory blocks to return */
        let mut block = self.block_list_head;
        let mut prev_block: Option<*mut HeapBlock> = None;
        unsafe
        {
            loop
            {
                match ((*block).source, (*block).magic)
                {
                    /* remove physical region from single-linked list if successfully deallocated.
                    the physical memory manager will avoid fragmentation by rejecting regions that
                    are not multiples of prefered region sizes */
                    (HeapSource::Temporary, HeapMagic::Free) =>
                    {
                        let region = physmem::Region::new(block as PhysMemBase, (*block).size, RegionHygiene::Dirty);
                        if physmem::dealloc_region(region).is_ok()
                        {
                            hvdebug!("Returning heap block {:p} size {} to physical memory pool",
                            block, (*block).size);

                            /* delink the block - do not touch the contents of the
                            deallocated block: it's back in the pool and another CPU core
                            could grab it at any time. After dealloc_region() returns Ok,
                            it's gone as far as this core is concerned. */
                            match prev_block
                            {
                                Some(b) => (*b).next = (*block).next,
                                None => ()
                            };
                        }
                    },

                    (_, _) => ()
                }

                match (*block).next
                {
                    Some(n) =>
                    {
                        prev_block = Some(block);
                        block = n;
                    }
                    None => break
                };
            }
        }
    }

    /* pass once over the heap and try to merge adjacent free blocks
    <= size of the largest block seen, in bytes including header */
    fn consolidate(&mut self) -> PhysMemSize
    {
        let mut largest_merged_block: PhysMemSize = 0;

        let mut block = self.block_list_head;
        unsafe
        {
            /* can't merge if we're the last block in the list */
            while (*block).next.is_some()
            {
                let next = (*block).next.unwrap();
                if (*block).magic == HeapMagic::Free && (*next).magic == HeapMagic::Free
                {
                    let target_ptr = (block as usize) + (*block).size;
                    if target_ptr == next as usize
                    {
                        /* we're adjacent, we're both free, and we can merge */
                        let merged_size = (*block).size + (*next).size;
                        if merged_size > largest_merged_block
                        {
                            largest_merged_block = merged_size;
                        }
                        (*block).size = merged_size;
                        (*block).next = (*next).next;
                    }
                }
                match (*block).next
                {
                    Some(n) => block = n,
                    None => break,
                };
            }

            /* catch corner case of there being two free blocks: the first on the
            list is higher than the last block on the list, and they are both free */
            if (*self.block_list_head).magic == HeapMagic::Free
            {
                match (*self.block_list_head).next
                {
                    Some(next) =>
                    {
                        if (*next).magic == HeapMagic::Free
                        {
                            if (next as usize) + (*next).size == self.block_list_head as usize
                            {
                                (*next).size = (*next).size + (*self.block_list_head).size;
                                self.block_list_head = next;
                                if (*next).size > largest_merged_block
                                {
                                    largest_merged_block = (*next).size;
                                }
                            }
                        }
                    },
                    _ => ()
                }
            }
        }

        return largest_merged_block;
    }
}
<commit_msg>Tidy up heap stats reporting  * Move logic back into Heap and use HeapStats to describe the allocation totals<commit_after>/* diosix heap management
 *
 * Simple heap manager. A CPU can allocate only from its own
 * heap pool, though it can share these pointers with any CPU.
 * Any CPU can free them back to the owner's heap pool when
 * they are done with these allocations.
 *
 * Thus this code is *single threaded* per individual CPU core
 *
 * Each CPU heap is primed with a small amount of fixed
 * physical RAM, defined by the platform code. When this
 * fixed pool runs low, the heap code requests a temporary
 * block of memory from the physical memory manager.
 * this block is added as a free block to the heap and
 * subsequently allocated from.
 *
 * We use Rust's memory safety features to prevent any
 * use-after-free(). Blocks are free()'d atomically
 * preventing any races.
 *
 * This code interfaces with Rust's global allocator API
 * so things like vec! and Box just work. Heap is
 * the underlying engine for HVallocator.
 *
 * (c) Chris Williams, 2019-2020.
 *
 * See LICENSE for usage and copying.
 */

use core::alloc::{GlobalAlloc, Layout};
use core::ptr::null_mut;
use core::mem;
use core::fmt;
use core::result::Result;
use platform::physmem::{barrier, PhysMemSize, PhysMemBase};
use super::physmem::{self, alloc_region, RegionHygiene};
use super::error::Cause;

/* different states each recognized heap block can be in */
#[derive(PartialEq, Debug, Clone, Copy)]
enum HeapMagic
{
    Free   = 0x0deadded,
    InUse  = 0x0d10c0de
}

/* source of a heap block */
#[derive(PartialEq, Debug, Clone, Copy)]
enum HeapSource
{
    Fixed,      /* allocated during startup by platform code */
    Temporary   /* allocated dynamically from physical memory pool */
}

/* to avoid fragmentation, allocate in block sizes of this multiple, including header */
const HEAP_BLOCK_SIZE: usize = 128;

/* follow Rust's heap allocator API so we can drop our per-CPU allocator in and use things
like Box. We allow the Rust toolchain to track and check pointers and object lifetimes,
while we'll manage the underlying physical memory used by the heap. */
pub struct HVallocator;

unsafe impl GlobalAlloc for HVallocator
{
    unsafe fn alloc(&self, layout: Layout) -> *mut u8
    {
        let bytes = layout.size();
        match (*<super::pcore::PhysicalCore>::this()).heap.alloc::<u8>(bytes)
        {
            Ok(p) => p,
            Err(e) =>
            {
                hvalert!("HVallocator: request for {} bytes failed ({:?})", bytes, e);
                null_mut() /* yeesh */
            }
        }
    }

    unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout)
    {
        match (*<super::pcore::PhysicalCore>::this()).heap.free::<u8>(ptr)
        {
            Err(e) =>
            {
                hvalert!("HVallocator: request to free {:p} failed ({:?})", ptr, e)
            },
            _ => ()
        }
    }
}

/* describe the layout of a per-CPU heap block */
#[repr(C)]
pub struct HeapBlock
{
    /* heap is a single-link-list to keep it simple and safe */
    next: Option<*mut HeapBlock>,
    /* size of this block *including* header */
    size: PhysMemSize,
    /* define block state using magic words */
    magic: HeapMagic,
    /* define the source of the memory */
    source: HeapSource
    /* block contents follows... */
}

/* this is our own internal API for the per-CPU hypervisor heap. use high-level abstractions, such as Box,
rather than this directly, so we get all the safety measures and lifetime checking. think of kallocator
as the API and Heap as the engine. kallocator is built on top of Heap, and each CPU core has its own Heap. */
#[repr(C)]
pub struct Heap
{
    /* pointer to list of in-use and freed blocks */
    block_list_head: *mut HeapBlock,
    /* stash a copy of the block header size here */
    block_header_size: PhysMemSize,
}

/* describe a heap by its totals */
pub struct HeapStats
{
    pub free_total: usize,      /* total free space in bytes */
    pub alloc_total: usize,     /* total bytes allocated */
    pub largest_free: usize,    /* largest single free block in bytes */
    pub largest_alloc: usize    /* largest allocated block in bytes */
}

/* pretty print the heap's stats */
impl fmt::Debug for Heap
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result
    {
        let stats = self.calculate_stats();

        write!(f, "size: {} alloc'd {} free {} largest alloc'd {} largest free {}",
            stats.alloc_total + stats.free_total,
            stats.alloc_total, stats.free_total,
            stats.largest_alloc, stats.largest_free)
    }
}

/* clean up heap list by returning chunks of free temporary physical RAM */
macro_rules! heaphousekeeper
{
    () => ((*<super::pcore::PhysicalCore>::this()).heap.return_unused();)
}

impl Heap
{
    /* initialize this heap area. start off with one giant block
    covering all of free space, from which other blocks will be carved.
    this initial block is assuemd to be a fixed platform-allocated area
    of physical memory.
    => start = pointer to start of heap area
       size = number of available bytes in heap */
    pub fn init(&mut self, start: *mut HeapBlock, size: PhysMemSize)
    {
        /* here's our enormo free block */
        unsafe
        {
            let block = start;
            (*block).size = size;
            (*block).next = None;
            (*block).magic = HeapMagic::Free;
            (*block).source = HeapSource::Fixed;

            self.block_header_size = mem::size_of::<HeapBlock>();
            self.block_list_head = block;
        }
    }

    /* insert a free temporary physical memory block at the head of the list
    => base = base address of the memory block to add
       size = total size of the block, including header that will be automatically added
    <= OK or error code */
    pub fn insert_free(&mut self, base: PhysMemBase, size: PhysMemSize) -> Result<(), Cause>
    {
        unsafe
        {
            /* craft free block from scratch */
            let block = base as *mut HeapBlock;
            (*block).size = size;
            (*block).next = Some(self.block_list_head);
            (*block).magic = HeapMagic::Free;
            (*block).source = HeapSource::Temporary;

            /* add the free block to the start of the list */
            self.block_list_head = block;
        }

        Ok(())
    }

    /* free a previously allocated block
    => to_free = pointer previously returned by alloc()
    <= OK or failure code */
    pub fn free<T>(&mut self, to_free: *mut T) -> Result<(), Cause>
    {
        /* convert this into a raw pointer so we can find the heap block header */
        let mut ptr = to_free as usize;
        ptr = ptr - self.block_header_size;
        let block = ptr as *mut HeapBlock;

        unsafe
        {
            match (*block).magic
            {
                HeapMagic::InUse =>
                {
                    /* assume writes are atomic. place a memory barrier to avoid any release ordering issues */
                    barrier();
                    (*block).magic = HeapMagic::Free;
                    Ok(())
                },
                /* if it's not in use, or bad magic, then bail out */
                HeapMagic::Free => Err(Cause::HeapNotInUse)
            }
        }
    }

    /* allocate memory for the given object type. the returned pointer skips
    the heap block header, pointing to the available space,
    just like malloc() on other platforms.
    => T = type of object to allocate memory for
       num = number of objects to allocate for
    <= pointer to memory, or error code */
    pub fn alloc<T>(&mut self, num: usize) -> Result<*mut T, Cause>
    {
        if num == 0
        {
            return Err(Cause::HeapBadSize);
        }

        let mut done = false;
        let mut extended = false;

        /* calculate size of block required, including header, rounded up to
        nearest whole heap block multiple */
        let mut size_req = (mem::size_of::<T>() * num) + self.block_header_size;
        size_req = ((size_req / HEAP_BLOCK_SIZE) + 1) * HEAP_BLOCK_SIZE;

        /* scan all blocks for first free fit */
        let mut search_block = self.block_list_head;
        unsafe
        {
            while !done
            {
                if (*search_block).magic == HeapMagic::Free && (*search_block).size >= size_req
                {
                    /* we've got a winner. if the found block is equal size, or only a few bytes
                    larger than the required size, then take the whole block */
                    if ((*search_block).size - size_req) < HEAP_BLOCK_SIZE
                    {
                        (*search_block).magic = HeapMagic::InUse;
                        let found_ptr = (search_block as usize) + self.block_header_size;
                        return Result::Ok(found_ptr as *mut T);
                    }
                    else
                    {
                        /* carve the end of a large-enough free block off to make a new block.
                        then add this new block to the start of the list */
                        (*search_block).size = (*search_block).size - size_req;

                        /* skip to the new (shorter) end of the free block */
                        let mut found_ptr = (search_block as usize) + (*search_block).size;

                        /* set metadata for newly allocated block */
                        let alloc_block = found_ptr as *mut HeapBlock;
                        (*alloc_block).next  = Some(self.block_list_head);
                        (*alloc_block).magic = HeapMagic::InUse;
                        (*alloc_block).size  = size_req;

                        /* point the head of the list at new block */
                        self.block_list_head = alloc_block;

                        /* adjust pointer to skip the header of our new block, and we're done */
                        found_ptr = found_ptr + self.block_header_size;
                        return Result::Ok(found_ptr as *mut T);
                    }
                }

                /* make sure we don't run off the end of the list.
                also, attempt to consolidate neighboring blocks to make
                more bytes available and reduce fragmentation. do this
                after we've tried searching for available blocks */
                match (*search_block).next
                {
                    None => if self.consolidate() < HEAP_BLOCK_SIZE
                    {
                        if extended == false
                        {
                            /* if we can't squeeze any more bytes out of the list
                            then grab a chunk of available RAM from the physical
                            memory manager and add it to the free list */
                            let region = match alloc_region(size_req)
                            {
                                Ok(r) => r,
                                Err(_e) =>
                                {
                                    /* give up and bail out if there's no more physical memory */
                                    hvdebug!("Failed to extend heap by {} bytes: {:?}", size_req, _e);
                                    return Result::Err(Cause::HeapNoFreeMem);
                                }
                            };

                            hvdebug!("Extending heap by {} bytes (needed {}), base: 0x{:x}",
                            region.size(), size_req, region.base());

                            if self.insert_free(region.base(), region.size()).is_ok()
                            {
                                extended = true;

                                /* start the search over, starting with the new block */
                                search_block = self.block_list_head;
                            }
                            else
                            {
                                /* if we couldn't insert free block, give up */
                                done = true;
                            }
                        }
                        else
                        {
                            /* can't squeeze any more out of list and we've tried allocating more
                            physical memory. give up at this point, though we shouldn't really
                            end up here */
                            hvdebug!("Giving up allocating {} bytes", size_req);
                            done = true;
                        }
                    }
                    else
                    {
                        /* start the search over */
                        search_block = self.block_list_head;
                    },
                    Some(n) => search_block = n
                };
            }
        }

        return Result::Err(Cause::HeapNoFreeMem);
    }

    /* deallocate any free temporary physical memory regions that are no longer needed */
    pub fn return_unused(&mut self)
    {
        /* ensure all blocks are gathered up */
        loop
        {
            if self.consolidate() < HEAP_BLOCK_SIZE
            {
                break;
            }
        }

        /* search for unused physical memory blocks to return */
        let mut block = self.block_list_head;
        let mut prev_block: Option<*mut HeapBlock> = None;
        unsafe
        {
            loop
            {
                match ((*block).source, (*block).magic)
                {
                    /* remove physical region from single-linked list if successfully deallocated.
                    the physical memory manager will avoid fragmentation by rejecting regions that
                    are not multiples of prefered region sizes */
                    (HeapSource::Temporary, HeapMagic::Free) =>
                    {
                        let region = physmem::Region::new(block as PhysMemBase, (*block).size, RegionHygiene::Dirty);
                        if physmem::dealloc_region(region).is_ok()
                        {
                            hvdebug!("Returning heap block {:p} size {} to physical memory pool",
                            block, (*block).size);

                            /* delink the block - do not touch the contents of the
                            deallocated block: it's back in the pool and another CPU core
                            could grab it at any time. After dealloc_region() returns Ok,
                            it's gone as far as this core is concerned. */
                            match prev_block
                            {
                                Some(b) => (*b).next = (*block).next,
                                None => ()
                            };
                        }
                    },

                    (_, _) => ()
                }

                match (*block).next
                {
                    Some(n) =>
                    {
                        prev_block = Some(block);
                        block = n;
                    }
                    None => break
                };
            }
        }
    }

    /* pass once over the heap and try to merge adjacent free blocks
    <= size of the largest block seen, in bytes including header */
    fn consolidate(&mut self) -> PhysMemSize
    {
        let mut largest_merged_block: PhysMemSize = 0;

        let mut block = self.block_list_head;
        unsafe
        {
            /* can't merge if we're the last block in the list */
            while (*block).next.is_some()
            {
                let next = (*block).next.unwrap();
                if (*block).magic == HeapMagic::Free && (*next).magic == HeapMagic::Free
                {
                    let target_ptr = (block as usize) + (*block).size;
                    if target_ptr == next as usize
                    {
                        /* we're adjacent, we're both free, and we can merge */
                        let merged_size = (*block).size + (*next).size;
                        if merged_size > largest_merged_block
                        {
                            largest_merged_block = merged_size;
                        }
                        (*block).size = merged_size;
                        (*block).next = (*next).next;
                    }
                }
                match (*block).next
                {
                    Some(n) => block = n,
                    None => break,
                };
            }

            /* catch corner case of there being two free blocks: the first on the
            list is higher than the last block on the list, and they are both free */
            if (*self.block_list_head).magic == HeapMagic::Free
            {
                match (*self.block_list_head).next
                {
                    Some(next) =>
                    {
                        if (*next).magic == HeapMagic::Free
                        {
                            if (next as usize) + (*next).size == self.block_list_head as usize
                            {
                                (*next).size = (*next).size + (*self.block_list_head).size;
                                self.block_list_head = next;
                                if (*next).size > largest_merged_block
                                {
                                    largest_merged_block = (*next).size;
                                }
                            }
                        }
                    },
                    _ => ()
                }
            }
        }

        return largest_merged_block;
    }

    /* generate a block of statistics describing the heap */
    pub fn calculate_stats(&self) -> HeapStats
    {
        let mut free_total = 0;
        let mut alloc_total = 0;
        let mut largest_free = 0;
        let mut largest_alloc = 0;

        let mut done = false;
        let mut block = self.block_list_head;
        unsafe
        {
            while !done
            {
                let size = (*block).size;
                match (*block).magic
                {
                    HeapMagic::InUse =>
                    {
                        alloc_total = alloc_total + size;
                        if size > largest_alloc
                        {
                            largest_alloc = size;
                        }
                    },
                    HeapMagic::Free =>
                    {
                        free_total = free_total + size;
                        if size > largest_free
                        {
                            largest_free = size;
                        }
                    }
                };

                match (*block).next
                {
                    None => done = true,
                    Some(b) => block = b
                };
            }
        }

        HeapStats
        {
            free_total: free_total,
            alloc_total: alloc_total,
            largest_alloc: largest_alloc,
            largest_free: largest_free
        }
    }
}
<|endoftext|>


<commit_before>fn main() {
    let x = 5i32;

    match x {
        1 | 2  => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything"),
    }

    match x {
        1...5 => println!("one through five"),
        _ => println!("anything"),
    }
}
<commit_msg>add more pattern samples<commit_after>fn main() {
    let x = 4;

    match x {
        1 | 2  => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything"),
    }

    match x {
        1...5 => println!("one through five"),
        _ => println!("anything"),
    }

    match x {
        e @ 1...5 => println!("one through five, {}", e),
        _ => println!("anything"),
    }
}
<|endoftext|>


<commit_before>// DOCS

#![feature(plugin)]
#[plugin] extern crate peg_syntax_ext;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

pub struct Property {
    params: HashMap<String, String>,
    raw_value: String,
    prop_group: Option<String>
}

impl Property {
    fn new(params: HashMap<String, String>, raw_value: String, prop_group: Option<String>) -> Property {
        Property {
            params: params,
            raw_value: raw_value,
            prop_group: prop_group
        }
    }

    /// Get property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
    /// `"foo"`.
    pub fn get_prop_group(&self) -> &Option<String> {
        &self.prop_group
    }

    /// Get parameters.
    pub fn get_params(&self) -> &HashMap<String, String> {
        &self.params
    }

    /// Get value as unparsed string.
    pub fn get_raw_value(&self) -> &String {
        &self.raw_value
    }

    /// Get value as unescaped string.
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.get_raw_value())
    }
}

pub struct Component {
    /// The name of the component, such as `VCARD` or `VEVENT`.
    pub name: String,

    /// The component's properties.
    pub props: HashMap<String, Vec<Property>>,

    /// The component's child- or sub-components.
    pub subcomponents: Vec<Component>
}

impl Component {
    fn new(name: String) -> Component {
        Component {
            name: name,
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

    /// Retrieve one property (from many) by key. Returns `None` if nothing is found.
    pub fn single_prop(&self, key: &str) -> Option<&Property> {
        match self.props.get(key) {
            Some(x) => {
                match x.len() {
                    1 => Some(&x[0]),
                    _ => None
                }
            },
            None => None
        }
    }

    /// Retrieve a mutable vector of properties for this key. Creates one (and inserts it into the
    /// component) if none exists.
    pub fn all_props_mut(&mut self, key: &str) -> &mut Vec<Property> {
        match self.props.entry(String::from_str(key)) {
            Occupied(values) => values.into_mut(),
            Vacant(values) => values.insert(vec![])
        }
    }

    /// Retrieve properties by key. Returns an empty slice if key doesn't exist.
    pub fn all_props(&self, key: &str) -> &[Property] {
        static EMPTY: &'static [Property] = &[];
        match self.props.get(key) {
            Some(values) => values.as_slice(),
            None => EMPTY
        }
    }
}

peg! parser(r#"
use super::{Component,Property};
use std::collections::HashMap;

#[pub]
component -> Component
    = name:component_begin
      ps:props
      cs:components?
      component_end {
        let mut rv = Component::new(name);

        match cs {
            Some(components) => { rv.subcomponents = components; },
            None => ()
        };

        for (k, v) in ps.into_iter() {
            rv.all_props_mut(k).push(v);
        };

        rv
    }

component_begin -> String
    = "BEGIN:" v:value __ { v.to_string() }

component_end -> String
    = "END:" v:value __ { v.to_string() }

components -> Vec<Component>
    = cs:component ++ eols __ { cs }

props -> Vec<(&'input str, Property)>
    = ps:prop ++ eols __ { ps }

prop -> (&'input str, Property)
    = !"BEGIN:" !"END:" g:group? k:name p:params ":" v:value {
        (k, Property::new(p, v, g))
    }

group -> String
    = g:group_name "." { g }

group_name -> String
    = group_char+ { match_str.to_string() }

name -> &'input str
    = iana_token+ { match_str }

params -> HashMap<String, String>
    = ps:(";" p:param {p})* {
        let mut rv: HashMap<String, String> = HashMap::new();
        for (k, v) in ps.into_iter() {
            rv.insert(k.to_string(), v.to_string());
        };
        rv
    }

param -> (&'input str, &'input str)
    // FIXME: Doesn't handle comma-separated values
    = k:param_name v:("=" v:param_value { v })? {
        (k, match v {
            Some(x) => x,
            None => ""
        })
    }

param_name -> &'input str
    = iana_token+ { match_str }

param_value -> &'input str
    = x:(quoted_string / param_text) { x }

param_text -> &'input str
    = safe_char* { match_str }

value -> String
    = value_char+ { match_str.to_string() }

quoted_string -> &'input str
    = dquote x:quoted_content dquote { x }

quoted_content -> &'input str
    = qsafe_char* { match_str }

iana_token = ([a-zA-Z0-9] / "-")+
group_char = ([a-zA-Z0-9] / "-")
safe_char = !";" !":" !"," value_char
qsafe_char = !dquote value_char // FIXME

value_char = !eol .

eol = "\r\n" / "\n" / "\r"
dquote = "\""
eols = eol+
whitespace = " " / "\t"
__ = (eol / whitespace)*

"#);

/// Parse a component. The error value is a human-readable message.
pub fn parse_component(s: &String) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = s
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\r ", "").replace("\r\t", "");

    parser::component(unfolded.as_slice())
}

/// Escape text for a VObject property value.
pub fn escape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\n")
        .replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\r\n", "\\n")
        .replace("\n", "\\n")
}

/// Unescape text from a VObject property value.
pub fn unescape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\\n")
        .replace("\r\n", "\n")
        .replace("\\n", "\n")
        .replace("\\,", ",")
        .replace("\\;", ";")
        .replace("\\\\", "\\")
}
<commit_msg>fix unfolding<commit_after>// DOCS

#![feature(plugin)]
#[plugin] extern crate peg_syntax_ext;

use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};

pub struct Property {
    params: HashMap<String, String>,
    raw_value: String,
    prop_group: Option<String>
}

impl Property {
    fn new(params: HashMap<String, String>, raw_value: String, prop_group: Option<String>) -> Property {
        Property {
            params: params,
            raw_value: raw_value,
            prop_group: prop_group
        }
    }

    /// Get property group. E.g. a contentline like `foo.FN:Markus` would result in the group being
    /// `"foo"`.
    pub fn get_prop_group(&self) -> &Option<String> {
        &self.prop_group
    }

    /// Get parameters.
    pub fn get_params(&self) -> &HashMap<String, String> {
        &self.params
    }

    /// Get value as unparsed string.
    pub fn get_raw_value(&self) -> &String {
        &self.raw_value
    }

    /// Get value as unescaped string.
    pub fn value_as_string(&self) -> String {
        unescape_chars(self.get_raw_value())
    }
}

pub struct Component {
    /// The name of the component, such as `VCARD` or `VEVENT`.
    pub name: String,

    /// The component's properties.
    pub props: HashMap<String, Vec<Property>>,

    /// The component's child- or sub-components.
    pub subcomponents: Vec<Component>
}

impl Component {
    fn new(name: String) -> Component {
        Component {
            name: name,
            props: HashMap::new(),
            subcomponents: vec![]
        }
    }

    /// Retrieve one property (from many) by key. Returns `None` if nothing is found.
    pub fn single_prop(&self, key: &str) -> Option<&Property> {
        match self.props.get(key) {
            Some(x) => {
                match x.len() {
                    1 => Some(&x[0]),
                    _ => None
                }
            },
            None => None
        }
    }

    /// Retrieve a mutable vector of properties for this key. Creates one (and inserts it into the
    /// component) if none exists.
    pub fn all_props_mut(&mut self, key: &str) -> &mut Vec<Property> {
        match self.props.entry(String::from_str(key)) {
            Occupied(values) => values.into_mut(),
            Vacant(values) => values.insert(vec![])
        }
    }

    /// Retrieve properties by key. Returns an empty slice if key doesn't exist.
    pub fn all_props(&self, key: &str) -> &[Property] {
        static EMPTY: &'static [Property] = &[];
        match self.props.get(key) {
            Some(values) => values.as_slice(),
            None => EMPTY
        }
    }
}

peg! parser(r#"
use super::{Component,Property};
use std::collections::HashMap;

#[pub]
component -> Component
    = name:component_begin
      ps:props
      cs:components?
      component_end {
        let mut rv = Component::new(name);

        match cs {
            Some(components) => { rv.subcomponents = components; },
            None => ()
        };

        for (k, v) in ps.into_iter() {
            rv.all_props_mut(k).push(v);
        };

        rv
    }

component_begin -> String
    = "BEGIN:" v:value __ { v.to_string() }

component_end -> String
    = "END:" v:value __ { v.to_string() }

components -> Vec<Component>
    = cs:component ++ eols __ { cs }

props -> Vec<(&'input str, Property)>
    = ps:prop ++ eols __ { ps }

prop -> (&'input str, Property)
    = !"BEGIN:" !"END:" g:group? k:name p:params ":" v:value {
        (k, Property::new(p, v, g))
    }

group -> String
    = g:group_name "." { g }

group_name -> String
    = group_char+ { match_str.to_string() }

name -> &'input str
    = iana_token+ { match_str }

params -> HashMap<String, String>
    = ps:(";" p:param {p})* {
        let mut rv: HashMap<String, String> = HashMap::new();
        for (k, v) in ps.into_iter() {
            rv.insert(k.to_string(), v.to_string());
        };
        rv
    }

param -> (&'input str, &'input str)
    // FIXME: Doesn't handle comma-separated values
    = k:param_name v:("=" v:param_value { v })? {
        (k, match v {
            Some(x) => x,
            None => ""
        })
    }

param_name -> &'input str
    = iana_token+ { match_str }

param_value -> &'input str
    = x:(quoted_string / param_text) { x }

param_text -> &'input str
    = safe_char* { match_str }

value -> String
    = value_char+ { match_str.to_string() }

quoted_string -> &'input str
    = dquote x:quoted_content dquote { x }

quoted_content -> &'input str
    = qsafe_char* { match_str }

iana_token = ([a-zA-Z0-9] / "-")+
group_char = ([a-zA-Z0-9] / "-")
safe_char = !";" !":" !"," value_char
qsafe_char = !dquote value_char // FIXME

value_char = !eol .

eol = "\r\n" / "\n" / "\r"
dquote = "\""
eols = eol+
whitespace = " " / "\t"
__ = (eol / whitespace)*

"#);

/// Parse a component. The error value is a human-readable message.
pub fn parse_component(s: &String) -> Result<Component, String> {
    // XXX: The unfolding should be worked into the PEG
    // See feature request: https://github.com/kevinmehall/rust-peg/issues/26
    let unfolded = s
        .replace("\r\n ", "").replace("\r\n\t", "")
        .replace("\n ", "").replace("\n\t", "")
        .replace("\r ", "").replace("\r\t", "");

    parser::component(unfolded.as_slice())
}

/// Escape text for a VObject property value.
pub fn escape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\n")
        .replace("\\", "\\\\")
        .replace(";", "\\;")
        .replace(",", "\\,")
        .replace("\r\n", "\\n")
        .replace("\n", "\\n")
}

/// Unescape text from a VObject property value.
pub fn unescape_chars(s: &String) -> String {
    // Order matters! Lifted from icalendar.parser
    // https://github.com/collective/icalendar/
    s
        .replace("\\N", "\\n")
        .replace("\r\n", "\n")
        .replace("\\n", "\n")
        .replace("\\,", ",")
        .replace("\\;", ";")
        .replace("\\\\", "\\")
}
<|endoftext|>