<commit_before>//! Method lookup: the secret sauce of Rust. See the [rustc guide] for more information.
//!
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/method-lookup.html

mod confirm;
pub mod probe;
mod suggest;

pub use self::MethodError::*;
pub use self::CandidateSource::*;
pub use self::suggest::{SelfSource, TraitInfo};

use crate::check::FnCtxt;
use crate::namespace::Namespace;
use errors::{Applicability, DiagnosticBuilder};
use rustc_data_structures::sync::Lrc;
use rustc::hir;
use rustc::hir::def::{CtorOf, DefKind};
use rustc::hir::def_id::DefId;
use rustc::traits;
use rustc::ty::subst::{InternalSubsts, SubstsRef};
use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
use rustc::ty::GenericParamDefKind;
use rustc::ty::subst::Subst;
use rustc::infer::{self, InferOk};
use syntax::ast;
use syntax_pos::Span;

use self::probe::{IsSuggestion, ProbeScope};

pub fn provide(providers: &mut ty::query::Providers<'_>) {
    suggest::provide(providers);
    probe::provide(providers);
}

#[derive(Clone, Copy, Debug)]
pub struct MethodCallee<'tcx> {
    /// Impl method ID, for inherent methods, or trait method ID, otherwise.
    pub def_id: DefId,
    pub substs: SubstsRef<'tcx>,

    /// Instantiated method signature, i.e., it has been
    /// substituted, normalized, and has had late-bound
    /// lifetimes replaced with inference variables.
    pub sig: ty::FnSig<'tcx>,
}

pub enum MethodError<'tcx> {
    // Did not find an applicable method, but we did find various near-misses that may work.
    NoMatch(NoMatchData<'tcx>),

    // Multiple methods might apply.
    Ambiguity(Vec<CandidateSource>),

    // Found an applicable method, but it is not visible. The third argument contains a list of
    // not-in-scope traits which may work.
    PrivateMatch(DefKind, DefId, Vec<DefId>),

    // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
    // forgotten to import a trait.
    IllegalSizedBound(Vec<DefId>, bool),

    // Found a match, but the return type is wrong
    BadReturnType,
}

// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
pub struct NoMatchData<'tcx> {
    pub static_candidates: Vec<CandidateSource>,
    pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
    pub out_of_scope_traits: Vec<DefId>,
    pub lev_candidate: Option<ty::AssocItem>,
    pub mode: probe::Mode,
}

impl<'tcx> NoMatchData<'tcx> {
    pub fn new(static_candidates: Vec<CandidateSource>,
               unsatisfied_predicates: Vec<TraitRef<'tcx>>,
               out_of_scope_traits: Vec<DefId>,
               lev_candidate: Option<ty::AssocItem>,
               mode: probe::Mode)
               -> Self {
        NoMatchData {
            static_candidates,
            unsatisfied_predicates,
            out_of_scope_traits,
            lev_candidate,
            mode,
        }
    }
}

// A pared down enum describing just the places from which a method
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CandidateSource {
    ImplSource(DefId),
    TraitSource(DefId /* trait id */),
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    /// Determines whether the type `self_ty` supports a method name `method_name` or not.
    pub fn method_exists(&self,
                         method_name: ast::Ident,
                         self_ty: Ty<'tcx>,
                         call_expr_id: hir::HirId,
                         allow_private: bool)
                         -> bool {
        let mode = probe::Mode::MethodCall;
        match self.probe_for_name(method_name.span, mode, method_name,
                                  IsSuggestion(false), self_ty, call_expr_id,
                                  ProbeScope::TraitsInScope) {
            Ok(..) => true,
            Err(NoMatch(..)) => false,
            Err(Ambiguity(..)) => true,
            Err(PrivateMatch(..)) => allow_private,
            Err(IllegalSizedBound(..)) => true,
            Err(BadReturnType) => {
                bug!("no return type expectations but got BadReturnType")
            }

        }
    }

    /// Adds a suggestion to call the given method to the provided diagnostic.
    crate fn suggest_method_call(
        &self,
        err: &mut DiagnosticBuilder<'a>,
        msg: &str,
        method_name: ast::Ident,
        self_ty: Ty<'tcx>,
        call_expr_id: hir::HirId,
    ) {
        let has_params = self
            .probe_for_name(
                method_name.span,
                probe::Mode::MethodCall,
                method_name,
                IsSuggestion(false),
                self_ty,
                call_expr_id,
                ProbeScope::TraitsInScope,
            )
            .and_then(|pick| {
                let sig = self.tcx.fn_sig(pick.item.def_id);
                Ok(sig.inputs().skip_binder().len() > 1)
            });

        let (suggestion, applicability) = if has_params.unwrap_or_default() {
            (
                format!("{}(...)", method_name),
                Applicability::HasPlaceholders,
            )
        } else {
            (format!("{}()", method_name), Applicability::MaybeIncorrect)
        };

        err.span_suggestion(method_name.span, msg, suggestion, applicability);
    }

    /// Performs method lookup. If lookup is successful, it will return the callee
    /// and store an appropriate adjustment for the self-expr. In some cases it may
    /// report an error (e.g., invoking the `drop` method).
    ///
    /// # Arguments
    ///
    /// Given a method call like `foo.bar::<T1,...Tn>(...)`:
    ///
    /// * `fcx`:                   the surrounding `FnCtxt` (!)
    /// * `span`:                  the span for the method call
    /// * `method_name`:           the name of the method being called (`bar`)
    /// * `self_ty`:               the (unadjusted) type of the self expression (`foo`)
    /// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
    /// * `self_expr`:             the self expression (`foo`)
    pub fn lookup_method(
        &self,
        self_ty: Ty<'tcx>,
        segment: &hir::PathSegment,
        span: Span,
        call_expr: &'tcx hir::Expr,
        self_expr: &'tcx hir::Expr,
    ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
        debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
               segment.ident,
               self_ty,
               call_expr,
               self_expr);

        let pick = self.lookup_probe(
            span,
            segment.ident,
            self_ty,
            call_expr,
            ProbeScope::TraitsInScope
        )?;

        for import_id in &pick.import_ids {
            let import_def_id = self.tcx.hir().local_def_id(*import_id);
            debug!("used_trait_import: {:?}", import_def_id);
            Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
                .unwrap().insert(import_def_id);
        }

        self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span);

        let result = self.confirm_method(
            span,
            self_expr,
            call_expr,
            self_ty,
            pick.clone(),
            segment,
        );

        let mut needs_mut = false;
        if let ty::Ref(region, t_type, mutability) = self_ty.kind {
            let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut {
                ty: t_type,
                mutbl: mutability.invert(),
            });
            match self.lookup_probe(
                span,
                segment.ident,
                trait_type,
                call_expr,
                ProbeScope::TraitsInScope
            ) {
                Ok(ref new_pick) if *new_pick != pick => {
                    needs_mut = true;
                }
                _ => {}
            }
        }

        if result.illegal_sized_bound {
            // We probe again, taking all traits into account (not only those in scope).
            let candidates = match self.lookup_probe(
                span,
                segment.ident,
                self_ty,
                call_expr,
                ProbeScope::AllTraits,
            ) {
                // If we find a different result the caller probably forgot to import a trait.
                Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
                Err(Ambiguity(ref sources)) => sources.iter().filter_map(|source| {
                    match *source {
                        // Note: this cannot come from an inherent impl,
                        // because the first probing succeeded.
                        ImplSource(def) => self.tcx.trait_id_of_impl(def),
                        TraitSource(_) => None,
                    }
                }).collect(),
                _ => Vec::new(),
            };

            return Err(IllegalSizedBound(candidates, needs_mut));
        }
<commit_msg>Move diagnostics code out of the critical path<commit_after>//! Method lookup: the secret sauce of Rust. See the [rustc guide] for more information.
//!
//! [rustc guide]: https://rust-lang.github.io/rustc-guide/method-lookup.html

mod confirm;
pub mod probe;
mod suggest;

pub use self::MethodError::*;
pub use self::CandidateSource::*;
pub use self::suggest::{SelfSource, TraitInfo};

use crate::check::FnCtxt;
use crate::namespace::Namespace;
use errors::{Applicability, DiagnosticBuilder};
use rustc_data_structures::sync::Lrc;
use rustc::hir;
use rustc::hir::def::{CtorOf, DefKind};
use rustc::hir::def_id::DefId;
use rustc::traits;
use rustc::ty::subst::{InternalSubsts, SubstsRef};
use rustc::ty::{self, Ty, ToPredicate, ToPolyTraitRef, TraitRef, TypeFoldable};
use rustc::ty::GenericParamDefKind;
use rustc::ty::subst::Subst;
use rustc::infer::{self, InferOk};
use syntax::ast;
use syntax_pos::Span;

use self::probe::{IsSuggestion, ProbeScope};

pub fn provide(providers: &mut ty::query::Providers<'_>) {
    suggest::provide(providers);
    probe::provide(providers);
}

#[derive(Clone, Copy, Debug)]
pub struct MethodCallee<'tcx> {
    /// Impl method ID, for inherent methods, or trait method ID, otherwise.
    pub def_id: DefId,
    pub substs: SubstsRef<'tcx>,

    /// Instantiated method signature, i.e., it has been
    /// substituted, normalized, and has had late-bound
    /// lifetimes replaced with inference variables.
    pub sig: ty::FnSig<'tcx>,
}

pub enum MethodError<'tcx> {
    // Did not find an applicable method, but we did find various near-misses that may work.
    NoMatch(NoMatchData<'tcx>),

    // Multiple methods might apply.
    Ambiguity(Vec<CandidateSource>),

    // Found an applicable method, but it is not visible. The third argument contains a list of
    // not-in-scope traits which may work.
    PrivateMatch(DefKind, DefId, Vec<DefId>),

    // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have
    // forgotten to import a trait.
    IllegalSizedBound(Vec<DefId>, bool),

    // Found a match, but the return type is wrong
    BadReturnType,
}

// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which
// could lead to matches if satisfied, and a list of not-in-scope traits which may work.
pub struct NoMatchData<'tcx> {
    pub static_candidates: Vec<CandidateSource>,
    pub unsatisfied_predicates: Vec<TraitRef<'tcx>>,
    pub out_of_scope_traits: Vec<DefId>,
    pub lev_candidate: Option<ty::AssocItem>,
    pub mode: probe::Mode,
}

impl<'tcx> NoMatchData<'tcx> {
    pub fn new(static_candidates: Vec<CandidateSource>,
               unsatisfied_predicates: Vec<TraitRef<'tcx>>,
               out_of_scope_traits: Vec<DefId>,
               lev_candidate: Option<ty::AssocItem>,
               mode: probe::Mode)
               -> Self {
        NoMatchData {
            static_candidates,
            unsatisfied_predicates,
            out_of_scope_traits,
            lev_candidate,
            mode,
        }
    }
}

// A pared down enum describing just the places from which a method
// candidate can arise. Used for error reporting only.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum CandidateSource {
    ImplSource(DefId),
    TraitSource(DefId /* trait id */),
}

impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
    /// Determines whether the type `self_ty` supports a method name `method_name` or not.
    pub fn method_exists(&self,
                         method_name: ast::Ident,
                         self_ty: Ty<'tcx>,
                         call_expr_id: hir::HirId,
                         allow_private: bool)
                         -> bool {
        let mode = probe::Mode::MethodCall;
        match self.probe_for_name(method_name.span, mode, method_name,
                                  IsSuggestion(false), self_ty, call_expr_id,
                                  ProbeScope::TraitsInScope) {
            Ok(..) => true,
            Err(NoMatch(..)) => false,
            Err(Ambiguity(..)) => true,
            Err(PrivateMatch(..)) => allow_private,
            Err(IllegalSizedBound(..)) => true,
            Err(BadReturnType) => {
                bug!("no return type expectations but got BadReturnType")
            }

        }
    }

    /// Adds a suggestion to call the given method to the provided diagnostic.
    crate fn suggest_method_call(
        &self,
        err: &mut DiagnosticBuilder<'a>,
        msg: &str,
        method_name: ast::Ident,
        self_ty: Ty<'tcx>,
        call_expr_id: hir::HirId,
    ) {
        let has_params = self
            .probe_for_name(
                method_name.span,
                probe::Mode::MethodCall,
                method_name,
                IsSuggestion(false),
                self_ty,
                call_expr_id,
                ProbeScope::TraitsInScope,
            )
            .and_then(|pick| {
                let sig = self.tcx.fn_sig(pick.item.def_id);
                Ok(sig.inputs().skip_binder().len() > 1)
            });

        let (suggestion, applicability) = if has_params.unwrap_or_default() {
            (
                format!("{}(...)", method_name),
                Applicability::HasPlaceholders,
            )
        } else {
            (format!("{}()", method_name), Applicability::MaybeIncorrect)
        };

        err.span_suggestion(method_name.span, msg, suggestion, applicability);
    }

    /// Performs method lookup. If lookup is successful, it will return the callee
    /// and store an appropriate adjustment for the self-expr. In some cases it may
    /// report an error (e.g., invoking the `drop` method).
    ///
    /// # Arguments
    ///
    /// Given a method call like `foo.bar::<T1,...Tn>(...)`:
    ///
    /// * `fcx`:                   the surrounding `FnCtxt` (!)
    /// * `span`:                  the span for the method call
    /// * `method_name`:           the name of the method being called (`bar`)
    /// * `self_ty`:               the (unadjusted) type of the self expression (`foo`)
    /// * `supplied_method_types`: the explicit method type parameters, if any (`T1..Tn`)
    /// * `self_expr`:             the self expression (`foo`)
    pub fn lookup_method(
        &self,
        self_ty: Ty<'tcx>,
        segment: &hir::PathSegment,
        span: Span,
        call_expr: &'tcx hir::Expr,
        self_expr: &'tcx hir::Expr,
    ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> {
        debug!("lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})",
               segment.ident,
               self_ty,
               call_expr,
               self_expr);

        let pick = self.lookup_probe(
            span,
            segment.ident,
            self_ty,
            call_expr,
            ProbeScope::TraitsInScope
        )?;

        for import_id in &pick.import_ids {
            let import_def_id = self.tcx.hir().local_def_id(*import_id);
            debug!("used_trait_import: {:?}", import_def_id);
            Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
                .unwrap().insert(import_def_id);
        }

        self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span);

        let result = self.confirm_method(
            span,
            self_expr,
            call_expr,
            self_ty,
            pick.clone(),
            segment,
        );

        if result.illegal_sized_bound {
            let mut needs_mut = false;
            if let ty::Ref(region, t_type, mutability) = self_ty.kind {
                let trait_type = self.tcx.mk_ref(region, ty::TypeAndMut {
                    ty: t_type,
                    mutbl: mutability.invert(),
                });
                // We probe again to see if there might be a borrow mutability discrepancy.
                match self.lookup_probe(
                    span,
                    segment.ident,
                    trait_type,
                    call_expr,
                    ProbeScope::TraitsInScope
                ) {
                    Ok(ref new_pick) if *new_pick != pick => {
                        needs_mut = true;
                    }
                    _ => {}
                }
            }

            // We probe again, taking all traits into account (not only those in scope).
            let candidates = match self.lookup_probe(
                span,
                segment.ident,
                self_ty,
                call_expr,
                ProbeScope::AllTraits,
            ) {
                // If we find a different result the caller probably forgot to import a trait.
                Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container.id()],
                Err(Ambiguity(ref sources)) => sources.iter().filter_map(|source| {
                    match *source {
                        // Note: this cannot come from an inherent impl,
                        // because the first probing succeeded.
                        ImplSource(def) => self.tcx.trait_id_of_impl(def),
                        TraitSource(_) => None,
                    }
                }).collect(),
                _ => Vec::new(),
            };

            return Err(IllegalSizedBound(candidates, needs_mut));
        }

{'fraction_non_alphanumeric': 0.11751686909581646, 'fraction_numerical': 0.00032388663967611336, 'mean_word_length': 1.888819585217527, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 101, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1444793', 'n_tokens_mistral': 5445, 'n_tokens_neox': 5317, 'n_words': 1370}