<commit_before>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: Symbol,
    span: Span,
    len: usize,
    exemptions: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 > threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => {
                if !pat.span.from_expansion() {
                    self.check_ident(ident);
                }
            },
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
}

#[must_use]
fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
    for &list in ALLOWED_TO_BE_SIMILAR {
        if allowed_to_be_similar(interned_name, list) {
            return Some(list);
        }
    }
    None
}

#[must_use]
fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1
                        || levenstein_not_1(&interned_name, &existing_name.interned.as_str())
                    {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1
                        || levenstein_not_1(&existing_name.interned.as_str(), &interned_name)
                    {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let interned_str = existing_name.interned.as_str();
                    let mut existing_chars = interned_str.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
            interned: ident.name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<commit_msg>rustc_ast: `visit_mac` -> `visit_mac_call`<commit_after>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind,
};
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: Symbol,
    span: Span,
    len: usize,
    exemptions: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 > threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => {
                if !pat.span.from_expansion() {
                    self.check_ident(ident);
                }
            },
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
}

#[must_use]
fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
    for &list in ALLOWED_TO_BE_SIMILAR {
        if allowed_to_be_similar(interned_name, list) {
            return Some(list);
        }
    }
    None
}

#[must_use]
fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1
                        || levenstein_not_1(&interned_name, &existing_name.interned.as_str())
                    {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1
                        || levenstein_not_1(&existing_name.interned.as_str(), &interned_name)
                    {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let interned_str = existing_name.interned.as_str();
                    let mut existing_chars = interned_str.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
            interned: ident.name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<|endoftext|>


<commit_before>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Computing deltas between two ropes.

use std::collections::HashMap;

use compare::RopeScanner;
use delta::{Delta, DeltaElement};
use interval::Interval;
use rope::{Rope, RopeInfo, RopeDelta};
use tree::{Node, NodeInfo};

use memchr::memchr;

/// A trait implemented by various diffing strategies.
pub trait Diff<N: NodeInfo> {
    fn compute_delta(base: &Node<N>, target: &Node<N>) -> Delta<N>;
}

/// The minimum length of non-whitespace characters in a line before
/// we consider it for diffing purposes.
const MIN_SIZE: usize = 32;

/// A line-oriented, hash based diff algorithm.
///
/// This works by taking a hash of each line in either document that
/// has a length, ignoring leading whitespace, above some threshold.
///
/// Lines in the target document are matched against lines in the
/// base document. When a match is found, it is extended forwards
/// and backwards as far as possible.
///
/// This runs in O(n+m) in the lengths of the two ropes, and produces
/// results on a variety of workloads that are comparable in quality
/// (measured in terms of serialized diff size) with the results from
/// using a suffix array, while being an order of magnitude faster.
pub struct LineHashDiff;

impl Diff<RopeInfo> for LineHashDiff {
    fn compute_delta(base: &Rope, target: &Rope) -> RopeDelta {
        let mut builder = DiffBuilder::default();

        // before doing anything, scan top down and bottom up for like-ness.
        let mut scanner = RopeScanner::new(base, target);
        let (start_offset, diff_end) = scanner.find_min_diff_range();
        let target_end = target.len() - diff_end;

        if start_offset > 0 {
            builder.copy(0, 0, start_offset);
        }

        // if our preliminary scan finds no differences we're done
        if start_offset == base.len() && target.len() == base.len() {
            return builder.to_delta(base, target);
        }

        //TODO: because of how `lines_raw` returns Cows, we can't easily build
        //the lookup table without allocating. The eventual solution would be to have
        //a custom iter on the rope that returns suitable chunks.
        let base_string = String::from(base);
        let line_hashes = make_line_hashes(&base_string, MIN_SIZE);

        let mut offset = start_offset;

        // When we find a matching region, we extend it forwards and backwards.
        // we keep track of how far forward we extend it each time, to avoid
        // having a subsequent scan extend backwards over the same region.
        let mut prev_targ_end = start_offset;
        let mut prev_base_end = 0;

        for line in target.lines_raw(start_offset..target_end) {
            let non_ws = non_ws_offset(&line);
            if offset + non_ws < prev_targ_end {
                // no-op, but we don't break because we still want to bump offset
            } else if line.len() - non_ws >= MIN_SIZE {
                if let Some(base_off) = line_hashes.get(&line[non_ws..]) {
                    let targ_off = offset + non_ws;
                    let (left_dist, mut right_dist) = fast_expand_match(base, target,
                                                                        *base_off,
                                                                        targ_off,
                                                                        prev_targ_end);
                    if targ_off + right_dist > target_end {
                        // don't let last match expand past target_end
                        right_dist = target_end - targ_off;
                    }
                    let targ_start = targ_off - left_dist;
                    let base_start = base_off - left_dist;
                    let len = left_dist + right_dist;

                    // other parts of the code (Delta::factor) require that delta ops
                    // be in non-decreasing order, so we only actually copy a region
                    // when this is true. This algorithm was initially designed without
                    // this constraint; a better design would prioritize early matches,
                    // and more efficiently avoid searching in disallowed regions.
                    if base_start >= prev_base_end {
                        builder.copy(base_start, targ_start, len);
                        prev_targ_end = targ_start + len;
                        prev_base_end = base_start + len;
                    }
                }
            }
            offset += line.len();
        }

        if diff_end > 0 {
            builder.copy(base.len() - diff_end, target.len() - diff_end, diff_end);
        }

        builder.to_delta(base, target)
    }
}

/// Given two ropes and the offsets of two equal bytes, finds the largest
/// identical substring shared between the two ropes which contains the offset.
///
/// The return value is a pair of offsets, each of which represents an absolute
/// distance. That is to say, the position of the start and end boundaries
/// relative to the input offset.
fn fast_expand_match(base: &Rope, target: &Rope, base_off: usize, targ_off: usize,
                prev_match_targ_end: usize) -> (usize, usize) {

    let mut scanner = RopeScanner::new(base, target);
    let max_left = targ_off - prev_match_targ_end;
    let start = scanner.find_ne_char_left(base_off, targ_off, max_left);
    debug_assert!(start <= max_left, "{} <= {}", start, max_left);
    let end = scanner.find_ne_char_right(base_off, targ_off, None);
    (start.min(max_left), end)
}

#[inline]
fn non_ws_offset(s: &str) -> usize {
    s.as_bytes()
        .iter()
        .take_while(|b| **b == b' ' || **b == b'\t')
        .count()
}

/// Represents copying `len` bytes from base to target.
#[derive(Debug, Clone, Copy)]
struct DiffOp {
    target_idx: usize,
    base_idx: usize,
    len: usize,
}

/// Keeps track of copy ops during diff construction.
#[derive(Debug, Clone, Default)]
pub struct DiffBuilder {
    ops: Vec<DiffOp>,
}

impl DiffBuilder {
    fn copy(&mut self, base: usize, target: usize, len: usize) {
        if let Some(prev) = self.ops.last_mut() {
            let prev_end = prev.target_idx + prev.len;
            let base_end = prev.base_idx + prev.len;
            assert!(prev_end <= target, "{} <= {} prev {:?}", prev_end, target, prev);
            if prev_end == target && base_end == base {
                prev.len += len;
                return;
            }
        }
        self.ops.push(DiffOp { target_idx: target, base_idx: base, len: len })
    }

    fn to_delta(self, base: &Rope, target: &Rope) -> RopeDelta {
        let mut els = Vec::with_capacity(self.ops.len() * 2);
        let mut targ_pos = 0;
        for DiffOp { base_idx, target_idx, len } in self.ops {
            if target_idx > targ_pos {
                let iv = Interval::new_closed_open(targ_pos, target_idx);
                els.push(DeltaElement::Insert(target.subseq(iv)));
            }
            els.push(DeltaElement::Copy(base_idx, base_idx + len));
            targ_pos = target_idx + len;
        }

        if targ_pos < target.len() {
            let iv = Interval::new_closed_open(targ_pos, target.len());
            els.push(DeltaElement::Insert(target.subseq(iv)));
        }

        Delta { els, base_len: base.len() }
    }
}

/// Fast iterator over lines in a string, not removing newline characters.
struct LineScanner<'a> {
    inner: &'a str,
    idx: usize,
}

impl<'a> Iterator for LineScanner<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let idx = memchr(b'\n', &self.inner.as_bytes()[self.idx..])?;
        let result = &self.inner[self.idx..self.idx + idx+1];
        self.idx += idx + 1;
        Some(result)
    }
}

fn make_line_hashes<'a>(base: &'a str, min_size: usize) -> HashMap<&'a str, usize> {
    let mut offset = 0;
    let mut line_hashes = HashMap::with_capacity(base.len() / 60);
    let iter = LineScanner { inner: base, idx: 0 };
    for line in iter {
        let non_ws = non_ws_offset(&line);
        if line.len() - non_ws >= min_size {
            line_hashes.insert(&line[non_ws..], offset + non_ws);
        }
        offset += line.len();
    }
    line_hashes
}

#[cfg(test)]
mod tests {
    use super::*;

    static SMALL_ONE: &str = "This adds FixedSizeAdler32, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be removed when it fills up.

Current logic (and implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise a faster way of removing a sequence might be needed (one by one is inefficient).";

    static SMALL_TWO: &str = "This adds some function, I guess?, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be ground up and injested when it fills up.

Currently my sense of smell (and the pain of implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise crying might be needed (one by one is inefficient).";

    static INTERVAL_STR: &str = include_str!("../src/interval.rs");
    static BREAKS_STR: &str = include_str!("../src/breaks.rs");

    #[test]
    fn diff_smoke_test() {
        let one = SMALL_ONE.into();
        let two = SMALL_TWO.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);
    }

    #[test]
    fn test_larger_diff() {
        let one = INTERVAL_STR.into();
        let two = BREAKS_STR.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        let result = delta.apply(&one);
        assert_eq!(String::from(result), String::from(two));
    }
}
<commit_msg>Modify diff algorithm to handle delta constraints<commit_after>// Copyright 2018 The xi-editor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Computing deltas between two ropes.

use std::collections::HashMap;

use compare::RopeScanner;
use delta::{Delta, DeltaElement};
use interval::Interval;
use rope::{LinesMetric, Rope, RopeInfo, RopeDelta};
use tree::{Node, NodeInfo};

use memchr::memchr;

/// A trait implemented by various diffing strategies.
pub trait Diff<N: NodeInfo> {
    fn compute_delta(base: &Node<N>, target: &Node<N>) -> Delta<N>;
}

/// The minimum length of non-whitespace characters in a line before
/// we consider it for diffing purposes.
const MIN_SIZE: usize = 32;

/// A line-oriented, hash based diff algorithm.
///
/// This works by taking a hash of each line in either document that
/// has a length, ignoring leading whitespace, above some threshold.
///
/// Lines in the target document are matched against lines in the
/// base document. When a match is found, it is extended forwards
/// and backwards as far as possible.
///
/// This runs in O(n+m) in the lengths of the two ropes, and produces
/// results on a variety of workloads that are comparable in quality
/// (measured in terms of serialized diff size) with the results from
/// using a suffix array, while being an order of magnitude faster.
pub struct LineHashDiff;

impl Diff<RopeInfo> for LineHashDiff {
    fn compute_delta(base: &Rope, target: &Rope) -> RopeDelta {
        let mut builder = DiffBuilder::default();

        // before doing anything, scan top down and bottom up for like-ness.
        let mut scanner = RopeScanner::new(base, target);
        let (start_offset, diff_end) = scanner.find_min_diff_range();
        let target_end = target.len() - diff_end;

        if start_offset > 0 {
            builder.copy(0, 0, start_offset);
        }

        // if our preliminary scan finds no differences we're done
        if start_offset == base.len() && target.len() == base.len() {
            return builder.to_delta(base, target);
        }

        // TODO: because of how `lines_raw` returns Cows, we can't easily build
        // the lookup table without allocating. The eventual solution would be
        // to have a custom iter on the rope that returns suitable chunks.
        let base_string = String::from(base);
        let line_hashes = make_line_hashes(&base_string, MIN_SIZE);

        let line_count = target.measure::<LinesMetric>() + 1;
        let mut matches = Vec::with_capacity(line_count);

        let mut targ_line_offset = 0;
        let mut prev_base = 0;

        let mut needs_subseq = false;
        for line in target.lines_raw(start_offset..target_end) {
            let non_ws = non_ws_offset(&line);
            if line.len() - non_ws >= MIN_SIZE {
                if let Some(base_off) = line_hashes.get(&line[non_ws..]) {
                    let targ_off = targ_line_offset + non_ws;
                    matches.push((start_offset + targ_off, *base_off));
                    if *base_off < prev_base {
                        needs_subseq = true;
                    }
                    prev_base = *base_off;
                }
            }
            targ_line_offset += line.len();
        }

        // we now have an ordered list of matches and their positions.
        // to ensure that our delta only copies non-decreasing base regions,
        // we take the longest increasing subsequence.
        // TODO: a possible optimization here would be to expand matches
        // to adjacent lines first? this would be at best a small win though..

        let longest_subseq = if needs_subseq {
            longest_increasing_region_set(&matches)
        } else {
            matches
        };

        // for each matching region, we extend it forwards and backwards.
        // we keep track of how far forward we extend it each time, to avoid
        // having a subsequent scan extend backwards over the same region.
        let mut prev_end = start_offset;

        for (targ_off, base_off) in longest_subseq {
            if targ_off <= prev_end { continue; }
            let (left_dist, mut right_dist) = expand_match(base, target, base_off,
                                                           targ_off, prev_end);
            if targ_off + right_dist > target_end {
                // don't let last match expand past target_end
                right_dist = target_end - targ_off;
            }

            let targ_start = targ_off - left_dist;
            let base_start = base_off - left_dist;
            let len = left_dist + right_dist;
            prev_end = targ_start + len;

            builder.copy(base_start, targ_start, len);
        }

        if diff_end > 0 {
            builder.copy(base.len() - diff_end,
            target.len() - diff_end, diff_end);
        }

        builder.to_delta(base, target)
    }
}

/// Given two ropes and the offsets of two equal bytes, finds the largest
/// identical substring shared between the two ropes which contains the offset.
///
/// The return value is a pair of offsets, each of which represents an absolute
/// distance. That is to say, the position of the start and end boundaries
/// relative to the input offset.
fn expand_match(base: &Rope, target: &Rope, base_off: usize, targ_off: usize,
                prev_match_targ_end: usize) -> (usize, usize) {

    let mut scanner = RopeScanner::new(base, target);
    let max_left = targ_off - prev_match_targ_end;
    let start = scanner.find_ne_char_left(base_off, targ_off, max_left);
    debug_assert!(start <= max_left, "{} <= {}", start, max_left);
    let end = scanner.find_ne_char_right(base_off, targ_off, None);
    (start.min(max_left), end)
}

/// Finds the longest increasing subset of copyable regions. This is essentially
/// the longest increasing subsequence problem. This implementation is adapted
/// from https://codereview.stackexchange.com/questions/187337/longest-increasing-subsequence-algorithm
fn longest_increasing_region_set(items: &[(usize, usize)]) -> Vec<(usize, usize)> {
    let mut result = vec![0];
    let mut prev_chain = vec![0; items.len()];

    for i in 1..items.len() {
        // If the next item is greater than the last item of the current longest
        // subsequence, push its index at the end of the result and continue.
        let last_idx = *result.last().unwrap();
        if items[last_idx].1 < items[i].1 {
            prev_chain[i] = last_idx;
            result.push(i);
            continue;
        }

        let next_idx = match result.binary_search_by(
            |&j| items[j].1.cmp(&items[i].1)) {
                Ok(_) => continue, // we ignore duplicates
                Err(idx) => idx,
        };

        if &items[i].1 < &items[result[next_idx]].1 {
            if next_idx > 0 {
                prev_chain[i] = result[next_idx - 1];
            }
            result[next_idx] = i;
        }
    }

    // walk backwards from the last item in result to build the final sequence
    let mut u = result.len();
    let mut v = *result.last().unwrap();
    while u != 0 {
        u -= 1;
        result[u] = v;
        v = prev_chain[v];
    }
    result.iter().map(|i| items[*i]).collect()
}

#[inline]
fn non_ws_offset(s: &str) -> usize {
    s.as_bytes()
        .iter()
        .take_while(|b| **b == b' ' || **b == b'\t')
        .count()
}

/// Represents copying `len` bytes from base to target.
#[derive(Debug, Clone, Copy)]
struct DiffOp {
    target_idx: usize,
    base_idx: usize,
    len: usize,
}

/// Keeps track of copy ops during diff construction.
#[derive(Debug, Clone, Default)]
pub struct DiffBuilder {
    ops: Vec<DiffOp>,
}

impl DiffBuilder {
    fn copy(&mut self, base: usize, target: usize, len: usize) {
        if let Some(prev) = self.ops.last_mut() {
            let prev_end = prev.target_idx + prev.len;
            let base_end = prev.base_idx + prev.len;
            assert!(prev_end <= target, "{} <= {} prev {:?}", prev_end, target, prev);
            if prev_end == target && base_end == base {
                prev.len += len;
                return;
            }
        }
        self.ops.push(DiffOp { target_idx: target, base_idx: base, len: len })
    }

    fn to_delta(self, base: &Rope, target: &Rope) -> RopeDelta {
        let mut els = Vec::with_capacity(self.ops.len() * 2);
        let mut targ_pos = 0;
        for DiffOp { base_idx, target_idx, len } in self.ops {
            if target_idx > targ_pos {
                let iv = Interval::new_closed_open(targ_pos, target_idx);
                els.push(DeltaElement::Insert(target.subseq(iv)));
            }
            els.push(DeltaElement::Copy(base_idx, base_idx + len));
            targ_pos = target_idx + len;
        }

        if targ_pos < target.len() {
            let iv = Interval::new_closed_open(targ_pos, target.len());
            els.push(DeltaElement::Insert(target.subseq(iv)));
        }

        Delta { els, base_len: base.len() }
    }
}

/// Fast iterator over lines in a string, not removing newline characters.
struct LineScanner<'a> {
    inner: &'a str,
    idx: usize,
}

impl<'a> Iterator for LineScanner<'a> {
    type Item = &'a str;
    fn next(&mut self) -> Option<&'a str> {
        let idx = memchr(b'\n', &self.inner.as_bytes()[self.idx..])?;
        let result = &self.inner[self.idx..self.idx + idx+1];
        self.idx += idx + 1;
        Some(result)
    }
}

fn make_line_hashes<'a>(base: &'a str, min_size: usize) -> HashMap<&'a str, usize> {
    let mut offset = 0;
    let mut line_hashes = HashMap::with_capacity(base.len() / 60);
    let iter = LineScanner { inner: base, idx: 0 };
    for line in iter {
        let non_ws = non_ws_offset(&line);
        if line.len() - non_ws >= min_size {
            line_hashes.insert(&line[non_ws..], offset + non_ws);
        }
        offset += line.len();
    }
    line_hashes
}

#[cfg(test)]
mod tests {
    use super::*;

    static SMALL_ONE: &str = "This adds FixedSizeAdler32, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be removed when it fills up.

Current logic (and implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise a faster way of removing a sequence might be needed (one by one is inefficient).";

    static SMALL_TWO: &str = "This adds some function, I guess?, that has a size set at construction, and keeps bytes in a cyclic buffer of that size to be ground up and injested when it fills up.

Currently my sense of smell (and the pain of implementing Write) might be too much, since bytes will probably always be fed one by one anyway. Otherwise crying might be needed (one by one is inefficient).";

    static INTERVAL_STR: &str = include_str!("../src/interval.rs");
    static BREAKS_STR: &str = include_str!("../src/breaks.rs");

    #[test]
    fn diff_smoke_test() {
        let one = SMALL_ONE.into();
        let two = SMALL_TWO.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);

        let delta = LineHashDiff::compute_delta(&one, &two);
        println!("delta: {:?}", &delta);

        let result = delta.apply(&one);
        assert_eq!(result, two);
    }

    #[test]
    fn test_larger_diff() {
        let one = INTERVAL_STR.into();
        let two = BREAKS_STR.into();

        let delta = LineHashDiff::compute_delta(&one, &two);
        let result = delta.apply(&one);
        assert_eq!(String::from(result), String::from(two));
    }
}
<|endoftext|>


<commit_before>
use super::*;
#[ allow( unused_imports ) ]
use super::TheModule::meta as TheModule;

mod inc;
<commit_msg>!test<commit_after>
use super::*;
#[ allow( unused_imports ) ]
use super::TheModule::meta as TheModule;

mod inc;

<|endoftext|>


<commit_before>// rustfmt-wrap_comments: true
// rustfmt-error_on_line_overflow: false
// Enums test

#[atrr]
pub enum Test {
    A,
    B(u32, A /* comment */, SomeType),
    /// Doc comment
    C,
}

pub enum Foo<'a, Y: Baz>
where
    X: Whatever,
{
    A,
}

enum EmtpyWithComment {
    // Some comment
}

// C-style enum
enum Bar {
    A = 1,
    #[someAttr(test)] B = 2, // comment
    C,
}

enum LongVariants {
    First(
        LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment
        VARIANT,
    ),
    // This is the second variant
    Second,
}

enum StructLikeVariants {
    Normal(u32, String),
    StructLike {
        x: i32, // Test comment
        // Pre-comment
        #[Attr50] y: SomeType, // Aanother Comment
    },
    SL { a: A },
}

enum X {
    CreateWebGLPaintTask(
        Size2D<i32>,
        GLContextAttributes,
        IpcSender<Result<(IpcSender<CanvasMsg>, usize), String>>,
    ), // This is a post comment
}

pub enum EnumWithAttributes {
    // This is a pre comment
    // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    TupleVar(usize, usize, usize), /* AAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA
                                    * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
    // Pre Comment
    #[rustfmt_skip]
    SkippedItem(String,String,), // Post-comment
    #[another_attr]
    #[attr2]
    ItemStruct { x: usize, y: usize }, /* Comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
    // And another
    ForcedPreflight, /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                      * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
}

pub enum SingleTuple {
    // Pre Comment AAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Match(usize, usize, String), /* Post-comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
}

pub enum SingleStruct {
    Match { name: String, loc: usize }, // Post-comment
}

pub enum GenericEnum<I, T>
where
    I: Iterator<Item = T>,
{
    // Pre Comment
    Left { list: I, root: T },  // Post-comment
    Right { list: I, root: T }, // Post Comment
}

enum EmtpyWithComment {
    // Some comment
}

enum TestFormatFails {
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
}

fn nested_enum_test() {
    if true {
        enum TestEnum {
            One(
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
            ), /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA
                * AAAAAAAAAAAAAAAAAAAAAA */
            Two, /* AAAAAAAAAAAAAAAAAA  AAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                  * AAAAAAAAAAAAAAAAAA */
        }
        enum TestNestedFormatFail {
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
        }
    }
}

pub struct EmtpyWithComment {
    // FIXME: Implement this struct
}

// #1115
pub enum Bencoding<'i> {
    Str(&'i [u8]),
    Int(i64),
    List(Vec<Bencoding<'i>>),
    /// A bencoded dict value. The first element the slice of bytes in the
    /// source that the dict is
    /// composed of. The second is the dict, decoded into an ordered map.
    // TODO make Dict "structlike" AKA name the two values.
    Dict(&'i [u8], BTreeMap<&'i [u8], Bencoding<'i>>),
}

// #1261
pub enum CoreResourceMsg {
    SetCookieForUrl(
        ServoUrl,
        #[serde(deserialize_with = "::hyper_serde::deserialize",
                serialize_with = "::hyper_serde::serialize")]
        Cookie,
        CookieSource,
    ),
}

enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{}
enum Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{}
enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{}
enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{
    Foo,
}

// #1046
pub enum Entry<'a, K: 'a, V: 'a> {
    Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>),
    Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>),
}

// #2081
pub enum ForegroundColor {
    CYAN =
        (winapi::FOREGROUND_INTENSITY | winapi::FOREGROUND_GREEN | winapi::FOREGROUND_BLUE) as u16,
}

// #2098
pub enum E<'a> {
    V(<std::slice::Iter<'a, Xxxxxxxxxxxxxx> as Iterator>::Item),
}
<commit_msg>Update a test<commit_after>// rustfmt-wrap_comments: true
// rustfmt-error_on_line_overflow: false
// Enums test

#[atrr]
pub enum Test {
    A,
    B(u32, A /* comment */, SomeType),
    /// Doc comment
    C,
}

pub enum Foo<'a, Y: Baz>
where
    X: Whatever,
{
    A,
}

enum EmtpyWithComment {
    // Some comment
}

// C-style enum
enum Bar {
    A = 1,
    #[someAttr(test)] B = 2, // comment
    C,
}

enum LongVariants {
    First(
        LOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOONG, // comment
        VARIANT,
    ),
    // This is the second variant
    Second,
}

enum StructLikeVariants {
    Normal(u32, String),
    StructLike {
        x: i32, // Test comment
        // Pre-comment
        #[Attr50] y: SomeType, // Aanother Comment
    },
    SL { a: A },
}

enum X {
    CreateWebGLPaintTask(
        Size2D<i32>,
        GLContextAttributes,
        IpcSender<Result<(IpcSender<CanvasMsg>, usize), String>>,
    ), // This is a post comment
}

pub enum EnumWithAttributes {
    // This is a pre comment
    // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    TupleVar(usize, usize, usize), /* AAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAA
                                    * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
    // Pre Comment
    #[rustfmt_skip]
    SkippedItem(String,String,), // Post-comment
    #[another_attr]
    #[attr2]
    ItemStruct { x: usize, y: usize }, /* Comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
    // And another
    ForcedPreflight, /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                      * AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
}

pub enum SingleTuple {
    // Pre Comment AAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    // AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
    Match(usize, usize, String), /* Post-comment AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA */
}

pub enum SingleStruct {
    Match { name: String, loc: usize }, // Post-comment
}

pub enum GenericEnum<I, T>
where
    I: Iterator<Item = T>,
{
    // Pre Comment
    Left { list: I, root: T },  // Post-comment
    Right { list: I, root: T }, // Post Comment
}

enum EmtpyWithComment {
    // Some comment
}

enum TestFormatFails {
    AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
}

fn nested_enum_test() {
    if true {
        enum TestEnum {
            One(
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
                usize,
            ), /* AAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAA
                * AAAAAAAAAAAAAAAAAAAAAA */
            Two, /* AAAAAAAAAAAAAAAAAA  AAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
                  * AAAAAAAAAAAAAAAAAA */
        }
        enum TestNestedFormatFail {
            AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,
        }
    }
}

pub struct EmtpyWithComment {
    // FIXME: Implement this struct
}

// #1115
pub enum Bencoding<'i> {
    Str(&'i [u8]),
    Int(i64),
    List(Vec<Bencoding<'i>>),
    /// A bencoded dict value. The first element the slice of bytes in the source that the dict is
    /// composed of. The second is the dict, decoded into an ordered map.
    // TODO make Dict "structlike" AKA name the two values.
    Dict(&'i [u8], BTreeMap<&'i [u8], Bencoding<'i>>),
}

// #1261
pub enum CoreResourceMsg {
    SetCookieForUrl(
        ServoUrl,
        #[serde(deserialize_with = "::hyper_serde::deserialize",
                serialize_with = "::hyper_serde::serialize")]
        Cookie,
        CookieSource,
    ),
}

enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{}
enum Looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{}
enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{}
enum Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
{
    Foo,
}

// #1046
pub enum Entry<'a, K: 'a, V: 'a> {
    Vacant(#[stable(feature = "rust1", since = "1.0.0")] VacantEntry<'a, K, V>),
    Occupied(#[stable(feature = "rust1", since = "1.0.0")] OccupiedEntry<'a, K, V>),
}

// #2081
pub enum ForegroundColor {
    CYAN =
        (winapi::FOREGROUND_INTENSITY | winapi::FOREGROUND_GREEN | winapi::FOREGROUND_BLUE) as u16,
}

// #2098
pub enum E<'a> {
    V(<std::slice::Iter<'a, Xxxxxxxxxxxxxx> as Iterator>::Item),
}
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::mpsc as std_mpsc;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use futures::sync::mpsc;
use futures::{future, stream as streams, Async, Future, Poll, Sink, Stream};
use grpcio::*;
use grpcio_proto::example::route_guide::*;
use grpcio_proto::example::route_guide_grpc::*;

type Handler<T> = Arc<Mutex<Option<Box<T>>>>;
type BoxFuture = Box<dyn Future<Item = (), Error = ()> + Send + 'static>;
type RecordRouteHandler =
    Handler<dyn Fn(RequestStream<Point>, ClientStreamingSink<RouteSummary>) -> BoxFuture + Send>;
type RouteChatHandler =
    Handler<dyn Fn(RequestStream<RouteNote>, DuplexSink<RouteNote>) -> BoxFuture + Send>;

#[derive(Clone)]
struct CancelService {
    list_feature_listener: Arc<Mutex<Option<std_mpsc::Sender<()>>>>,
    record_route_handler: RecordRouteHandler,
    route_chat_handler: RouteChatHandler,
}

impl CancelService {
    fn new() -> CancelService {
        CancelService {
            list_feature_listener: Arc::default(),
            record_route_handler: Arc::new(Mutex::new(None)),
            route_chat_handler: Arc::new(Mutex::new(None)),
        }
    }
}

impl RouteGuide for CancelService {
    fn get_feature(&mut self, _: RpcContext<'_>, _: Point, sink: UnarySink<Feature>) {
        // Drop the sink, client should receive Cancelled.
        drop(sink);
    }

    fn list_features(
        &mut self,
        ctx: RpcContext<'_>,
        _: Rectangle,
        sink: ServerStreamingSink<Feature>,
    ) {
        // Drop the sink, client should receive Cancelled.
        let listener = match self.list_feature_listener.lock().unwrap().take() {
            Some(l) => l,
            None => {
                drop(sink);
                return;
            }
        };
        let (tx, rx) = mpsc::unbounded();

        thread::spawn(move || loop {
            tx.unbounded_send(1).unwrap();
            thread::sleep(Duration::from_secs(5));
        });

        let f = rx
            .map(|_| {
                let f = Feature::default();
                (f, WriteFlags::default())
            })
            .forward(sink.sink_map_err(|_| ()))
            .map(|_| ())
            .map_err(|_| ())
            .then(move |_| {
                let _ = listener.send(());
                Ok(())
            });

        ctx.spawn(f);
    }

    fn record_route(
        &mut self,
        ctx: RpcContext<'_>,
        stream: RequestStream<Point>,
        sink: ClientStreamingSink<RouteSummary>,
    ) {
        let handler = self.record_route_handler.lock().unwrap();
        if handler.is_some() {
            let f = (handler.as_ref().unwrap())(stream, sink);
            ctx.spawn(f);
        }
    }

    fn route_chat(
        &mut self,
        ctx: RpcContext<'_>,
        stream: RequestStream<RouteNote>,
        sink: DuplexSink<RouteNote>,
    ) {
        let handler = self.route_chat_handler.lock().unwrap();
        if handler.is_some() {
            let f = (handler.as_ref().unwrap())(stream, sink);
            ctx.spawn(f);
        }
    }
}

fn check_cancel<S, T>(rx: S, sink: bool)
where
    S: Stream<Item = T, Error = Error>,
{
    match rx.into_future().wait() {
        Err((Error::RpcFailure(s), _)) | Err((Error::RpcFinished(Some(s)), _)) => {
            assert_eq!(s.status, RpcStatusCode::CANCELLED)
        }
        Err((Error::RemoteStopped, _)) if sink => return,
        Err((e, _)) => panic!("expected cancel, but got: {:?}", e),
        Ok(_) => panic!("expected error, but got: Ok(_)"),
    }
}

fn prepare_suite() -> (CancelService, RouteGuideClient, Server) {
    let env = Arc::new(EnvBuilder::new().build());
    let service = CancelService::new();
    let mut server = ServerBuilder::new(env.clone())
        .register_service(create_route_guide(service.clone()))
        .bind("127.0.0.1", 0)
        .build()
        .unwrap();
    server.start();
    let port = server.bind_addrs()[0].1;
    let ch = ChannelBuilder::new(env).connect(&format!("127.0.0.1:{}", port));
    let client = RouteGuideClient::new(ch);
    (service, client, server)
}

#[test]
fn test_client_cancel_on_dropping() {
    let (service, client, _server) = prepare_suite();

    // Client streaming.
    *service.record_route_handler.lock().unwrap() = Some(Box::new(|stream, sink| {
        // Start the call and keep the stream and the sink.
        let f = stream.for_each(|_| Ok(())).then(|_| {
            let _sink = sink;
            Ok(())
        });
        Box::new(f)
    }));
    let (tx, rx) = client.record_route().unwrap();
    drop(tx);
    check_cancel(rx.into_stream(), false);

    let (tx, rx) = client.record_route().unwrap();
    drop(rx);
    check_cancel(tx.send(Default::default()).into_stream(), true);

    // Duplex streaming.
    *service.record_route_handler.lock().unwrap() = Some(Box::new(|stream, sink| {
        // Start the call and keep the stream and the sink.
        let f = stream.for_each(|_| Ok(())).then(|_| {
            let _sink = sink;
            Ok(())
        });
        Box::new(f)
    }));
    let (tx, rx) = client.route_chat().unwrap();
    drop(tx);
    check_cancel(rx, false);

    let (tx, rx) = client.route_chat().unwrap();
    drop(rx);
    check_cancel(tx.send(Default::default()).into_stream(), true);
}

#[test]
fn test_server_cancel_on_dropping() {
    let (service, client, _server) = prepare_suite();

    // Unary
    let rx = client.get_feature_async(&Default::default()).unwrap();
    check_cancel(rx.into_stream(), false);

    // Server streaming
    let rx = client.list_features(&Default::default()).unwrap();
    check_cancel(rx, false);

    // Start the call, keep the stream and drop the sink.
    fn drop_sink<S, R, T>(stream: S, sink: T) -> BoxFuture
    where
        S: Stream<Item = R, Error = Error> + Send + 'static,
        R: Send + 'static,
        T: Send + 'static,
    {
        let f = stream
            .for_each(|_| Ok(()))
            .join(future::result(Ok(())).map(move |_| {
                drop(sink);
            }))
            .then(|_| Ok(()));
        Box::new(f)
    }

    // Start the call, drop the stream and keep the sink.
    fn drop_stream<S, R, T>(stream: S, sink: T) -> BoxFuture
    where
        S: Stream<Item = R, Error = Error> + Send + 'static,
        R: Send + 'static,
        T: Send + 'static,
    {
        let mut stream = Some(stream);
        let f = streams::poll_fn(move || -> Poll<Option<()>, ()> {
            if stream.is_some() {
                let s = stream.as_mut().unwrap();
                // start the call.
                let _ = s.poll();
            }
            // drop the stream.
            stream.take();
            Ok(Async::NotReady)
        });
        // It never resolves.
        let f = f.for_each(|_| Ok(())).then(move |_| {
            let _sink = sink;
            Ok(())
        });
        Box::new(f)
    }

    // Client streaming, drop sink.
    *service.record_route_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_sink(stream, sink)));
    let (_tx, rx) = client.record_route().unwrap();
    check_cancel(rx.into_stream(), false);

    // Client streaming, drop stream.
    *service.record_route_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_stream(stream, sink)));
    let (_tx, rx) = client.record_route().unwrap();
    check_cancel(rx.into_stream(), false);

    // Duplex streaming, drop sink.
    *service.route_chat_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_sink(stream, sink)));
    let (_tx, rx) = client.route_chat().unwrap();
    check_cancel(rx, false);

    // Duplex streaming, drop stream.
    *service.route_chat_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_stream(stream, sink)));
    let (_tx, rx) = client.route_chat().unwrap();
    check_cancel(rx, false);
}

#[test]
fn test_early_exit() {
    let (service, client, _server) = prepare_suite();
    let (tx, rx) = std_mpsc::channel();
    *service.list_feature_listener.lock().unwrap() = Some(tx);

    let rect = Rectangle::default();
    let l = client.list_features(&rect).unwrap();
    let f = l.into_future();
    match f.wait() {
        Ok((Some(_), _)) => {}
        Ok((None, _)) => panic!("should have result"),
        Err((e, _)) => panic!("unexpected error {:?}", e),
    };

    rx.recv_timeout(Duration::from_secs(1)).unwrap();
}
<commit_msg>Fix panic in test_early_exit (#440)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::sync::mpsc as std_mpsc;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;

use futures::sync::mpsc;
use futures::{future, stream as streams, Async, Future, Poll, Sink, Stream};
use grpcio::*;
use grpcio_proto::example::route_guide::*;
use grpcio_proto::example::route_guide_grpc::*;

type Handler<T> = Arc<Mutex<Option<Box<T>>>>;
type BoxFuture = Box<dyn Future<Item = (), Error = ()> + Send + 'static>;
type RecordRouteHandler =
    Handler<dyn Fn(RequestStream<Point>, ClientStreamingSink<RouteSummary>) -> BoxFuture + Send>;
type RouteChatHandler =
    Handler<dyn Fn(RequestStream<RouteNote>, DuplexSink<RouteNote>) -> BoxFuture + Send>;

#[derive(Clone)]
struct CancelService {
    list_feature_listener: Arc<Mutex<Option<std_mpsc::Sender<()>>>>,
    record_route_handler: RecordRouteHandler,
    route_chat_handler: RouteChatHandler,
}

impl CancelService {
    fn new() -> CancelService {
        CancelService {
            list_feature_listener: Arc::default(),
            record_route_handler: Arc::new(Mutex::new(None)),
            route_chat_handler: Arc::new(Mutex::new(None)),
        }
    }
}

impl RouteGuide for CancelService {
    fn get_feature(&mut self, _: RpcContext<'_>, _: Point, sink: UnarySink<Feature>) {
        // Drop the sink, client should receive Cancelled.
        drop(sink);
    }

    fn list_features(
        &mut self,
        ctx: RpcContext<'_>,
        _: Rectangle,
        sink: ServerStreamingSink<Feature>,
    ) {
        // Drop the sink, client should receive Cancelled.
        let listener = match self.list_feature_listener.lock().unwrap().take() {
            Some(l) => l,
            None => {
                drop(sink);
                return;
            }
        };
        let (tx, rx) = mpsc::unbounded();

        thread::spawn(move || loop {
            if tx.unbounded_send(1).is_ok() {
                thread::sleep(Duration::from_secs(5));
            } else {
                break;
            }
        });

        let f = rx
            .map(|_| {
                let f = Feature::default();
                (f, WriteFlags::default())
            })
            .forward(sink.sink_map_err(|_| ()))
            .map(|_| ())
            .map_err(|_| ())
            .then(move |_| {
                let _ = listener.send(());
                Ok(())
            });

        ctx.spawn(f);
    }

    fn record_route(
        &mut self,
        ctx: RpcContext<'_>,
        stream: RequestStream<Point>,
        sink: ClientStreamingSink<RouteSummary>,
    ) {
        let handler = self.record_route_handler.lock().unwrap();
        if handler.is_some() {
            let f = (handler.as_ref().unwrap())(stream, sink);
            ctx.spawn(f);
        }
    }

    fn route_chat(
        &mut self,
        ctx: RpcContext<'_>,
        stream: RequestStream<RouteNote>,
        sink: DuplexSink<RouteNote>,
    ) {
        let handler = self.route_chat_handler.lock().unwrap();
        if handler.is_some() {
            let f = (handler.as_ref().unwrap())(stream, sink);
            ctx.spawn(f);
        }
    }
}

fn check_cancel<S, T>(rx: S, sink: bool)
where
    S: Stream<Item = T, Error = Error>,
{
    match rx.into_future().wait() {
        Err((Error::RpcFailure(s), _)) | Err((Error::RpcFinished(Some(s)), _)) => {
            assert_eq!(s.status, RpcStatusCode::CANCELLED)
        }
        Err((Error::RemoteStopped, _)) if sink => return,
        Err((e, _)) => panic!("expected cancel, but got: {:?}", e),
        Ok(_) => panic!("expected error, but got: Ok(_)"),
    }
}

fn prepare_suite() -> (CancelService, RouteGuideClient, Server) {
    let env = Arc::new(EnvBuilder::new().build());
    let service = CancelService::new();
    let mut server = ServerBuilder::new(env.clone())
        .register_service(create_route_guide(service.clone()))
        .bind("127.0.0.1", 0)
        .build()
        .unwrap();
    server.start();
    let port = server.bind_addrs()[0].1;
    let ch = ChannelBuilder::new(env).connect(&format!("127.0.0.1:{}", port));
    let client = RouteGuideClient::new(ch);
    (service, client, server)
}

#[test]
fn test_client_cancel_on_dropping() {
    let (service, client, _server) = prepare_suite();

    // Client streaming.
    *service.record_route_handler.lock().unwrap() = Some(Box::new(|stream, sink| {
        // Start the call and keep the stream and the sink.
        let f = stream.for_each(|_| Ok(())).then(|_| {
            let _sink = sink;
            Ok(())
        });
        Box::new(f)
    }));
    let (tx, rx) = client.record_route().unwrap();
    drop(tx);
    check_cancel(rx.into_stream(), false);

    let (tx, rx) = client.record_route().unwrap();
    drop(rx);
    check_cancel(tx.send(Default::default()).into_stream(), true);

    // Duplex streaming.
    *service.record_route_handler.lock().unwrap() = Some(Box::new(|stream, sink| {
        // Start the call and keep the stream and the sink.
        let f = stream.for_each(|_| Ok(())).then(|_| {
            let _sink = sink;
            Ok(())
        });
        Box::new(f)
    }));
    let (tx, rx) = client.route_chat().unwrap();
    drop(tx);
    check_cancel(rx, false);

    let (tx, rx) = client.route_chat().unwrap();
    drop(rx);
    check_cancel(tx.send(Default::default()).into_stream(), true);
}

#[test]
fn test_server_cancel_on_dropping() {
    let (service, client, _server) = prepare_suite();

    // Unary
    let rx = client.get_feature_async(&Default::default()).unwrap();
    check_cancel(rx.into_stream(), false);

    // Server streaming
    let rx = client.list_features(&Default::default()).unwrap();
    check_cancel(rx, false);

    // Start the call, keep the stream and drop the sink.
    fn drop_sink<S, R, T>(stream: S, sink: T) -> BoxFuture
    where
        S: Stream<Item = R, Error = Error> + Send + 'static,
        R: Send + 'static,
        T: Send + 'static,
    {
        let f = stream
            .for_each(|_| Ok(()))
            .join(future::result(Ok(())).map(move |_| {
                drop(sink);
            }))
            .then(|_| Ok(()));
        Box::new(f)
    }

    // Start the call, drop the stream and keep the sink.
    fn drop_stream<S, R, T>(stream: S, sink: T) -> BoxFuture
    where
        S: Stream<Item = R, Error = Error> + Send + 'static,
        R: Send + 'static,
        T: Send + 'static,
    {
        let mut stream = Some(stream);
        let f = streams::poll_fn(move || -> Poll<Option<()>, ()> {
            if stream.is_some() {
                let s = stream.as_mut().unwrap();
                // start the call.
                let _ = s.poll();
            }
            // drop the stream.
            stream.take();
            Ok(Async::NotReady)
        });
        // It never resolves.
        let f = f.for_each(|_| Ok(())).then(move |_| {
            let _sink = sink;
            Ok(())
        });
        Box::new(f)
    }

    // Client streaming, drop sink.
    *service.record_route_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_sink(stream, sink)));
    let (_tx, rx) = client.record_route().unwrap();
    check_cancel(rx.into_stream(), false);

    // Client streaming, drop stream.
    *service.record_route_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_stream(stream, sink)));
    let (_tx, rx) = client.record_route().unwrap();
    check_cancel(rx.into_stream(), false);

    // Duplex streaming, drop sink.
    *service.route_chat_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_sink(stream, sink)));
    let (_tx, rx) = client.route_chat().unwrap();
    check_cancel(rx, false);

    // Duplex streaming, drop stream.
    *service.route_chat_handler.lock().unwrap() =
        Some(Box::new(|stream, sink| drop_stream(stream, sink)));
    let (_tx, rx) = client.route_chat().unwrap();
    check_cancel(rx, false);
}

#[test]
fn test_early_exit() {
    let (service, client, _server) = prepare_suite();
    let (tx, rx) = std_mpsc::channel();
    *service.list_feature_listener.lock().unwrap() = Some(tx);

    let rect = Rectangle::default();
    let l = client.list_features(&rect).unwrap();
    let f = l.into_future();
    match f.wait() {
        Ok((Some(_), _)) => {}
        Ok((None, _)) => panic!("should have result"),
        Err((e, _)) => panic!("unexpected error {:?}", e),
    };

    rx.recv_timeout(Duration::from_secs(1)).unwrap();
}
<|endoftext|>


<commit_before>use crate::ast::SyntaxKind;
use crate::grammar::expr::expression;
use crate::parser::CompletedMarker;
use crate::parser::CspParser;
use crate::token::Token;
use crate::token::TokenType;

pub fn path_expr(p: &mut CspParser) -> CompletedMarker<SyntaxKind, Token> {
    let m = p.mark();
    p.expect_one_of(vec![TokenType::Dot, TokenType::Name]);

    while p.at(TokenType::Dot) {
        p.bump();
        p.expect(TokenType::Name);
    }

    m.complete(p, SyntaxKind::PathExpr)
}

pub fn list_or_paren_expr(p: &mut CspParser) -> CompletedMarker<SyntaxKind, Token> {
    assert!(p.at(TokenType::OpenParenthesis));

    let m = p.mark();
    p.bump();

    let mut non_empty = false;
    let mut has_comma = false;

    while !p.at(TokenType::Eof) && !p.at(TokenType::CloseParenthesis) {
        // TODO: Validate that we're at a valid expression token.
        non_empty = true;
        expression(p);

        if !p.at(TokenType::CloseParenthesis) {
            has_comma = true;
            p.expect(TokenType::Comma);
        }
    }

    p.expect(TokenType::CloseParenthesis);
    m.complete(
        p,
        if non_empty && !has_comma {
            SyntaxKind::ParenExpr
        } else {
            SyntaxKind::ListExpr
        },
    )
}

pub fn literal_expr(p: &mut CspParser) -> CompletedMarker<SyntaxKind, Token> {
    let m = p.mark();
    p.expect_one_of(vec![TokenType::String, TokenType::Integer]);
    m.complete(p, SyntaxKind::LiteralExpr)
}

pub fn is_at_path_start(p: &CspParser, offset: usize) -> bool {
    let tok = p.nth(offset);
    tok == SyntaxKind::Token(TokenType::Dot) || tok == SyntaxKind::Token(TokenType::Name)
}
<commit_msg>Add tests for parsing atom expressions<commit_after>use crate::ast::SyntaxKind;
use crate::grammar::expr::expression;
use crate::parser::CompletedMarker;
use crate::parser::CspParser;
use crate::token::Token;
use crate::token::TokenType;

pub fn path_expr(p: &mut CspParser) -> CompletedMarker<SyntaxKind, Token> {
    let m = p.mark();

    if p.at(TokenType::Dot) {
        p.bump();
    }

    p.expect(TokenType::Name);

    while p.at(TokenType::Dot) {
        p.bump();
        p.expect(TokenType::Name);
    }

    m.complete(p, SyntaxKind::PathExpr)
}

pub fn list_or_paren_expr(p: &mut CspParser) -> CompletedMarker<SyntaxKind, Token> {
    assert!(p.at(TokenType::OpenParenthesis));

    let m = p.mark();
    p.bump();

    let mut non_empty = false;
    let mut has_comma = false;

    while !p.at(TokenType::Eof) && !p.at(TokenType::CloseParenthesis) {
        // TODO: Validate that we're at a valid expression token.
        non_empty = true;
        expression(p);

        if !p.at(TokenType::CloseParenthesis) {
            has_comma = true;
            p.expect(TokenType::Comma);
        }
    }

    p.expect(TokenType::CloseParenthesis);
    m.complete(
        p,
        if non_empty && !has_comma {
            SyntaxKind::ParenExpr
        } else {
            SyntaxKind::ListExpr
        },
    )
}

pub fn literal_expr(p: &mut CspParser) -> CompletedMarker<SyntaxKind, Token> {
    let m = p.mark();
    p.expect_one_of(vec![TokenType::String, TokenType::Integer]);
    m.complete(p, SyntaxKind::LiteralExpr)
}

pub fn is_at_path_start(p: &CspParser, offset: usize) -> bool {
    let tok = p.nth(offset);
    tok == SyntaxKind::Token(TokenType::Dot) || tok == SyntaxKind::Token(TokenType::Name)
}

#[test]
fn parse_global_path() {
    crate::grammar::test::test_parser(r#"
        callstub(<marker type="pathexpr">.global.item</marker>);
    "#)
}

#[test]
fn parse_nested_path() {
    crate::grammar::test::test_parser(r#"
        callstub(<marker type="pathexpr">nested1.nested2.nested3</marker>);
    "#)
}

#[test]
fn parse_list_expr() {
    crate::grammar::test::test_parser(r#"
        callstub(<marker type="listexpr">(item1, item2, item3)</marker>);
    "#)
}

#[test]
fn parse_paren_expr() {
    crate::grammar::test::test_parser(r#"
        callstub(<marker type="parenexpr">(a && b)</marker>);
    "#)
}
<|endoftext|>