<commit_before>// Copyright 2012-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.

//! Error Reporting Code for the inference engine
//!
//! Because of the way inference, and in particular region inference,
//! works, it often happens that errors are not detected until far after
//! the relevant line of code has been type-checked. Therefore, there is
//! an elaborate system to track why a particular constraint in the
//! inference graph arose so that we can explain to the user what gave
//! rise to a particular error.
//!
//! The basis of the system are the "origin" types. An "origin" is the
//! reason that a constraint or inference variable arose. There are
//! different "origin" enums for different kinds of constraints/variables
//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
//! a span, but also more information so that we can generate a meaningful
//! error message.
//!
//! Having a catalog of all the different reasons an error can arise is
//! also useful for other reasons, like cross-referencing FAQs etc, though
//! we are not really taking advantage of this yet.
//!
//! # Region Inference
//!
//! Region inference is particularly tricky because it always succeeds "in
//! the moment" and simply registers a constraint. Then, at the end, we
//! can compute the full graph and report errors, so we need to be able to
//! store and later report what gave rise to the conflicting constraints.
//!
//! # Subtype Trace
//!
//! Determining whether `T1 <: T2` often involves a number of subtypes and
//! subconstraints along the way. A "TypeTrace" is an extended version
//! of an origin that traces the types and other values that were being
//! compared. It is not necessarily comprehensive (in fact, at the time of
//! this writing it only tracks the root values being compared) but I'd
//! like to extend it to include significant "waypoints". For example, if
//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
//! <: T4` fails, I'd like the trace to include enough information to say
//! "in the 2nd element of the tuple". Similarly, failures when comparing
//! arguments or return types in fn types should be able to cite the
//! specific position, etc.
//!
//! # Reality vs plan
//!
//! Of course, there is still a LOT of code in typeck that has yet to be
//! ported to this system, and which relies on string concatenation at the
//! time of error detection.

use super::lexical_region_resolve::RegionResolutionError;
use super::region_constraints::GenericKind;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
use infer::{self, SuppressRegionErrors};

use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use hir;
use hir::def_id::DefId;
use hir::Node;
use middle::region;
use std::{cmp, fmt};
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
use traits::{ObligationCause, ObligationCauseCode};
use ty::error::TypeError;
use ty::{self, subst::Subst, Region, Ty, TyCtxt, TyKind, TypeFoldable};

mod note;

mod need_type_info;

pub mod nice_region_error;

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
    pub fn note_and_explain_region(
        self,
        region_scope_tree: &region::ScopeTree,
        err: &mut DiagnosticBuilder<'_>,
        prefix: &str,
        region: ty::Region<'tcx>,
        suffix: &str,
    ) {
        let (description, span) = match *region {
            ty::ReScope(scope) => {
                let new_string;
                let unknown_scope = || {
                    format!(
                        "{}unknown scope: {:?}{}.  Please report a bug.",
                        prefix, scope, suffix
                    )
                };
                let span = scope.span(self, region_scope_tree);
                let tag = match self.hir.find(scope.node_id(self, region_scope_tree)) {
                    Some(Node::Block(_)) => "block",
                    Some(Node::Expr(expr)) => match expr.node {
                        hir::ExprKind::Call(..) => "call",
                        hir::ExprKind::MethodCall(..) => "method call",
                        hir::ExprKind::Match(.., hir::MatchSource::IfLetDesugar { .. }) => "if let",
                        hir::ExprKind::Match(.., hir::MatchSource::WhileLetDesugar) => "while let",
                        hir::ExprKind::Match(.., hir::MatchSource::ForLoopDesugar) => "for",
                        hir::ExprKind::Match(..) => "match",
                        _ => "expression",
                    },
                    Some(Node::Stmt(_)) => "statement",
                    Some(Node::Item(it)) => Self::item_scope_tag(&it),
                    Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
                    Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
                    Some(_) | None => {
                        err.span_note(span, &unknown_scope());
                        return;
                    }
                };
                let scope_decorated_tag = match scope.data {
                    region::ScopeData::Node => tag,
                    region::ScopeData::CallSite => "scope of call-site for function",
                    region::ScopeData::Arguments => "scope of function body",
                    region::ScopeData::Destruction => {
                        new_string = format!("destruction scope surrounding {}", tag);
                        &new_string[..]
                    }
                    region::ScopeData::Remainder(first_statement_index) => {
                        new_string = format!(
                            "block suffix following statement {}",
                            first_statement_index.index()
                        );
                        &new_string[..]
                    }
                };
                self.explain_span(scope_decorated_tag, span)
            }

            ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
                self.msg_span_from_free_region(region)
            }

            ty::ReEmpty => ("the empty lifetime".to_owned(), None),

            // FIXME(#13998) RePlaceholder should probably print like
            // ReFree rather than dumping Debug output on the user.
            //
            // We shouldn't really be having unification failures with ReVar
            // and ReLateBound though.
            ty::RePlaceholder(..) | ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
                (format!("lifetime {:?}", region), None)
            }

            // We shouldn't encounter an error message with ReClosureBound.
            ty::ReCanonical(..) | ty::ReClosureBound(..) => {
                bug!("encountered unexpected ReClosureBound: {:?}", region,);
            }
        };

        TyCtxt::emit_msg_span(err, prefix, description, span, suffix);
    }

    pub fn note_and_explain_free_region(
        self,
        err: &mut DiagnosticBuilder<'_>,
        prefix: &str,
        region: ty::Region<'tcx>,
        suffix: &str,
    ) {
        let (description, span) = self.msg_span_from_free_region(region);

        TyCtxt::emit_msg_span(err, prefix, description, span, suffix);
    }

    fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option<Span>) {
        match *region {
            ty::ReEarlyBound(_) | ty::ReFree(_) => {
                self.msg_span_from_early_bound_and_free_regions(region)
            }
            ty::ReStatic => ("the static lifetime".to_owned(), None),
            _ => bug!("{:?}", region),
        }
    }

    fn msg_span_from_early_bound_and_free_regions(
        self,
        region: ty::Region<'tcx>,
    ) -> (String, Option<Span>) {
        let cm = self.sess.source_map();

        let scope = region.free_region_binding_scope(self);
        let node = self.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
        let tag = match self.hir.find(node) {
            Some(Node::Block(_)) | Some(Node::Expr(_)) => "body",
            Some(Node::Item(it)) => Self::item_scope_tag(&it),
            Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
            Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
            _ => unreachable!(),
        };
        let (prefix, span) = match *region {
            ty::ReEarlyBound(ref br) => {
                let mut sp = cm.def_span(self.hir.span(node));
                if let Some(param) = self.hir
                    .get_generics(scope)
                    .and_then(|generics| generics.get_named(&br.name))
                {
                    sp = param.span;
                }<commit_msg>Sidestep an ICE by providing *some* description for `ReEmpty` when it arises.<commit_after>// Copyright 2012-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.

//! Error Reporting Code for the inference engine
//!
//! Because of the way inference, and in particular region inference,
//! works, it often happens that errors are not detected until far after
//! the relevant line of code has been type-checked. Therefore, there is
//! an elaborate system to track why a particular constraint in the
//! inference graph arose so that we can explain to the user what gave
//! rise to a particular error.
//!
//! The basis of the system are the "origin" types. An "origin" is the
//! reason that a constraint or inference variable arose. There are
//! different "origin" enums for different kinds of constraints/variables
//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has
//! a span, but also more information so that we can generate a meaningful
//! error message.
//!
//! Having a catalog of all the different reasons an error can arise is
//! also useful for other reasons, like cross-referencing FAQs etc, though
//! we are not really taking advantage of this yet.
//!
//! # Region Inference
//!
//! Region inference is particularly tricky because it always succeeds "in
//! the moment" and simply registers a constraint. Then, at the end, we
//! can compute the full graph and report errors, so we need to be able to
//! store and later report what gave rise to the conflicting constraints.
//!
//! # Subtype Trace
//!
//! Determining whether `T1 <: T2` often involves a number of subtypes and
//! subconstraints along the way. A "TypeTrace" is an extended version
//! of an origin that traces the types and other values that were being
//! compared. It is not necessarily comprehensive (in fact, at the time of
//! this writing it only tracks the root values being compared) but I'd
//! like to extend it to include significant "waypoints". For example, if
//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2
//! <: T4` fails, I'd like the trace to include enough information to say
//! "in the 2nd element of the tuple". Similarly, failures when comparing
//! arguments or return types in fn types should be able to cite the
//! specific position, etc.
//!
//! # Reality vs plan
//!
//! Of course, there is still a LOT of code in typeck that has yet to be
//! ported to this system, and which relies on string concatenation at the
//! time of error detection.

use super::lexical_region_resolve::RegionResolutionError;
use super::region_constraints::GenericKind;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
use infer::{self, SuppressRegionErrors};

use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use hir;
use hir::def_id::DefId;
use hir::Node;
use middle::region;
use std::{cmp, fmt};
use syntax::ast::DUMMY_NODE_ID;
use syntax_pos::{Pos, Span};
use traits::{ObligationCause, ObligationCauseCode};
use ty::error::TypeError;
use ty::{self, subst::Subst, Region, Ty, TyCtxt, TyKind, TypeFoldable};

mod note;

mod need_type_info;

pub mod nice_region_error;

impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
    pub fn note_and_explain_region(
        self,
        region_scope_tree: &region::ScopeTree,
        err: &mut DiagnosticBuilder<'_>,
        prefix: &str,
        region: ty::Region<'tcx>,
        suffix: &str,
    ) {
        let (description, span) = match *region {
            ty::ReScope(scope) => {
                let new_string;
                let unknown_scope = || {
                    format!(
                        "{}unknown scope: {:?}{}.  Please report a bug.",
                        prefix, scope, suffix
                    )
                };
                let span = scope.span(self, region_scope_tree);
                let tag = match self.hir.find(scope.node_id(self, region_scope_tree)) {
                    Some(Node::Block(_)) => "block",
                    Some(Node::Expr(expr)) => match expr.node {
                        hir::ExprKind::Call(..) => "call",
                        hir::ExprKind::MethodCall(..) => "method call",
                        hir::ExprKind::Match(.., hir::MatchSource::IfLetDesugar { .. }) => "if let",
                        hir::ExprKind::Match(.., hir::MatchSource::WhileLetDesugar) => "while let",
                        hir::ExprKind::Match(.., hir::MatchSource::ForLoopDesugar) => "for",
                        hir::ExprKind::Match(..) => "match",
                        _ => "expression",
                    },
                    Some(Node::Stmt(_)) => "statement",
                    Some(Node::Item(it)) => Self::item_scope_tag(&it),
                    Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
                    Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
                    Some(_) | None => {
                        err.span_note(span, &unknown_scope());
                        return;
                    }
                };
                let scope_decorated_tag = match scope.data {
                    region::ScopeData::Node => tag,
                    region::ScopeData::CallSite => "scope of call-site for function",
                    region::ScopeData::Arguments => "scope of function body",
                    region::ScopeData::Destruction => {
                        new_string = format!("destruction scope surrounding {}", tag);
                        &new_string[..]
                    }
                    region::ScopeData::Remainder(first_statement_index) => {
                        new_string = format!(
                            "block suffix following statement {}",
                            first_statement_index.index()
                        );
                        &new_string[..]
                    }
                };
                self.explain_span(scope_decorated_tag, span)
            }

            ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => {
                self.msg_span_from_free_region(region)
            }

            ty::ReEmpty => ("the empty lifetime".to_owned(), None),

            // FIXME(#13998) RePlaceholder should probably print like
            // ReFree rather than dumping Debug output on the user.
            //
            // We shouldn't really be having unification failures with ReVar
            // and ReLateBound though.
            ty::RePlaceholder(..) | ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => {
                (format!("lifetime {:?}", region), None)
            }

            // We shouldn't encounter an error message with ReClosureBound.
            ty::ReCanonical(..) | ty::ReClosureBound(..) => {
                bug!("encountered unexpected ReClosureBound: {:?}", region,);
            }
        };

        TyCtxt::emit_msg_span(err, prefix, description, span, suffix);
    }

    pub fn note_and_explain_free_region(
        self,
        err: &mut DiagnosticBuilder<'_>,
        prefix: &str,
        region: ty::Region<'tcx>,
        suffix: &str,
    ) {
        let (description, span) = self.msg_span_from_free_region(region);

        TyCtxt::emit_msg_span(err, prefix, description, span, suffix);
    }

    fn msg_span_from_free_region(self, region: ty::Region<'tcx>) -> (String, Option<Span>) {
        match *region {
            ty::ReEarlyBound(_) | ty::ReFree(_) => {
                self.msg_span_from_early_bound_and_free_regions(region)
            }
            ty::ReStatic => ("the static lifetime".to_owned(), None),
            ty::ReEmpty => ("an empty lifetime".to_owned(), None),
            _ => bug!("{:?}", region),
        }
    }

    fn msg_span_from_early_bound_and_free_regions(
        self,
        region: ty::Region<'tcx>,
    ) -> (String, Option<Span>) {
        let cm = self.sess.source_map();

        let scope = region.free_region_binding_scope(self);
        let node = self.hir.as_local_node_id(scope).unwrap_or(DUMMY_NODE_ID);
        let tag = match self.hir.find(node) {
            Some(Node::Block(_)) | Some(Node::Expr(_)) => "body",
            Some(Node::Item(it)) => Self::item_scope_tag(&it),
            Some(Node::TraitItem(it)) => Self::trait_item_scope_tag(&it),
            Some(Node::ImplItem(it)) => Self::impl_item_scope_tag(&it),
            _ => unreachable!(),
        };
        let (prefix, span) = match *region {
            ty::ReEarlyBound(ref br) => {
                let mut sp = cm.def_span(self.hir.span(node));
                if let Some(param) = self.hir
                    .get_generics(scope)
                    .and_then(|generics| generics.get_named(&br.name))
                {
                    sp = param.span;
                }
{'fraction_non_alphanumeric': 0.12621199671322925, 'fraction_numerical': 0.0028485346480416323, 'mean_word_length': 2.1738525730180807, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 98, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5924819', 'n_tokens_mistral': 5206, 'n_tokens_neox': 4916, 'n_words': 1504}