<commit_before>use crate::utils::{span_lint, span_lint_and_then};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, impl_lint_pass};
use syntax::ast::*;
use syntax::attr;
use syntax::source_map::Span;
use syntax::symbol::LocalInternedString;
use syntax::visit::{walk_block, walk_expr, walk_pat, Visitor};

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: LocalInternedString,
    span: Span,
    len: usize,
    whitelist: &'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 WHITELIST: &[&[&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.node {
            PatKind::Ident(_, ident, _) => self.check_ident(ident),
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
    for &allow in WHITELIST {
        if whitelisted(interned_name, allow) {
            return Some(allow);
        }
    }
    None
}

fn whitelisted(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 whitelisted(&interned_name, existing_name.whitelist) {
                continue;
            }
            let mut split_at = None;
            if existing_name.len > count {
                if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
                    continue;
                }
            } else if existing_name.len < count {
                if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
                    continue;
                }
            } else {
                let mut interned_chars = interned_name.chars();
                let mut existing_chars = existing_name.interned.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 {
            whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
            interned: interned_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| {
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            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
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Fn(ref decl, _, _, ref blk) = item.node {
            do_check(self, cx, &item.attrs, decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &ImplItem) {
        if let ImplItemKind::Method(ref sig, ref blk) = item.node {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, 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()`.
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>Update non_expressive_names for or patterns<commit_after>use crate::utils::{span_lint, span_lint_and_then};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_tool_lint, impl_lint_pass};
use syntax::ast::*;
use syntax::attr;
use syntax::source_map::Span;
use syntax::symbol::LocalInternedString;
use syntax::visit::{walk_block, walk_expr, walk_pat, Visitor};

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: LocalInternedString,
    span: Span,
    len: usize,
    whitelist: &'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 WHITELIST: &[&[&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.node {
            PatKind::Ident(_, ident, _) => 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),
        }
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

fn get_whitelist(interned_name: &str) -> Option<&'static [&'static str]> {
    for &allow in WHITELIST {
        if whitelisted(interned_name, allow) {
            return Some(allow);
        }
    }
    None
}

fn whitelisted(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 whitelisted(&interned_name, existing_name.whitelist) {
                continue;
            }
            let mut split_at = None;
            if existing_name.len > count {
                if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned) {
                    continue;
                }
            } else if existing_name.len < count {
                if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned, &interned_name) {
                    continue;
                }
            } else {
                let mut interned_chars = interned_name.chars();
                let mut existing_chars = existing_name.interned.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 {
            whitelist: get_whitelist(&interned_name).unwrap_or(&[]),
            interned: interned_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
    }
    fn visit_mac(&mut self, _mac: &Mac) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if let ItemKind::Fn(ref decl, _, _, ref blk) = item.node {
            do_check(self, cx, &item.attrs, decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &ImplItem) {
        if let ImplItemKind::Method(ref sig, ref blk) = item.node {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, 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()`.
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>use itertools::Itertools;

use fall_tree::{AstNode, AstClass, Text};
use fall_tree::search::{child_of_type, child_of_type_exn};
use fall_tree::visitor::{Visitor, NodeVisitor};

use super::Analysis;
use ::{Expr, CallExpr, SynRule, IDENT, SIMPLE_STRING};

#[derive(Debug, Eq, PartialEq)]
pub enum CallKind<'f> {
    Eof,
    Any,
    Commit,

    Not(Expr<'f>),
    Layer(Expr<'f>, Expr<'f>),
    WithSkip(Expr<'f>, Expr<'f>),

    Enter(u32, Expr<'f>),
    Exit(u32, Expr<'f>),
    IsIn(u32),

    RuleCall(SynRule<'f>, Vec<(u32, Expr<'f>)>),
}

pub fn resolve<'f>(a: &Analysis<'f>, call: CallExpr<'f>) -> Option<CallKind<'f>> {
    let n_args = call.args().count();
    let expect_args = |n_expected: usize| {
        if n_expected != n_args {
            a.diagnostics.error(
                call.node(),
                format!("Wrong number of arguments, expected {}, got {}", n_expected, n_args)
            )
        }
    };

    let zero_arg = vec![
        ("any", CallKind::Any),
        ("commit", CallKind::Commit),
        ("eof", CallKind::Eof)
    ];

    for (name, kind) in zero_arg.into_iter() {
        if call.fn_name() == name {
            expect_args(0);
            return Some(kind);
        }
    }

    if call.fn_name() == "not" {
        expect_args(1);
        return call.args().next().map(CallKind::Not);
    }

    let two_arg = vec![
        ("layer", CallKind::Layer as fn(_, _) -> _),
        ("with_skip", CallKind::WithSkip)
    ];

    for (name, kind) in two_arg.into_iter() {
        if call.fn_name() == name {
            expect_args(2);
            return call.args().next_tuple()
                .map(|(a, b)| kind(a, b));
        }
    }

    if call.fn_name() == "is_in" {
        expect_args(1);
        return call.args().next()
            .and_then(|ctx| resolve_context(a, ctx))
            .map(CallKind::IsIn);
    }

    let layer_expr = vec![
        ("enter", CallKind::Enter as fn(_, _) -> _),
        ("exit", CallKind::Exit),
    ];

    for (name, kind) in layer_expr.into_iter() {
        if call.fn_name() == name {
            expect_args(2);
            return call.args().next_tuple()
                .and_then(|(ctx, body)| {
                    resolve_context(a, ctx).map(|ctx| kind(ctx, body))
                });
        }
    }

    if let Some(rule) = a.file.resolve_rule(call.fn_name()) {
        if let Some(parameters) = rule.parameters() {
            let params = parameters.parameters()
                .map(|p| p.idx())
                .zip(call.args())
                .collect();
            return Some(CallKind::RuleCall(rule, params));
        }
    }

    a.diagnostics.error(
        child_of_type_exn(call.node(), IDENT),
        "Unresolved reference"
    );
    return None;
}

pub fn contexts<'f>(a: &Analysis<'f>) -> Vec<Text<'f>> {
    Visitor(Vec::new())
        .visit::<CallExpr, _>(|contexts, call| {
            if call.fn_name() == "is_in" || call.fn_name() == "enter" || call.fn_name() == "exit" {
                if let Some(ctx) = call.args().next().and_then(context_name) {
                    contexts.push(ctx);
                }
            }
        })
        .walk_recursively_children_first(a.file().node())
}

fn context_name<'f>(ctx: Expr<'f>) -> Option<Text<'f>> {
    child_of_type(ctx.node(), SIMPLE_STRING)
        .map(|n| ::ast_ext::lit_body(n.text()))
}

fn resolve_context(a: &Analysis, ctx: Expr) -> Option<u32> {
    if let Some(name) = context_name(ctx) {
        a.contexts().iter()
            .position(|&c| c == name)
            .map(|usize_| usize_ as u32)
    } else {
        a.diagnostics.error(ctx.node(), "Context should be a single quoted string");
        None
    }
}

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

    use fall_tree::search;
    use fall_tree::search::ast;

    use ::test_util::parse_with_caret;

    #[test]
    fn test_resolve_no_args() {
        check_resolved("rule foo { <^eof> }", "Eof");
        check_resolved("rule foo { <^any> }", "Any");
        check_resolved("rule foo { <^commit> }", "Commit");
    }

    #[test]
    fn test_resolve_simple_args() {
        check_resolved("rule foo { <^not a> }", "Not(RefExpr@[16; 17))");
        check_resolved("rule foo { <^with_skip a b> }", "WithSkip(RefExpr@[22; 23), RefExpr@[24; 25))");
        check_resolved("rule foo { <^layer a b> }", "Layer(RefExpr@[18; 19), RefExpr@[20; 21))");
    }

    #[test]
    fn test_resolve_layer() {
        check_resolved("rule foo { <^enter 'ctx' a> }", "Enter(0, RefExpr@[24; 25))");
        check_resolved("rule foo { <^exit  'ctx' a> }", "Exit(0, RefExpr@[24; 25))");

        check_resolved("rule foo { <is_in 'ctx1'> <^enter 'ctx2' a> }", "Enter(1, RefExpr@[40; 41))");
        check_resolved("rule foo { <is_in 'ctx1'> <^exit  'ctx2' a> }", "Exit(1, RefExpr@[40; 41))");

        check_resolved(
            "rule foo { <enter 'ctx1' a> <enter 'ctx2' b> <^is_in 'ctx2'> }",
            "IsIn(1)"
        );

        check(
            r" rule foo { <^is_in foo>}",
            None,
            r#"[(foo, "Context should be a single quoted string")]"#,
        )
    }

    #[test]
    fn test_not() {
        resolve_call("rule foo { <^not bar>}", |c, _| {
            let c = c.unwrap();
            assert_eq!(format!("{:?}", c), "Not(RefExpr@[16; 19))");
        })
    }

    #[test]
    fn test_unresolved_call() {
        check(
            r"rule foo { <^abracadabra>}",
            None,
            r#"[(abracadabra, "Unresolved reference")]"#
        );
    }

    #[test]
    fn wrong_arity() {
        check(
            "rule foo { <^eof foo> }",
            Some(CallKind::Eof),
            r#"[(<eof foo>, "Wrong number of arguments, expected 0, got 1")]"#
        );

        check(
            "rule foo { <^any foo bar> }",
            Some(CallKind::Any),
            r#"[(<any foo bar>, "Wrong number of arguments, expected 0, got 2")]"#
        );

        check(
            "rule foo { <^commit foo bar baz> }",
            Some(CallKind::Commit),
            r#"[(<commit foo bar baz>, "Wrong number of arguments, expected 0, got 3")]"#
        );

        check(
            "rule foo { <^not> }",
            None,
            r#"[(<not>, "Wrong number of arguments, expected 1, got 0")]"#
        );
    }

    #[test]
    fn call_custom_rule() {
        //TODO: check number of arguments
        //TODO: analysis based resolve of SynRule
        check_resolved(
            "rule foo { <^bar a b>} rule bar(x, y) { }",
            "RuleCall(SynRule@[22; 40), [(0, RefExpr@[16; 17)), (1, RefExpr@[18; 19))])"
        )
    }

    fn check(text: &str, kind: Option<CallKind>, diagnostics: &str) {
        resolve_call(text, |c, a| {
            assert_eq!(c, kind);
            assert_eq!(a.diagnostics.debug(a.file.node().text()), diagnostics);
        })
    }

    fn check_resolved(text: &str, kind: &str) {
        resolve_call(text, |c, a| {
            let c = c.unwrap();
            assert_eq!(format!("{:?}", c), kind);
            assert_eq!(a.diagnostics.debug(a.file.node().text()), "[]");
        })
    }

    fn resolve_call<F: FnOnce(Option<CallKind>, Analysis)>(text: &str, f: F) {
        let (file, caret) = parse_with_caret(&text);
        let call = {
            let leaf = search::find_leaf_at_offset(file.root(), caret)
                .left_biased().unwrap();
            ast::ancestor_exn(leaf)
        };
        let a = Analysis::new(::ast(&file));
        let c = a.resolve_call(call);
        f(c, a)
    }
}

<commit_msg>Resolve prev_is<commit_after>use itertools::Itertools;

use fall_tree::{AstNode, AstClass, Text};
use fall_tree::search::{child_of_type, child_of_type_exn};
use fall_tree::visitor::{Visitor, NodeVisitor};

use super::Analysis;
use ::{Expr, CallExpr, SynRule, IDENT, SIMPLE_STRING, RefKind};

#[derive(Debug, Eq, PartialEq)]
pub enum CallKind<'f> {
    Eof,
    Any,
    Commit,

    Not(Expr<'f>),
    Layer(Expr<'f>, Expr<'f>),
    WithSkip(Expr<'f>, Expr<'f>),

    Enter(u32, Expr<'f>),
    Exit(u32, Expr<'f>),
    IsIn(u32),

    RuleCall(SynRule<'f>, Vec<(u32, Expr<'f>)>), // TODO: u32 is unclear
    PrevIs(Vec<usize>)
}

pub fn resolve<'f>(a: &Analysis<'f>, call: CallExpr<'f>) -> Option<CallKind<'f>> {
    let n_args = call.args().count();
    let expect_args = |n_expected: usize| {
        if n_expected != n_args {
            a.diagnostics.error(
                call.node(),
                format!("Wrong number of arguments, expected {}, got {}", n_expected, n_args)
            )
        }
    };

    let zero_arg = vec![
        ("any", CallKind::Any),
        ("commit", CallKind::Commit),
        ("eof", CallKind::Eof)
    ];

    for (name, kind) in zero_arg.into_iter() {
        if call.fn_name() == name {
            expect_args(0);
            return Some(kind);
        }
    }

    if call.fn_name() == "not" {
        expect_args(1);
        return call.args().next().map(CallKind::Not);
    }

    let two_arg = vec![
        ("layer", CallKind::Layer as fn(_, _) -> _),
        ("with_skip", CallKind::WithSkip)
    ];

    for (name, kind) in two_arg.into_iter() {
        if call.fn_name() == name {
            expect_args(2);
            return call.args().next_tuple()
                .map(|(a, b)| kind(a, b));
        }
    }

    if call.fn_name() == "is_in" {
        expect_args(1);
        return call.args().next()
            .and_then(|ctx| resolve_context(a, ctx))
            .map(CallKind::IsIn);
    }

    let layer_expr = vec![
        ("enter", CallKind::Enter as fn(_, _) -> _),
        ("exit", CallKind::Exit),
    ];

    for (name, kind) in layer_expr.into_iter() {
        if call.fn_name() == name {
            expect_args(2);
            return call.args().next_tuple()
                .and_then(|(ctx, body)| {
                    resolve_context(a, ctx).map(|ctx| kind(ctx, body))
                });
        }
    }

    if call.fn_name() == "prev_is" {
        let mut args = Vec::new();
        for arg in call.args() {
            let mut ok = false;
            if let Expr::RefExpr(expr) = arg {
                if let Some(RefKind::RuleReference(rule)) = expr.resolve() {
                    if let Some(ty) = rule.resolve_ty() {
                        args.push(ty);
                        ok = true;
                    }
                }
            }
            if !ok {
                a.diagnostics.error(call.node(), "<prev_is> arguments must be public rules")
            }
        }
        return Some(CallKind::PrevIs(args))
    }

    if let Some(rule) = a.file.resolve_rule(call.fn_name()) {
        if let Some(parameters) = rule.parameters() {
            let params = parameters.parameters()
                .map(|p| p.idx())
                .zip(call.args())
                .collect();
            return Some(CallKind::RuleCall(rule, params));
        }
    }

    a.diagnostics.error(
        child_of_type_exn(call.node(), IDENT),
        "Unresolved reference"
    );
    return None;
}

pub fn contexts<'f>(a: &Analysis<'f>) -> Vec<Text<'f>> {
    Visitor(Vec::new())
        .visit::<CallExpr, _>(|contexts, call| {
            if call.fn_name() == "is_in" || call.fn_name() == "enter" || call.fn_name() == "exit" {
                if let Some(ctx) = call.args().next().and_then(context_name) {
                    contexts.push(ctx);
                }
            }
        })
        .walk_recursively_children_first(a.file().node())
}

fn context_name<'f>(ctx: Expr<'f>) -> Option<Text<'f>> {
    child_of_type(ctx.node(), SIMPLE_STRING)
        .map(|n| ::ast_ext::lit_body(n.text()))
}

fn resolve_context(a: &Analysis, ctx: Expr) -> Option<u32> {
    if let Some(name) = context_name(ctx) {
        a.contexts().iter()
            .position(|&c| c == name)
            .map(|usize_| usize_ as u32)
    } else {
        a.diagnostics.error(ctx.node(), "Context should be a single quoted string");
        None
    }
}

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

    use fall_tree::search;
    use fall_tree::search::ast;

    use ::test_util::parse_with_caret;

    #[test]
    fn test_resolve_no_args() {
        check_resolved("rule foo { <^eof> }", "Eof");
        check_resolved("rule foo { <^any> }", "Any");
        check_resolved("rule foo { <^commit> }", "Commit");
    }

    #[test]
    fn test_resolve_simple_args() {
        check_resolved("rule foo { <^not a> }", "Not(RefExpr@[16; 17))");
        check_resolved("rule foo { <^with_skip a b> }", "WithSkip(RefExpr@[22; 23), RefExpr@[24; 25))");
        check_resolved("rule foo { <^layer a b> }", "Layer(RefExpr@[18; 19), RefExpr@[20; 21))");
    }

    #[test]
    fn test_resolve_layer() {
        check_resolved("rule foo { <^enter 'ctx' a> }", "Enter(0, RefExpr@[24; 25))");
        check_resolved("rule foo { <^exit  'ctx' a> }", "Exit(0, RefExpr@[24; 25))");

        check_resolved("rule foo { <is_in 'ctx1'> <^enter 'ctx2' a> }", "Enter(1, RefExpr@[40; 41))");
        check_resolved("rule foo { <is_in 'ctx1'> <^exit  'ctx2' a> }", "Exit(1, RefExpr@[40; 41))");

        check_resolved(
            "rule foo { <enter 'ctx1' a> <enter 'ctx2' b> <^is_in 'ctx2'> }",
            "IsIn(1)"
        );

        check(
            r" rule foo { <^is_in foo>}",
            None,
            r#"[(foo, "Context should be a single quoted string")]"#,
        )
    }

    #[test]
    fn test_not() {
        resolve_call("rule foo { <^not bar>}", |c, _| {
            let c = c.unwrap();
            assert_eq!(format!("{:?}", c), "Not(RefExpr@[16; 19))");
        })
    }

    #[test]
    fn test_unresolved_call() {
        check(
            r"rule foo { <^abracadabra>}",
            None,
            r#"[(abracadabra, "Unresolved reference")]"#
        );
    }

    #[test]
    fn wrong_arity() {
        check(
            "rule foo { <^eof foo> }",
            Some(CallKind::Eof),
            r#"[(<eof foo>, "Wrong number of arguments, expected 0, got 1")]"#
        );

        check(
            "rule foo { <^any foo bar> }",
            Some(CallKind::Any),
            r#"[(<any foo bar>, "Wrong number of arguments, expected 0, got 2")]"#
        );

        check(
            "rule foo { <^commit foo bar baz> }",
            Some(CallKind::Commit),
            r#"[(<commit foo bar baz>, "Wrong number of arguments, expected 0, got 3")]"#
        );

        check(
            "rule foo { <^not> }",
            None,
            r#"[(<not>, "Wrong number of arguments, expected 1, got 0")]"#
        );
    }

    #[test]
    fn check_prev_is() {
        //TODO: check for errors
        check_resolved(
            "pub rule foo {} pub rule bar {} rule baz { <^prev_is foo bar> }",
            "PrevIs([1, 2])"
        )
    }

    #[test]
    fn call_custom_rule() {
        //TODO: check number of arguments
        //TODO: analysis based resolve of SynRule
        check_resolved(
            "rule foo { <^bar a b>} rule bar(x, y) { }",
            "RuleCall(SynRule@[22; 40), [(0, RefExpr@[16; 17)), (1, RefExpr@[18; 19))])"
        )
    }

    fn check(text: &str, kind: Option<CallKind>, diagnostics: &str) {
        resolve_call(text, |c, a| {
            assert_eq!(c, kind);
            assert_eq!(a.diagnostics.debug(a.file.node().text()), diagnostics);
        })
    }

    fn check_resolved(text: &str, kind: &str) {
        resolve_call(text, |c, a| {
            let c = c.unwrap();
            assert_eq!(format!("{:?}", c), kind);
            assert_eq!(a.diagnostics.debug(a.file.node().text()), "[]");
        })
    }

    fn resolve_call<F: FnOnce(Option<CallKind>, Analysis)>(text: &str, f: F) {
        let (file, caret) = parse_with_caret(&text);
        let call = {
            let leaf = search::find_leaf_at_offset(file.root(), caret)
                .left_biased().unwrap();
            ast::ancestor_exn(leaf)
        };
        let a = Analysis::new(::ast(&file));
        let c = a.resolve_call(call);
        f(c, a)
    }
}

<|endoftext|>


<commit_before>use std::io;

fn encrypt(input: String, key: i16) -> String {
	// parse the inputted string into bytes
	let mut vector_input = input.into_bytes();
	// iterate through the entire Vector to adjust UTF values
	for v in &mut vector_input {
		if *v >= 65 && *v <= 90 {
			*v = (key + (*v as i16)) as u8;
			if *v > 90 {
				*v = 65 + (*v - 91);
			} else if *v < 65 {
				*v = 90 - (64 - *v);
			}
		} else if *v >= 97 && *v <= 122 {
			*v = (key + (*v as i16)) as u8;
			if *v > 122 {
				*v = 97 + (*v - 123);
			} else if *v < 97 {
				*v = 122 - (96 - *v);
			}
		}
	}
	// replace the original with the encrypted...
	let input = String::from_utf8(vector_input)
		.ok()
		.expect("Something went wrong");
	// return the string back to the user, encrypted.
	return input;
}
fn decrypt(input: String, key: i16) -> String {
	return encrypt(input, (key * -1));
}
fn main() {
	println!("Enter a value to encrypt");
	// declare variable to hold user input
	let mut user_input = String::new();
	// Get input from user for text to encrypt
	io::stdin().read_line(&mut user_input)
		.ok()
		.expect("Error: Invalid Input");

	println!("Now enter a key to encrypt on");
	// Here, we declare a String to hold user's input for key
	let mut key = String::new();
	// Get input from user for key
	io::stdin().read_line(&mut key)
		.ok()
		.expect("Error: Invalid Input");
	// parse and convert key String to integer
	let mut key: i16 = key.trim().parse()
		.ok()
		.expect("Invalid Key");
	// convert key to a value between 0 and 25, and make key immutable
	if key >= 26 {
		key -= 26
	}
	let mut encrypted = encrypt(user_input, key);
	println!("{}", encrypted);

	println!("{}", decrypt(encrypted, key));
}<commit_msg>fixed small bug in RUST version<commit_after>use std::io;

fn encrypt(input: String, key: i16) -> String {
	// parse the inputted string into bytes
	let mut vector_input = input.into_bytes();
	// iterate through the entire Vector to adjust UTF values
	for v in &mut vector_input {
		if *v >= 65 && *v <= 90 {
			*v = (key + (*v as i16)) as u8;
			if *v > 90 {
				*v = 65 + (*v - 91);
			} else if *v < 65 {
				*v = 90 - (64 - *v);
			}
		} else if *v >= 97 && *v <= 122 {
			*v = (key + (*v as i16)) as u8;
			if *v > 122 {
				*v = 97 + (*v - 123);
			} else if *v < 97 {
				*v = 122 - (96 - *v);
			}
		}
	}
	// replace the original with the encrypted...
	let input = String::from_utf8(vector_input)
		.ok()
		.expect("Something went wrong");
	// return the string back to the user, encrypted.
	return input;
}
fn decrypt(input: String, key: i16) -> String {
	return encrypt(input, (key * -1));
}
fn main() {
	println!("Enter a value to encrypt");
	// declare variable to hold user input
	let mut user_input = String::new();
	// Get input from user for text to encrypt
	io::stdin().read_line(&mut user_input)
		.ok()
		.expect("Error: Invalid Input");

	println!("Now enter a key to encrypt on");
	// Here, we declare a String to hold user's input for key
	let mut key = String::new();
	// Get input from user for key
	io::stdin().read_line(&mut key)
		.ok()
		.expect("Error: Invalid Input");
	// parse and convert key String to integer
	let mut key: i16 = key.trim().parse()
		.ok()
		.expect("Invalid Key");
	// convert key to a value between 0 and 25, and make key immutable
	while (key >= 26) {
		key = key - 26;
	}
	let mut encrypted = encrypt(user_input, key);
	println!("{}", encrypted);

	println!("{}", decrypt(encrypted, key));
}<|endoftext|>


<commit_before>//! This will build the proc macro in `imp`, and copy the resulting dylib artifact into the
//! `OUT_DIR`.
//!
//! `proc-macro-test` itself contains only a path to that artifact.

use std::{
    env, fs,
    path::{Path, PathBuf},
    process::Command,
};

use cargo_metadata::Message;

fn main() {
    println!("cargo:rerun-if-changed=imp");

    let out_dir = env::var_os("OUT_DIR").unwrap();
    let out_dir = Path::new(&out_dir);

    let name = "proc-macro-test-impl";
    let version = "0.0.0";

    let imp_dir = std::env::current_dir().unwrap().join("imp");

    let staging_dir = out_dir.join("proc-macro-test-imp-staging");
    // this'll error out if the staging dir didn't previously. using
    // `std::fs::exists` would suffer from TOCTOU so just do our best to
    // wip it and ignore errors.
    let _ = std::fs::remove_dir_all(&staging_dir);

    println!("Creating {}", staging_dir.display());
    std::fs::create_dir_all(&staging_dir).unwrap();

    let src_dir = staging_dir.join("src");
    println!("Creating {}", src_dir.display());
    std::fs::create_dir_all(src_dir).unwrap();

    for item_els in [&["Cargo.toml"][..], &["Cargo.lock"], &["src", "lib.rs"]] {
        let mut src = imp_dir.clone();
        let mut dst = staging_dir.clone();
        for el in item_els {
            src.push(el);
            dst.push(el);
        }
        println!("Copying {} to {}", src.display(), dst.display());
        std::fs::copy(src, dst).unwrap();
    }

    let target_dir = out_dir.join("target");
    let output = Command::new(toolchain::cargo())
        .current_dir(&staging_dir)
        .args(&["build", "-p", "proc-macro-test-impl", "--message-format", "json"])
        // Explicit override the target directory to avoid using the same one which the parent
        // cargo is using, or we'll deadlock.
        // This can happen when `CARGO_TARGET_DIR` is set or global config forces all cargo
        // instance to use the same target directory.
        .arg("--target-dir")
        .arg(&target_dir)
        .output()
        .unwrap();
    if !output.status.success() {
        println!("proc-macro-test-impl failed to build");
        println!("============ stdout ============");
        println!("{}", String::from_utf8_lossy(&output.stdout));
        println!("============ stderr ============");
        println!("{}", String::from_utf8_lossy(&output.stderr));
        panic!("proc-macro-test-impl failed to build");
    }

    let mut artifact_path = None;
    for message in Message::parse_stream(output.stdout.as_slice()) {
        match message.unwrap() {
            Message::CompilerArtifact(artifact) => {
                if artifact.target.kind.contains(&"proc-macro".to_string()) {
                    let repr = format!("{} {}", name, version);
                    if artifact.package_id.repr.starts_with(&repr) {
                        artifact_path = Some(PathBuf::from(&artifact.filenames[0]));
                    }
                }
            }
            _ => (), // Unknown message
        }
    }

    // This file is under `target_dir` and is already under `OUT_DIR`.
    let artifact_path = artifact_path.expect("no dylib for proc-macro-test-impl found");

    let info_path = out_dir.join("proc_macro_test_location.txt");
    fs::write(info_path, artifact_path.to_str().unwrap()).unwrap();
}
<commit_msg>Don't copy the Cargo.lock<commit_after>//! This will build the proc macro in `imp`, and copy the resulting dylib artifact into the
//! `OUT_DIR`.
//!
//! `proc-macro-test` itself contains only a path to that artifact.

use std::{
    env, fs,
    path::{Path, PathBuf},
    process::Command,
};

use cargo_metadata::Message;

fn main() {
    println!("cargo:rerun-if-changed=imp");

    let out_dir = env::var_os("OUT_DIR").unwrap();
    let out_dir = Path::new(&out_dir);

    let name = "proc-macro-test-impl";
    let version = "0.0.0";

    let imp_dir = std::env::current_dir().unwrap().join("imp");

    let staging_dir = out_dir.join("proc-macro-test-imp-staging");
    // this'll error out if the staging dir didn't previously. using
    // `std::fs::exists` would suffer from TOCTOU so just do our best to
    // wip it and ignore errors.
    let _ = std::fs::remove_dir_all(&staging_dir);

    println!("Creating {}", staging_dir.display());
    std::fs::create_dir_all(&staging_dir).unwrap();

    let src_dir = staging_dir.join("src");
    println!("Creating {}", src_dir.display());
    std::fs::create_dir_all(src_dir).unwrap();

    for item_els in [&["Cargo.toml"][..], &["src", "lib.rs"]] {
        let mut src = imp_dir.clone();
        let mut dst = staging_dir.clone();
        for el in item_els {
            src.push(el);
            dst.push(el);
        }
        println!("Copying {} to {}", src.display(), dst.display());
        std::fs::copy(src, dst).unwrap();
    }

    let target_dir = out_dir.join("target");
    let output = Command::new(toolchain::cargo())
        .current_dir(&staging_dir)
        .args(&["build", "-p", "proc-macro-test-impl", "--message-format", "json"])
        // Explicit override the target directory to avoid using the same one which the parent
        // cargo is using, or we'll deadlock.
        // This can happen when `CARGO_TARGET_DIR` is set or global config forces all cargo
        // instance to use the same target directory.
        .arg("--target-dir")
        .arg(&target_dir)
        .output()
        .unwrap();
    if !output.status.success() {
        println!("proc-macro-test-impl failed to build");
        println!("============ stdout ============");
        println!("{}", String::from_utf8_lossy(&output.stdout));
        println!("============ stderr ============");
        println!("{}", String::from_utf8_lossy(&output.stderr));
        panic!("proc-macro-test-impl failed to build");
    }

    let mut artifact_path = None;
    for message in Message::parse_stream(output.stdout.as_slice()) {
        match message.unwrap() {
            Message::CompilerArtifact(artifact) => {
                if artifact.target.kind.contains(&"proc-macro".to_string()) {
                    let repr = format!("{} {}", name, version);
                    if artifact.package_id.repr.starts_with(&repr) {
                        artifact_path = Some(PathBuf::from(&artifact.filenames[0]));
                    }
                }
            }
            _ => (), // Unknown message
        }
    }

    // This file is under `target_dir` and is already under `OUT_DIR`.
    let artifact_path = artifact_path.expect("no dylib for proc-macro-test-impl found");

    let info_path = out_dir.join("proc_macro_test_location.txt");
    fs::write(info_path, artifact_path.to_str().unwrap()).unwrap();
}
<|endoftext|>


<commit_before>use futures::sync::mpsc::{self, Sender};
use godcoin::{blockchain::ReindexOpts, net::*, prelude::*};
use log::{error, info, warn};
use std::{io::Cursor, net::SocketAddr, path::PathBuf, sync::Arc};
use tokio::{net::TcpListener, prelude::*};
use tokio_tungstenite::tungstenite::{protocol, Message};

mod block_range;
mod forever;
pub mod minter;
pub mod pool;

pub mod prelude {
    pub use super::minter::*;
    pub use super::pool::SubscriptionPool;
}

use block_range::AsyncBlockRange;
use prelude::*;

pub struct ServerOpts {
    pub blocklog_loc: PathBuf,
    pub index_loc: PathBuf,
    pub minter_key: KeyPair,
    pub bind_addr: String,
    pub reindex: Option<ReindexOpts>,
    pub enable_stale_production: bool,
}

#[derive(Clone)]
pub struct ServerData {
    pub chain: Arc<Blockchain>,
    pub minter: Minter,
    pub sub_pool: SubscriptionPool,
}

pub fn start(opts: ServerOpts) {
    let blockchain = Arc::new(Blockchain::new(&opts.blocklog_loc, &opts.index_loc));

    let is_empty = blockchain.is_empty();
    if !is_empty && blockchain.index_status() != IndexStatus::Complete {
        warn!(
            "Indexing not complete (status = {:?})",
            blockchain.index_status()
        );
        match opts.reindex {
            Some(opts) => blockchain.reindex(opts),
            None => panic!("index incomplete, aborting..."),
        }
    }

    if is_empty {
        let info = blockchain.create_genesis_block(opts.minter_key.clone());
        info!("=> Generated new block chain");
        info!("=> {:?}", info.script);
        for (index, key) in info.wallet_keys.iter().enumerate() {
            info!("=> Wallet key {}: {}", index + 1, key.1.to_wif());
        }
    }

    info!(
        "Using height in block log at {}",
        blockchain.get_chain_height()
    );

    let sub_pool = SubscriptionPool::new();
    let minter = Minter::new(
        Arc::clone(&blockchain),
        opts.minter_key,
        sub_pool.clone(),
        opts.enable_stale_production,
    );
    minter.clone().start_production_loop();

    let data = Arc::new(ServerData {
        chain: Arc::clone(&blockchain),
        minter,
        sub_pool,
    });

    let addr = opts.bind_addr.parse::<SocketAddr>().unwrap();
    start_server(addr, data);
}

fn start_server(server_addr: SocketAddr, data: Arc<ServerData>) {
    let server = TcpListener::bind(&server_addr).unwrap();
    let incoming = forever::ListenForever::new(server.incoming());
    tokio::spawn(incoming.for_each(move |stream| {
        let peer_addr = stream.peer_addr().unwrap();
        let data = Arc::clone(&data);
        tokio::spawn(
            tokio_tungstenite::accept_async(stream)
                .and_then(move |ws| {
                    info!("[{}] Connection opened", peer_addr);

                    let (tx, rx) = mpsc::channel(32);
                    let (sink, stream) = ws.split();
                    let mut state = WsState::new(peer_addr, tx.clone());

                    let ws_reader = stream.for_each({
                        let data = Arc::clone(&data);
                        move |msg| {
                            let res = process_message(&data, &mut state, msg);
                            if let Some(res) = res {
                                let peer_addr = peer_addr.clone();
                                future::Either::A(tx.clone().send(res).then(move |res| {
                                    if let Err(_) = res {
                                        error!("[{}] Failed to send message", peer_addr);
                                    }
                                    Ok(())
                                }))
                            } else {
                                future::Either::B(future::ok(()))
                            }
                        }
                    });
                    let ws_writer = rx.forward(sink.sink_map_err(move |e| {
                        error!("[{}] Sink send error: {:?}", peer_addr, e);
                    }));

                    let conn = ws_reader
                        .map_err(|_| ())
                        .select(ws_writer.map(|_| ()).map_err(|_| ()));
                    tokio::spawn(conn.then(move |_| {
                        info!("[{}] Connection closed", peer_addr);
                        // Remove block subscriptions if there are any
                        data.sub_pool.remove(peer_addr);
                        Ok(())
                    }));

                    Ok(())
                })
                .map_err(move |e| {
                    error!("[{}] WS accept error = {:?}", peer_addr, e);
                }),
        );
        Ok(())
    }));
}

pub fn process_message(data: &ServerData, state: &mut WsState, msg: Message) -> Option<Message> {
    match msg {
        Message::Binary(buf) => {
            let mut cur = Cursor::<&[u8]>::new(&buf);
            let res = match Request::deserialize(&mut cur) {
                Ok(req) => {
                    let id = req.id;
                    if id == u32::max_value() {
                        // Max value is reserved
                        Response {
                            id: u32::max_value(),
                            body: ResponseBody::Error(ErrorKind::Io),
                        }
                    } else if cur.position() != buf.len() as u64 {
                        Response {
                            id,
                            body: ResponseBody::Error(ErrorKind::BytesRemaining),
                        }
                    } else {
                        match handle_request(data, state, req) {
                            Some(res) => Response { id, body: res },
                            None => return None,
                        }
                    }
                }
                Err(e) => {
                    error!("Error occurred during deserialization: {:?}", e);
                    Response {
                        id: u32::max_value(),
                        body: ResponseBody::Error(ErrorKind::Io),
                    }
                }
            };

            let mut buf = Vec::with_capacity(65536);
            res.serialize(&mut buf);
            Some(Message::Binary(buf))
        }
        Message::Text(_) => Some(Message::Close(Some(protocol::CloseFrame {
            code: protocol::frame::coding::CloseCode::Unsupported,
            reason: "text is not supported".into(),
        }))),
        _ => None,
    }
}

fn handle_request(data: &ServerData, state: &mut WsState, req: Request) -> Option<ResponseBody> {
    Some(match req.body {
        RequestBody::Broadcast(tx) => {
            let res = data.minter.push_tx(tx);
            match res {
                Ok(_) => ResponseBody::Broadcast,
                Err(e) => ResponseBody::Error(ErrorKind::TxValidation(e)),
            }
        }
        RequestBody::SetBlockFilter(filter) => {
            if filter.len() > 16 {
                return Some(ResponseBody::Error(ErrorKind::InvalidRequest));
            }
            state.filter = Some(filter);
            ResponseBody::SetBlockFilter
        }
        RequestBody::ClearBlockFilter => {
            state.filter = None;
            ResponseBody::ClearBlockFilter
        }
        RequestBody::Subscribe => {
            data.sub_pool.insert(state.addr(), state.sender());
            ResponseBody::Subscribe
        }
        RequestBody::Unsubscribe => {
            data.sub_pool.remove(state.addr());
            ResponseBody::Unsubscribe
        }
        RequestBody::GetProperties => {
            let props = data.chain.get_properties();
            ResponseBody::GetProperties(props)
        }
        RequestBody::GetBlock(height) => match &state.filter {
            Some(filter) => match data.chain.get_filtered_block(height, filter) {
                Some(block) => ResponseBody::GetBlock(block),
                None => ResponseBody::Error(ErrorKind::InvalidHeight),
            },
            None => match data.chain.get_block(height) {
                Some(block) => ResponseBody::GetBlock(FilteredBlock::Block(block)),
                None => ResponseBody::Error(ErrorKind::InvalidHeight),
            },
        },
        RequestBody::GetFullBlock(height) => match data.chain.get_block(height) {
            Some(block) => ResponseBody::GetFullBlock(block),
            None => ResponseBody::Error(ErrorKind::InvalidHeight),
        },
        RequestBody::GetBlockRange(min_height, max_height) => {
            let range = AsyncBlockRange::try_new(Arc::clone(&data.chain), min_height, max_height);
            match range {
                Some(mut range) => {
                    if let Some(filter) = state.filter() {
                        range.set_filter(Some(filter.clone()));
                    }

                    let peer_addr = state.addr();
                    let tx = state.sender();
                    let id = req.id;

                    tokio::spawn(range.map(move |block| {
                        let msg = Response {
                            id,
                            body: ResponseBody::GetBlock(block),
                        };

                        let mut buf = Vec::with_capacity(65536);
                        msg.serialize(&mut buf);
                        Message::Binary(buf)
                    }).forward(tx.clone().sink_map_err(move |_| {
                        error!("[{}] Failed to send block range update", peer_addr);
                    })).and_then(move |_| {
                        let msg = Response {
                            id,
                            body: ResponseBody::GetBlockRange,
                        };

                        let mut buf = Vec::with_capacity(32);
                        msg.serialize(&mut buf);
                        tx.send(Message::Binary(buf)).map(|_sink| ()).map_err(move |_| {
                            error!("[{}] Failed to send block range finalizer", peer_addr);
                        })
                    }));

                    return None;
                }
                None => ResponseBody::Error(ErrorKind::InvalidHeight),
            }
        }
        RequestBody::GetAddressInfo(addr) => {
            let res = data.minter.get_addr_info(&addr);
            match res {
                Ok(info) => ResponseBody::GetAddressInfo(info),
                Err(e) => ResponseBody::Error(ErrorKind::TxValidation(e)),
            }
        }
    })
}

pub struct WsState {
    filter: Option<BlockFilter>,
    addr: SocketAddr,
    tx: Sender<Message>,
}

impl WsState {
    #[inline]
    pub fn new(addr: SocketAddr, tx: Sender<Message>) -> Self {
        Self {
            filter: None,
            addr,
            tx,
        }
    }

    #[inline]
    pub fn addr(&self) -> SocketAddr {
        self.addr
    }

    #[inline]
    pub fn filter(&self) -> Option<&BlockFilter> {
        self.filter.as_ref()
    }

    #[inline]
    pub fn sender(&self) -> Sender<Message> {
        self.tx.clone()
    }
}
<commit_msg>Apply formatting<commit_after>use futures::sync::mpsc::{self, Sender};
use godcoin::{blockchain::ReindexOpts, net::*, prelude::*};
use log::{error, info, warn};
use std::{io::Cursor, net::SocketAddr, path::PathBuf, sync::Arc};
use tokio::{net::TcpListener, prelude::*};
use tokio_tungstenite::tungstenite::{protocol, Message};

mod block_range;
mod forever;
pub mod minter;
pub mod pool;

pub mod prelude {
    pub use super::minter::*;
    pub use super::pool::SubscriptionPool;
}

use block_range::AsyncBlockRange;
use prelude::*;

pub struct ServerOpts {
    pub blocklog_loc: PathBuf,
    pub index_loc: PathBuf,
    pub minter_key: KeyPair,
    pub bind_addr: String,
    pub reindex: Option<ReindexOpts>,
    pub enable_stale_production: bool,
}

#[derive(Clone)]
pub struct ServerData {
    pub chain: Arc<Blockchain>,
    pub minter: Minter,
    pub sub_pool: SubscriptionPool,
}

pub fn start(opts: ServerOpts) {
    let blockchain = Arc::new(Blockchain::new(&opts.blocklog_loc, &opts.index_loc));

    let is_empty = blockchain.is_empty();
    if !is_empty && blockchain.index_status() != IndexStatus::Complete {
        warn!(
            "Indexing not complete (status = {:?})",
            blockchain.index_status()
        );
        match opts.reindex {
            Some(opts) => blockchain.reindex(opts),
            None => panic!("index incomplete, aborting..."),
        }
    }

    if is_empty {
        let info = blockchain.create_genesis_block(opts.minter_key.clone());
        info!("=> Generated new block chain");
        info!("=> {:?}", info.script);
        for (index, key) in info.wallet_keys.iter().enumerate() {
            info!("=> Wallet key {}: {}", index + 1, key.1.to_wif());
        }
    }

    info!(
        "Using height in block log at {}",
        blockchain.get_chain_height()
    );

    let sub_pool = SubscriptionPool::new();
    let minter = Minter::new(
        Arc::clone(&blockchain),
        opts.minter_key,
        sub_pool.clone(),
        opts.enable_stale_production,
    );
    minter.clone().start_production_loop();

    let data = Arc::new(ServerData {
        chain: Arc::clone(&blockchain),
        minter,
        sub_pool,
    });

    let addr = opts.bind_addr.parse::<SocketAddr>().unwrap();
    start_server(addr, data);
}

fn start_server(server_addr: SocketAddr, data: Arc<ServerData>) {
    let server = TcpListener::bind(&server_addr).unwrap();
    let incoming = forever::ListenForever::new(server.incoming());
    tokio::spawn(incoming.for_each(move |stream| {
        let peer_addr = stream.peer_addr().unwrap();
        let data = Arc::clone(&data);
        tokio::spawn(
            tokio_tungstenite::accept_async(stream)
                .and_then(move |ws| {
                    info!("[{}] Connection opened", peer_addr);

                    let (tx, rx) = mpsc::channel(32);
                    let (sink, stream) = ws.split();
                    let mut state = WsState::new(peer_addr, tx.clone());

                    let ws_reader = stream.for_each({
                        let data = Arc::clone(&data);
                        move |msg| {
                            let res = process_message(&data, &mut state, msg);
                            if let Some(res) = res {
                                let peer_addr = peer_addr.clone();
                                future::Either::A(tx.clone().send(res).then(move |res| {
                                    if let Err(_) = res {
                                        error!("[{}] Failed to send message", peer_addr);
                                    }
                                    Ok(())
                                }))
                            } else {
                                future::Either::B(future::ok(()))
                            }
                        }
                    });
                    let ws_writer = rx.forward(sink.sink_map_err(move |e| {
                        error!("[{}] Sink send error: {:?}", peer_addr, e);
                    }));

                    let conn = ws_reader
                        .map_err(|_| ())
                        .select(ws_writer.map(|_| ()).map_err(|_| ()));
                    tokio::spawn(conn.then(move |_| {
                        info!("[{}] Connection closed", peer_addr);
                        // Remove block subscriptions if there are any
                        data.sub_pool.remove(peer_addr);
                        Ok(())
                    }));

                    Ok(())
                })
                .map_err(move |e| {
                    error!("[{}] WS accept error = {:?}", peer_addr, e);
                }),
        );
        Ok(())
    }));
}

pub fn process_message(data: &ServerData, state: &mut WsState, msg: Message) -> Option<Message> {
    match msg {
        Message::Binary(buf) => {
            let mut cur = Cursor::<&[u8]>::new(&buf);
            let res = match Request::deserialize(&mut cur) {
                Ok(req) => {
                    let id = req.id;
                    if id == u32::max_value() {
                        // Max value is reserved
                        Response {
                            id: u32::max_value(),
                            body: ResponseBody::Error(ErrorKind::Io),
                        }
                    } else if cur.position() != buf.len() as u64 {
                        Response {
                            id,
                            body: ResponseBody::Error(ErrorKind::BytesRemaining),
                        }
                    } else {
                        match handle_request(data, state, req) {
                            Some(res) => Response { id, body: res },
                            None => return None,
                        }
                    }
                }
                Err(e) => {
                    error!("Error occurred during deserialization: {:?}", e);
                    Response {
                        id: u32::max_value(),
                        body: ResponseBody::Error(ErrorKind::Io),
                    }
                }
            };

            let mut buf = Vec::with_capacity(65536);
            res.serialize(&mut buf);
            Some(Message::Binary(buf))
        }
        Message::Text(_) => Some(Message::Close(Some(protocol::CloseFrame {
            code: protocol::frame::coding::CloseCode::Unsupported,
            reason: "text is not supported".into(),
        }))),
        _ => None,
    }
}

fn handle_request(data: &ServerData, state: &mut WsState, req: Request) -> Option<ResponseBody> {
    Some(match req.body {
        RequestBody::Broadcast(tx) => {
            let res = data.minter.push_tx(tx);
            match res {
                Ok(_) => ResponseBody::Broadcast,
                Err(e) => ResponseBody::Error(ErrorKind::TxValidation(e)),
            }
        }
        RequestBody::SetBlockFilter(filter) => {
            if filter.len() > 16 {
                return Some(ResponseBody::Error(ErrorKind::InvalidRequest));
            }
            state.filter = Some(filter);
            ResponseBody::SetBlockFilter
        }
        RequestBody::ClearBlockFilter => {
            state.filter = None;
            ResponseBody::ClearBlockFilter
        }
        RequestBody::Subscribe => {
            data.sub_pool.insert(state.addr(), state.sender());
            ResponseBody::Subscribe
        }
        RequestBody::Unsubscribe => {
            data.sub_pool.remove(state.addr());
            ResponseBody::Unsubscribe
        }
        RequestBody::GetProperties => {
            let props = data.chain.get_properties();
            ResponseBody::GetProperties(props)
        }
        RequestBody::GetBlock(height) => match &state.filter {
            Some(filter) => match data.chain.get_filtered_block(height, filter) {
                Some(block) => ResponseBody::GetBlock(block),
                None => ResponseBody::Error(ErrorKind::InvalidHeight),
            },
            None => match data.chain.get_block(height) {
                Some(block) => ResponseBody::GetBlock(FilteredBlock::Block(block)),
                None => ResponseBody::Error(ErrorKind::InvalidHeight),
            },
        },
        RequestBody::GetFullBlock(height) => match data.chain.get_block(height) {
            Some(block) => ResponseBody::GetFullBlock(block),
            None => ResponseBody::Error(ErrorKind::InvalidHeight),
        },
        RequestBody::GetBlockRange(min_height, max_height) => {
            let range = AsyncBlockRange::try_new(Arc::clone(&data.chain), min_height, max_height);
            match range {
                Some(mut range) => {
                    if let Some(filter) = state.filter() {
                        range.set_filter(Some(filter.clone()));
                    }

                    let peer_addr = state.addr();
                    let tx = state.sender();
                    let id = req.id;

                    tokio::spawn(
                        range
                            .map(move |block| {
                                let msg = Response {
                                    id,
                                    body: ResponseBody::GetBlock(block),
                                };

                                let mut buf = Vec::with_capacity(65536);
                                msg.serialize(&mut buf);
                                Message::Binary(buf)
                            })
                            .forward(tx.clone().sink_map_err(move |_| {
                                error!("[{}] Failed to send block range update", peer_addr);
                            }))
                            .and_then(move |_| {
                                let msg = Response {
                                    id,
                                    body: ResponseBody::GetBlockRange,
                                };

                                let mut buf = Vec::with_capacity(32);
                                msg.serialize(&mut buf);
                                tx.send(Message::Binary(buf))
                                    .map(|_sink| ())
                                    .map_err(move |_| {
                                        error!(
                                            "[{}] Failed to send block range finalizer",
                                            peer_addr
                                        );
                                    })
                            }),
                    );

                    return None;
                }
                None => ResponseBody::Error(ErrorKind::InvalidHeight),
            }
        }
        RequestBody::GetAddressInfo(addr) => {
            let res = data.minter.get_addr_info(&addr);
            match res {
                Ok(info) => ResponseBody::GetAddressInfo(info),
                Err(e) => ResponseBody::Error(ErrorKind::TxValidation(e)),
            }
        }
    })
}

pub struct WsState {
    filter: Option<BlockFilter>,
    addr: SocketAddr,
    tx: Sender<Message>,
}

impl WsState {
    #[inline]
    pub fn new(addr: SocketAddr, tx: Sender<Message>) -> Self {
        Self {
            filter: None,
            addr,
            tx,
        }
    }

    #[inline]
    pub fn addr(&self) -> SocketAddr {
        self.addr
    }

    #[inline]
    pub fn filter(&self) -> Option<&BlockFilter> {
        self.filter.as_ref()
    }

    #[inline]
    pub fn sender(&self) -> Sender<Message> {
        self.tx.clone()
    }
}
<|endoftext|>