<commit_before>//! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)]

use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ast, token};
use rustc_ast_pretty::pprust::token_kind_to_string;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{EarlyContext, LateContext, LintContext};
use rustc_span::source_map::{CharPos, Span};
use rustc_span::{BytePos, Pos};
use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::Display;
use std::ops::{Add, Neg, Not, Sub};

/// A helper type to build suggestion correctly handling parenthesis.
#[derive(Clone, PartialEq)]
pub enum Sugg<'a> {
    /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
    NonParen(Cow<'a, str>),
    /// An expression that does not fit in other variants.
    MaybeParen(Cow<'a, str>),
    /// A binary operator expression, including `as`-casts and explicit type
    /// coercion.
    BinOp(AssocOp, Cow<'a, str>),
}

/// Literal constant `0`, for convenience.
pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
/// Literal constant `1`, for convenience.
pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
/// a constant represents an empty string, for convenience.
pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));

impl Display for Sugg<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        match *self {
            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
        }
    }
}

#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
impl<'a> Sugg<'a> {
    /// Prepare a suggestion from an expression.
    pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
        snippet_opt(cx, expr.span).map(|snippet| {
            let snippet = Cow::Owned(snippet);
            Self::hir_from_snippet(expr, snippet)
        })
    }

    /// Convenience function around `hir_opt` for suggestions with a default
    /// text.
    pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
    }

    /// Same as `hir`, but it adapts the applicability level by following rules:
    ///
    /// - Applicability level `Unspecified` will never be changed.
    /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
    /// - If the default value is used and the applicability level is `MachineApplicable`, change it
    ///   to
    /// `HasPlaceholders`
    pub fn hir_with_applicability(
        cx: &LateContext<'_>,
        expr: &hir::Expr<'_>,
        default: &'a str,
        applicability: &mut Applicability,
    ) -> Self {
        if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
            *applicability = Applicability::MaybeIncorrect;
        }
        Self::hir_opt(cx, expr).unwrap_or_else(|| {
            if *applicability == Applicability::MachineApplicable {
                *applicability = Applicability::HasPlaceholders;
            }
            Sugg::NonParen(Cow::Borrowed(default))
        })
    }

    /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
    pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
        let snippet = snippet_with_macro_callsite(cx, expr.span, default);

        Self::hir_from_snippet(expr, snippet)
    }

    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
    /// function variants of `Sugg`, since these use different snippet functions.
    fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
        if let Some(range) = higher::range(expr) {
            let op = match range.limits {
                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
                ast::RangeLimits::Closed => AssocOp::DotDotEq,
            };
            return Sugg::BinOp(op, snippet);
        }

        match expr.kind {
            hir::ExprKind::AddrOf(..)
            | hir::ExprKind::Box(..)
            | hir::ExprKind::Closure(..)
            | hir::ExprKind::Unary(..)
            | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
            hir::ExprKind::Continue(..)
            | hir::ExprKind::Yield(..)
            | hir::ExprKind::Array(..)
            | hir::ExprKind::Block(..)
            | hir::ExprKind::Break(..)
            | hir::ExprKind::Call(..)
            | hir::ExprKind::Field(..)
            | hir::ExprKind::Index(..)
            | hir::ExprKind::InlineAsm(..)
            | hir::ExprKind::LlvmInlineAsm(..)
            | hir::ExprKind::ConstBlock(..)
            | hir::ExprKind::Lit(..)
            | hir::ExprKind::Loop(..)
            | hir::ExprKind::MethodCall(..)
            | hir::ExprKind::Path(..)
            | hir::ExprKind::Repeat(..)
            | hir::ExprKind::Ret(..)
            | hir::ExprKind::Struct(..)
            | hir::ExprKind::Tup(..)
            | hir::ExprKind::DropTemps(_)
            | hir::ExprKind::Err => Sugg::NonParen(snippet),
            hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
            hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
            hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
            hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
            hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
        }
    }

    /// Prepare a suggestion from an expression.
    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
        use rustc_ast::ast::RangeLimits;

        let snippet = if expr.span.from_expansion() {
            snippet_with_macro_callsite(cx, expr.span, default)
        } else {
            snippet(cx, expr.span, default)
        };

        match expr.kind {
            ast::ExprKind::AddrOf(..)
            | ast::ExprKind::Box(..)
            | ast::ExprKind::Closure(..)
            | ast::ExprKind::If(..)
            | ast::ExprKind::Let(..)
            | ast::ExprKind::Unary(..)
            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
            ast::ExprKind::Async(..)
            | ast::ExprKind::Block(..)
            | ast::ExprKind::Break(..)
            | ast::ExprKind::Call(..)
            | ast::ExprKind::Continue(..)
            | ast::ExprKind::Yield(..)
            | ast::ExprKind::Field(..)
            | ast::ExprKind::ForLoop(..)
            | ast::ExprKind::Index(..)
            | ast::ExprKind::InlineAsm(..)
            | ast::ExprKind::LlvmInlineAsm(..)
            | ast::ExprKind::ConstBlock(..)
            | ast::ExprKind::Lit(..)
            | ast::ExprKind::Loop(..)
            | ast::ExprKind::MacCall(..)
            | ast::ExprKind::MethodCall(..)
            | ast::ExprKind::Paren(..)<commit_msg>Add underscore expressions for destructuring assignments<commit_after>//! Contains utility functions to generate suggestions.
#![deny(clippy::missing_docs_in_private_items)]

use crate::utils::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{ast, token};
use rustc_ast_pretty::pprust::token_kind_to_string;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{EarlyContext, LateContext, LintContext};
use rustc_span::source_map::{CharPos, Span};
use rustc_span::{BytePos, Pos};
use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::Display;
use std::ops::{Add, Neg, Not, Sub};

/// A helper type to build suggestion correctly handling parenthesis.
#[derive(Clone, PartialEq)]
pub enum Sugg<'a> {
    /// An expression that never needs parenthesis such as `1337` or `[0; 42]`.
    NonParen(Cow<'a, str>),
    /// An expression that does not fit in other variants.
    MaybeParen(Cow<'a, str>),
    /// A binary operator expression, including `as`-casts and explicit type
    /// coercion.
    BinOp(AssocOp, Cow<'a, str>),
}

/// Literal constant `0`, for convenience.
pub const ZERO: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("0"));
/// Literal constant `1`, for convenience.
pub const ONE: Sugg<'static> = Sugg::NonParen(Cow::Borrowed("1"));
/// a constant represents an empty string, for convenience.
pub const EMPTY: Sugg<'static> = Sugg::NonParen(Cow::Borrowed(""));

impl Display for Sugg<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
        match *self {
            Sugg::NonParen(ref s) | Sugg::MaybeParen(ref s) | Sugg::BinOp(_, ref s) => s.fmt(f),
        }
    }
}

#[allow(clippy::wrong_self_convention)] // ok, because of the function `as_ty` method
impl<'a> Sugg<'a> {
    /// Prepare a suggestion from an expression.
    pub fn hir_opt(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Self> {
        snippet_opt(cx, expr.span).map(|snippet| {
            let snippet = Cow::Owned(snippet);
            Self::hir_from_snippet(expr, snippet)
        })
    }

    /// Convenience function around `hir_opt` for suggestions with a default
    /// text.
    pub fn hir(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
        Self::hir_opt(cx, expr).unwrap_or(Sugg::NonParen(Cow::Borrowed(default)))
    }

    /// Same as `hir`, but it adapts the applicability level by following rules:
    ///
    /// - Applicability level `Unspecified` will never be changed.
    /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
    /// - If the default value is used and the applicability level is `MachineApplicable`, change it
    ///   to
    /// `HasPlaceholders`
    pub fn hir_with_applicability(
        cx: &LateContext<'_>,
        expr: &hir::Expr<'_>,
        default: &'a str,
        applicability: &mut Applicability,
    ) -> Self {
        if *applicability != Applicability::Unspecified && expr.span.from_expansion() {
            *applicability = Applicability::MaybeIncorrect;
        }
        Self::hir_opt(cx, expr).unwrap_or_else(|| {
            if *applicability == Applicability::MachineApplicable {
                *applicability = Applicability::HasPlaceholders;
            }
            Sugg::NonParen(Cow::Borrowed(default))
        })
    }

    /// Same as `hir`, but will use the pre expansion span if the `expr` was in a macro.
    pub fn hir_with_macro_callsite(cx: &LateContext<'_>, expr: &hir::Expr<'_>, default: &'a str) -> Self {
        let snippet = snippet_with_macro_callsite(cx, expr.span, default);

        Self::hir_from_snippet(expr, snippet)
    }

    /// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
    /// function variants of `Sugg`, since these use different snippet functions.
    fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
        if let Some(range) = higher::range(expr) {
            let op = match range.limits {
                ast::RangeLimits::HalfOpen => AssocOp::DotDot,
                ast::RangeLimits::Closed => AssocOp::DotDotEq,
            };
            return Sugg::BinOp(op, snippet);
        }

        match expr.kind {
            hir::ExprKind::AddrOf(..)
            | hir::ExprKind::Box(..)
            | hir::ExprKind::Closure(..)
            | hir::ExprKind::Unary(..)
            | hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
            hir::ExprKind::Continue(..)
            | hir::ExprKind::Yield(..)
            | hir::ExprKind::Array(..)
            | hir::ExprKind::Block(..)
            | hir::ExprKind::Break(..)
            | hir::ExprKind::Call(..)
            | hir::ExprKind::Field(..)
            | hir::ExprKind::Index(..)
            | hir::ExprKind::InlineAsm(..)
            | hir::ExprKind::LlvmInlineAsm(..)
            | hir::ExprKind::ConstBlock(..)
            | hir::ExprKind::Lit(..)
            | hir::ExprKind::Loop(..)
            | hir::ExprKind::MethodCall(..)
            | hir::ExprKind::Path(..)
            | hir::ExprKind::Repeat(..)
            | hir::ExprKind::Ret(..)
            | hir::ExprKind::Struct(..)
            | hir::ExprKind::Tup(..)
            | hir::ExprKind::DropTemps(_)
            | hir::ExprKind::Err => Sugg::NonParen(snippet),
            hir::ExprKind::Assign(..) => Sugg::BinOp(AssocOp::Assign, snippet),
            hir::ExprKind::AssignOp(op, ..) => Sugg::BinOp(hirbinop2assignop(op), snippet),
            hir::ExprKind::Binary(op, ..) => Sugg::BinOp(AssocOp::from_ast_binop(higher::binop(op.node)), snippet),
            hir::ExprKind::Cast(..) => Sugg::BinOp(AssocOp::As, snippet),
            hir::ExprKind::Type(..) => Sugg::BinOp(AssocOp::Colon, snippet),
        }
    }

    /// Prepare a suggestion from an expression.
    pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
        use rustc_ast::ast::RangeLimits;

        let snippet = if expr.span.from_expansion() {
            snippet_with_macro_callsite(cx, expr.span, default)
        } else {
            snippet(cx, expr.span, default)
        };

        match expr.kind {
            ast::ExprKind::AddrOf(..)
            | ast::ExprKind::Box(..)
            | ast::ExprKind::Closure(..)
            | ast::ExprKind::If(..)
            | ast::ExprKind::Let(..)
            | ast::ExprKind::Unary(..)
            | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
            ast::ExprKind::Async(..)
            | ast::ExprKind::Block(..)
            | ast::ExprKind::Break(..)
            | ast::ExprKind::Call(..)
            | ast::ExprKind::Continue(..)
            | ast::ExprKind::Yield(..)
            | ast::ExprKind::Field(..)
            | ast::ExprKind::ForLoop(..)
            | ast::ExprKind::Index(..)
            | ast::ExprKind::InlineAsm(..)
            | ast::ExprKind::LlvmInlineAsm(..)
            | ast::ExprKind::ConstBlock(..)
            | ast::ExprKind::Lit(..)
            | ast::ExprKind::Loop(..)
            | ast::ExprKind::MacCall(..)
            | ast::ExprKind::MethodCall(..)
            | ast::ExprKind::Paren(..)
            | ast::ExprKind::Underscore
{'fraction_non_alphanumeric': 0.17221257381035082, 'fraction_numerical': 0.001667245571378951, 'mean_word_length': 2.5301618440411966, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6319510', 'n_tokens_mistral': 4767, 'n_tokens_neox': 4663, 'n_words': 933}