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

pub use self::OrderingOp::*;

use deriving::{path_local, pathvec_std, path_std};
use deriving::generic::*;
use deriving::generic::ty::*;

use syntax::ast::{self, BinOpKind, Expr, MetaItem, Ident};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::Span;

pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
                                   span: Span,
                                   mitem: &MetaItem,
                                   item: &Annotatable,
                                   push: &mut FnMut(Annotatable)) {
    macro_rules! md {
        ($name:expr, $op:expr, $equal:expr) => { {
            let inline = cx.meta_word(span, Symbol::intern("inline"));
            let attrs = vec![cx.attribute(span, inline)];
            MethodDef {
                name: $name,
                generics: LifetimeBounds::empty(),
                explicit_self: borrowed_explicit_self(),
                args: vec![(borrowed_self(), "other")],
                ret_ty: Literal(path_local!(bool)),
                attributes: attrs,
                is_unsafe: false,
                unify_fieldless_variants: true,
                combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
                    cs_op($op, $equal, cx, span, substr)
                }))
            }
        } }
    }

    let ordering_ty = Literal(path_std!(cx, cmp::Ordering));
    let ret_ty = Literal(Path::new_(pathvec_std!(cx, option::Option),
                                    None,
                                    vec![Box::new(ordering_ty)],
                                    PathKind::Std));

    let inline = cx.meta_word(span, Symbol::intern("inline"));
    let attrs = vec![cx.attribute(span, inline)];

    let partial_cmp_def = MethodDef {
        name: "partial_cmp",
        generics: LifetimeBounds::empty(),
        explicit_self: borrowed_explicit_self(),
        args: vec![(borrowed_self(), "other")],
        ret_ty,
        attributes: attrs,
        is_unsafe: false,
        unify_fieldless_variants: true,
        combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
            cs_partial_cmp(cx, span, substr)
        })),
    };

    // avoid defining extra methods if we can
    // c-like enums, enums without any fields and structs without fields
    // can safely define only `partial_cmp`.
    let methods = if is_type_without_fields(item) {
        vec![partial_cmp_def]
    } else {
        vec![partial_cmp_def,
             md!("lt", true, false),
             md!("le", true, true),
             md!("gt", false, false),
             md!("ge", false, true)]
    };

    let trait_def = TraitDef {
        span,
        attributes: vec![],
        path: path_std!(cx, cmp::PartialOrd),
        additional_bounds: vec![],
        generics: LifetimeBounds::empty(),
        is_unsafe: false,
        supports_unions: false,
        methods,
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

#[derive(Copy, Clone)]
pub enum OrderingOp {
    PartialCmpOp,
    LtOp,
    LeOp,
    GtOp,
    GeOp,
}

pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
                               span: Span,
                               op: OrderingOp,
                               self_arg_tags: &[ast::Ident])
                               -> P<ast::Expr> {
    let lft = cx.expr_ident(span, self_arg_tags[0]);
    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
    let op_str = match op {
        PartialCmpOp => "partial_cmp",
        LtOp => "lt",
        LeOp => "le",
        GtOp => "gt",
        GeOp => "ge",
    };
    cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
}

pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
    let test_id = cx.ident_of("cmp").gensym();
    let ordering = cx.path_global(span, cx.std_path(&["cmp", "Ordering", "Equal"]));
    let ordering_expr = cx.expr_path(ordering.clone());
    let equals_expr = cx.expr_some(span, ordering_expr);

    let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]);

    // Builds:
    //
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) =>
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {
    // ...
    // }
    // cmp => cmp
    // },
    // cmp => cmp
    // }
    //
    cs_fold(// foldr nests the if-elses correctly, leaving the first field
            // as the outermost one, and the last as the innermost.
            false,
            |cx, span, old, self_f, other_fs| {
                // match new {
                //     Some(::std::cmp::Ordering::Equal) => old,
                //     cmp => cmp
                // }

                let new = {
                    let other_f = match (other_fs.len(), other_fs.get(0)) {
                        (1, Some(o_f)) => o_f,
                                _ => {
                                    cx.span_bug(span,
                                        "not exactly 2 arguments in `derive(PartialOrd)`")
                                }
                    };

                    let args = vec![
                            cx.expr_addr_of(span, self_f),
                            cx.expr_addr_of(span, other_f.clone()),
                        ];

                    cx.expr_call_global(span, partial_cmp_path.clone(), args)
                };

                let eq_arm = cx.arm(span,
                                    vec![cx.pat_some(span, cx.pat_path(span, ordering.clone()))],
                                    old);
                let neq_arm = cx.arm(span,
                                    vec![cx.pat_ident(span, test_id)],
                                    cx.expr_ident(span, test_id));

                cx.expr_match(span, new, vec![eq_arm, neq_arm])
            },
            equals_expr.clone(),
            Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
        if self_args.len() != 2 {
            cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
        } else {
            some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
        }
    }),
            cx,
            span,
            substr)
}

/// Strict inequality.
fn cs_op(less: bool,
         inclusive: bool,
         cx: &mut ExtCtxt,
         span: Span,
         substr: &Substructure) -> P<Expr> {
    let ordering_path = |cx: &mut ExtCtxt, name: &str| {
        cx.expr_path(cx.path_global(span, cx.std_path(&["cmp", "Ordering", name])))
    };

    let par_cmp = |cx: &mut ExtCtxt, span, self_f: P<Expr>, other_fs: &[P<Expr>], default| {
        let other_f = match (other_fs.len(), other_fs.get(0)) {
            (1, Some(o_f)) => o_f,
            _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
        };

        // `PartialOrd::partial_cmp(self.fi, other.fi)`
        let cmp_path = cx.expr_path(cx.path_global(span, cx.std_path(&["cmp",
                                                                       "PartialOrd",
                                                                       "partial_cmp"])));
        let cmp = cx.expr_call(span,
                               cmp_path,
                               vec![cx.expr_addr_of(span, self_f),
                                    cx.expr_addr_of(span, other_f.clone())]);

        let default = ordering_path(cx, default);
        // `Option::unwrap_or(_, Ordering::Equal)`
        let unwrap_path = cx.expr_path(cx.path_global(span, cx.std_path(&["option",
                                                                          "Option",
                                                                          "unwrap_or"])));
        cx.expr_call(span, unwrap_path, vec![cmp, default])
    };

    let fold = cs_fold1(false, // need foldr
        |cx, span, subexpr, self_f, other_fs| {
            // build up a series of `partial_cmp`s from the inside
            // out (hence foldr) to get lexical ordering, i.e. for op ==
            // `ast::lt`
            //
            // ```
            // self.f1.partial_cmp(other.f1).unwrap_or(Ordering::Equal)
            //     .then_with(|| self.f2.partial_cmp(other.f2).unwrap_or(Ordering::Equal))
            // == Ordering::Less
            // ```
            //
            // and for op ==
            // `ast::le`
            //
            // ```
            // self.f1.partial_cmp(other.f1).unwrap_or(Ordering::Equal)
            //     .then_with(|| self.f2.partial_cmp(other.f2).unwrap_or(Ordering::Equal))
            // != Ordering::Greater
            // ```
            //
            // The optimiser should remove the redundancy. We explicitly
            // get use the binops to avoid auto-deref dereferencing too many
            // layers of pointers, if the type includes pointers.

            // `self.fi.partial_cmp(other.fi).unwrap_or(Ordering::Equal)`
            let par_cmp = par_cmp(cx, span, self_f, other_fs, "Equal");

            // `self.fi.partial_cmp(other.fi).unwrap_or(Ordering::Equal).then_with(...)`
            cx.expr_method_call(span,
                                par_cmp,
                                Ident::from_str("then_with"),
                                vec![cx.lambda0(span, subexpr)])
        },
        |cx, args| {
            match args {
                Some((span, self_f, other_fs)) => {
                    let opposite = if less { "Greater" } else { "Less" };
                    par_cmp(cx, span, self_f, other_fs, opposite)
                },
                None => cx.expr_bool(span, inclusive)
            }
        },
        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
            if self_args.len() != 2 {
                cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
            } else {
                let op = match (less, inclusive) {
                    (false, false) => GtOp,
                    (false, true) => GeOp,
                    (true, false) => LtOp,
                    (true, true) => LeOp,
                };
                some_ordering_collapsed(cx, span, op, tag_tuple)
            }
        }),
        cx,
        span,
        substr);

    match *substr.fields {
        EnumMatching(.., ref all_fields) |
        Struct(.., ref all_fields) if !all_fields.is_empty() => {
            let ordering = ordering_path(cx, if less ^ inclusive { "Less" } else { "Greater" });
            let comp_op = if inclusive { BinOpKind::Ne } else { BinOpKind::Eq };

            cx.expr_binary(span, comp_op, fold, ordering)
        }
        _ => fold
    }
}
<commit_msg>Update comments to UFCS style<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

pub use self::OrderingOp::*;

use deriving::{path_local, pathvec_std, path_std};
use deriving::generic::*;
use deriving::generic::ty::*;

use syntax::ast::{self, BinOpKind, Expr, MetaItem};
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax_pos::Span;

pub fn expand_deriving_partial_ord(cx: &mut ExtCtxt,
                                   span: Span,
                                   mitem: &MetaItem,
                                   item: &Annotatable,
                                   push: &mut FnMut(Annotatable)) {
    macro_rules! md {
        ($name:expr, $op:expr, $equal:expr) => { {
            let inline = cx.meta_word(span, Symbol::intern("inline"));
            let attrs = vec![cx.attribute(span, inline)];
            MethodDef {
                name: $name,
                generics: LifetimeBounds::empty(),
                explicit_self: borrowed_explicit_self(),
                args: vec![(borrowed_self(), "other")],
                ret_ty: Literal(path_local!(bool)),
                attributes: attrs,
                is_unsafe: false,
                unify_fieldless_variants: true,
                combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
                    cs_op($op, $equal, cx, span, substr)
                }))
            }
        } }
    }

    let ordering_ty = Literal(path_std!(cx, cmp::Ordering));
    let ret_ty = Literal(Path::new_(pathvec_std!(cx, option::Option),
                                    None,
                                    vec![Box::new(ordering_ty)],
                                    PathKind::Std));

    let inline = cx.meta_word(span, Symbol::intern("inline"));
    let attrs = vec![cx.attribute(span, inline)];

    let partial_cmp_def = MethodDef {
        name: "partial_cmp",
        generics: LifetimeBounds::empty(),
        explicit_self: borrowed_explicit_self(),
        args: vec![(borrowed_self(), "other")],
        ret_ty,
        attributes: attrs,
        is_unsafe: false,
        unify_fieldless_variants: true,
        combine_substructure: combine_substructure(Box::new(|cx, span, substr| {
            cs_partial_cmp(cx, span, substr)
        })),
    };

    // avoid defining extra methods if we can
    // c-like enums, enums without any fields and structs without fields
    // can safely define only `partial_cmp`.
    let methods = if is_type_without_fields(item) {
        vec![partial_cmp_def]
    } else {
        vec![partial_cmp_def,
             md!("lt", true, false),
             md!("le", true, true),
             md!("gt", false, false),
             md!("ge", false, true)]
    };

    let trait_def = TraitDef {
        span,
        attributes: vec![],
        path: path_std!(cx, cmp::PartialOrd),
        additional_bounds: vec![],
        generics: LifetimeBounds::empty(),
        is_unsafe: false,
        supports_unions: false,
        methods,
        associated_types: Vec::new(),
    };
    trait_def.expand(cx, mitem, item, push)
}

#[derive(Copy, Clone)]
pub enum OrderingOp {
    PartialCmpOp,
    LtOp,
    LeOp,
    GtOp,
    GeOp,
}

pub fn some_ordering_collapsed(cx: &mut ExtCtxt,
                               span: Span,
                               op: OrderingOp,
                               self_arg_tags: &[ast::Ident])
                               -> P<ast::Expr> {
    let lft = cx.expr_ident(span, self_arg_tags[0]);
    let rgt = cx.expr_addr_of(span, cx.expr_ident(span, self_arg_tags[1]));
    let op_str = match op {
        PartialCmpOp => "partial_cmp",
        LtOp => "lt",
        LeOp => "le",
        GtOp => "gt",
        GeOp => "ge",
    };
    cx.expr_method_call(span, lft, cx.ident_of(op_str), vec![rgt])
}

pub fn cs_partial_cmp(cx: &mut ExtCtxt, span: Span, substr: &Substructure) -> P<Expr> {
    let test_id = cx.ident_of("cmp").gensym();
    let ordering = cx.path_global(span, cx.std_path(&["cmp", "Ordering", "Equal"]));
    let ordering_expr = cx.expr_path(ordering.clone());
    let equals_expr = cx.expr_some(span, ordering_expr);

    let partial_cmp_path = cx.std_path(&["cmp", "PartialOrd", "partial_cmp"]);

    // Builds:
    //
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field1, &other_field1) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) =>
    // match ::std::cmp::PartialOrd::partial_cmp(&self_field2, &other_field2) {
    // ::std::option::Option::Some(::std::cmp::Ordering::Equal) => {
    // ...
    // }
    // cmp => cmp
    // },
    // cmp => cmp
    // }
    //
    cs_fold(// foldr nests the if-elses correctly, leaving the first field
            // as the outermost one, and the last as the innermost.
            false,
            |cx, span, old, self_f, other_fs| {
                // match new {
                //     Some(::std::cmp::Ordering::Equal) => old,
                //     cmp => cmp
                // }

                let new = {
                    let other_f = match (other_fs.len(), other_fs.get(0)) {
                        (1, Some(o_f)) => o_f,
                                _ => {
                                    cx.span_bug(span,
                                        "not exactly 2 arguments in `derive(PartialOrd)`")
                                }
                    };

                    let args = vec![
                            cx.expr_addr_of(span, self_f),
                            cx.expr_addr_of(span, other_f.clone()),
                        ];

                    cx.expr_call_global(span, partial_cmp_path.clone(), args)
                };

                let eq_arm = cx.arm(span,
                                    vec![cx.pat_some(span, cx.pat_path(span, ordering.clone()))],
                                    old);
                let neq_arm = cx.arm(span,
                                    vec![cx.pat_ident(span, test_id)],
                                    cx.expr_ident(span, test_id));

                cx.expr_match(span, new, vec![eq_arm, neq_arm])
            },
            equals_expr.clone(),
            Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
        if self_args.len() != 2 {
            cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
        } else {
            some_ordering_collapsed(cx, span, PartialCmpOp, tag_tuple)
        }
    }),
            cx,
            span,
            substr)
}

/// Strict inequality.
fn cs_op(less: bool,
         inclusive: bool,
         cx: &mut ExtCtxt,
         span: Span,
         substr: &Substructure) -> P<Expr> {
    let ordering_path = |cx: &mut ExtCtxt, name: &str| {
        cx.expr_path(cx.path_global(span, cx.std_path(&["cmp", "Ordering", name])))
    };

    let par_cmp = |cx: &mut ExtCtxt, span, self_f: P<Expr>, other_fs: &[P<Expr>], default| {
        let other_f = match (other_fs.len(), other_fs.get(0)) {
            (1, Some(o_f)) => o_f,
            _ => cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`"),
        };

        // `PartialOrd::partial_cmp(self.fi, other.fi)`
        let cmp_path = cx.expr_path(cx.path_global(span, cx.std_path(&["cmp",
                                                                       "PartialOrd",
                                                                       "partial_cmp"])));
        let cmp = cx.expr_call(span,
                               cmp_path,
                               vec![cx.expr_addr_of(span, self_f),
                                    cx.expr_addr_of(span, other_f.clone())]);

        let default = ordering_path(cx, default);
        // `Option::unwrap_or(_, Ordering::Equal)`
        let unwrap_path = cx.expr_path(cx.path_global(span, cx.std_path(&["option",
                                                                          "Option",
                                                                          "unwrap_or"])));
        cx.expr_call(span, unwrap_path, vec![cmp, default])
    };

    let fold = cs_fold1(false, // need foldr
        |cx, span, subexpr, self_f, other_fs| {
            // build up a series of `partial_cmp`s from the inside
            // out (hence foldr) to get lexical ordering, i.e. for op ==
            // `ast::lt`
            //
            // ```
            // Ordering::then_with(
            //    Option::unwrap_or(
            //        PartialOrd::partial_cmp(self.f1, other.f1), Ordering::Equal)
            //    ),
            //    Option::unwrap_or(
            //        PartialOrd::partial_cmp(self.f2, other.f2), Ordering::Greater)
            //    )
            // )
            // == Ordering::Less
            // ```
            //
            // and for op ==
            // `ast::le`
            //
            // ```
            // Ordering::then_with(
            //    Option::unwrap_or(
            //        PartialOrd::partial_cmp(self.f1, other.f1), Ordering::Equal)
            //    ),
            //    Option::unwrap_or(
            //        PartialOrd::partial_cmp(self.f2, other.f2), Ordering::Greater)
            //    )
            // )
            // != Ordering::Greater
            // ```
            //
            // The optimiser should remove the redundancy. We explicitly
            // get use the binops to avoid auto-deref dereferencing too many
            // layers of pointers, if the type includes pointers.

            // `Option::unwrap_or(PartialOrd::partial_cmp(self.fi, other.fi), Ordering::Equal)`
            let par_cmp = par_cmp(cx, span, self_f, other_fs, "Equal");

            // `Ordering::then_with(Option::unwrap_or(..), ..)`
            let then_with_path = cx.expr_path(cx.path_global(span,
                                                             cx.std_path(&["cmp",
                                                                           "Ordering",
                                                                           "then_with"])));
            cx.expr_call(span, then_with_path, vec![par_cmp, cx.lambda0(span, subexpr)])
        },
        |cx, args| {
            match args {
                Some((span, self_f, other_fs)) => {
                    let opposite = if less { "Greater" } else { "Less" };
                    par_cmp(cx, span, self_f, other_fs, opposite)
                },
                None => cx.expr_bool(span, inclusive)
            }
        },
        Box::new(|cx, span, (self_args, tag_tuple), _non_self_args| {
            if self_args.len() != 2 {
                cx.span_bug(span, "not exactly 2 arguments in `derive(PartialOrd)`")
            } else {
                let op = match (less, inclusive) {
                    (false, false) => GtOp,
                    (false, true) => GeOp,
                    (true, false) => LtOp,
                    (true, true) => LeOp,
                };
                some_ordering_collapsed(cx, span, op, tag_tuple)
            }
        }),
        cx,
        span,
        substr);

    match *substr.fields {
        EnumMatching(.., ref all_fields) |
        Struct(.., ref all_fields) if !all_fields.is_empty() => {
            let ordering = ordering_path(cx, if less ^ inclusive { "Less" } else { "Greater" });
            let comp_op = if inclusive { BinOpKind::Ne } else { BinOpKind::Eq };

            cx.expr_binary(span, comp_op, fold, ordering)
        }
        _ => fold
    }
}
<|endoftext|>


<commit_before>use rustc_hash::FxHashSet;

use ra_syntax::{
    ast, AstNode, Direction, SourceFile, SyntaxNode, TextRange,
    SyntaxKind::{self, *},
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Block,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();

    for node in file.syntax().descendants() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(node.kind()) {
            if has_newline(node) {
                res.push(Fold {
                    range: node.range(),
                    kind,
                });
            }
        }

        // Fold groups of comments
        if node.kind() == COMMENT && !visited_comments.contains(&node) {
            if let Some(range) = contiguous_range_for_comment(node, &mut visited_comments) {
                res.push(Fold {
                    range,
                    kind: FoldKind::Comment,
                })
            }
        }

        // Fold groups of imports
        if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
            if let Some(range) = contiguous_range_for_group(node, &mut visited_imports) {
                res.push(Fold {
                    range,
                    kind: FoldKind::Imports,
                })
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        USE_ITEM => Some(FoldKind::Imports),
        NAMED_FIELD_DEF_LIST | FIELD_PAT_LIST | ITEM_LIST | EXTERN_ITEM_LIST | USE_TREE_LIST
        | BLOCK | ENUM_VARIANT_LIST => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_newline(node: &SyntaxNode) -> bool {
    for descendant in node.descendants() {
        if let Some(ws) = ast::Whitespace::cast(descendant) {
            if ws.has_newlines() {
                return true;
            }
        } else if let Some(comment) = ast::Comment::cast(descendant) {
            if comment.has_newlines() {
                return true;
            }
        }
    }

    false
}

fn contiguous_range_for_group<'a>(
    first: &'a SyntaxNode,
    visited: &mut FxHashSet<&'a SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first);

    let mut last = first;
    for node in first.siblings(Direction::Next) {
        if let Some(ws) = ast::Whitespace::cast(node) {
            // There is a blank line, which means that the group ends here
            if ws.count_newlines_lazy().take(2).count() == 2 {
                break;
            }

            // Ignore whitespace without blank lines
            continue;
        }

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() {
            break;
        }

        visited.insert(node);
        last = node;
    }

    if first != last {
        Some(TextRange::from_to(
            first.range().start(),
            last.range().end(),
        ))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment<'a>(
    first: &'a SyntaxNode,
    visited: &mut FxHashSet<&'a SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first);

    // Only fold comments of the same flavor
    let group_flavor = ast::Comment::cast(first)?.flavor();

    let mut last = first;
    for node in first.siblings(Direction::Next) {
        if let Some(ws) = ast::Whitespace::cast(node) {
            // There is a blank line, which means the group ends here
            if ws.count_newlines_lazy().take(2).count() == 2 {
                break;
            }

            // Ignore whitespace without blank lines
            continue;
        }

        match ast::Comment::cast(node) {
            Some(next_comment) if next_comment.flavor() == group_flavor => {
                visited.insert(node);
                last = node;
            }
            // The comment group ends because either:
            // * An element of a different kind was reached
            // * A comment of a different flavor was reached
            _ => break,
        }
    }

    if first != last {
        Some(TextRange::from_to(
            first.range().start(),
            last.range().end(),
        ))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

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

    fn do_check(text: &str, fold_kinds: &[FoldKind]) {
        let (ranges, text) = extract_ranges(text, "fold");
        let file = SourceFile::parse(&text);
        let folds = folding_ranges(&file);

        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );
        assert_eq!(
            folds.len(),
            fold_kinds.len(),
            "The amount of fold kinds is different than the expected amount"
        );
        for ((fold, range), fold_kind) in folds
            .into_iter()
            .zip(ranges.into_iter())
            .zip(fold_kinds.into_iter())
        {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());
            assert_eq!(&fold.kind, fold_kind);
        }
    }

    #[test]
    fn test_fold_comments() {
        let text = r#"
<fold>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold>{
    <fold>// We should
    // also
    // fold
    // this one.</fold>
    <fold>//! But this one is different
    //! because it has another flavor</fold>
    <fold>/* As does this
    multiline comment */</fold>
}</fold>"#;

        let fold_kinds = &[
            FoldKind::Comment,
            FoldKind::Block,
            FoldKind::Comment,
            FoldKind::Comment,
            FoldKind::Comment,
        ];
        do_check(text, fold_kinds);
    }

    #[test]
    fn test_fold_imports() {
        let text = r#"
<fold>use std::<fold>{
    str,
    vec,
    io as iop
}</fold>;</fold>

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_and_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

<fold>use std::collections::<fold>{
    HashMap,
    VecDeque,
}</fold>;</fold>
// Some random comment

fn main() <fold>{
}</fold>"#;

        let folds = &[
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Block,
            FoldKind::Block,
        ];
        do_check(text, folds);
    }

}
<commit_msg>fold macros<commit_after>use rustc_hash::FxHashSet;

use ra_syntax::{
    ast, AstNode, Direction, SourceFile, SyntaxNode, TextRange,
    SyntaxKind::{self, *},
};

#[derive(Debug, PartialEq, Eq)]
pub enum FoldKind {
    Comment,
    Imports,
    Block,
}

#[derive(Debug)]
pub struct Fold {
    pub range: TextRange,
    pub kind: FoldKind,
}

pub fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
    let mut res = vec![];
    let mut visited_comments = FxHashSet::default();
    let mut visited_imports = FxHashSet::default();

    for node in file.syntax().descendants() {
        // Fold items that span multiple lines
        if let Some(kind) = fold_kind(node.kind()) {
            if has_newline(node) {
                res.push(Fold {
                    range: node.range(),
                    kind,
                });
            }
        }

        // Fold groups of comments
        if node.kind() == COMMENT && !visited_comments.contains(&node) {
            if let Some(range) = contiguous_range_for_comment(node, &mut visited_comments) {
                res.push(Fold {
                    range,
                    kind: FoldKind::Comment,
                })
            }
        }

        // Fold groups of imports
        if node.kind() == USE_ITEM && !visited_imports.contains(&node) {
            if let Some(range) = contiguous_range_for_group(node, &mut visited_imports) {
                res.push(Fold {
                    range,
                    kind: FoldKind::Imports,
                })
            }
        }
    }

    res
}

fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
    match kind {
        COMMENT => Some(FoldKind::Comment),
        USE_ITEM => Some(FoldKind::Imports),
        NAMED_FIELD_DEF_LIST | FIELD_PAT_LIST | ITEM_LIST | EXTERN_ITEM_LIST | USE_TREE_LIST
        | BLOCK | ENUM_VARIANT_LIST | TOKEN_TREE => Some(FoldKind::Block),
        _ => None,
    }
}

fn has_newline(node: &SyntaxNode) -> bool {
    for descendant in node.descendants() {
        if let Some(ws) = ast::Whitespace::cast(descendant) {
            if ws.has_newlines() {
                return true;
            }
        } else if let Some(comment) = ast::Comment::cast(descendant) {
            if comment.has_newlines() {
                return true;
            }
        }
    }

    false
}

fn contiguous_range_for_group<'a>(
    first: &'a SyntaxNode,
    visited: &mut FxHashSet<&'a SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first);

    let mut last = first;
    for node in first.siblings(Direction::Next) {
        if let Some(ws) = ast::Whitespace::cast(node) {
            // There is a blank line, which means that the group ends here
            if ws.count_newlines_lazy().take(2).count() == 2 {
                break;
            }

            // Ignore whitespace without blank lines
            continue;
        }

        // Stop if we find a node that doesn't belong to the group
        if node.kind() != first.kind() {
            break;
        }

        visited.insert(node);
        last = node;
    }

    if first != last {
        Some(TextRange::from_to(
            first.range().start(),
            last.range().end(),
        ))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

fn contiguous_range_for_comment<'a>(
    first: &'a SyntaxNode,
    visited: &mut FxHashSet<&'a SyntaxNode>,
) -> Option<TextRange> {
    visited.insert(first);

    // Only fold comments of the same flavor
    let group_flavor = ast::Comment::cast(first)?.flavor();

    let mut last = first;
    for node in first.siblings(Direction::Next) {
        if let Some(ws) = ast::Whitespace::cast(node) {
            // There is a blank line, which means the group ends here
            if ws.count_newlines_lazy().take(2).count() == 2 {
                break;
            }

            // Ignore whitespace without blank lines
            continue;
        }

        match ast::Comment::cast(node) {
            Some(next_comment) if next_comment.flavor() == group_flavor => {
                visited.insert(node);
                last = node;
            }
            // The comment group ends because either:
            // * An element of a different kind was reached
            // * A comment of a different flavor was reached
            _ => break,
        }
    }

    if first != last {
        Some(TextRange::from_to(
            first.range().start(),
            last.range().end(),
        ))
    } else {
        // The group consists of only one element, therefore it cannot be folded
        None
    }
}

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

    fn do_check(text: &str, fold_kinds: &[FoldKind]) {
        let (ranges, text) = extract_ranges(text, "fold");
        let file = SourceFile::parse(&text);
        let folds = folding_ranges(&file);

        assert_eq!(
            folds.len(),
            ranges.len(),
            "The amount of folds is different than the expected amount"
        );
        assert_eq!(
            folds.len(),
            fold_kinds.len(),
            "The amount of fold kinds is different than the expected amount"
        );
        for ((fold, range), fold_kind) in folds
            .into_iter()
            .zip(ranges.into_iter())
            .zip(fold_kinds.into_iter())
        {
            assert_eq!(fold.range.start(), range.start());
            assert_eq!(fold.range.end(), range.end());
            assert_eq!(&fold.kind, fold_kind);
        }
    }

    #[test]
    fn test_fold_comments() {
        let text = r#"
<fold>// Hello
// this is a multiline
// comment
//</fold>

// But this is not

fn main() <fold>{
    <fold>// We should
    // also
    // fold
    // this one.</fold>
    <fold>//! But this one is different
    //! because it has another flavor</fold>
    <fold>/* As does this
    multiline comment */</fold>
}</fold>"#;

        let fold_kinds = &[
            FoldKind::Comment,
            FoldKind::Block,
            FoldKind::Comment,
            FoldKind::Comment,
            FoldKind::Comment,
        ];
        do_check(text, fold_kinds);
    }

    #[test]
    fn test_fold_imports() {
        let text = r#"
<fold>use std::<fold>{
    str,
    vec,
    io as iop
}</fold>;</fold>

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Block, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

use std::collections::HashMap;
// Some random comment
use std::collections::VecDeque;

fn main() <fold>{
}</fold>"#;

        let folds = &[FoldKind::Imports, FoldKind::Imports, FoldKind::Block];
        do_check(text, folds);
    }

    #[test]
    fn test_fold_import_and_groups() {
        let text = r#"
<fold>use std::str;
use std::vec;
use std::io as iop;</fold>

<fold>use std::mem;
use std::f64;</fold>

<fold>use std::collections::<fold>{
    HashMap,
    VecDeque,
}</fold>;</fold>
// Some random comment

fn main() <fold>{
}</fold>"#;

        let folds = &[
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Imports,
            FoldKind::Block,
            FoldKind::Block,
        ];
        do_check(text, folds);
    }

    #[test]
    fn test_folds_macros() {
        let text = r#"
macro_rules! foo <fold>{
    ($($tt:tt)*) => { $($tt)* }
}</fold>
"#;

        let folds = &[FoldKind::Block];
        do_check(text, folds);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use quote::{Tokens, ToTokens};
use syn::{Ident, Lit, MetaItem, NestedMetaItem, MacroInput};
use case::CaseExt;

pub struct Command {
    ast: MacroInput,
}

impl Command {
    pub fn new(ast: MacroInput) -> Command {
        Command {
            ast: ast,
        }
    }

    pub fn derive(&self) -> Tokens {
        let typ = self.typ();
        let docs = self.docs();
        let sig = self.sig();
        let body = self.body();
        let note = self.note();
        let related = self.related();

        quote! {
            #docs
            #note
            #related
            pub trait #typ {
                fn #sig;
            }

            impl #typ for ::commands::Command {
                fn #sig {
                    #body
                }
            }
        }
    }

    fn note(&self) -> Tokens {
        if self.docs().to_string().contains("Example") {
            return Tokens::new();
        }

        let msg = format!(r" **Note:** This command is not yet fully documented. For more information and examples, please see [the equivalent command](https://rethinkdb.com/api/java/{}/) in the official Java driver documentation.", self.name());

        quote!{
            #[doc=r""]
            #[doc=#msg]
        }
    }

    fn related(&self) -> Tokens {
        let mut related = Tokens::new();
        for item in self.value() {
            if let NestedMetaItem::MetaItem(MetaItem::List(ref name, ref value)) = *item {
                if name == "related" {
                    related.append_all(&[quote!{
                        ///
                        /// # Related commands
                        ///
                    }]);
                    for item in value {
                        match *item {
                            NestedMetaItem::MetaItem(ref item) => {
                                if let MetaItem::Word(ref cmd) = *item {
                                    let item = format!(r" * [{}](trait.{}.html)", cmd, cmd.as_ref().to_camel());
                                    related.append_all(&[quote!(#[doc=#item])]);
                                } else {
                                    panic!("Related commands must be identifiers");
                                }
                            },
                            NestedMetaItem::Literal(_) => { panic!("Related commands must not be literals"); },
                        }
                    }
                }
            }
        }
        related
    }

    fn command(&self) -> &MetaItem {
        let mut commands = self.ast.attrs.iter().filter(|attr| {
                if let MetaItem::List(ref attr, _) = attr.value {
                    attr == "command"
                } else {
                    false
                }
            })
        .peekable();
        let command = commands.peek().expect("command attribute not defined");
        let mut meta_items = Vec::new();
        match command.value {
            MetaItem::List(_, ref items) => {
                if items.len() != 1 {
                    panic!("A command attribute must only carry one command.");
                }
                for item in items {
                    match *item {
                        NestedMetaItem::MetaItem(ref command) => { meta_items.push(command); },
                        NestedMetaItem::Literal(_) => { panic!("A command attribute should not define commands as literals"); },
                    }
                }
            }
            _ => { panic!("A command attribute must be defined as a list of items. That is `#[command(..)]`."); },
        };
        meta_items.iter().next().unwrap()
    }

    fn name(&self) -> &Ident {
        match *self.command() {
            MetaItem::Word(ref name) => name,
            MetaItem::List(ref name, _) => name,
            MetaItem::NameValue(..) => { panic!("A command must not be a name value pair"); },
        }
    }

    fn value(&self) -> Vec<&NestedMetaItem> {
        let mut value = Vec::new();
        match *self.command() {
            MetaItem::Word(_) => { },
            MetaItem::List(_, ref items) => {
                for item in items {
                    value.push(item);
                }
            }
            MetaItem::NameValue(..) => { panic!("A command must not be a name value pair"); },
        }
        value
    }

    fn args(&self) -> Vec<(Ident, Ident)> {
        let mut args = Vec::new();
        for item in self.value() {
            if let NestedMetaItem::MetaItem(MetaItem::List(ref name, ref value)) = *item {
                if name == "args" {
                    for item in value {
                        match *item {
                            NestedMetaItem::MetaItem(ref item) => {
                                if let MetaItem::NameValue(ref name, ref typ) = *item {
                                    match *typ {
                                        Lit::Str(ref typ, _) => {
                                            args.push((name.clone(), Ident::new(typ.as_str())));
                                        }
                                        _ => { panic!("An arg name must be defined as a string"); },
                                    };
                                } else {
                                    panic!("Args must be key value pairs");
                                }
                            },
                            NestedMetaItem::Literal(_) => { panic!("Args must not be literals"); },
                        }
                    }
                }
            }
        }
        args
    }

    fn typ(&self) -> Tokens {
        let name = self.name().to_string().to_camel();
        let typ = Ident::new(name);
        quote!(#typ)
    }

    fn docs(&self) -> Tokens {
        let mut docs = Tokens::new();
        let mut in_code_block = false;
        for attr in self.ast.attrs.iter() {
            if let MetaItem::NameValue(ref name, ref value) = attr.value {
                if name == "doc" {
                    if let Lit::Str(ref doc_str, _) = *value {
                        if doc_str.contains("```reql") {
                            in_code_block = true;
                            let token = quote! {
                                /// ```
                                /// # #![allow(unused_imports)]
                                /// # #![allow(unused_variables)]
                                /// # #![allow(unused_must_use)]
                                /// # #[macro_use] extern crate reql;
                                /// # use reql::commands::*;
                                /// # fn main() {
                                /// # let r = Command::new();
                            };
                            token.to_tokens(&mut docs);
                        } else if doc_str.contains("```") && in_code_block {
                            in_code_block = false;
                            let token = quote! {
                                /// # }
                                /// ```
                            };
                            token.to_tokens(&mut docs);
                        } else {
                            attr.to_tokens(&mut docs);
                        }
                    }
                }
            }
        }
        docs
    }

    fn sig(&self) -> Tokens {
        let mut types = HashMap::new();

        let func = self.name().clone();
        let mut generics = Tokens::new();
        let mut func_args = Tokens::new();
        let mut _where = Tokens::new();

        let args = self.args();
        if !args.is_empty() {
            let mut gen = Vec::new();
            let mut whe = Vec::new();

            gen.push(quote!(<));
            whe.push(quote!(where));

            for (arg, typ) in args {
                let typ_str = typ.to_string();
                if let None = types.get(&typ_str) {
                    gen.push(quote!(#typ));
                    gen.push(quote!(,));
                    whe.push(quote!(#typ: ::ToArg));
                    whe.push(quote!(,));
                    types.insert(typ_str, ());
                }
                func_args.append_all(&[ quote!(, #arg: #typ) ]);
            }

            gen.pop();
            whe.pop();

            gen.push(quote!(>));

            generics.append_all(gen);
            _where.append_all(whe);
        }

        quote! {
            #func #generics (&self #func_args) -> ::commands::Command #_where
        }
    }

    fn body(&self) -> Tokens {
        // Rename commands to term types
        let cmd = match self.name() {
            name if name == "match_" => "match".to_string(),
            name if name == "mod_" => "mod".to_string(),
            name if name == "do_" => "funcall".to_string(),
            name if name == "js" => "javascript".to_string(),
            name if name == "to_json" => "to_json_string".to_string(),
            name => name.to_string(),
        };
        // Prepare args
        let mut args = Tokens::new();
        for (arg, _) in self.args() {
            let token = quote! {
                cmd = ::commands::WithArgs::with_args(&cmd, #arg);
            };
            token.to_tokens(&mut args);
        }
        let cmd_type = Ident::new(cmd.to_snake().to_uppercase());
        // Build the body
        quote! {
            use ::ql2::proto::Term;
            use ::protobuf::repeated::RepeatedField;
            use ::ql2::proto::Term_TermType;

            let mut term = Term::new();
            term.set_field_type(Term_TermType::#cmd_type);
            if *self.term() != Term::new() {
                let prev_cmd = RepeatedField::from_vec(vec![self.term().clone()]);
                term.set_args(prev_cmd);
            }
            let mut cmd = ::commands::Command::new();
            cmd.set_term(term);
            #args
            cmd
        }
    }
}
<commit_msg>Add child logger for each command<commit_after>use std::collections::HashMap;

use quote::{Tokens, ToTokens};
use syn::{Ident, Lit, MetaItem, NestedMetaItem, MacroInput};
use case::CaseExt;

pub struct Command {
    ast: MacroInput,
}

impl Command {
    pub fn new(ast: MacroInput) -> Command {
        Command {
            ast: ast,
        }
    }

    pub fn derive(&self) -> Tokens {
        let typ = self.typ();
        let docs = self.docs();
        let sig = self.sig();
        let body = self.body();
        let note = self.note();
        let related = self.related();

        quote! {
            #docs
            #note
            #related
            pub trait #typ {
                fn #sig;
            }

            impl #typ for ::commands::Command {
                fn #sig {
                    #body
                }
            }
        }
    }

    fn note(&self) -> Tokens {
        if self.docs().to_string().contains("Example") {
            return Tokens::new();
        }

        let msg = format!(r" **Note:** This command is not yet fully documented. For more information and examples, please see [the equivalent command](https://rethinkdb.com/api/java/{}/) in the official Java driver documentation.", self.name());

        quote!{
            #[doc=r""]
            #[doc=#msg]
        }
    }

    fn related(&self) -> Tokens {
        let mut related = Tokens::new();
        for item in self.value() {
            if let NestedMetaItem::MetaItem(MetaItem::List(ref name, ref value)) = *item {
                if name == "related" {
                    related.append_all(&[quote!{
                        ///
                        /// # Related commands
                        ///
                    }]);
                    for item in value {
                        match *item {
                            NestedMetaItem::MetaItem(ref item) => {
                                if let MetaItem::Word(ref cmd) = *item {
                                    let item = format!(r" * [{}](trait.{}.html)", cmd, cmd.as_ref().to_camel());
                                    related.append_all(&[quote!(#[doc=#item])]);
                                } else {
                                    panic!("Related commands must be identifiers");
                                }
                            },
                            NestedMetaItem::Literal(_) => { panic!("Related commands must not be literals"); },
                        }
                    }
                }
            }
        }
        related
    }

    fn command(&self) -> &MetaItem {
        let mut commands = self.ast.attrs.iter().filter(|attr| {
                if let MetaItem::List(ref attr, _) = attr.value {
                    attr == "command"
                } else {
                    false
                }
            })
        .peekable();
        let command = commands.peek().expect("command attribute not defined");
        let mut meta_items = Vec::new();
        match command.value {
            MetaItem::List(_, ref items) => {
                if items.len() != 1 {
                    panic!("A command attribute must only carry one command.");
                }
                for item in items {
                    match *item {
                        NestedMetaItem::MetaItem(ref command) => { meta_items.push(command); },
                        NestedMetaItem::Literal(_) => { panic!("A command attribute should not define commands as literals"); },
                    }
                }
            }
            _ => { panic!("A command attribute must be defined as a list of items. That is `#[command(..)]`."); },
        };
        meta_items.iter().next().unwrap()
    }

    fn name(&self) -> &Ident {
        match *self.command() {
            MetaItem::Word(ref name) => name,
            MetaItem::List(ref name, _) => name,
            MetaItem::NameValue(..) => { panic!("A command must not be a name value pair"); },
        }
    }

    fn value(&self) -> Vec<&NestedMetaItem> {
        let mut value = Vec::new();
        match *self.command() {
            MetaItem::Word(_) => { },
            MetaItem::List(_, ref items) => {
                for item in items {
                    value.push(item);
                }
            }
            MetaItem::NameValue(..) => { panic!("A command must not be a name value pair"); },
        }
        value
    }

    fn args(&self) -> Vec<(Ident, Ident)> {
        let mut args = Vec::new();
        for item in self.value() {
            if let NestedMetaItem::MetaItem(MetaItem::List(ref name, ref value)) = *item {
                if name == "args" {
                    for item in value {
                        match *item {
                            NestedMetaItem::MetaItem(ref item) => {
                                if let MetaItem::NameValue(ref name, ref typ) = *item {
                                    match *typ {
                                        Lit::Str(ref typ, _) => {
                                            args.push((name.clone(), Ident::new(typ.as_str())));
                                        }
                                        _ => { panic!("An arg name must be defined as a string"); },
                                    };
                                } else {
                                    panic!("Args must be key value pairs");
                                }
                            },
                            NestedMetaItem::Literal(_) => { panic!("Args must not be literals"); },
                        }
                    }
                }
            }
        }
        args
    }

    fn typ(&self) -> Tokens {
        let name = self.name().to_string().to_camel();
        let typ = Ident::new(name);
        quote!(#typ)
    }

    fn docs(&self) -> Tokens {
        let mut docs = Tokens::new();
        let mut in_code_block = false;
        for attr in self.ast.attrs.iter() {
            if let MetaItem::NameValue(ref name, ref value) = attr.value {
                if name == "doc" {
                    if let Lit::Str(ref doc_str, _) = *value {
                        if doc_str.contains("```reql") {
                            in_code_block = true;
                            let token = quote! {
                                /// ```
                                /// # #![allow(unused_imports)]
                                /// # #![allow(unused_variables)]
                                /// # #![allow(unused_must_use)]
                                /// # #[macro_use] extern crate reql;
                                /// # use reql::commands::*;
                                /// # fn main() {
                                /// # let r = Command::new();
                            };
                            token.to_tokens(&mut docs);
                        } else if doc_str.contains("```") && in_code_block {
                            in_code_block = false;
                            let token = quote! {
                                /// # }
                                /// ```
                            };
                            token.to_tokens(&mut docs);
                        } else {
                            attr.to_tokens(&mut docs);
                        }
                    }
                }
            }
        }
        docs
    }

    fn sig(&self) -> Tokens {
        let mut types = HashMap::new();

        let func = self.name().clone();
        let mut generics = Tokens::new();
        let mut func_args = Tokens::new();
        let mut _where = Tokens::new();

        let args = self.args();
        if !args.is_empty() {
            let mut gen = Vec::new();
            let mut whe = Vec::new();

            gen.push(quote!(<));
            whe.push(quote!(where));

            for (arg, typ) in args {
                let typ_str = typ.to_string();
                if let None = types.get(&typ_str) {
                    gen.push(quote!(#typ));
                    gen.push(quote!(,));
                    whe.push(quote!(#typ: ::ToArg));
                    whe.push(quote!(,));
                    types.insert(typ_str, ());
                }
                func_args.append_all(&[ quote!(, #arg: #typ) ]);
            }

            gen.pop();
            whe.pop();

            gen.push(quote!(>));

            generics.append_all(gen);
            _where.append_all(whe);
        }

        quote! {
            #func #generics (&self #func_args) -> ::commands::Command #_where
        }
    }

    fn body(&self) -> Tokens {
        let name = self.name();
        // Rename commands to term types
        let cmd = match name {
            name if name == "match_" => "match".to_string(),
            name if name == "mod_" => "mod".to_string(),
            name if name == "do_" => "funcall".to_string(),
            name if name == "js" => "javascript".to_string(),
            name if name == "to_json" => "to_json_string".to_string(),
            name => name.to_string(),
        };
        // Put quotations around the name of the command quote macro
        let name = format!(r#""{}""#, name);
        // Prepare args
        let mut args = Tokens::new();
        for (arg, _) in self.args() {
            let token = quote! {
                cmd = ::commands::WithArgs::with_args(&cmd, #arg);
            };
            token.to_tokens(&mut args);
        }
        let cmd_type = Ident::new(cmd.to_snake().to_uppercase());
        // Build the body
        quote! {
            use ::ql2::proto::Term;
            use ::protobuf::repeated::RepeatedField;
            use ::ql2::proto::Term_TermType;

            let logger = self.logger.new(o!("command" => #name));
            let mut term = Term::new();
            term.set_field_type(Term_TermType::#cmd_type);
            if *self.term() != Term::new() {
                let prev_cmd = RepeatedField::from_vec(vec![self.term().clone()]);
                term.set_args(prev_cmd);
            }
            let mut cmd = ::commands::Command::new().with_logger(logger);
            cmd.set_term(term);
            #args
            cmd
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>Improve test scenarios<commit_after><|endoftext|>


<commit_before>use crate::utils::ptr::get_spans;
use crate::utils::{
    get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
    snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Impl, Node, PatKind, QPath, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy;
use rustc_typeck::expr_use_visitor as euv;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** Checks for functions taking arguments by value, but not
    /// consuming them in its
    /// body.
    ///
    /// **Why is this bad?** Taking arguments by reference is more flexible and can
    /// sometimes avoid
    /// unnecessary allocations.
    ///
    /// **Known problems:**
    /// * This lint suggests taking an argument by reference,
    /// however sometimes it is better to let users decide the argument type
    /// (by using `Borrow` trait, for example), depending on how the function is used.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(v: Vec<i32>) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    /// should be
    /// ```rust
    /// fn foo(v: &[i32]) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    pub NEEDLESS_PASS_BY_VALUE,
    pedantic,
    "functions taking arguments by value, but not consuming them in its body"
}

declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);

macro_rules! need {
    ($e: expr) => {
        if let Some(x) = $e {
            x
        } else {
            return;
        }
    };
}

impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if span.from_expansion() {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
                    return;
                }
            },
            FnKind::Method(..) => (),
            FnKind::Closure(..) => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
            if matches!(
                item.kind,
                ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
            ) {
                return;
            }
        }

        // Allow `Borrow` or functions to be taken by value
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let allowed_traits = [
            need!(cx.tcx.lang_items().fn_trait()),
            need!(cx.tcx.lang_items().fn_once_trait()),
            need!(cx.tcx.lang_items().fn_mut_trait()),
            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
        ];

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
            .filter(|p| !p.is_global())
            .filter_map(|obligation| {
                // Note that we do not want to deal with qualified predicates here.
                let binder = obligation.predicate.bound_atom();
                match binder.skip_binder() {
                    ty::PredicateAtom::Trait(pred, _) if !binder.has_escaping_bound_vars() => {
                        if pred.def_id() == sized_trait {
                            return None;
                        }
                        Some(pred)
                    }
                    _ => None,
                }
            })
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::default();
            cx.tcx.infer_ctxt().enter(|infcx| {
                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
                    .consume_body(body);
            });
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
            // All spans generated from a proc-macro invocation are the same...
            if span == input.span {
                return;
            }

            // Ignore `self`s.
            if idx == 0 {
                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
                    if ident.as_str() == "self" {
                        continue;
                    }
                }
            }

            //
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
            //   `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && {
                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
                        preds.iter().all(|t| {
                            let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
                            implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
                        })
                    },
                )
            };

            if_chain! {
                if !is_self(arg);
                if !ty.is_mutable_ptr();
                if !is_copy(cx, ty);
                if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
                if !implements_borrow_trait;
                if !all_borrowable_trait;

                if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
                if !moved_vars.contains(&canonical_id);
                then {
                    if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                        continue;
                    }

                    // Dereference suggestion
                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
                        if let ty::Adt(def, ..) = ty.kind() {
                            if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
                                    diag.span_help(span, "consider marking this type as `Copy`");
                                }
                            }
                        }

                        let deref_span = spans_need_deref.get(&canonical_id);
                        if_chain! {
                            if is_type_diagnostic_item(cx, ty, sym::vec_type);
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
                            if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
                            if let Some(elem_ty) = path.segments.iter()
                                .find(|seg| seg.ident.name == sym::Vec)
                                .and_then(|ps| ps.args.as_ref())
                                .map(|params| params.args.iter().find_map(|arg| match arg {
                                    GenericArg::Type(ty) => Some(ty),
                                    _ => None,
                                }).unwrap());
                            then {
                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    slice_ty,
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x)),
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                // cannot be destructured, no need for `*` suggestion
                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        if is_type_diagnostic_item(cx, ty, sym::string_type) {
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    "&str".to_string(),
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x))
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                        // Suggests adding `*` to dereference the added reference.
                        if let Some(deref_span) = deref_span {
                            spans.extend(
                                deref_span
                                    .iter()
                                    .cloned()
                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                            );
                            spans.sort_by_key(|&(span, _)| span);
                        }
                        multispan_sugg(diag, "consider taking a reference instead", spans);
                    };

                    span_lint_and_then(
                        cx,
                        NEEDLESS_PASS_BY_VALUE,
                        input.span,
                        "this argument is passed by value, but not consumed in the function body",
                        sugg,
                    );
                }
            }
        }
    }
}

/// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
    attrs.iter().any(|attr| {
        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
            .iter()
            .any(|&allow| attr.has_name(allow))
    })
}

#[derive(Default)]
struct MovedVariablesCtxt {
    moved_vars: FxHashSet<HirId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
}

impl MovedVariablesCtxt {
    fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
        if let euv::PlaceBase::Local(vid) = cmt.place.base {
            self.moved_vars.insert(vid);
        }
    }
}

impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move = mode {
            self.move_common(cmt);
        }
    }

    fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}

    fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
}
<commit_msg>Use pred not binder<commit_after>use crate::utils::ptr::get_spans;
use crate::utils::{
    get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
    snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Impl, Node, PatKind, QPath, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, Span};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy;
use rustc_typeck::expr_use_visitor as euv;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** Checks for functions taking arguments by value, but not
    /// consuming them in its
    /// body.
    ///
    /// **Why is this bad?** Taking arguments by reference is more flexible and can
    /// sometimes avoid
    /// unnecessary allocations.
    ///
    /// **Known problems:**
    /// * This lint suggests taking an argument by reference,
    /// however sometimes it is better to let users decide the argument type
    /// (by using `Borrow` trait, for example), depending on how the function is used.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(v: Vec<i32>) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    /// should be
    /// ```rust
    /// fn foo(v: &[i32]) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    pub NEEDLESS_PASS_BY_VALUE,
    pedantic,
    "functions taking arguments by value, but not consuming them in its body"
}

declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);

macro_rules! need {
    ($e: expr) => {
        if let Some(x) = $e {
            x
        } else {
            return;
        }
    };
}

impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if span.from_expansion() {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
                    return;
                }
            },
            FnKind::Method(..) => (),
            FnKind::Closure(..) => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
            if matches!(
                item.kind,
                ItemKind::Impl(Impl { of_trait: Some(_), .. }) | ItemKind::Trait(..)
            ) {
                return;
            }
        }

        // Allow `Borrow` or functions to be taken by value
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let allowed_traits = [
            need!(cx.tcx.lang_items().fn_trait()),
            need!(cx.tcx.lang_items().fn_once_trait()),
            need!(cx.tcx.lang_items().fn_mut_trait()),
            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
        ];

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
            .filter(|p| !p.is_global())
            .filter_map(|obligation| {
                // Note that we do not want to deal with qualified predicates here.
                let binder = obligation.predicate.bound_atom();
                match binder.skip_binder() {
                    ty::PredicateAtom::Trait(pred, _) if !pred.has_escaping_bound_vars() => {
                        if pred.def_id() == sized_trait {
                            return None;
                        }
                        Some(pred)
                    }
                    _ => None,
                }
            })
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::default();
            cx.tcx.infer_ctxt().enter(|infcx| {
                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.typeck_results())
                    .consume_body(body);
            });
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
            // All spans generated from a proc-macro invocation are the same...
            if span == input.span {
                return;
            }

            // Ignore `self`s.
            if idx == 0 {
                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
                    if ident.as_str() == "self" {
                        continue;
                    }
                }
            }

            //
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
            //   `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds.iter().filter(|t| t.self_ty() == ty).collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && {
                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
                        preds.iter().all(|t| {
                            let ty_params = t.trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
                            implements_trait(cx, ty_empty_region, t.def_id(), &ty_params)
                        })
                    },
                )
            };

            if_chain! {
                if !is_self(arg);
                if !ty.is_mutable_ptr();
                if !is_copy(cx, ty);
                if !allowed_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
                if !implements_borrow_trait;
                if !all_borrowable_trait;

                if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
                if !moved_vars.contains(&canonical_id);
                then {
                    if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                        continue;
                    }

                    // Dereference suggestion
                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
                        if let ty::Adt(def, ..) = ty.kind() {
                            if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
                                    diag.span_help(span, "consider marking this type as `Copy`");
                                }
                            }
                        }

                        let deref_span = spans_need_deref.get(&canonical_id);
                        if_chain! {
                            if is_type_diagnostic_item(cx, ty, sym::vec_type);
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
                            if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
                            if let Some(elem_ty) = path.segments.iter()
                                .find(|seg| seg.ident.name == sym::Vec)
                                .and_then(|ps| ps.args.as_ref())
                                .map(|params| params.args.iter().find_map(|arg| match arg {
                                    GenericArg::Type(ty) => Some(ty),
                                    _ => None,
                                }).unwrap());
                            then {
                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    slice_ty,
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x)),
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                // cannot be destructured, no need for `*` suggestion
                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        if is_type_diagnostic_item(cx, ty, sym::string_type) {
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    "&str".to_string(),
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x))
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                        // Suggests adding `*` to dereference the added reference.
                        if let Some(deref_span) = deref_span {
                            spans.extend(
                                deref_span
                                    .iter()
                                    .cloned()
                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                            );
                            spans.sort_by_key(|&(span, _)| span);
                        }
                        multispan_sugg(diag, "consider taking a reference instead", spans);
                    };

                    span_lint_and_then(
                        cx,
                        NEEDLESS_PASS_BY_VALUE,
                        input.span,
                        "this argument is passed by value, but not consumed in the function body",
                        sugg,
                    );
                }
            }
        }
    }
}

/// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
    attrs.iter().any(|attr| {
        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
            .iter()
            .any(|&allow| attr.has_name(allow))
    })
}

#[derive(Default)]
struct MovedVariablesCtxt {
    moved_vars: FxHashSet<HirId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
}

impl MovedVariablesCtxt {
    fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
        if let euv::PlaceBase::Local(vid) = cmt.place.base {
            self.moved_vars.insert(vid);
        }
    }
}

impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, _: HirId, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move = mode {
            self.move_common(cmt);
        }
    }

    fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind) {}

    fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
}
<|endoftext|>