<commit_before>//! Checks for usage of  `&Vec[_]` and `&String`.

use crate::utils::ptr::get_spans;
use crate::utils::{
    is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg,
    span_lint_and_then, walk_ptrs_hir_ty,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
    BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind,
    Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::MultiSpan;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** This lint checks for function arguments of type `&String`
    /// or `&Vec` unless the references are mutable. It will also suggest you
    /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
    /// calls.
    ///
    /// **Why is this bad?** Requiring the argument to be of the specific size
    /// makes the function less useful for no benefit; slices in the form of `&[T]`
    /// or `&str` usually suffice and can be obtained from other types, too.
    ///
    /// **Known problems:** The lint does not follow data. So if you have an
    /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
    /// changing that `.clone()` to `.to_owned()`.
    ///
    /// Other functions called from this function taking a `&String` or `&Vec`
    /// argument may also fail to compile if you change the argument. Applying
    /// this lint on them will fix the problem, but they may be in other crates.
    ///
    /// One notable example of a function that may cause issues, and which cannot
    /// easily be changed due to beinng in the standard library is `Vec::contains`.
    /// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then
    /// it will compile, but if a `&[T]` is passed then it will not compile.
    ///
    /// ```ignore
    /// fn cannot_take_a_slice(v: &Vec<u8>) -> bool {
    ///     let vec_of_vecs: Vec<Vec<u8>> = some_other_fn();
    ///
    ///     vec_of_vecs.contains(v)
    /// }
    /// ```
    ///
    /// Also there may be `fn(&Vec)`-typed references pointing to your function.
    /// If you have them, you will get a compiler error after applying this lint's
    /// suggestions. You then have the choice to undo your changes or change the
    /// type of the reference.
    ///
    /// Note that if the function is part of your public interface, there may be
    /// other crates referencing it, of which you may not be aware. Carefully
    /// deprecate the function before applying the lint suggestions in this case.
    ///
    /// **Example:**
    /// ```ignore
    /// // Bad
    /// fn foo(&Vec<u32>) { .. }
    ///
    /// // Good
    /// fn foo(&[u32]) { .. }
    /// ```
    pub PTR_ARG,
    style,
    "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
}

declare_clippy_lint! {
    /// **What it does:** This lint checks for equality comparisons with `ptr::null`
    ///
    /// **Why is this bad?** It's easier and more readable to use the inherent
    /// `.is_null()`
    /// method instead
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// // Bad
    /// if x == ptr::null {
    ///     ..
    /// }
    ///
    /// // Good
    /// if x.is_null() {
    ///     ..
    /// }
    /// ```
    pub CMP_NULL,
    style,
    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
}

declare_clippy_lint! {
    /// **What it does:** This lint checks for functions that take immutable
    /// references and return mutable ones.
    ///
    /// **Why is this bad?** This is trivially unsound, as one can create two
    /// mutable references from the same (immutable!) source.
    /// This [error](https://github.com/rust-lang/rust/issues/39465)
    /// actually lead to an interim Rust release 1.15.1.
    ///
    /// **Known problems:** To be on the conservative side, if there's at least one
    /// mutable reference with the output lifetime, this lint will not trigger.
    /// In practice, this case is unlikely anyway.
    ///
    /// **Example:**
    /// ```ignore
    /// fn foo(&Foo) -> &mut Bar { .. }
    /// ```
    pub MUT_FROM_REF,
    correctness,
    "fns that create mutable refs from immutable ref args"
}

declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);

impl<'tcx> LateLintPass<'tcx> for Ptr {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
        if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
            check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
        }
    }

    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
        if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
            let parent_item = cx.tcx.hir().get_parent_item(item.hir_id);
            if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
                if let ItemKind::Impl { of_trait: Some(_), .. } = it.kind {
                    return; // ignore trait impls
                }
            }
            check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
        }
    }

    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
        if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
            let body_id = if let TraitFn::Provided(b) = *trait_method {
                Some(b)
            } else {
                None
            };
            check_fn(cx, &sig.decl, item.hir_id, body_id);
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind {
            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
                span_lint(
                    cx,
                    CMP_NULL,
                    expr.span,
                    "Comparing with null is better expressed by the `.is_null()` method",
                );
            }
        }
    }
}

#[allow(clippy::too_many_lines)]
fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option<BodyId>) {
    let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
    let sig = cx.tcx.fn_sig(fn_def_id);
    let fn_ty = sig.skip_binder();
    let body = opt_body_id.map(|id| cx.tcx.hir().body(id));

    for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
        // Honor the allow attribute on parameters. See issue 5644.
        if let Some(body) = &body {
            if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
                continue;
            }
        }

        if let ty::Ref(_, ty, Mutability::Not) = ty.kind {
            if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
                let mut ty_snippet = None;
                if_chain! {
                    if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
                    if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
                    then {
                        let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
                            GenericArg::Type(ty) => Some(ty),
                            _ => None,
                        }).collect();
                        if types.len() == 1 {
                            ty_snippet = snippet_opt(cx, types[0].span);
                        }
                    }
                };
                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
                    span_lint_and_then(
                        cx,
                        PTR_ARG,
                        arg.span,
                        "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
                         with non-Vec-based slices.",
                        |diag| {
                            if let Some(ref snippet) = ty_snippet {
                                diag.span_suggestion(
                                    arg.span,
                                    "change this to",
                                    format!("&[{}]", snippet),
                                    Applicability::Unspecified,
                                );
                            }
                            for (clonespan, suggestion) in spans {
                                diag.span_suggestion(
                                    clonespan,
                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
                                        Cow::Owned(format!("change `{}` to", x))
                                    }),
                                    suggestion.into(),
                                    Applicability::Unspecified,
                                );
                            }
                        },
                    );
                }
            } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                    span_lint_and_then(
                        cx,
                        PTR_ARG,
                        arg.span,
                        "writing `&String` instead of `&str` involves a new object where a slice will do.",
                        |diag| {
                            diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
                            for (clonespan, suggestion) in spans {
                                diag.span_suggestion_short(
                                    clonespan,
                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
                                        Cow::Owned(format!("change `{}` to", x))
                                    }),
                                    suggestion.into(),
                                    Applicability::Unspecified,
                                );
                            }
                        },
                    );
                }
            } else if match_type(cx, ty, &paths::COW) {
                if_chain! {
                    if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
                    if let TyKind::Path(ref path) = ty.kind;
                    if let QPath::Resolved(None, ref pp) = *path;
                    if let [ref bx] = *pp.segments;
                    if let Some(ref params) = bx.args;
                    if !params.parenthesized;
                    if let Some(inner) = params.args.iter().find_map(|arg| match arg {
                        GenericArg::Type(ty) => Some(ty),
                        _ => None,
                    });
                    then {
                        let replacement = snippet_opt(cx, inner.span);
                        if let Some(r) = replacement {
                            span_lint_and_sugg(
                                cx,
                                PTR_ARG,
                                arg.span,
                                "using a reference to `Cow` is not recommended.",
                                "change this to",
                                "&".to_owned() + &r,
                                Applicability::Unspecified,
                            );
                        }
                    }
                }
            }
        }
    }

    if let FnRetTy::Return(ref ty) = decl.output {
        if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
            let mut immutables = vec![];
            for (_, ref mutbl, ref argspan) in decl
                .inputs
                .iter()
                .filter_map(|ty| get_rptr_lm(ty))
                .filter(|&(lt, _, _)| lt.name == out.name)
            {
                if *mutbl == Mutability::Mut {
                    return;
                }
                immutables.push(*argspan);
            }
            if immutables.is_empty() {
                return;
            }
            span_lint_and_then(
                cx,
                MUT_FROM_REF,
                ty.span,
                "mutable borrow from immutable input(s)",
                |diag| {
                    let ms = MultiSpan::from_spans(immutables);
                    diag.span_note(ms, "immutable borrow here");
                },
            );
        }
    }
}

fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
    if let TyKind::Rptr(ref lt, ref m) = ty.kind {
        Some((lt, m.mutbl, ty.span))
    } else {
        None
    }
}

fn is_null_path(expr: &Expr<'_>) -> bool {
    if let ExprKind::Call(ref pathexp, ref args) = expr.kind {
        if args.is_empty() {
            if let ExprKind::Path(ref path) = pathexp.kind {
                return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
            }
        }
    }
    false
}
<commit_msg>formatting<commit_after>//! Checks for usage of  `&Vec[_]` and `&String`.

use crate::utils::ptr::get_spans;
use crate::utils::{
    is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg,
    span_lint_and_then, walk_ptrs_hir_ty,
};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{
    BinOpKind, BodyId, Expr, ExprKind, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind,
    Lifetime, MutTy, Mutability, Node, PathSegment, QPath, TraitFn, TraitItem, TraitItemKind, Ty, TyKind,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use rustc_span::MultiSpan;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** This lint checks for function arguments of type `&String`
    /// or `&Vec` unless the references are mutable. It will also suggest you
    /// replace `.clone()` calls with the appropriate `.to_owned()`/`to_string()`
    /// calls.
    ///
    /// **Why is this bad?** Requiring the argument to be of the specific size
    /// makes the function less useful for no benefit; slices in the form of `&[T]`
    /// or `&str` usually suffice and can be obtained from other types, too.
    ///
    /// **Known problems:** The lint does not follow data. So if you have an
    /// argument `x` and write `let y = x; y.clone()` the lint will not suggest
    /// changing that `.clone()` to `.to_owned()`.
    ///
    /// Other functions called from this function taking a `&String` or `&Vec`
    /// argument may also fail to compile if you change the argument. Applying
    /// this lint on them will fix the problem, but they may be in other crates.
    ///
    /// One notable example of a function that may cause issues, and which cannot
    /// easily be changed due to beinng in the standard library is `Vec::contains`.
    /// when called on a `Vec<Vec<T>>`. If a `&Vec` is passed to that method then
    /// it will compile, but if a `&[T]` is passed then it will not compile.
    ///
    /// ```ignore
    /// fn cannot_take_a_slice(v: &Vec<u8>) -> bool {
    ///     let vec_of_vecs: Vec<Vec<u8>> = some_other_fn();
    ///
    ///     vec_of_vecs.contains(v)
    /// }
    /// ```
    ///
    /// Also there may be `fn(&Vec)`-typed references pointing to your function.
    /// If you have them, you will get a compiler error after applying this lint's
    /// suggestions. You then have the choice to undo your changes or change the
    /// type of the reference.
    ///
    /// Note that if the function is part of your public interface, there may be
    /// other crates referencing it, of which you may not be aware. Carefully
    /// deprecate the function before applying the lint suggestions in this case.
    ///
    /// **Example:**
    /// ```ignore
    /// // Bad
    /// fn foo(&Vec<u32>) { .. }
    ///
    /// // Good
    /// fn foo(&[u32]) { .. }
    /// ```
    pub PTR_ARG,
    style,
    "fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively"
}

declare_clippy_lint! {
    /// **What it does:** This lint checks for equality comparisons with `ptr::null`
    ///
    /// **Why is this bad?** It's easier and more readable to use the inherent
    /// `.is_null()`
    /// method instead
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// // Bad
    /// if x == ptr::null {
    ///     ..
    /// }
    ///
    /// // Good
    /// if x.is_null() {
    ///     ..
    /// }
    /// ```
    pub CMP_NULL,
    style,
    "comparing a pointer to a null pointer, suggesting to use `.is_null()` instead."
}

declare_clippy_lint! {
    /// **What it does:** This lint checks for functions that take immutable
    /// references and return mutable ones.
    ///
    /// **Why is this bad?** This is trivially unsound, as one can create two
    /// mutable references from the same (immutable!) source.
    /// This [error](https://github.com/rust-lang/rust/issues/39465)
    /// actually lead to an interim Rust release 1.15.1.
    ///
    /// **Known problems:** To be on the conservative side, if there's at least one
    /// mutable reference with the output lifetime, this lint will not trigger.
    /// In practice, this case is unlikely anyway.
    ///
    /// **Example:**
    /// ```ignore
    /// fn foo(&Foo) -> &mut Bar { .. }
    /// ```
    pub MUT_FROM_REF,
    correctness,
    "fns that create mutable refs from immutable ref args"
}

declare_lint_pass!(Ptr => [PTR_ARG, CMP_NULL, MUT_FROM_REF]);

impl<'tcx> LateLintPass<'tcx> for Ptr {
    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
        if let ItemKind::Fn(ref sig, _, body_id) = item.kind {
            check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
        }
    }

    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
        if let ImplItemKind::Fn(ref sig, body_id) = item.kind {
            let parent_item = cx.tcx.hir().get_parent_item(item.hir_id);
            if let Some(Node::Item(it)) = cx.tcx.hir().find(parent_item) {
                if let ItemKind::Impl { of_trait: Some(_), .. } = it.kind {
                    return; // ignore trait impls
                }
            }
            check_fn(cx, &sig.decl, item.hir_id, Some(body_id));
        }
    }

    fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
        if let TraitItemKind::Fn(ref sig, ref trait_method) = item.kind {
            let body_id = if let TraitFn::Provided(b) = *trait_method {
                Some(b)
            } else {
                None
            };
            check_fn(cx, &sig.decl, item.hir_id, body_id);
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if let ExprKind::Binary(ref op, ref l, ref r) = expr.kind {
            if (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne) && (is_null_path(l) || is_null_path(r)) {
                span_lint(
                    cx,
                    CMP_NULL,
                    expr.span,
                    "Comparing with null is better expressed by the `.is_null()` method",
                );
            }
        }
    }
}

#[allow(clippy::too_many_lines)]
fn check_fn(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_id: HirId, opt_body_id: Option<BodyId>) {
    let fn_def_id = cx.tcx.hir().local_def_id(fn_id);
    let sig = cx.tcx.fn_sig(fn_def_id);
    let fn_ty = sig.skip_binder();
    let body = opt_body_id.map(|id| cx.tcx.hir().body(id));

    for (idx, (arg, ty)) in decl.inputs.iter().zip(fn_ty.inputs()).enumerate() {
        // Honor the allow attribute on parameters. See issue 5644.
        if let Some(body) = &body {
            if is_allowed(cx, PTR_ARG, body.params[idx].hir_id) {
                continue;
            }
        }

        if let ty::Ref(_, ty, Mutability::Not) = ty.kind {
            if is_type_diagnostic_item(cx, ty, sym!(vec_type)) {
                let mut ty_snippet = None;
                if_chain! {
                    if let TyKind::Path(QPath::Resolved(_, ref path)) = walk_ptrs_hir_ty(arg).kind;
                    if let Some(&PathSegment{args: Some(ref parameters), ..}) = path.segments.last();
                    then {
                        let types: Vec<_> = parameters.args.iter().filter_map(|arg| match arg {
                            GenericArg::Type(ty) => Some(ty),
                            _ => None,
                        }).collect();
                        if types.len() == 1 {
                            ty_snippet = snippet_opt(cx, types[0].span);
                        }
                    }
                };
                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_owned()")]) {
                    span_lint_and_then(
                        cx,
                        PTR_ARG,
                        arg.span,
                        "writing `&Vec<_>` instead of `&[_]` involves one more reference and cannot be used \
                         with non-Vec-based slices.",
                        |diag| {
                            if let Some(ref snippet) = ty_snippet {
                                diag.span_suggestion(
                                    arg.span,
                                    "change this to",
                                    format!("&[{}]", snippet),
                                    Applicability::Unspecified,
                                );
                            }
                            for (clonespan, suggestion) in spans {
                                diag.span_suggestion(
                                    clonespan,
                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
                                        Cow::Owned(format!("change `{}` to", x))
                                    }),
                                    suggestion.into(),
                                    Applicability::Unspecified,
                                );
                            }
                        },
                    );
                }
            } else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
                if let Some(spans) = get_spans(cx, opt_body_id, idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                    span_lint_and_then(
                        cx,
                        PTR_ARG,
                        arg.span,
                        "writing `&String` instead of `&str` involves a new object where a slice will do.",
                        |diag| {
                            diag.span_suggestion(arg.span, "change this to", "&str".into(), Applicability::Unspecified);
                            for (clonespan, suggestion) in spans {
                                diag.span_suggestion_short(
                                    clonespan,
                                    &snippet_opt(cx, clonespan).map_or("change the call to".into(), |x| {
                                        Cow::Owned(format!("change `{}` to", x))
                                    }),
                                    suggestion.into(),
                                    Applicability::Unspecified,
                                );
                            }
                        },
                    );
                }
            } else if match_type(cx, ty, &paths::COW) {
                if_chain! {
                    if let TyKind::Rptr(_, MutTy { ref ty, ..} ) = arg.kind;
                    if let TyKind::Path(ref path) = ty.kind;
                    if let QPath::Resolved(None, ref pp) = *path;
                    if let [ref bx] = *pp.segments;
                    if let Some(ref params) = bx.args;
                    if !params.parenthesized;
                    if let Some(inner) = params.args.iter().find_map(|arg| match arg {
                        GenericArg::Type(ty) => Some(ty),
                        _ => None,
                    });
                    then {
                        let replacement = snippet_opt(cx, inner.span);
                        if let Some(r) = replacement {
                            span_lint_and_sugg(
                                cx,
                                PTR_ARG,
                                arg.span,
                                "using a reference to `Cow` is not recommended.",
                                "change this to",
                                "&".to_owned() + &r,
                                Applicability::Unspecified,
                            );
                        }
                    }
                }
            }
        }
    }

    if let FnRetTy::Return(ref ty) = decl.output {
        if let Some((out, Mutability::Mut, _)) = get_rptr_lm(ty) {
            let mut immutables = vec![];
            for (_, ref mutbl, ref argspan) in decl
                .inputs
                .iter()
                .filter_map(|ty| get_rptr_lm(ty))
                .filter(|&(lt, _, _)| lt.name == out.name)
            {
                if *mutbl == Mutability::Mut {
                    return;
                }
                immutables.push(*argspan);
            }
            if immutables.is_empty() {
                return;
            }
            span_lint_and_then(
                cx,
                MUT_FROM_REF,
                ty.span,
                "mutable borrow from immutable input(s)",
                |diag| {
                    let ms = MultiSpan::from_spans(immutables);
                    diag.span_note(ms, "immutable borrow here");
                },
            );
        }
    }
}

fn get_rptr_lm<'tcx>(ty: &'tcx Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutability, Span)> {
    if let TyKind::Rptr(ref lt, ref m) = ty.kind {
        Some((lt, m.mutbl, ty.span))
    } else {
        None
    }
}

fn is_null_path(expr: &Expr<'_>) -> bool {
    if let ExprKind::Call(ref pathexp, ref args) = expr.kind {
        if args.is_empty() {
            if let ExprKind::Path(ref path) = pathexp.kind {
                return match_qpath(path, &paths::PTR_NULL) || match_qpath(path, &paths::PTR_NULL_MUT);
            }
        }
    }
    false
}
<|endoftext|>


<commit_before>use super::{Field, Type, TypeExt};

use bitflags::bitflags;

bitflags! {
    #[derive(Deserialize, Serialize)]
    pub struct Flags: u8 {
        const HAS_VARIABLES = 1 << 0;
        const HAS_SKOLEMS = 1 << 1;
        const HAS_GENERICS = 1 << 2;
        const HAS_FORALL = 1 << 3;
        const HAS_IDENTS = 1 << 4;

        const NEEDS_GENERALIZE =
            Flags::HAS_VARIABLES.bits | Flags::HAS_SKOLEMS.bits;
    }
}

trait AddFlags {
    fn add_flags(&self, flags: &mut Flags);
}

impl<T> AddFlags for [T]
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        for t in self {
            t.add_flags(flags);
        }
    }
}

impl<Id, T> AddFlags for Field<Id, T>
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        self.typ.add_flags(flags);
    }
}

impl<T> AddFlags for T
where
    T: TypeExt,
{
    fn add_flags(&self, flags: &mut Flags) {
        *flags |= self.flags()
    }
}

impl<Id, T> AddFlags for Type<Id, T>
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        match self {
            Type::Function(_, arg, ret) => {
                arg.add_flags(flags);
                ret.add_flags(flags);
            }
            Type::App(ref f, ref args) => {
                f.add_flags(flags);
                args.add_flags(flags);
            }
            Type::Record(ref typ)
            | Type::Variant(ref typ)
            | Type::Effect(ref typ)
            | Type::Forall(_, ref typ) => {
                *flags |= Flags::HAS_FORALL;
                typ.add_flags(flags);
            }
            Type::Skolem(_) => *flags |= Flags::HAS_SKOLEMS,
            Type::ExtendRow { fields, rest, .. } => {
                fields.add_flags(flags);
                rest.add_flags(flags);
            }
            Type::Variable(_) => *flags |= Flags::HAS_VARIABLES,
            Type::Generic(_) => *flags |= Flags::HAS_GENERICS,
            Type::Ident(_) => *flags |= Flags::HAS_IDENTS,
            Type::Hole
            | Type::Opaque
            | Type::Error
            | Type::Builtin(..)
            | Type::Projection(_)
            | Type::Alias(_)
            | Type::EmptyRow => (),
        }
    }
}

impl Flags {
    pub fn from_type<Id, T>(typ: &Type<Id, T>) -> Self
    where
        T: TypeExt,
    {
        let mut flags = Flags::empty();
        typ.add_flags(&mut flags);
        flags
    }
}
<commit_msg>Add missing serde conf attribute<commit_after>use super::{Field, Type, TypeExt};

use bitflags::bitflags;

bitflags! {
    #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
    pub struct Flags: u8 {
        const HAS_VARIABLES = 1 << 0;
        const HAS_SKOLEMS = 1 << 1;
        const HAS_GENERICS = 1 << 2;
        const HAS_FORALL = 1 << 3;
        const HAS_IDENTS = 1 << 4;

        const NEEDS_GENERALIZE =
            Flags::HAS_VARIABLES.bits | Flags::HAS_SKOLEMS.bits;
    }
}

trait AddFlags {
    fn add_flags(&self, flags: &mut Flags);
}

impl<T> AddFlags for [T]
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        for t in self {
            t.add_flags(flags);
        }
    }
}

impl<Id, T> AddFlags for Field<Id, T>
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        self.typ.add_flags(flags);
    }
}

impl<T> AddFlags for T
where
    T: TypeExt,
{
    fn add_flags(&self, flags: &mut Flags) {
        *flags |= self.flags()
    }
}

impl<Id, T> AddFlags for Type<Id, T>
where
    T: AddFlags,
{
    fn add_flags(&self, flags: &mut Flags) {
        match self {
            Type::Function(_, arg, ret) => {
                arg.add_flags(flags);
                ret.add_flags(flags);
            }
            Type::App(ref f, ref args) => {
                f.add_flags(flags);
                args.add_flags(flags);
            }
            Type::Record(ref typ)
            | Type::Variant(ref typ)
            | Type::Effect(ref typ)
            | Type::Forall(_, ref typ) => {
                *flags |= Flags::HAS_FORALL;
                typ.add_flags(flags);
            }
            Type::Skolem(_) => *flags |= Flags::HAS_SKOLEMS,
            Type::ExtendRow { fields, rest, .. } => {
                fields.add_flags(flags);
                rest.add_flags(flags);
            }
            Type::Variable(_) => *flags |= Flags::HAS_VARIABLES,
            Type::Generic(_) => *flags |= Flags::HAS_GENERICS,
            Type::Ident(_) => *flags |= Flags::HAS_IDENTS,
            Type::Hole
            | Type::Opaque
            | Type::Error
            | Type::Builtin(..)
            | Type::Projection(_)
            | Type::Alias(_)
            | Type::EmptyRow => (),
        }
    }
}

impl Flags {
    pub fn from_type<Id, T>(typ: &Type<Id, T>) -> Self
    where
        T: TypeExt,
    {
        let mut flags = Flags::empty();
        typ.add_flags(&mut flags);
        flags
    }
}
<|endoftext|>


<commit_before>use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, Local, Location};

use crate::{AnalysisDomain, Backward, GenKill, GenKillAnalysis};

/// A [live-variable dataflow analysis][liveness].
///
/// This analysis considers references as being used only at the point of the
/// borrow. In other words, this analysis does not track uses because of references that already
/// exist. See [this `mir-dataflow` test][flow-test] for an example. You almost never want to use
/// this analysis without also looking at the results of [`MaybeBorrowedLocals`].
///
/// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals
/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs
/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
pub struct MaybeLiveLocals;

impl MaybeLiveLocals {
    fn transfer_function<T>(&self, trans: &'a mut T) -> TransferFunction<'a, T> {
        TransferFunction(trans)
    }
}

impl AnalysisDomain<'tcx> for MaybeLiveLocals {
    type Domain = BitSet<Local>;
    type Direction = Backward;

    const NAME: &'static str = "liveness";

    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
        // bottom = not live
        BitSet::new_empty(body.local_decls.len())
    }

    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
        // No variables are live until we observe a use
    }
}

impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
    type Idx = Local;

    fn statement_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        statement: &mir::Statement<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_statement(statement, location);
    }

    fn terminator_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        terminator: &mir::Terminator<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_terminator(terminator, location);
    }

    fn call_return_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        _block: mir::BasicBlock,
        _func: &mir::Operand<'tcx>,
        _args: &[mir::Operand<'tcx>],
        dest_place: mir::Place<'tcx>,
    ) {
        if let Some(local) = dest_place.as_local() {
            trans.kill(local);
        }
    }

    fn yield_resume_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        _resume_block: mir::BasicBlock,
        resume_place: mir::Place<'tcx>,
    ) {
        if let Some(local) = resume_place.as_local() {
            trans.kill(local);
        }
    }
}

struct TransferFunction<'a, T>(&'a mut T);

impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
where
    T: GenKill<Local>,
{
    fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
        let mir::Place { projection, local } = *place;

        // We purposefully do not call `super_place` here to avoid calling `visit_local` for this
        // place with one of the `Projection` variants of `PlaceContext`.
        self.visit_projection(place.as_ref(), context, location);

        match DefUse::for_place(context) {
            // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use.
            Some(_) if place.is_indirect() => self.0.gen(local),

            Some(DefUse::Def) if projection.is_empty() => self.0.kill(local),
            Some(DefUse::Use) => self.0.gen(local),
            _ => {}
        }
    }

    fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
        // Because we do not call `super_place` above, `visit_local` is only called for locals that
        // do not appear as part of  a `Place` in the MIR. This handles cases like the implicit use
        // of the return place in a `Return` terminator or the index in an `Index` projection.
        match DefUse::for_place(context) {
            Some(DefUse::Def) => self.0.kill(local),
            Some(DefUse::Use) => self.0.gen(local),
            _ => {}
        }
    }
}

#[derive(Eq, PartialEq, Clone)]
enum DefUse {
    Def,
    Use,
}

impl DefUse {
    fn for_place(context: PlaceContext) -> Option<DefUse> {
        match context {
            PlaceContext::NonUse(_) => None,

            PlaceContext::MutatingUse(MutatingUseContext::Store) => Some(DefUse::Def),

            // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the
            // destination place for a `Call` return or `Yield` resume respectively. Since this is
            // only a `Def` when the function returns successfully, we handle this case separately
            // in `call_return_effect` above.
            PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None,

            // All other contexts are uses...
            PlaceContext::MutatingUse(
                MutatingUseContext::AddressOf
                | MutatingUseContext::AsmOutput
                | MutatingUseContext::Borrow
                | MutatingUseContext::Drop
                | MutatingUseContext::Retag,
            )
            | PlaceContext::NonMutatingUse(
                NonMutatingUseContext::AddressOf
                | NonMutatingUseContext::Copy
                | NonMutatingUseContext::Inspect
                | NonMutatingUseContext::Move
                | NonMutatingUseContext::ShallowBorrow
                | NonMutatingUseContext::SharedBorrow
                | NonMutatingUseContext::UniqueBorrow,
            ) => Some(DefUse::Use),

            PlaceContext::MutatingUse(MutatingUseContext::Projection)
            | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
                unreachable!("A projection could be a def or a use and must be handled separately")
            }
        }
    }
}
<commit_msg>Discuss field-sensitivity and enums in context of `MaybeLiveLocals`<commit_after>use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, Local, Location};

use crate::{AnalysisDomain, Backward, GenKill, GenKillAnalysis};

/// A [live-variable dataflow analysis][liveness].
///
/// This analysis considers references as being used only at the point of the
/// borrow. In other words, this analysis does not track uses because of references that already
/// exist. See [this `mir-dataflow` test][flow-test] for an example. You almost never want to use
/// this analysis without also looking at the results of [`MaybeBorrowedLocals`].
///
/// ## Field-(in)sensitivity
///
/// As the name suggests, this analysis is field insensitive. If a projection of a variable `x` is
/// assigned to (e.g. `x.0 = 42`), it does not "define" `x` as far as liveness is concerned. In fact,
/// such an assignment is currently marked as a "use" of `x` in an attempt to be maximally
/// conservative.
///
/// ## Enums and `SetDiscriminant`
///
/// Assigning a literal value to an `enum` (e.g. `Option<i32>`), does not result in a simple
/// assignment of the form `_1 = /*...*/` in the MIR. For example, the following assignment to `x`:
///
/// ```
/// x = Some(4);
/// ```
///
/// compiles to this MIR
///
/// ```
/// ((_1 as Some).0: i32) = const 4_i32;
/// discriminant(_1) = 1;
/// ```
///
/// However, `MaybeLiveLocals` **does** mark `x` (`_1`) as "killed" after a statement like this.
/// That's because it treats the `SetDiscriminant` operation as a definition of `x`, even though
/// the writes that actually initialized the locals happened earlier.
///
/// This makes `MaybeLiveLocals` unsuitable for certain classes of optimization normally associated
/// with a live variables analysis, notably dead-store elimination. It's a dirty hack, but it works
/// okay for the generator state transform (currently the main consumuer of this analysis).
///
/// [`MaybeBorrowedLocals`]: super::MaybeBorrowedLocals
/// [flow-test]: https://github.com/rust-lang/rust/blob/a08c47310c7d49cbdc5d7afb38408ba519967ecd/src/test/ui/mir-dataflow/liveness-ptr.rs
/// [liveness]: https://en.wikipedia.org/wiki/Live_variable_analysis
pub struct MaybeLiveLocals;

impl MaybeLiveLocals {
    fn transfer_function<T>(&self, trans: &'a mut T) -> TransferFunction<'a, T> {
        TransferFunction(trans)
    }
}

impl AnalysisDomain<'tcx> for MaybeLiveLocals {
    type Domain = BitSet<Local>;
    type Direction = Backward;

    const NAME: &'static str = "liveness";

    fn bottom_value(&self, body: &mir::Body<'tcx>) -> Self::Domain {
        // bottom = not live
        BitSet::new_empty(body.local_decls.len())
    }

    fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut Self::Domain) {
        // No variables are live until we observe a use
    }
}

impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
    type Idx = Local;

    fn statement_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        statement: &mir::Statement<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_statement(statement, location);
    }

    fn terminator_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        terminator: &mir::Terminator<'tcx>,
        location: Location,
    ) {
        self.transfer_function(trans).visit_terminator(terminator, location);
    }

    fn call_return_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        _block: mir::BasicBlock,
        _func: &mir::Operand<'tcx>,
        _args: &[mir::Operand<'tcx>],
        dest_place: mir::Place<'tcx>,
    ) {
        if let Some(local) = dest_place.as_local() {
            trans.kill(local);
        }
    }

    fn yield_resume_effect(
        &self,
        trans: &mut impl GenKill<Self::Idx>,
        _resume_block: mir::BasicBlock,
        resume_place: mir::Place<'tcx>,
    ) {
        if let Some(local) = resume_place.as_local() {
            trans.kill(local);
        }
    }
}

struct TransferFunction<'a, T>(&'a mut T);

impl<'tcx, T> Visitor<'tcx> for TransferFunction<'_, T>
where
    T: GenKill<Local>,
{
    fn visit_place(&mut self, place: &mir::Place<'tcx>, context: PlaceContext, location: Location) {
        let mir::Place { projection, local } = *place;

        // We purposefully do not call `super_place` here to avoid calling `visit_local` for this
        // place with one of the `Projection` variants of `PlaceContext`.
        self.visit_projection(place.as_ref(), context, location);

        match DefUse::for_place(context) {
            // Treat derefs as a use of the base local. `*p = 4` is not a def of `p` but a use.
            Some(_) if place.is_indirect() => self.0.gen(local),

            Some(DefUse::Def) if projection.is_empty() => self.0.kill(local),
            Some(DefUse::Use) => self.0.gen(local),
            _ => {}
        }
    }

    fn visit_local(&mut self, &local: &Local, context: PlaceContext, _: Location) {
        // Because we do not call `super_place` above, `visit_local` is only called for locals that
        // do not appear as part of  a `Place` in the MIR. This handles cases like the implicit use
        // of the return place in a `Return` terminator or the index in an `Index` projection.
        match DefUse::for_place(context) {
            Some(DefUse::Def) => self.0.kill(local),
            Some(DefUse::Use) => self.0.gen(local),
            _ => {}
        }
    }
}

#[derive(Eq, PartialEq, Clone)]
enum DefUse {
    Def,
    Use,
}

impl DefUse {
    fn for_place(context: PlaceContext) -> Option<DefUse> {
        match context {
            PlaceContext::NonUse(_) => None,

            PlaceContext::MutatingUse(MutatingUseContext::Store) => Some(DefUse::Def),

            // `MutatingUseContext::Call` and `MutatingUseContext::Yield` indicate that this is the
            // destination place for a `Call` return or `Yield` resume respectively. Since this is
            // only a `Def` when the function returns successfully, we handle this case separately
            // in `call_return_effect` above.
            PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None,

            // All other contexts are uses...
            PlaceContext::MutatingUse(
                MutatingUseContext::AddressOf
                | MutatingUseContext::AsmOutput
                | MutatingUseContext::Borrow
                | MutatingUseContext::Drop
                | MutatingUseContext::Retag,
            )
            | PlaceContext::NonMutatingUse(
                NonMutatingUseContext::AddressOf
                | NonMutatingUseContext::Copy
                | NonMutatingUseContext::Inspect
                | NonMutatingUseContext::Move
                | NonMutatingUseContext::ShallowBorrow
                | NonMutatingUseContext::SharedBorrow
                | NonMutatingUseContext::UniqueBorrow,
            ) => Some(DefUse::Use),

            PlaceContext::MutatingUse(MutatingUseContext::Projection)
            | PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection) => {
                unreachable!("A projection could be a def or a use and must be handled separately")
            }
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y"
                    spec="https://drafts.csswg.org/css-overflow/#propdef-overflow">
    use properties::longhands::{overflow_x, overflow_y};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let overflow = try!(overflow_x::parse(context, input));
        Ok(Longhands {
            overflow_x: overflow,
            overflow_y: overflow_y::SpecifiedValue(overflow),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if *self.overflow_x == self.overflow_y.0 {
                self.overflow_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>

macro_rules! try_parse_one {
    ($input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try($prop_module::SingleSpecifiedValue::parse) {
                $var = Some(value);
                continue;
            }
        }
    };
    ($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try(|i| {
                $prop_module::SingleSpecifiedValue::parse($context, i)
            }) {
                $var = Some(value);
                continue;
            }
        }
    };
}

<%helpers:shorthand name="transition" extra_prefixes="moz webkit"
                    sub_properties="transition-property transition-duration
                                    transition-timing-function
                                    transition-delay"
                    spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
    use parser::Parse;
    use properties::longhands::{transition_delay, transition_duration, transition_property};
    use properties::longhands::{transition_timing_function};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleTransition {
            transition_property: transition_property::SingleSpecifiedValue,
            transition_duration: transition_duration::SingleSpecifiedValue,
            transition_timing_function: transition_timing_function::SingleSpecifiedValue,
            transition_delay: transition_delay::SingleSpecifiedValue,
        }

        fn parse_one_transition(context: &ParserContext, input: &mut Parser) -> Result<SingleTransition,()> {
            let (mut property, mut duration) = (None, None);
            let (mut timing_function, mut delay) = (None, None);
            loop {
                try_parse_one!(input, property, transition_property);
                try_parse_one!(context, input, duration, transition_duration);
                try_parse_one!(context, input, timing_function, transition_timing_function);
                try_parse_one!(context, input, delay, transition_delay);

                break
            }

            if let Some(property) = property {
                Ok(SingleTransition {
                    transition_property: property,
                    transition_duration:
                        duration.unwrap_or_else(transition_duration::single_value::get_initial_value),
                    transition_timing_function:
                        timing_function.unwrap_or_else(transition_timing_function::single_value
                                                                                ::get_initial_specified_value),
                    transition_delay:
                        delay.unwrap_or_else(transition_delay::single_value::get_initial_value),
                })
            } else {
                Err(())
            }
        }

        let (mut properties, mut durations) = (Vec::new(), Vec::new());
        let (mut timing_functions, mut delays) = (Vec::new(), Vec::new());

        if input.try(|input| input.expect_ident_matching("none")).is_err() {
            let results = try!(input.parse_comma_separated(|i| parse_one_transition(context, i)));
            for result in results {
                properties.push(result.transition_property);
                durations.push(result.transition_duration);
                timing_functions.push(result.transition_timing_function);
                delays.push(result.transition_delay);
            }
        }

        Ok(Longhands {
            transition_property: transition_property::SpecifiedValue(properties),
            transition_duration: transition_duration::SpecifiedValue(durations),
            transition_timing_function:
                transition_timing_function::SpecifiedValue(timing_functions),
            transition_delay: transition_delay::SpecifiedValue(delays),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.transition_property.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in "property duration delay timing_function".split():
                if len != self.transition_${name}.0.len() {
                    return Ok(());
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    write!(dest, ", ")?;
                }
                self.transition_property.0[i].to_css(dest)?;
                % for name in "duration timing_function delay".split():
                    dest.write_str(" ")?;
                    self.transition_${name}.0[i].to_css(dest)?;
                % endfor
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="animation" extra_prefixes="moz webkit"
                    sub_properties="animation-name animation-duration
                                    animation-timing-function animation-delay
                                    animation-iteration-count animation-direction
                                    animation-fill-mode animation-play-state"
                    allowed_in_keyframe_block="False"
                    spec="https://drafts.csswg.org/css-animations/#propdef-animation">
    use parser::Parse;
    use properties::longhands::{animation_name, animation_duration, animation_timing_function};
    use properties::longhands::{animation_delay, animation_iteration_count, animation_direction};
    use properties::longhands::{animation_fill_mode, animation_play_state};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleAnimation {
            animation_name: animation_name::SingleSpecifiedValue,
            animation_duration: animation_duration::SingleSpecifiedValue,
            animation_timing_function: animation_timing_function::SingleSpecifiedValue,
            animation_delay: animation_delay::SingleSpecifiedValue,
            animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
            animation_direction: animation_direction::SingleSpecifiedValue,
            animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
            animation_play_state: animation_play_state::SingleSpecifiedValue,
        }

        fn parse_one_animation(context: &ParserContext, input: &mut Parser) -> Result<SingleAnimation,()> {
            let mut duration = None;
            let mut timing_function = None;
            let mut delay = None;
            let mut iteration_count = None;
            let mut direction = None;
            let mut fill_mode = None;
            let mut play_state = None;
            let mut name = None;

            // NB: Name must be the last one here so that keywords valid for other
            // longhands are not interpreted as names.
            //
            // Also, duration must be before delay, see
            // https://drafts.csswg.org/css-animations/#typedef-single-animation
            loop {
                try_parse_one!(context, input, duration, animation_duration);
                try_parse_one!(context, input, timing_function, animation_timing_function);
                try_parse_one!(context, input, delay, animation_delay);
                try_parse_one!(context, input, iteration_count, animation_iteration_count);
                try_parse_one!(input, direction, animation_direction);
                try_parse_one!(input, fill_mode, animation_fill_mode);
                try_parse_one!(input, play_state, animation_play_state);
                try_parse_one!(context, input, name, animation_name);

                break
            }

            if let Some(name) = name {
                Ok(SingleAnimation {
                    animation_name: name,
                    animation_duration:
                        duration.unwrap_or_else(animation_duration::single_value::get_initial_value),
                    animation_timing_function:
                        timing_function.unwrap_or_else(animation_timing_function::single_value
                                                                                ::get_initial_specified_value),
                    animation_delay:
                        delay.unwrap_or_else(animation_delay::single_value::get_initial_value),
                    animation_iteration_count:
                        iteration_count.unwrap_or_else(animation_iteration_count::single_value::get_initial_value),
                    animation_direction:
                        direction.unwrap_or_else(animation_direction::single_value::get_initial_value),
                    animation_fill_mode:
                        fill_mode.unwrap_or_else(animation_fill_mode::single_value::get_initial_value),
                    animation_play_state:
                        play_state.unwrap_or_else(animation_play_state::single_value::get_initial_value),
                })
            } else {
                Err(())
            }
        }

        let mut names = vec![];
        let mut durations = vec![];
        let mut timing_functions = vec![];
        let mut delays = vec![];
        let mut iteration_counts = vec![];
        let mut directions = vec![];
        let mut fill_modes = vec![];
        let mut play_states = vec![];

        if input.try(|input| input.expect_ident_matching("none")).is_err() {
            let results = try!(input.parse_comma_separated(|i| parse_one_animation(context, i)));
            for result in results.into_iter() {
                names.push(result.animation_name);
                durations.push(result.animation_duration);
                timing_functions.push(result.animation_timing_function);
                delays.push(result.animation_delay);
                iteration_counts.push(result.animation_iteration_count);
                directions.push(result.animation_direction);
                fill_modes.push(result.animation_fill_mode);
                play_states.push(result.animation_play_state);
            }
        }

        Ok(Longhands {
            animation_name: animation_name::SpecifiedValue(names),
            animation_duration: animation_duration::SpecifiedValue(durations),
            animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions),
            animation_delay: animation_delay::SpecifiedValue(delays),
            animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts),
            animation_direction: animation_direction::SpecifiedValue(directions),
            animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes),
            animation_play_state: animation_play_state::SpecifiedValue(play_states),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.animation_name.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            <%
                subproperties = "duration timing_function delay direction \
                                 fill_mode iteration_count play_state".split()
            %>

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in subproperties:
                if len != self.animation_${name}.0.len() {
                    return Ok(())
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    try!(write!(dest, ", "));
                }

                % for name in subproperties:
                    self.animation_${name}.0[i].to_css(dest)?;
                    dest.write_str(" ")?;
                % endfor
                self.animation_name.0[i].to_css(dest)?;
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="scroll-snap-type" products="gecko"
                    sub_properties="scroll-snap-type-x scroll-snap-type-y"
                    spec="https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-snap-type">
    use properties::longhands::scroll_snap_type_x;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let result = try!(scroll_snap_type_x::parse(context, input));
        Ok(Longhands {
            scroll_snap_type_x: result,
            scroll_snap_type_y: result,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        // Serializes into the single keyword value if both scroll-snap-type and scroll-snap-type-y are same.
        // Otherwise into an empty string. This is done to match Gecko's behaviour.
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.scroll_snap_type_x == self.scroll_snap_type_y {
                self.scroll_snap_type_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>
<commit_msg>Allow animation-name to be omitted from shorthand<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="overflow" sub_properties="overflow-x overflow-y"
                    spec="https://drafts.csswg.org/css-overflow/#propdef-overflow">
    use properties::longhands::{overflow_x, overflow_y};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let overflow = try!(overflow_x::parse(context, input));
        Ok(Longhands {
            overflow_x: overflow,
            overflow_y: overflow_y::SpecifiedValue(overflow),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if *self.overflow_x == self.overflow_y.0 {
                self.overflow_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>

macro_rules! try_parse_one {
    ($input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try($prop_module::SingleSpecifiedValue::parse) {
                $var = Some(value);
                continue;
            }
        }
    };
    ($context: expr, $input: expr, $var: ident, $prop_module: ident) => {
        if $var.is_none() {
            if let Ok(value) = $input.try(|i| {
                $prop_module::SingleSpecifiedValue::parse($context, i)
            }) {
                $var = Some(value);
                continue;
            }
        }
    };
}

<%helpers:shorthand name="transition" extra_prefixes="moz webkit"
                    sub_properties="transition-property transition-duration
                                    transition-timing-function
                                    transition-delay"
                    spec="https://drafts.csswg.org/css-transitions/#propdef-transition">
    use parser::Parse;
    use properties::longhands::{transition_delay, transition_duration, transition_property};
    use properties::longhands::{transition_timing_function};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleTransition {
            transition_property: transition_property::SingleSpecifiedValue,
            transition_duration: transition_duration::SingleSpecifiedValue,
            transition_timing_function: transition_timing_function::SingleSpecifiedValue,
            transition_delay: transition_delay::SingleSpecifiedValue,
        }

        fn parse_one_transition(context: &ParserContext, input: &mut Parser) -> Result<SingleTransition,()> {
            let (mut property, mut duration) = (None, None);
            let (mut timing_function, mut delay) = (None, None);
            loop {
                try_parse_one!(input, property, transition_property);
                try_parse_one!(context, input, duration, transition_duration);
                try_parse_one!(context, input, timing_function, transition_timing_function);
                try_parse_one!(context, input, delay, transition_delay);

                break
            }

            if let Some(property) = property {
                Ok(SingleTransition {
                    transition_property: property,
                    transition_duration:
                        duration.unwrap_or_else(transition_duration::single_value::get_initial_value),
                    transition_timing_function:
                        timing_function.unwrap_or_else(transition_timing_function::single_value
                                                                                ::get_initial_specified_value),
                    transition_delay:
                        delay.unwrap_or_else(transition_delay::single_value::get_initial_value),
                })
            } else {
                Err(())
            }
        }

        let (mut properties, mut durations) = (Vec::new(), Vec::new());
        let (mut timing_functions, mut delays) = (Vec::new(), Vec::new());

        if input.try(|input| input.expect_ident_matching("none")).is_err() {
            let results = try!(input.parse_comma_separated(|i| parse_one_transition(context, i)));
            for result in results {
                properties.push(result.transition_property);
                durations.push(result.transition_duration);
                timing_functions.push(result.transition_timing_function);
                delays.push(result.transition_delay);
            }
        }

        Ok(Longhands {
            transition_property: transition_property::SpecifiedValue(properties),
            transition_duration: transition_duration::SpecifiedValue(durations),
            transition_timing_function:
                transition_timing_function::SpecifiedValue(timing_functions),
            transition_delay: transition_delay::SpecifiedValue(delays),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.transition_property.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in "property duration delay timing_function".split():
                if len != self.transition_${name}.0.len() {
                    return Ok(());
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    write!(dest, ", ")?;
                }
                self.transition_property.0[i].to_css(dest)?;
                % for name in "duration timing_function delay".split():
                    dest.write_str(" ")?;
                    self.transition_${name}.0[i].to_css(dest)?;
                % endfor
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="animation" extra_prefixes="moz webkit"
                    sub_properties="animation-name animation-duration
                                    animation-timing-function animation-delay
                                    animation-iteration-count animation-direction
                                    animation-fill-mode animation-play-state"
                    allowed_in_keyframe_block="False"
                    spec="https://drafts.csswg.org/css-animations/#propdef-animation">
    use parser::Parse;
    use properties::longhands::{animation_name, animation_duration, animation_timing_function};
    use properties::longhands::{animation_delay, animation_iteration_count, animation_direction};
    use properties::longhands::{animation_fill_mode, animation_play_state};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        struct SingleAnimation {
            animation_name: animation_name::SingleSpecifiedValue,
            animation_duration: animation_duration::SingleSpecifiedValue,
            animation_timing_function: animation_timing_function::SingleSpecifiedValue,
            animation_delay: animation_delay::SingleSpecifiedValue,
            animation_iteration_count: animation_iteration_count::SingleSpecifiedValue,
            animation_direction: animation_direction::SingleSpecifiedValue,
            animation_fill_mode: animation_fill_mode::SingleSpecifiedValue,
            animation_play_state: animation_play_state::SingleSpecifiedValue,
        }

        fn parse_one_animation(context: &ParserContext, input: &mut Parser) -> Result<SingleAnimation,()> {
            let mut duration = None;
            let mut timing_function = None;
            let mut delay = None;
            let mut iteration_count = None;
            let mut direction = None;
            let mut fill_mode = None;
            let mut play_state = None;
            let mut name = None;

            // NB: Name must be the last one here so that keywords valid for other
            // longhands are not interpreted as names.
            //
            // Also, duration must be before delay, see
            // https://drafts.csswg.org/css-animations/#typedef-single-animation
            loop {
                try_parse_one!(context, input, duration, animation_duration);
                try_parse_one!(context, input, timing_function, animation_timing_function);
                try_parse_one!(context, input, delay, animation_delay);
                try_parse_one!(context, input, iteration_count, animation_iteration_count);
                try_parse_one!(input, direction, animation_direction);
                try_parse_one!(input, fill_mode, animation_fill_mode);
                try_parse_one!(input, play_state, animation_play_state);
                try_parse_one!(context, input, name, animation_name);

                break
            }

            Ok(SingleAnimation {
                animation_name:
                    name.unwrap_or_else(animation_name::single_value::get_initial_specified_value),
                animation_duration:
                    duration.unwrap_or_else(animation_duration::single_value::get_initial_value),
                animation_timing_function:
                    timing_function.unwrap_or_else(animation_timing_function::single_value
                                                                            ::get_initial_specified_value),
                animation_delay:
                    delay.unwrap_or_else(animation_delay::single_value::get_initial_value),
                animation_iteration_count:
                    iteration_count.unwrap_or_else(animation_iteration_count::single_value::get_initial_value),
                animation_direction:
                    direction.unwrap_or_else(animation_direction::single_value::get_initial_value),
                animation_fill_mode:
                    fill_mode.unwrap_or_else(animation_fill_mode::single_value::get_initial_value),
                animation_play_state:
                    play_state.unwrap_or_else(animation_play_state::single_value::get_initial_value),
            })
        }

        let mut names = vec![];
        let mut durations = vec![];
        let mut timing_functions = vec![];
        let mut delays = vec![];
        let mut iteration_counts = vec![];
        let mut directions = vec![];
        let mut fill_modes = vec![];
        let mut play_states = vec![];

        if input.try(|input| input.expect_ident_matching("none")).is_err() {
            let results = try!(input.parse_comma_separated(|i| parse_one_animation(context, i)));
            for result in results.into_iter() {
                names.push(result.animation_name);
                durations.push(result.animation_duration);
                timing_functions.push(result.animation_timing_function);
                delays.push(result.animation_delay);
                iteration_counts.push(result.animation_iteration_count);
                directions.push(result.animation_direction);
                fill_modes.push(result.animation_fill_mode);
                play_states.push(result.animation_play_state);
            }
        }

        Ok(Longhands {
            animation_name: animation_name::SpecifiedValue(names),
            animation_duration: animation_duration::SpecifiedValue(durations),
            animation_timing_function: animation_timing_function::SpecifiedValue(timing_functions),
            animation_delay: animation_delay::SpecifiedValue(delays),
            animation_iteration_count: animation_iteration_count::SpecifiedValue(iteration_counts),
            animation_direction: animation_direction::SpecifiedValue(directions),
            animation_fill_mode: animation_fill_mode::SpecifiedValue(fill_modes),
            animation_play_state: animation_play_state::SpecifiedValue(play_states),
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            let len = self.animation_name.0.len();
            // There should be at least one declared value
            if len == 0 {
                return Ok(());
            }

            <%
                subproperties = "duration timing_function delay direction \
                                 fill_mode iteration_count play_state".split()
            %>

            // If any value list length is differs then we don't do a shorthand serialization
            // either.
            % for name in subproperties:
                if len != self.animation_${name}.0.len() {
                    return Ok(())
                }
            % endfor

            for i in 0..len {
                if i != 0 {
                    try!(write!(dest, ", "));
                }

                % for name in subproperties:
                    self.animation_${name}.0[i].to_css(dest)?;
                    dest.write_str(" ")?;
                % endfor
                self.animation_name.0[i].to_css(dest)?;
            }
            Ok(())
        }
    }
</%helpers:shorthand>

<%helpers:shorthand name="scroll-snap-type" products="gecko"
                    sub_properties="scroll-snap-type-x scroll-snap-type-y"
                    spec="https://drafts.csswg.org/css-scroll-snap/#propdef-scroll-snap-type">
    use properties::longhands::scroll_snap_type_x;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let result = try!(scroll_snap_type_x::parse(context, input));
        Ok(Longhands {
            scroll_snap_type_x: result,
            scroll_snap_type_y: result,
        })
    }

    impl<'a> ToCss for LonghandsToSerialize<'a>  {
        // Serializes into the single keyword value if both scroll-snap-type and scroll-snap-type-y are same.
        // Otherwise into an empty string. This is done to match Gecko's behaviour.
        fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if self.scroll_snap_type_x == self.scroll_snap_type_y {
                self.scroll_snap_type_x.to_css(dest)
            } else {
                Ok(())
            }
        }
    }
</%helpers:shorthand>
<|endoftext|>


<commit_before>use utils::bigint::M256;
use utils::gas::Gas;
use super::commit::{AccountState, BlockhashState};
use super::errors::{RequireError, MachineError};
use super::{Stack, Context, BlockHeader, Patch, PC, Storage, Memory};

pub mod cost;
pub mod run;

/// A VM state without PC.
pub struct State<M, S> {
    pub memory: M,
    pub stack: Stack,

    pub context: Context,
    pub block: BlockHeader,
    pub patch: Patch,

    pub memory_gas: Gas,
    pub used_gas: Gas,
    pub refuneded_gas: Gas,

    pub account_state: AccountState<S>,
    pub blockhash_state: BlockhashState,
}

/// A VM state with PC.
pub struct Machine<M, S> {
    state: State<M, S>,
    pc: PC,
    status: Status,
}

pub enum Status {
    Running,
    ExitedOk(Vec<u8>),
    ExitedError(MachineError),
    InvokeCall(Context, (M256, M256)),
}

impl<M: Memory + Default, S: Storage + Default + Clone> Machine<M, S> {
    pub fn step(&mut self) -> Result<(), RequireError> {
        unimplemented!()
    }
}
<commit_msg>Problem: no account commitment in Machine Solution: add it<commit_after>use utils::bigint::M256;
use utils::gas::Gas;
use super::commit::{AccountState, BlockhashState};
use super::errors::{RequireError, MachineError, CommitError};
use super::{Stack, Context, BlockHeader, Patch, PC, Storage, Memory, AccountCommitment};

pub mod cost;
pub mod run;

/// A VM state without PC.
pub struct State<M, S> {
    pub memory: M,
    pub stack: Stack,

    pub context: Context,
    pub block: BlockHeader,
    pub patch: Patch,

    pub memory_gas: Gas,
    pub used_gas: Gas,
    pub refuneded_gas: Gas,

    pub account_state: AccountState<S>,
    pub blockhash_state: BlockhashState,
}

/// A VM state with PC.
pub struct Machine<M, S> {
    state: State<M, S>,
    pc: PC,
    status: Status,
}

pub enum Status {
    Running,
    ExitedOk(Vec<u8>),
    ExitedError(MachineError),
    InvokeCall(Context, (M256, M256)),
}

impl<M: Memory + Default, S: Storage + Default + Clone> Machine<M, S> {
    pub fn commit_account(&mut self, commitment: AccountCommitment<S>) -> Result<(), CommitError> {
        self.state.account_state.commit(commitment)
    }

    pub fn commit_blockhash(&mut self, number: M256, hash: M256) -> Result<(), CommitError> {
        self.state.blockhash_state.commit(number, hash)
    }

    pub fn step(&mut self) -> Result<(), RequireError> {
        unimplemented!()
    }
}
<|endoftext|>


<commit_before>// Code for creating styled buffers

use crate::snippet::{Style, StyledString};

#[derive(Debug)]
pub struct StyledBuffer {
    text: Vec<Vec<StyledChar>>,
}

#[derive(Debug)]
struct StyledChar {
    chr: char,
    style: Style,
}

impl StyledChar {
    fn new(chr: char, style: Style) -> Self {
        StyledChar { chr, style }
    }
}

impl Default for StyledChar {
    fn default() -> Self {
        StyledChar::new(' ', Style::NoStyle)
    }
}

impl StyledBuffer {
    pub fn new() -> StyledBuffer {
        StyledBuffer { text: vec![] }
    }

    /// Returns content of `StyledBuffer` splitted by lines and line styles
    pub fn render(&self) -> Vec<Vec<StyledString>> {
        // Tabs are assumed to have been replaced by spaces in calling code.
        debug_assert!(self.text.iter().all(|r| !r.iter().any(|sc| sc.chr == '\t')));

        let mut output: Vec<Vec<StyledString>> = vec![];
        let mut styled_vec: Vec<StyledString> = vec![];

        for styled_row in &self.text {
            let mut current_style = Style::NoStyle;
            let mut current_text = String::new();

            for sc in styled_row {
                if sc.style != current_style {
                    if !current_text.is_empty() {
                        styled_vec.push(StyledString { text: current_text, style: current_style });
                    }
                    current_style = sc.style;
                    current_text = String::new();
                }
                current_text.push(sc.chr);
            }
            if !current_text.is_empty() {
                styled_vec.push(StyledString { text: current_text, style: current_style });
            }

            // We're done with the row, push and keep going
            output.push(styled_vec);

            styled_vec = vec![];
        }

        output
    }

    fn ensure_lines(&mut self, line: usize) {
        while line >= self.text.len() {
            self.text.push(vec![]);
        }
    }

    /// Sets `chr` with `style` for given `line`, `col`.
    /// If line not exist in `StyledBuffer`, adds lines up to given
    /// and fills last line with spaces and `Style::NoStyle` style
    pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
        self.ensure_lines(line);
        if col < self.text[line].len() {
            self.text[line][col] = StyledChar::new(chr, style);
        } else {
            let mut i = self.text[line].len();
            while i < col {
                self.text[line].push(StyledChar::default());
                i += 1;
            }
            self.text[line].push(StyledChar::new(chr, style));
        }
    }

    /// Sets `string` with `style` for given `line`, starting from `col`.
    /// If line not exist in `StyledBuffer`, adds lines up to given
    /// and fills last line with spaces and `Style::NoStyle` style
    pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
        let mut n = col;
        for c in string.chars() {
            self.putc(line, n, c, style);
            n += 1;
        }
    }

    /// For given `line` inserts `string` with `style` before old content of that line,
    /// adding lines if needed
    pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
        self.ensure_lines(line);
        let string_len = string.chars().count();

        if !self.text[line].is_empty() {
            // Push the old content over to make room for new content
            for _ in 0..string_len {
                self.text[line].insert(0, StyledChar::default());
            }
        }

        self.puts(line, 0, string, style);
    }

    /// For given `line` inserts `string` with `style` after old content of that line,
    /// adding lines if needed
    pub fn append(&mut self, line: usize, string: &str, style: Style) {
        if line >= self.text.len() {
            self.puts(line, 0, string, style);
        } else {
            let col = self.text[line].len();
            self.puts(line, col, string, style);
        }
    }

    pub fn num_lines(&self) -> usize {
        self.text.len()
    }

    /// Set `style` for `line`, `col_start..col_end` range if:
    /// 1. That line and column range exist in `StyledBuffer`
    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
    pub fn set_style_range(
        &mut self,
        line: usize,
        col_start: usize,
        col_end: usize,
        style: Style,
        overwrite: bool,
    ) {
        for col in col_start..col_end {
            self.set_style(line, col, style, overwrite);
        }
    }

    /// Set `style` for `line`, `col` if:
    /// 1. That line and column exist in `StyledBuffer`
    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
    pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
        if let Some(ref mut line) = self.text.get_mut(line) {
            if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
                if overwrite || *s == Style::NoStyle || *s == Style::Quotation {
                    *s = style;
                }
            }
        }
    }
}
<commit_msg>rename StyledBuffer.text to lines<commit_after>// Code for creating styled buffers

use crate::snippet::{Style, StyledString};

#[derive(Debug)]
pub struct StyledBuffer {
    lines: Vec<Vec<StyledChar>>,
}

#[derive(Debug)]
struct StyledChar {
    chr: char,
    style: Style,
}

impl StyledChar {
    fn new(chr: char, style: Style) -> Self {
        StyledChar { chr, style }
    }
}

impl Default for StyledChar {
    fn default() -> Self {
        StyledChar::new(' ', Style::NoStyle)
    }
}

impl StyledBuffer {
    pub fn new() -> StyledBuffer {
        StyledBuffer { lines: vec![] }
    }

    /// Returns content of `StyledBuffer` splitted by lines and line styles
    pub fn render(&self) -> Vec<Vec<StyledString>> {
        // Tabs are assumed to have been replaced by spaces in calling code.
        debug_assert!(self.lines.iter().all(|r| !r.iter().any(|sc| sc.chr == '\t')));

        let mut output: Vec<Vec<StyledString>> = vec![];
        let mut styled_vec: Vec<StyledString> = vec![];

        for styled_line in &self.lines {
            let mut current_style = Style::NoStyle;
            let mut current_text = String::new();

            for sc in styled_line {
                if sc.style != current_style {
                    if !current_text.is_empty() {
                        styled_vec.push(StyledString { text: current_text, style: current_style });
                    }
                    current_style = sc.style;
                    current_text = String::new();
                }
                current_text.push(sc.chr);
            }
            if !current_text.is_empty() {
                styled_vec.push(StyledString { text: current_text, style: current_style });
            }

            // We're done with the row, push and keep going
            output.push(styled_vec);

            styled_vec = vec![];
        }

        output
    }

    fn ensure_lines(&mut self, line: usize) {
        while line >= self.lines.len() {
            self.lines.push(vec![]);
        }
    }

    /// Sets `chr` with `style` for given `line`, `col`.
    /// If line not exist in `StyledBuffer`, adds lines up to given
    /// and fills last line with spaces and `Style::NoStyle` style
    pub fn putc(&mut self, line: usize, col: usize, chr: char, style: Style) {
        self.ensure_lines(line);
        if col < self.lines[line].len() {
            self.lines[line][col] = StyledChar::new(chr, style);
        } else {
            let mut i = self.lines[line].len();
            while i < col {
                self.lines[line].push(StyledChar::default());
                i += 1;
            }
            self.lines[line].push(StyledChar::new(chr, style));
        }
    }

    /// Sets `string` with `style` for given `line`, starting from `col`.
    /// If line not exist in `StyledBuffer`, adds lines up to given
    /// and fills last line with spaces and `Style::NoStyle` style
    pub fn puts(&mut self, line: usize, col: usize, string: &str, style: Style) {
        let mut n = col;
        for c in string.chars() {
            self.putc(line, n, c, style);
            n += 1;
        }
    }

    /// For given `line` inserts `string` with `style` before old content of that line,
    /// adding lines if needed
    pub fn prepend(&mut self, line: usize, string: &str, style: Style) {
        self.ensure_lines(line);
        let string_len = string.chars().count();

        if !self.lines[line].is_empty() {
            // Push the old content over to make room for new content
            for _ in 0..string_len {
                self.lines[line].insert(0, StyledChar::default());
            }
        }

        self.puts(line, 0, string, style);
    }

    /// For given `line` inserts `string` with `style` after old content of that line,
    /// adding lines if needed
    pub fn append(&mut self, line: usize, string: &str, style: Style) {
        if line >= self.lines.len() {
            self.puts(line, 0, string, style);
        } else {
            let col = self.lines[line].len();
            self.puts(line, col, string, style);
        }
    }

    pub fn num_lines(&self) -> usize {
        self.lines.len()
    }

    /// Set `style` for `line`, `col_start..col_end` range if:
    /// 1. That line and column range exist in `StyledBuffer`
    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
    pub fn set_style_range(
        &mut self,
        line: usize,
        col_start: usize,
        col_end: usize,
        style: Style,
        overwrite: bool,
    ) {
        for col in col_start..col_end {
            self.set_style(line, col, style, overwrite);
        }
    }

    /// Set `style` for `line`, `col` if:
    /// 1. That line and column exist in `StyledBuffer`
    /// 2. `overwrite` is `true` or existing style is `Style::NoStyle` or `Style::Quotation`
    pub fn set_style(&mut self, line: usize, col: usize, style: Style, overwrite: bool) {
        if let Some(ref mut line) = self.lines.get_mut(line) {
            if let Some(StyledChar { style: s, .. }) = line.get_mut(col) {
                if overwrite || *s == Style::NoStyle || *s == Style::Quotation {
                    *s = style;
                }
            }
        }
    }
}
<|endoftext|>