<commit_before>use crate::utils::{
    constants, snippet, snippet_opt, span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then,
};
use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use syntax::ast::*;
use syntax::source_map::Span;
use syntax::visit::{walk_expr, FnKind, Visitor};

declare_clippy_lint! {
    /// **What it does:** Checks for structure field patterns bound to wildcards.
    ///
    /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
    /// the fields that are actually bound.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// let { a: _, b: ref b, c: _ } = ..
    /// ```
    pub UNNEEDED_FIELD_PATTERN,
    style,
    "struct fields bound to a wildcard instead of using `..`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for function arguments having the similar names
    /// differing by an underscore.
    ///
    /// **Why is this bad?** It affects code readability.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(a: i32, _a: i32) {}
    /// ```
    pub DUPLICATE_UNDERSCORE_ARGUMENT,
    style,
    "function arguments having names which only differ by an underscore"
}

declare_clippy_lint! {
    /// **What it does:** Detects closures called in the same expression where they
    /// are defined.
    ///
    /// **Why is this bad?** It is unnecessarily adding to the expression's
    /// complexity.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// (|| 42)()
    /// ```
    pub REDUNDANT_CLOSURE_CALL,
    complexity,
    "throwaway closures called in the expression they are defined"
}

declare_clippy_lint! {
    /// **What it does:** Detects expressions of the form `--x`.
    ///
    /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
    /// decremented.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let mut x = 3;
    /// --x;
    /// ```
    pub DOUBLE_NEG,
    style,
    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
}

declare_clippy_lint! {
    /// **What it does:** Warns on hexadecimal literals with mixed-case letter
    /// digits.
    ///
    /// **Why is this bad?** It looks confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let y = 0x1a9BAcD;
    /// ```
    pub MIXED_CASE_HEX_LITERALS,
    style,
    "hex literals whose letter digits are not consistently upper- or lowercased"
}

declare_clippy_lint! {
    /// **What it does:** Warns if literal suffixes are not separated by an
    /// underscore.
    ///
    /// **Why is this bad?** It is much less readable.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let y = 123832i32;
    /// ```
    pub UNSEPARATED_LITERAL_SUFFIX,
    pedantic,
    "literals whose suffix is not separated by an underscore"
}

declare_clippy_lint! {
    /// **What it does:** Warns if an integral constant literal starts with `0`.
    ///
    /// **Why is this bad?** In some languages (including the infamous C language
    /// and most of its
    /// family), this marks an octal constant. In Rust however, this is a decimal
    /// constant. This could
    /// be confusing for both the writer and a reader of the constant.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// In Rust:
    /// ```rust
    /// fn main() {
    ///     let a = 0123;
    ///     println!("{}", a);
    /// }
    /// ```
    ///
    /// prints `123`, while in C:
    ///
    /// ```c
    /// #include <stdio.h>
    ///
    /// int main() {
    ///     int a = 0123;
    ///     printf("%d\n", a);
    /// }
    /// ```
    ///
    /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
    pub ZERO_PREFIXED_LITERAL,
    complexity,
    "integer literals starting with `0`"
}

declare_clippy_lint! {
    /// **What it does:** Warns if a generic shadows a built-in type.
    ///
    /// **Why is this bad?** This gives surprising type errors.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```ignore
    /// impl<u32> Foo<u32> {
    ///     fn impl_func(&self) -> u32 {
    ///         42
    ///     }
    /// }
    /// ```
    pub BUILTIN_TYPE_SHADOW,
    style,
    "shadowing a builtin type"
}

declare_lint_pass!(MiscEarlyLints => [
    UNNEEDED_FIELD_PATTERN,
    DUPLICATE_UNDERSCORE_ARGUMENT,
    REDUNDANT_CLOSURE_CALL,
    DOUBLE_NEG,
    MIXED_CASE_HEX_LITERALS,
    UNSEPARATED_LITERAL_SUFFIX,
    ZERO_PREFIXED_LITERAL,
    BUILTIN_TYPE_SHADOW
]);

// Used to find `return` statements or equivalents e.g., `?`
struct ReturnVisitor {
    found_return: bool,
}

impl ReturnVisitor {
    fn new() -> Self {
        Self { found_return: false }
    }
}

impl<'ast> Visitor<'ast> for ReturnVisitor {
    fn visit_expr(&mut self, ex: &'ast Expr) {
        if let ExprKind::Ret(_) = ex.node {
            self.found_return = true;
        } else if let ExprKind::Try(_) = ex.node {
            self.found_return = true;
        }

        walk_expr(self, ex)
    }
}

impl EarlyLintPass for MiscEarlyLints {
    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
        for param in &gen.params {
            if let GenericParamKind::Type { .. } = param.kind {
                let name = param.ident.as_str();
                if constants::BUILTIN_TYPES.contains(&&*name) {
                    span_lint(
                        cx,
                        BUILTIN_TYPE_SHADOW,
                        param.ident.span,
                        &format!("This generic shadows the built-in type `{}`", name),
                    );
                }
            }
        }
    }

    fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
        if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
            let mut wilds = 0;
            let type_name = npat
                .segments
                .last()
                .expect("A path must have at least one segment")
                .ident
                .name;

            for field in pfields {
                if let PatKind::Wild = field.pat.node {
                    wilds += 1;
                }
            }
            if !pfields.is_empty() && wilds == pfields.len() {
                span_help_and_lint(
                    cx,
                    UNNEEDED_FIELD_PATTERN,
                    pat.span,
                    "All the struct fields are matched to a wildcard pattern, consider using `..`.",
                    &format!("Try with `{} {{ .. }}` instead", type_name),
                );
                return;
            }
            if wilds > 0 {
                let mut normal = vec![];

                for field in pfields {
                    match field.pat.node {
                        PatKind::Wild => {},
                        _ => {
                            if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
                                normal.push(n);
                            }
                        },
                    }
                }
                for field in pfields {
                    if let PatKind::Wild = field.pat.node {
                        wilds -= 1;
                        if wilds > 0 {
                            span_lint(
                                cx,
                                UNNEEDED_FIELD_PATTERN,
                                field.span,
                                "You matched a field with a wildcard pattern. Consider using `..` instead",
                            );
                        } else {
                            span_help_and_lint(
                                cx,
                                UNNEEDED_FIELD_PATTERN,
                                field.span,
                                "You matched a field with a wildcard pattern. Consider using `..` \
                                 instead",
                                &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
                            );
                        }
                    }
                }
            }
        }
    }

    fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {
        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();

        for arg in &decl.inputs {
            if let PatKind::Ident(_, ident, None) = arg.pat.node {
                let arg_name = ident.to_string();

                if arg_name.starts_with('_') {
                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
                        span_lint(
                            cx,
                            DUPLICATE_UNDERSCORE_ARGUMENT,
                            *correspondence,
                            &format!(
                                "`{}` already exists, having another argument having almost the same \
                                 name makes code comprehension and documentation more difficult",
                                arg_name[1..].to_owned()
                            ),
                        );
                    }
                } else {
                    registered_names.insert(arg_name, arg.pat.span);
                }
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if in_external_macro(cx.sess(), expr.span) {
            return;
        }
        match expr.node {
            ExprKind::Call(ref paren, _) => {
                if let ExprKind::Paren(ref closure) = paren.node {
                    if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.node {
                        let mut visitor = ReturnVisitor::new();
                        visitor.visit_expr(block);
                        if !visitor.found_return {
                            span_lint_and_then(
                                cx,
                                REDUNDANT_CLOSURE_CALL,
                                expr.span,
                                "Try not to call a closure in the expression where it is declared.",
                                |db| {
                                    if decl.inputs.is_empty() {
                                        let hint = snippet(cx, block.span, "..").into_owned();
                                        db.span_suggestion(
                                            expr.span,
                                            "Try doing something like: ",
                                            hint,
                                            Applicability::MachineApplicable, // snippet
                                        );
                                    }
                                },
                            );
                        }
                    }
                }
            },
            ExprKind::Unary(UnOp::Neg, ref inner) => {
                if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
                    span_lint(
                        cx,
                        DOUBLE_NEG,
                        expr.span,
                        "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
                    );
                }
            },
            ExprKind::Lit(ref lit) => self.check_lit(cx, lit),
            _ => (),
        }
    }

    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
        for w in block.stmts.windows(2) {
            if_chain! {
                if let StmtKind::Local(ref local) = w[0].node;
                if let Option::Some(ref t) = local.init;
                if let ExprKind::Closure(..) = t.node;
                if let PatKind::Ident(_, ident, _) = local.pat.node;
                if let StmtKind::Semi(ref second) = w[1].node;
                if let ExprKind::Assign(_, ref call) = second.node;
                if let ExprKind::Call(ref closure, _) = call.node;
                if let ExprKind::Path(_, ref path) = closure.node;
                then {
                    if ident == path.segments[0].ident {
                        span_lint(
                            cx,
                            REDUNDANT_CLOSURE_CALL,
                            second.span,
                            "Closure called just once immediately after it was declared",
                        );
                    }
                }
            }
        }
    }
}

impl MiscEarlyLints {
    fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
        // The `line!()` macro is compiler built-in and a special case for these lints.
        let lit_snip = match snippet_opt(cx, lit.span) {
            Some(snip) => {
                if snip.contains('!') {
                    return;
                }
                snip
            },
            _ => return,
        };

        if let LitKind::Int(value, lit_int_type) = lit.node {
            let suffix = match lit_int_type {
                LitIntType::Signed(ty) => ty.ty_to_string(),
                LitIntType::Unsigned(ty) => ty.ty_to_string(),
                LitIntType::Unsuffixed => "",
            };

            let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
            // Do not lint when literal is unsuffixed.
            if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
                span_lint_and_sugg(
                    cx,
                    UNSEPARATED_LITERAL_SUFFIX,
                    lit.span,
                    "integer type suffix should be separated by an underscore",
                    "add an underscore",
                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
                    Applicability::MachineApplicable,
                );
            }

            if lit_snip.starts_with("0x") {
                let mut seen = (false, false);
                for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
                    match ch {
                        b'a'..=b'f' => seen.0 = true,
                        b'A'..=b'F' => seen.1 = true,
                        _ => {},
                    }
                    if seen.0 && seen.1 {
                        span_lint(
                            cx,
                            MIXED_CASE_HEX_LITERALS,
                            lit.span,
                            "inconsistent casing in hexadecimal literal",
                        );
                        break;
                    }
                }
            } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
                /* nothing to do */
            } else if value != 0 && lit_snip.starts_with('0') {
                span_lint_and_then(
                    cx,
                    ZERO_PREFIXED_LITERAL,
                    lit.span,
                    "this is a decimal constant",
                    |db| {
                        db.span_suggestion(
                            lit.span,
                            "if you mean to use a decimal constant, remove the `0` to avoid confusion",
                            lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
                            Applicability::MaybeIncorrect,
                        );
                        db.span_suggestion(
                            lit.span,
                            "if you mean to use an octal constant, use `0o`",
                            format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
                            Applicability::MaybeIncorrect,
                        );
                    },
                );
            }
        } else if let LitKind::Float(_, float_ty) = lit.node {
            let suffix = float_ty.ty_to_string();
            let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
            if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
                span_lint_and_sugg(
                    cx,
                    UNSEPARATED_LITERAL_SUFFIX,
                    lit.span,
                    "float type suffix should be separated by an underscore",
                    "add an underscore",
                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
                    Applicability::MachineApplicable,
                );
            }
        }
    }
}
<commit_msg>Fix index out of bound in case of empty snippet<commit_after>use crate::utils::{
    constants, snippet, snippet_opt, span_help_and_lint, span_lint, span_lint_and_sugg, span_lint_and_then,
};
use if_chain::if_chain;
use rustc::lint::{in_external_macro, EarlyContext, EarlyLintPass, LintArray, LintContext, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use syntax::ast::*;
use syntax::source_map::Span;
use syntax::visit::{walk_expr, FnKind, Visitor};

declare_clippy_lint! {
    /// **What it does:** Checks for structure field patterns bound to wildcards.
    ///
    /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on
    /// the fields that are actually bound.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```ignore
    /// let { a: _, b: ref b, c: _ } = ..
    /// ```
    pub UNNEEDED_FIELD_PATTERN,
    style,
    "struct fields bound to a wildcard instead of using `..`"
}

declare_clippy_lint! {
    /// **What it does:** Checks for function arguments having the similar names
    /// differing by an underscore.
    ///
    /// **Why is this bad?** It affects code readability.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(a: i32, _a: i32) {}
    /// ```
    pub DUPLICATE_UNDERSCORE_ARGUMENT,
    style,
    "function arguments having names which only differ by an underscore"
}

declare_clippy_lint! {
    /// **What it does:** Detects closures called in the same expression where they
    /// are defined.
    ///
    /// **Why is this bad?** It is unnecessarily adding to the expression's
    /// complexity.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust,ignore
    /// (|| 42)()
    /// ```
    pub REDUNDANT_CLOSURE_CALL,
    complexity,
    "throwaway closures called in the expression they are defined"
}

declare_clippy_lint! {
    /// **What it does:** Detects expressions of the form `--x`.
    ///
    /// **Why is this bad?** It can mislead C/C++ programmers to think `x` was
    /// decremented.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let mut x = 3;
    /// --x;
    /// ```
    pub DOUBLE_NEG,
    style,
    "`--x`, which is a double negation of `x` and not a pre-decrement as in C/C++"
}

declare_clippy_lint! {
    /// **What it does:** Warns on hexadecimal literals with mixed-case letter
    /// digits.
    ///
    /// **Why is this bad?** It looks confusing.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let y = 0x1a9BAcD;
    /// ```
    pub MIXED_CASE_HEX_LITERALS,
    style,
    "hex literals whose letter digits are not consistently upper- or lowercased"
}

declare_clippy_lint! {
    /// **What it does:** Warns if literal suffixes are not separated by an
    /// underscore.
    ///
    /// **Why is this bad?** It is much less readable.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let y = 123832i32;
    /// ```
    pub UNSEPARATED_LITERAL_SUFFIX,
    pedantic,
    "literals whose suffix is not separated by an underscore"
}

declare_clippy_lint! {
    /// **What it does:** Warns if an integral constant literal starts with `0`.
    ///
    /// **Why is this bad?** In some languages (including the infamous C language
    /// and most of its
    /// family), this marks an octal constant. In Rust however, this is a decimal
    /// constant. This could
    /// be confusing for both the writer and a reader of the constant.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// In Rust:
    /// ```rust
    /// fn main() {
    ///     let a = 0123;
    ///     println!("{}", a);
    /// }
    /// ```
    ///
    /// prints `123`, while in C:
    ///
    /// ```c
    /// #include <stdio.h>
    ///
    /// int main() {
    ///     int a = 0123;
    ///     printf("%d\n", a);
    /// }
    /// ```
    ///
    /// prints `83` (as `83 == 0o123` while `123 == 0o173`).
    pub ZERO_PREFIXED_LITERAL,
    complexity,
    "integer literals starting with `0`"
}

declare_clippy_lint! {
    /// **What it does:** Warns if a generic shadows a built-in type.
    ///
    /// **Why is this bad?** This gives surprising type errors.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    ///
    /// ```ignore
    /// impl<u32> Foo<u32> {
    ///     fn impl_func(&self) -> u32 {
    ///         42
    ///     }
    /// }
    /// ```
    pub BUILTIN_TYPE_SHADOW,
    style,
    "shadowing a builtin type"
}

declare_lint_pass!(MiscEarlyLints => [
    UNNEEDED_FIELD_PATTERN,
    DUPLICATE_UNDERSCORE_ARGUMENT,
    REDUNDANT_CLOSURE_CALL,
    DOUBLE_NEG,
    MIXED_CASE_HEX_LITERALS,
    UNSEPARATED_LITERAL_SUFFIX,
    ZERO_PREFIXED_LITERAL,
    BUILTIN_TYPE_SHADOW
]);

// Used to find `return` statements or equivalents e.g., `?`
struct ReturnVisitor {
    found_return: bool,
}

impl ReturnVisitor {
    fn new() -> Self {
        Self { found_return: false }
    }
}

impl<'ast> Visitor<'ast> for ReturnVisitor {
    fn visit_expr(&mut self, ex: &'ast Expr) {
        if let ExprKind::Ret(_) = ex.node {
            self.found_return = true;
        } else if let ExprKind::Try(_) = ex.node {
            self.found_return = true;
        }

        walk_expr(self, ex)
    }
}

impl EarlyLintPass for MiscEarlyLints {
    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
        for param in &gen.params {
            if let GenericParamKind::Type { .. } = param.kind {
                let name = param.ident.as_str();
                if constants::BUILTIN_TYPES.contains(&&*name) {
                    span_lint(
                        cx,
                        BUILTIN_TYPE_SHADOW,
                        param.ident.span,
                        &format!("This generic shadows the built-in type `{}`", name),
                    );
                }
            }
        }
    }

    fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) {
        if let PatKind::Struct(ref npat, ref pfields, _) = pat.node {
            let mut wilds = 0;
            let type_name = npat
                .segments
                .last()
                .expect("A path must have at least one segment")
                .ident
                .name;

            for field in pfields {
                if let PatKind::Wild = field.pat.node {
                    wilds += 1;
                }
            }
            if !pfields.is_empty() && wilds == pfields.len() {
                span_help_and_lint(
                    cx,
                    UNNEEDED_FIELD_PATTERN,
                    pat.span,
                    "All the struct fields are matched to a wildcard pattern, consider using `..`.",
                    &format!("Try with `{} {{ .. }}` instead", type_name),
                );
                return;
            }
            if wilds > 0 {
                let mut normal = vec![];

                for field in pfields {
                    match field.pat.node {
                        PatKind::Wild => {},
                        _ => {
                            if let Ok(n) = cx.sess().source_map().span_to_snippet(field.span) {
                                normal.push(n);
                            }
                        },
                    }
                }
                for field in pfields {
                    if let PatKind::Wild = field.pat.node {
                        wilds -= 1;
                        if wilds > 0 {
                            span_lint(
                                cx,
                                UNNEEDED_FIELD_PATTERN,
                                field.span,
                                "You matched a field with a wildcard pattern. Consider using `..` instead",
                            );
                        } else {
                            span_help_and_lint(
                                cx,
                                UNNEEDED_FIELD_PATTERN,
                                field.span,
                                "You matched a field with a wildcard pattern. Consider using `..` \
                                 instead",
                                &format!("Try with `{} {{ {}, .. }}`", type_name, normal[..].join(", ")),
                            );
                        }
                    }
                }
            }
        }
    }

    fn check_fn(&mut self, cx: &EarlyContext<'_>, _: FnKind<'_>, decl: &FnDecl, _: Span, _: NodeId) {
        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();

        for arg in &decl.inputs {
            if let PatKind::Ident(_, ident, None) = arg.pat.node {
                let arg_name = ident.to_string();

                if arg_name.starts_with('_') {
                    if let Some(correspondence) = registered_names.get(&arg_name[1..]) {
                        span_lint(
                            cx,
                            DUPLICATE_UNDERSCORE_ARGUMENT,
                            *correspondence,
                            &format!(
                                "`{}` already exists, having another argument having almost the same \
                                 name makes code comprehension and documentation more difficult",
                                arg_name[1..].to_owned()
                            ),
                        );
                    }
                } else {
                    registered_names.insert(arg_name, arg.pat.span);
                }
            }
        }
    }

    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if in_external_macro(cx.sess(), expr.span) {
            return;
        }
        match expr.node {
            ExprKind::Call(ref paren, _) => {
                if let ExprKind::Paren(ref closure) = paren.node {
                    if let ExprKind::Closure(_, _, _, ref decl, ref block, _) = closure.node {
                        let mut visitor = ReturnVisitor::new();
                        visitor.visit_expr(block);
                        if !visitor.found_return {
                            span_lint_and_then(
                                cx,
                                REDUNDANT_CLOSURE_CALL,
                                expr.span,
                                "Try not to call a closure in the expression where it is declared.",
                                |db| {
                                    if decl.inputs.is_empty() {
                                        let hint = snippet(cx, block.span, "..").into_owned();
                                        db.span_suggestion(
                                            expr.span,
                                            "Try doing something like: ",
                                            hint,
                                            Applicability::MachineApplicable, // snippet
                                        );
                                    }
                                },
                            );
                        }
                    }
                }
            },
            ExprKind::Unary(UnOp::Neg, ref inner) => {
                if let ExprKind::Unary(UnOp::Neg, _) = inner.node {
                    span_lint(
                        cx,
                        DOUBLE_NEG,
                        expr.span,
                        "`--x` could be misinterpreted as pre-decrement by C programmers, is usually a no-op",
                    );
                }
            },
            ExprKind::Lit(ref lit) => self.check_lit(cx, lit),
            _ => (),
        }
    }

    fn check_block(&mut self, cx: &EarlyContext<'_>, block: &Block) {
        for w in block.stmts.windows(2) {
            if_chain! {
                if let StmtKind::Local(ref local) = w[0].node;
                if let Option::Some(ref t) = local.init;
                if let ExprKind::Closure(..) = t.node;
                if let PatKind::Ident(_, ident, _) = local.pat.node;
                if let StmtKind::Semi(ref second) = w[1].node;
                if let ExprKind::Assign(_, ref call) = second.node;
                if let ExprKind::Call(ref closure, _) = call.node;
                if let ExprKind::Path(_, ref path) = closure.node;
                then {
                    if ident == path.segments[0].ident {
                        span_lint(
                            cx,
                            REDUNDANT_CLOSURE_CALL,
                            second.span,
                            "Closure called just once immediately after it was declared",
                        );
                    }
                }
            }
        }
    }
}

impl MiscEarlyLints {
    fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
        // The `line!()` macro is compiler built-in and a special case for these lints.
        let lit_snip = match snippet_opt(cx, lit.span) {
            Some(snip) => {
                // The snip could be empty in case of expand from procedure macro
                if snip.is_empty() || snip.contains('!') {
                    return;
                }
                snip
            },
            _ => return,
        };

        if let LitKind::Int(value, lit_int_type) = lit.node {
            let suffix = match lit_int_type {
                LitIntType::Signed(ty) => ty.ty_to_string(),
                LitIntType::Unsigned(ty) => ty.ty_to_string(),
                LitIntType::Unsuffixed => "",
            };

            let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
            // Do not lint when literal is unsuffixed.
            if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
                span_lint_and_sugg(
                    cx,
                    UNSEPARATED_LITERAL_SUFFIX,
                    lit.span,
                    "integer type suffix should be separated by an underscore",
                    "add an underscore",
                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
                    Applicability::MachineApplicable,
                );
            }

            if lit_snip.starts_with("0x") {
                let mut seen = (false, false);
                for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
                    match ch {
                        b'a'..=b'f' => seen.0 = true,
                        b'A'..=b'F' => seen.1 = true,
                        _ => {},
                    }
                    if seen.0 && seen.1 {
                        span_lint(
                            cx,
                            MIXED_CASE_HEX_LITERALS,
                            lit.span,
                            "inconsistent casing in hexadecimal literal",
                        );
                        break;
                    }
                }
            } else if lit_snip.starts_with("0b") || lit_snip.starts_with("0o") {
                /* nothing to do */
            } else if value != 0 && lit_snip.starts_with('0') {
                span_lint_and_then(
                    cx,
                    ZERO_PREFIXED_LITERAL,
                    lit.span,
                    "this is a decimal constant",
                    |db| {
                        db.span_suggestion(
                            lit.span,
                            "if you mean to use a decimal constant, remove the `0` to avoid confusion",
                            lit_snip.trim_start_matches(|c| c == '_' || c == '0').to_string(),
                            Applicability::MaybeIncorrect,
                        );
                        db.span_suggestion(
                            lit.span,
                            "if you mean to use an octal constant, use `0o`",
                            format!("0o{}", lit_snip.trim_start_matches(|c| c == '_' || c == '0')),
                            Applicability::MaybeIncorrect,
                        );
                    },
                );
            }
        } else if let LitKind::Float(_, float_ty) = lit.node {
            let suffix = float_ty.ty_to_string();
            let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
            if lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
                span_lint_and_sugg(
                    cx,
                    UNSEPARATED_LITERAL_SUFFIX,
                    lit.span,
                    "float type suffix should be separated by an underscore",
                    "add an underscore",
                    format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
                    Applicability::MachineApplicable,
                );
            }
        }
    }
}
<|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/. */

//! Different checks done during the style sharing process in order to determine
//! quickly whether it's worth to share style, and whether two different
//! elements can indeed share the same style.

use Atom;
use bloom::StyleBloom;
use context::{SelectorFlagsMap, SharedStyleContext};
use dom::TElement;
use servo_arc::Arc;
use sharing::{StyleSharingCandidate, StyleSharingTarget};

/// Whether, given two elements, they have pointer-equal computed values.
///
/// Both elements need to be styled already.
///
/// This is used to know whether we can share style across cousins (if the two
/// parents have the same style).
pub fn same_computed_values<E>(first: Option<E>, second: Option<E>) -> bool
    where E: TElement,
{
    let (a, b) = match (first, second) {
        (Some(f), Some(s)) => (f, s),
        _ => return false,
    };

    let eq = Arc::ptr_eq(a.borrow_data().unwrap().styles.primary(),
                         b.borrow_data().unwrap().styles.primary());
    eq
}

/// Whether two elements have the same same style attribute (by pointer identity).
pub fn have_same_style_attribute<E>(
    target: &mut StyleSharingTarget<E>,
    candidate: &mut StyleSharingCandidate<E>
) -> bool
    where E: TElement,
{
    match (target.style_attribute(), candidate.style_attribute()) {
        (None, None) => true,
        (Some(_), None) | (None, Some(_)) => false,
        (Some(a), Some(b)) => &*a as *const _ == &*b as *const _
    }
}

/// Whether two elements have the same same presentational attributes.
pub fn have_same_presentational_hints<E>(
    target: &mut StyleSharingTarget<E>,
    candidate: &mut StyleSharingCandidate<E>
) -> bool
    where E: TElement,
{
    target.pres_hints() == candidate.pres_hints()
}

/// Whether a given element has the same class attribute than a given candidate.
///
/// We don't try to share style across elements with different class attributes.
pub fn have_same_class<E>(target: &mut StyleSharingTarget<E>,
                          candidate: &mut StyleSharingCandidate<E>)
                          -> bool
    where E: TElement,
{
    target.class_list() == candidate.class_list()
}

/// Whether a given element and a candidate match the same set of "revalidation"
/// selectors.
///
/// Revalidation selectors are those that depend on the DOM structure, like
/// :first-child, etc, or on attributes that we don't check off-hand (pretty
/// much every attribute selector except `id` and `class`.
#[inline]
pub fn revalidate<E>(target: &mut StyleSharingTarget<E>,
                     candidate: &mut StyleSharingCandidate<E>,
                     shared_context: &SharedStyleContext,
                     bloom: &StyleBloom<E>,
                     selector_flags_map: &mut SelectorFlagsMap<E>)
                     -> bool
    where E: TElement,
{
    let stylist = &shared_context.stylist;

    let for_element =
        target.revalidation_match_results(stylist, bloom, selector_flags_map);

    let for_candidate = candidate.revalidation_match_results(stylist, bloom);

    // This assert "ensures", to some extent, that the two candidates have
    // matched the same rulehash buckets, and as such, that the bits we're
    // comparing represent the same set of selectors.
    debug_assert_eq!(for_element.len(), for_candidate.len());

    for_element == for_candidate
}

/// Checks whether we might have rules for either of the two ids.
#[inline]
pub fn may_have_rules_for_ids(shared_context: &SharedStyleContext,
                              element_id: Option<&Atom>,
                              candidate_id: Option<&Atom>) -> bool
{
    // We shouldn't be called unless the ids are different.
    debug_assert!(element_id.is_some() || candidate_id.is_some());
    let stylist = &shared_context.stylist;

    let may_have_rules_for_element = match element_id {
        Some(id) => stylist.may_have_rules_for_id(id),
        None => false
    };

    if may_have_rules_for_element {
        return true;
    }

    match candidate_id {
        Some(id) => stylist.may_have_rules_for_id(id),
        None => false
    }
}
<commit_msg>style: Prevent cousin sharing if we haven't restyled the parents.<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/. */

//! Different checks done during the style sharing process in order to determine
//! quickly whether it's worth to share style, and whether two different
//! elements can indeed share the same style.

use Atom;
use bloom::StyleBloom;
use context::{SelectorFlagsMap, SharedStyleContext};
use dom::TElement;
use servo_arc::Arc;
use sharing::{StyleSharingCandidate, StyleSharingTarget};

/// Whether, given two elements, they have pointer-equal computed values.
///
/// Both elements need to be styled already.
///
/// This is used to know whether we can share style across cousins (if the two
/// parents have the same style).
///
/// We can only prove that they have the same class list, state, etc if they've
/// been restyled at the same time, otherwise the invalidation pass could make
/// them keep the same computed values even though now we wouldn't be able to
/// prove we match the same selectors.
pub fn same_computed_values<E>(first: Option<E>, second: Option<E>) -> bool
    where E: TElement,
{
    let (first, second) = match (first, second) {
        (Some(f), Some(s)) => (f, s),
        _ => return false,
    };

    debug_assert_ne!(first, second);

    let first_data = first.borrow_data().unwrap();
    let second_data = second.borrow_data().unwrap();

    // FIXME(emilio): This check effectively disables cousin style sharing
    // on the initial style.
    //
    // This is pretty bad, se the discussion in bug 1381821 for mor details.
    //
    // Bug 1387116 tracks fixing this, and various solutions are listed there.
    if !first_data.restyle.is_restyle() || !second_data.restyle.is_restyle() {
        return false;
    }

    let same_computed_values =
        Arc::ptr_eq(first_data.styles.primary(), second_data.styles.primary());

    same_computed_values
}

/// Whether two elements have the same same style attribute (by pointer identity).
pub fn have_same_style_attribute<E>(
    target: &mut StyleSharingTarget<E>,
    candidate: &mut StyleSharingCandidate<E>
) -> bool
    where E: TElement,
{
    match (target.style_attribute(), candidate.style_attribute()) {
        (None, None) => true,
        (Some(_), None) | (None, Some(_)) => false,
        (Some(a), Some(b)) => &*a as *const _ == &*b as *const _
    }
}

/// Whether two elements have the same same presentational attributes.
pub fn have_same_presentational_hints<E>(
    target: &mut StyleSharingTarget<E>,
    candidate: &mut StyleSharingCandidate<E>
) -> bool
    where E: TElement,
{
    target.pres_hints() == candidate.pres_hints()
}

/// Whether a given element has the same class attribute than a given candidate.
///
/// We don't try to share style across elements with different class attributes.
pub fn have_same_class<E>(target: &mut StyleSharingTarget<E>,
                          candidate: &mut StyleSharingCandidate<E>)
                          -> bool
    where E: TElement,
{
    target.class_list() == candidate.class_list()
}

/// Whether a given element and a candidate match the same set of "revalidation"
/// selectors.
///
/// Revalidation selectors are those that depend on the DOM structure, like
/// :first-child, etc, or on attributes that we don't check off-hand (pretty
/// much every attribute selector except `id` and `class`.
#[inline]
pub fn revalidate<E>(target: &mut StyleSharingTarget<E>,
                     candidate: &mut StyleSharingCandidate<E>,
                     shared_context: &SharedStyleContext,
                     bloom: &StyleBloom<E>,
                     selector_flags_map: &mut SelectorFlagsMap<E>)
                     -> bool
    where E: TElement,
{
    let stylist = &shared_context.stylist;

    let for_element =
        target.revalidation_match_results(stylist, bloom, selector_flags_map);

    let for_candidate = candidate.revalidation_match_results(stylist, bloom);

    // This assert "ensures", to some extent, that the two candidates have
    // matched the same rulehash buckets, and as such, that the bits we're
    // comparing represent the same set of selectors.
    debug_assert_eq!(for_element.len(), for_candidate.len());

    for_element == for_candidate
}

/// Checks whether we might have rules for either of the two ids.
#[inline]
pub fn may_have_rules_for_ids(shared_context: &SharedStyleContext,
                              element_id: Option<&Atom>,
                              candidate_id: Option<&Atom>) -> bool
{
    // We shouldn't be called unless the ids are different.
    debug_assert!(element_id.is_some() || candidate_id.is_some());
    let stylist = &shared_context.stylist;

    let may_have_rules_for_element = match element_id {
        Some(id) => stylist.may_have_rules_for_id(id),
        None => false
    };

    if may_have_rules_for_element {
        return true;
    }

    match candidate_id {
        Some(id) => stylist.may_have_rules_for_id(id),
        None => false
    }
}
<|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/. */

#![allow(unrooted_must_root)]

use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
use dom::bindings::js::{JS, RootedReference};
use dom::characterdata::CharacterData;
use dom::comment::Comment;
use dom::document::Document;
use dom::document::{DocumentSource, IsHTMLDocument};
use dom::documenttype::DocumentType;
use dom::element::{Element, ElementCreator};
use dom::htmlformelement::HTMLFormElement;
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmltemplateelement::HTMLTemplateElement;
use dom::node::Node;
use dom::node::{document_from_node, window_from_node};
use dom::processinginstruction::ProcessingInstruction;
use dom::servohtmlparser;
use dom::servohtmlparser::{FragmentContext, ServoHTMLParser};
use dom::text::Text;
use encoding::types::Encoding;
use html5ever::Attribute;
use html5ever::serialize::TraversalScope;
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
use html5ever::serialize::{AttrRef, Serializable, Serializer};
use html5ever::tree_builder::{NextParserState, NodeOrText, QuirksMode, TreeSink};
use msg::constellation_msg::PipelineId;
use parse::Parser;
use std::borrow::Cow;
use std::io::{self, Write};
use string_cache::QualName;
use tendril::StrTendril;
use url::Url;
use util::str::DOMString;

fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<Node>>) {
    match child {
        NodeOrText::AppendNode(n) => {
            assert!(parent.InsertBefore(&n, reference_child).is_ok());
        },
        NodeOrText::AppendText(t) => {
            // FIXME(ajeffrey): convert directly from tendrils to DOMStrings
            let s: String = t.into();
            let text = Text::new(DOMString::from(s), &parent.owner_doc());
            assert!(parent.InsertBefore(text.upcast(), reference_child).is_ok());
        }
    }
}

impl<'a> TreeSink for servohtmlparser::Sink {
    type Handle = JS<Node>;

    fn get_document(&mut self) -> JS<Node> {
        JS::from_ref(self.document.upcast())
    }

    fn get_template_contents(&self, target: JS<Node>) -> JS<Node> {
        let template = target.downcast::<HTMLTemplateElement>()
            .expect("tried to get template contents of non-HTMLTemplateElement in HTML parsing");
        JS::from_ref(template.Content().upcast())
    }

    fn same_node(&self, x: JS<Node>, y: JS<Node>) -> bool {
        x == y
    }

    fn elem_name(&self, target: JS<Node>) -> QualName {
        let elem = target.downcast::<Element>()
            .expect("tried to get name of non-Element in HTML parsing");
        QualName {
            ns: elem.namespace().clone(),
            local: elem.local_name().clone(),
        }
    }

    fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>)
            -> JS<Node> {
        let elem = Element::create(name, None, &*self.document,
                                   ElementCreator::ParserCreated);

        for attr in attrs {
            elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None);
        }

        JS::from_ref(elem.upcast())
    }

    fn create_comment(&mut self, text: StrTendril) -> JS<Node> {
        let comment = Comment::new(DOMString::from(String::from(text)), &*self.document);
        JS::from_ref(comment.upcast())
    }

    fn append_before_sibling(&mut self,
            sibling: JS<Node>,
            new_node: NodeOrText<JS<Node>>) -> Result<(), NodeOrText<JS<Node>>> {
        // If there is no parent, return the node to the parser.
        let parent = match sibling.GetParentNode() {
            Some(p) => p,
            None => return Err(new_node),
        };

        insert(&parent, Some(&*sibling), new_node);
        Ok(())
    }

    fn parse_error(&mut self, msg: Cow<'static, str>) {
        debug!("Parse error: {}", msg);
    }

    fn set_quirks_mode(&mut self, mode: QuirksMode) {
        self.document.set_quirks_mode(mode);
    }

    fn append(&mut self, parent: JS<Node>, child: NodeOrText<JS<Node>>) {
        // FIXME(#3701): Use a simpler algorithm and merge adjacent text nodes
        insert(&parent, None, child);
    }

    fn append_doctype_to_document(&mut self, name: StrTendril, public_id: StrTendril,
                                  system_id: StrTendril) {
        let doc = &*self.document;
        let doctype = DocumentType::new(
            DOMString::from(String::from(name)), Some(DOMString::from(String::from(public_id))),
            Some(DOMString::from(String::from(system_id))), doc);
        doc.upcast::<Node>().AppendChild(doctype.upcast()).expect("Appending failed");
    }

    fn add_attrs_if_missing(&mut self, target: JS<Node>, attrs: Vec<Attribute>) {
        let elem = target.downcast::<Element>()
            .expect("tried to set attrs on non-Element in HTML parsing");
        for attr in attrs {
            elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None);
        }
    }

    fn remove_from_parent(&mut self, target: JS<Node>) {
        if let Some(ref parent) = target.GetParentNode() {
            parent.RemoveChild(&*target).unwrap();
        }
    }

    fn mark_script_already_started(&mut self, node: JS<Node>) {
        let script = node.downcast::<HTMLScriptElement>();
        script.map(|script| script.mark_already_started());
    }

    fn complete_script(&mut self, node: JS<Node>) -> NextParserState {
        let script = node.downcast::<HTMLScriptElement>();
        if let Some(script) = script {
            return script.prepare();
        }
        NextParserState::Continue
    }

    fn reparent_children(&mut self, node: JS<Node>, new_parent: JS<Node>) {
        while let Some(ref child) = node.GetFirstChild() {
            new_parent.AppendChild(child.r()).unwrap();
        }

    }
}

impl<'a> Serializable for &'a Node {
    fn serialize<'wr, Wr: Write>(&self, serializer: &mut Serializer<'wr, Wr>,
                                 traversal_scope: TraversalScope) -> io::Result<()> {
        let node = *self;
        match (traversal_scope, node.type_id()) {
            (_, NodeTypeId::Element(..)) => {
                let elem = node.downcast::<Element>().unwrap();
                let name = QualName::new(elem.namespace().clone(),
                                         elem.local_name().clone());
                if traversal_scope == IncludeNode {
                    let attrs = elem.attrs().iter().map(|attr| {
                        let qname = QualName::new(attr.namespace().clone(),
                                                  attr.local_name().clone());
                        let value = attr.value().clone();
                        (qname, value)
                    }).collect::<Vec<_>>();
                    let attr_refs = attrs.iter().map(|&(ref qname, ref value)| {
                        let ar: AttrRef = (&qname, &**value);
                        ar
                    });
                    try!(serializer.start_elem(name.clone(), attr_refs));
                }

                let children = if let Some(tpl) = node.downcast::<HTMLTemplateElement>() {
                    // https://github.com/w3c/DOM-Parsing/issues/1
                    tpl.Content().upcast::<Node>().children()
                } else {
                    node.children()
                };

                for handle in children {
                    try!(handle.r().serialize(serializer, IncludeNode));
                }

                if traversal_scope == IncludeNode {
                    try!(serializer.end_elem(name.clone()));
                }
                Ok(())
            },

            (ChildrenOnly, NodeTypeId::Document(_)) => {
                for handle in node.children() {
                    try!(handle.r().serialize(serializer, IncludeNode));
                }
                Ok(())
            },

            (ChildrenOnly, _) => Ok(()),

            (IncludeNode, NodeTypeId::DocumentType) => {
                let doctype = node.downcast::<DocumentType>().unwrap();
                serializer.write_doctype(&doctype.name())
            },

            (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::Text)) => {
                let cdata = node.downcast::<CharacterData>().unwrap();
                serializer.write_text(&cdata.data())
            },

            (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::Comment)) => {
                let cdata = node.downcast::<CharacterData>().unwrap();
                serializer.write_comment(&cdata.data())
            },

            (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction)) => {
                let pi = node.downcast::<ProcessingInstruction>().unwrap();
                let data = pi.upcast::<CharacterData>().data();
                serializer.write_processing_instruction(&pi.target(), &data)
            },

            (IncludeNode, NodeTypeId::DocumentFragment) => Ok(()),

            (IncludeNode, NodeTypeId::Document(_)) => panic!("Can't serialize Document node itself"),
        }
    }
}

pub enum ParseContext<'a> {
    Fragment(FragmentContext<'a>),
    Owner(Option<PipelineId>),
}

pub fn parse_html(document: &Document,
                  input: DOMString,
                  url: Url,
                  context: ParseContext) {
    let parser = match context {
        ParseContext::Owner(owner) =>
            ServoHTMLParser::new(Some(url), document, owner),
        ParseContext::Fragment(fc) =>
            ServoHTMLParser::new_for_fragment(Some(url), document, fc),
    };
    parser.parse_chunk(String::from(input));
}

// https://html.spec.whatwg.org/multipage/#parsing-html-fragments
pub fn parse_html_fragment(context_node: &Node,
                           input: DOMString,
                           output: &Node) {
    let window = window_from_node(context_node);
    let context_document = document_from_node(context_node);
    let context_document = context_document.r();
    let url = context_document.url();

    // Step 1.
    let loader = DocumentLoader::new(&*context_document.loader());
    let document = Document::new(window.r(), Some(url.clone()),
                                 IsHTMLDocument::HTMLDocument,
                                 None, None,
                                 DocumentSource::FromParser,
                                 loader);

    // Step 2.
    document.set_quirks_mode(context_document.quirks_mode());

    // Step 11.
    let form = context_node.inclusive_ancestors()
                           .find(|element| element.is::<HTMLFormElement>());
    let fragment_context = FragmentContext {
        context_elem: context_node,
        form_elem: form.r(),
    };
    parse_html(document.r(), input, url.clone(), ParseContext::Fragment(fragment_context));

    // Step 14.
    let root_element = document.GetDocumentElement().expect("no document element");
    for child in root_element.upcast::<Node>().children() {
        output.AppendChild(child.r()).unwrap();
    }
}
<commit_msg>Remove unused import in script<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/. */

#![allow(unrooted_must_root)]

use document_loader::DocumentLoader;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::inheritance::{Castable, CharacterDataTypeId, NodeTypeId};
use dom::bindings::js::{JS, RootedReference};
use dom::characterdata::CharacterData;
use dom::comment::Comment;
use dom::document::Document;
use dom::document::{DocumentSource, IsHTMLDocument};
use dom::documenttype::DocumentType;
use dom::element::{Element, ElementCreator};
use dom::htmlformelement::HTMLFormElement;
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmltemplateelement::HTMLTemplateElement;
use dom::node::Node;
use dom::node::{document_from_node, window_from_node};
use dom::processinginstruction::ProcessingInstruction;
use dom::servohtmlparser;
use dom::servohtmlparser::{FragmentContext, ServoHTMLParser};
use dom::text::Text;
use html5ever::Attribute;
use html5ever::serialize::TraversalScope;
use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
use html5ever::serialize::{AttrRef, Serializable, Serializer};
use html5ever::tree_builder::{NextParserState, NodeOrText, QuirksMode, TreeSink};
use msg::constellation_msg::PipelineId;
use parse::Parser;
use std::borrow::Cow;
use std::io::{self, Write};
use string_cache::QualName;
use tendril::StrTendril;
use url::Url;
use util::str::DOMString;

fn insert(parent: &Node, reference_child: Option<&Node>, child: NodeOrText<JS<Node>>) {
    match child {
        NodeOrText::AppendNode(n) => {
            assert!(parent.InsertBefore(&n, reference_child).is_ok());
        },
        NodeOrText::AppendText(t) => {
            // FIXME(ajeffrey): convert directly from tendrils to DOMStrings
            let s: String = t.into();
            let text = Text::new(DOMString::from(s), &parent.owner_doc());
            assert!(parent.InsertBefore(text.upcast(), reference_child).is_ok());
        }
    }
}

impl<'a> TreeSink for servohtmlparser::Sink {
    type Handle = JS<Node>;

    fn get_document(&mut self) -> JS<Node> {
        JS::from_ref(self.document.upcast())
    }

    fn get_template_contents(&self, target: JS<Node>) -> JS<Node> {
        let template = target.downcast::<HTMLTemplateElement>()
            .expect("tried to get template contents of non-HTMLTemplateElement in HTML parsing");
        JS::from_ref(template.Content().upcast())
    }

    fn same_node(&self, x: JS<Node>, y: JS<Node>) -> bool {
        x == y
    }

    fn elem_name(&self, target: JS<Node>) -> QualName {
        let elem = target.downcast::<Element>()
            .expect("tried to get name of non-Element in HTML parsing");
        QualName {
            ns: elem.namespace().clone(),
            local: elem.local_name().clone(),
        }
    }

    fn create_element(&mut self, name: QualName, attrs: Vec<Attribute>)
            -> JS<Node> {
        let elem = Element::create(name, None, &*self.document,
                                   ElementCreator::ParserCreated);

        for attr in attrs {
            elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None);
        }

        JS::from_ref(elem.upcast())
    }

    fn create_comment(&mut self, text: StrTendril) -> JS<Node> {
        let comment = Comment::new(DOMString::from(String::from(text)), &*self.document);
        JS::from_ref(comment.upcast())
    }

    fn append_before_sibling(&mut self,
            sibling: JS<Node>,
            new_node: NodeOrText<JS<Node>>) -> Result<(), NodeOrText<JS<Node>>> {
        // If there is no parent, return the node to the parser.
        let parent = match sibling.GetParentNode() {
            Some(p) => p,
            None => return Err(new_node),
        };

        insert(&parent, Some(&*sibling), new_node);
        Ok(())
    }

    fn parse_error(&mut self, msg: Cow<'static, str>) {
        debug!("Parse error: {}", msg);
    }

    fn set_quirks_mode(&mut self, mode: QuirksMode) {
        self.document.set_quirks_mode(mode);
    }

    fn append(&mut self, parent: JS<Node>, child: NodeOrText<JS<Node>>) {
        // FIXME(#3701): Use a simpler algorithm and merge adjacent text nodes
        insert(&parent, None, child);
    }

    fn append_doctype_to_document(&mut self, name: StrTendril, public_id: StrTendril,
                                  system_id: StrTendril) {
        let doc = &*self.document;
        let doctype = DocumentType::new(
            DOMString::from(String::from(name)), Some(DOMString::from(String::from(public_id))),
            Some(DOMString::from(String::from(system_id))), doc);
        doc.upcast::<Node>().AppendChild(doctype.upcast()).expect("Appending failed");
    }

    fn add_attrs_if_missing(&mut self, target: JS<Node>, attrs: Vec<Attribute>) {
        let elem = target.downcast::<Element>()
            .expect("tried to set attrs on non-Element in HTML parsing");
        for attr in attrs {
            elem.set_attribute_from_parser(attr.name, DOMString::from(String::from(attr.value)), None);
        }
    }

    fn remove_from_parent(&mut self, target: JS<Node>) {
        if let Some(ref parent) = target.GetParentNode() {
            parent.RemoveChild(&*target).unwrap();
        }
    }

    fn mark_script_already_started(&mut self, node: JS<Node>) {
        let script = node.downcast::<HTMLScriptElement>();
        script.map(|script| script.mark_already_started());
    }

    fn complete_script(&mut self, node: JS<Node>) -> NextParserState {
        let script = node.downcast::<HTMLScriptElement>();
        if let Some(script) = script {
            return script.prepare();
        }
        NextParserState::Continue
    }

    fn reparent_children(&mut self, node: JS<Node>, new_parent: JS<Node>) {
        while let Some(ref child) = node.GetFirstChild() {
            new_parent.AppendChild(child.r()).unwrap();
        }

    }
}

impl<'a> Serializable for &'a Node {
    fn serialize<'wr, Wr: Write>(&self, serializer: &mut Serializer<'wr, Wr>,
                                 traversal_scope: TraversalScope) -> io::Result<()> {
        let node = *self;
        match (traversal_scope, node.type_id()) {
            (_, NodeTypeId::Element(..)) => {
                let elem = node.downcast::<Element>().unwrap();
                let name = QualName::new(elem.namespace().clone(),
                                         elem.local_name().clone());
                if traversal_scope == IncludeNode {
                    let attrs = elem.attrs().iter().map(|attr| {
                        let qname = QualName::new(attr.namespace().clone(),
                                                  attr.local_name().clone());
                        let value = attr.value().clone();
                        (qname, value)
                    }).collect::<Vec<_>>();
                    let attr_refs = attrs.iter().map(|&(ref qname, ref value)| {
                        let ar: AttrRef = (&qname, &**value);
                        ar
                    });
                    try!(serializer.start_elem(name.clone(), attr_refs));
                }

                let children = if let Some(tpl) = node.downcast::<HTMLTemplateElement>() {
                    // https://github.com/w3c/DOM-Parsing/issues/1
                    tpl.Content().upcast::<Node>().children()
                } else {
                    node.children()
                };

                for handle in children {
                    try!(handle.r().serialize(serializer, IncludeNode));
                }

                if traversal_scope == IncludeNode {
                    try!(serializer.end_elem(name.clone()));
                }
                Ok(())
            },

            (ChildrenOnly, NodeTypeId::Document(_)) => {
                for handle in node.children() {
                    try!(handle.r().serialize(serializer, IncludeNode));
                }
                Ok(())
            },

            (ChildrenOnly, _) => Ok(()),

            (IncludeNode, NodeTypeId::DocumentType) => {
                let doctype = node.downcast::<DocumentType>().unwrap();
                serializer.write_doctype(&doctype.name())
            },

            (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::Text)) => {
                let cdata = node.downcast::<CharacterData>().unwrap();
                serializer.write_text(&cdata.data())
            },

            (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::Comment)) => {
                let cdata = node.downcast::<CharacterData>().unwrap();
                serializer.write_comment(&cdata.data())
            },

            (IncludeNode, NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction)) => {
                let pi = node.downcast::<ProcessingInstruction>().unwrap();
                let data = pi.upcast::<CharacterData>().data();
                serializer.write_processing_instruction(&pi.target(), &data)
            },

            (IncludeNode, NodeTypeId::DocumentFragment) => Ok(()),

            (IncludeNode, NodeTypeId::Document(_)) => panic!("Can't serialize Document node itself"),
        }
    }
}

pub enum ParseContext<'a> {
    Fragment(FragmentContext<'a>),
    Owner(Option<PipelineId>),
}

pub fn parse_html(document: &Document,
                  input: DOMString,
                  url: Url,
                  context: ParseContext) {
    let parser = match context {
        ParseContext::Owner(owner) =>
            ServoHTMLParser::new(Some(url), document, owner),
        ParseContext::Fragment(fc) =>
            ServoHTMLParser::new_for_fragment(Some(url), document, fc),
    };
    parser.parse_chunk(String::from(input));
}

// https://html.spec.whatwg.org/multipage/#parsing-html-fragments
pub fn parse_html_fragment(context_node: &Node,
                           input: DOMString,
                           output: &Node) {
    let window = window_from_node(context_node);
    let context_document = document_from_node(context_node);
    let context_document = context_document.r();
    let url = context_document.url();

    // Step 1.
    let loader = DocumentLoader::new(&*context_document.loader());
    let document = Document::new(window.r(), Some(url.clone()),
                                 IsHTMLDocument::HTMLDocument,
                                 None, None,
                                 DocumentSource::FromParser,
                                 loader);

    // Step 2.
    document.set_quirks_mode(context_document.quirks_mode());

    // Step 11.
    let form = context_node.inclusive_ancestors()
                           .find(|element| element.is::<HTMLFormElement>());
    let fragment_context = FragmentContext {
        context_elem: context_node,
        form_elem: form.r(),
    };
    parse_html(document.r(), input, url.clone(), ParseContext::Fragment(fragment_context));

    // Step 14.
    let root_element = document.GetDocumentElement().expect("no document element");
    for child in root_element.upcast::<Node>().children() {
        output.AppendChild(child.r()).unwrap();
    }
}
<|endoftext|>


<commit_before>use std::process::Command;

fn main() {
    let child = Command::new("sleep").arg("5").spawn().unwrap();
    let _result = child.wait().unwrap();

    println!("reached end of main");
}
<commit_msg>Fix mutability issues<commit_after>use std::process::Command;

fn main() {
    let mut child = Command::new("sleep").arg("5").spawn().unwrap();
    let _result = child.wait().unwrap();

    println!("reached end of main");
}
<|endoftext|>


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

use rustc::hir;
use rustc::lint::*;
use syntax::ast;
use syntax::codemap::Span;

/// **What it does:** it lints if an exported function, method, trait method with default impl,
/// or trait method impl is not `#[inline]`.
///
/// **Why is this bad?** In general, it is not. Functions can be inlined across
/// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
/// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
/// might intend for most of the methods in their public API to be able to be inlined across
/// crates even when LTO is disabled. For these types of crates, enabling this lint might make sense.
/// It allows the crate to require all exported methods to be `#[inline]` by default, and then opt
/// out for specific methods where this might not make sense.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// pub fn foo() {} // missing #[inline]
/// fn ok() {} // ok
/// #[inline] pub fn bar() {} // ok
/// #[inline(always)] pub fn baz() {} // ok
///
/// pub trait Bar {
///   fn bar(); // ok
///   fn def_bar() {} // missing #[inline]
/// }
///
/// struct Baz;
/// impl Baz {
///    fn priv() {} // ok
/// }
///
/// impl Bar for Baz {
///   fn bar() {} // ok - Baz is not exported
/// }
///
/// pub struct PubBaz;
/// impl PubBaz {
///    fn priv() {} // ok
///    pub not_ptriv() {} // missing #[inline]
/// }
///
/// impl Bar for PubBaz {
///    fn bar() {} // missing #[inline]
///    fn def_bar() {} // missing #[inline]
/// }
/// ```
declare_clippy_lint! {
    pub MISSING_INLINE_IN_PUBLIC_ITEMS,
    restriction,
    "detects missing #[inline] attribute for public callables (functions, trait methods, methods...)"
}

pub struct MissingInline;

fn check_missing_inline_attrs(cx: &LateContext,
                              attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
    let has_inline = attrs
        .iter()
        .any(|a| a.name() == "inline" );
    if !has_inline {
        cx.span_lint(
            MISSING_INLINE_IN_PUBLIC_ITEMS,
            sp,
            &format!("missing `#[inline]` for {}", desc),
        );
    }
}

fn is_executable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>) -> bool {
    use rustc::session::config::CrateType;

    cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| {
        match t {
            CrateType::CrateTypeExecutable => true,
            _ => false,
        }
    })
}

impl LintPass for MissingInline {
    fn get_lints(&self) -> LintArray {
        lint_array![MISSING_INLINE_IN_PUBLIC_ITEMS]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item) {
        if is_executable(cx) {
            return;
        }

        if !cx.access_levels.is_exported(it.id) {
            return;
        }
        match it.node {
            hir::ItemFn(..) => {
                let desc = "a function";
                check_missing_inline_attrs(cx, &it.attrs, it.span, desc);
            },
            hir::ItemTrait(ref _is_auto, ref _unsafe, ref _generics,
                           ref _bounds, ref trait_items)  => {
                // note: we need to check if the trait is exported so we can't use
                // `LateLintPass::check_trait_item` here.
                for tit in trait_items {
                    let tit_ = cx.tcx.hir.trait_item(tit.id);
                    match tit_.node {
                        hir::TraitItemKind::Const(..) |
                        hir::TraitItemKind::Type(..) => {},
                        hir::TraitItemKind::Method(..) => {
                            if tit.defaultness.has_value() {
                                // trait method with default body needs inline in case
                                // an impl is not provided
                                let desc = "a default trait method";
                                let item = cx.tcx.hir.expect_trait_item(tit.id.node_id);
                                check_missing_inline_attrs(cx, &item.attrs,
                                                                item.span, desc);
                            }
                        },
                    }
                }
            }
            hir::ItemConst(..) |
            hir::ItemEnum(..) |
            hir::ItemMod(..) |
            hir::ItemStatic(..) |
            hir::ItemStruct(..) |
            hir::ItemTraitAlias(..) |
            hir::ItemGlobalAsm(..) |
            hir::ItemTy(..) |
            hir::ItemUnion(..) |
            hir::ItemExistential(..) |
            hir::ItemExternCrate(..) |
            hir::ItemForeignMod(..) |
            hir::ItemImpl(..) |
            hir::ItemUse(..) => {},
        };
    }

    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem) {
        use rustc::ty::{TraitContainer, ImplContainer};
        if is_executable(cx) {
            return;
        }

        // If the item being implemented is not exported, then we don't need #[inline]
        if !cx.access_levels.is_exported(impl_item.id) {
            return;
        }

        let desc = match impl_item.node {
            hir::ImplItemKind::Method(..) => "a method",
            hir::ImplItemKind::Const(..) |
            hir::ImplItemKind::Type(_) => return,
        };

        let def_id = cx.tcx.hir.local_def_id(impl_item.id);
        match cx.tcx.associated_item(def_id).container {
            TraitContainer(cid) => {
                if let Some(n) = cx.tcx.hir.as_local_node_id(cid) {
                    if !cx.access_levels.is_exported(n) {
                        // If a trait is being implemented for an item, and the
                        // trait is not exported, we don't need #[inline]
                        return;
                    }
                }
            },
            ImplContainer(cid) => {
                if cx.tcx.impl_trait_ref(cid).is_some() {
                    let trait_ref = cx.tcx.impl_trait_ref(cid).unwrap();
                    if let Some(n) = cx.tcx.hir.as_local_node_id(trait_ref.def_id) {
                        if !cx.access_levels.is_exported(n) {
                            // If a trait is being implemented for an item, and the
                            // trait is not exported, we don't need #[inline]
                            return;
                        }
                    }
                }
            },
        }

        check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc);
    }
}
<commit_msg>Remove duplication in missing_inline<commit_after>//   Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
//   file at the top-level directory of this distribution and at
//   http://rust-lang.org/COPYRIGHT.
//
//   Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
//   http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
//   <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
//   option. This file may not be copied, modified, or distributed
//   except according to those terms.
//

use rustc::hir;
use rustc::lint::*;
use syntax::ast;
use syntax::codemap::Span;

/// **What it does:** it lints if an exported function, method, trait method with default impl,
/// or trait method impl is not `#[inline]`.
///
/// **Why is this bad?** In general, it is not. Functions can be inlined across
/// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
/// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
/// might intend for most of the methods in their public API to be able to be inlined across
/// crates even when LTO is disabled. For these types of crates, enabling this lint might make sense.
/// It allows the crate to require all exported methods to be `#[inline]` by default, and then opt
/// out for specific methods where this might not make sense.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// pub fn foo() {} // missing #[inline]
/// fn ok() {} // ok
/// #[inline] pub fn bar() {} // ok
/// #[inline(always)] pub fn baz() {} // ok
///
/// pub trait Bar {
///   fn bar(); // ok
///   fn def_bar() {} // missing #[inline]
/// }
///
/// struct Baz;
/// impl Baz {
///    fn priv() {} // ok
/// }
///
/// impl Bar for Baz {
///   fn bar() {} // ok - Baz is not exported
/// }
///
/// pub struct PubBaz;
/// impl PubBaz {
///    fn priv() {} // ok
///    pub not_ptriv() {} // missing #[inline]
/// }
///
/// impl Bar for PubBaz {
///    fn bar() {} // missing #[inline]
///    fn def_bar() {} // missing #[inline]
/// }
/// ```
declare_clippy_lint! {
    pub MISSING_INLINE_IN_PUBLIC_ITEMS,
    restriction,
    "detects missing #[inline] attribute for public callables (functions, trait methods, methods...)"
}

pub struct MissingInline;

fn check_missing_inline_attrs(cx: &LateContext,
                              attrs: &[ast::Attribute], sp: Span, desc: &'static str) {
    let has_inline = attrs
        .iter()
        .any(|a| a.name() == "inline" );
    if !has_inline {
        cx.span_lint(
            MISSING_INLINE_IN_PUBLIC_ITEMS,
            sp,
            &format!("missing `#[inline]` for {}", desc),
        );
    }
}

fn is_executable<'a, 'tcx>(cx: &LateContext<'a, 'tcx>) -> bool {
    use rustc::session::config::CrateType;

    cx.tcx.sess.crate_types.get().iter().any(|t: &CrateType| {
        match t {
            CrateType::CrateTypeExecutable => true,
            _ => false,
        }
    })
}

impl LintPass for MissingInline {
    fn get_lints(&self) -> LintArray {
        lint_array![MISSING_INLINE_IN_PUBLIC_ITEMS]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for MissingInline {
    fn check_item(&mut self, cx: &LateContext<'a, 'tcx>, it: &'tcx hir::Item) {
        if is_executable(cx) {
            return;
        }

        if !cx.access_levels.is_exported(it.id) {
            return;
        }
        match it.node {
            hir::ItemFn(..) => {
                let desc = "a function";
                check_missing_inline_attrs(cx, &it.attrs, it.span, desc);
            },
            hir::ItemTrait(ref _is_auto, ref _unsafe, ref _generics,
                           ref _bounds, ref trait_items)  => {
                // note: we need to check if the trait is exported so we can't use
                // `LateLintPass::check_trait_item` here.
                for tit in trait_items {
                    let tit_ = cx.tcx.hir.trait_item(tit.id);
                    match tit_.node {
                        hir::TraitItemKind::Const(..) |
                        hir::TraitItemKind::Type(..) => {},
                        hir::TraitItemKind::Method(..) => {
                            if tit.defaultness.has_value() {
                                // trait method with default body needs inline in case
                                // an impl is not provided
                                let desc = "a default trait method";
                                let item = cx.tcx.hir.expect_trait_item(tit.id.node_id);
                                check_missing_inline_attrs(cx, &item.attrs,
                                                                item.span, desc);
                            }
                        },
                    }
                }
            }
            hir::ItemConst(..) |
            hir::ItemEnum(..) |
            hir::ItemMod(..) |
            hir::ItemStatic(..) |
            hir::ItemStruct(..) |
            hir::ItemTraitAlias(..) |
            hir::ItemGlobalAsm(..) |
            hir::ItemTy(..) |
            hir::ItemUnion(..) |
            hir::ItemExistential(..) |
            hir::ItemExternCrate(..) |
            hir::ItemForeignMod(..) |
            hir::ItemImpl(..) |
            hir::ItemUse(..) => {},
        };
    }

    fn check_impl_item(&mut self, cx: &LateContext<'a, 'tcx>, impl_item: &'tcx hir::ImplItem) {
        use rustc::ty::{TraitContainer, ImplContainer};
        if is_executable(cx) {
            return;
        }

        // If the item being implemented is not exported, then we don't need #[inline]
        if !cx.access_levels.is_exported(impl_item.id) {
            return;
        }

        let desc = match impl_item.node {
            hir::ImplItemKind::Method(..) => "a method",
            hir::ImplItemKind::Const(..) |
            hir::ImplItemKind::Type(_) => return,
        };

        let def_id = cx.tcx.hir.local_def_id(impl_item.id);
        let trait_def_id = match cx.tcx.associated_item(def_id).container {
            TraitContainer(cid) => Some(cid),
            ImplContainer(cid) => cx.tcx.impl_trait_ref(cid).map(|t| t.def_id),
        };

        if let Some(trait_def_id) = trait_def_id {
            if let Some(n) = cx.tcx.hir.as_local_node_id(trait_def_id) {
                if !cx.access_levels.is_exported(n) {
                    // If a trait is being implemented for an item, and the
                    // trait is not exported, we don't need #[inline]
                    return;
                }
            }
        }

        check_missing_inline_attrs(cx, &impl_item.attrs, impl_item.span, desc);
    }
}
<|endoftext|>