use super::lexical_region_resolve::RegionResolutionError;
use super::region_constraints::GenericKind;
use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs};
-use crate::infer::{self, SuppressRegionErrors};
use crate::hir;
use crate::hir::def_id::DefId;
use crate::hir::Node;
+use crate::infer::{self, SuppressRegionErrors};
use crate::infer::opaque_types;
use crate::middle::region;
-use crate::traits::{IfExpressionCause, MatchExpressionArmCause, ObligationCause};
-use crate::traits::{ObligationCauseCode};
+use crate::traits::{
+ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+};
use crate::ty::error::TypeError;
use crate::ty::{self, subst::{Subst, SubstsRef}, Region, Ty, TyCtxt, TypeFoldable};
+
use errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
-use std::{cmp, fmt};
+use rustc_error_codes::*;
+use rustc_target::spec::abi;
use syntax_pos::{Pos, Span};
+use std::{cmp, fmt};
mod note;
mod need_type_info;
+pub use need_type_info::TypeAnnotationNeeded;
pub mod nice_region_error;
&self,
err: &mut DiagnosticBuilder<'_>,
terr: &TypeError<'tcx>,
- sp: Span,
) {
use hir::def_id::CrateNum;
use hir::map::DisambiguatedDefPathData;
};
if same_path().unwrap_or(false) {
let crate_name = self.tcx.crate_name(did1.krate);
- err.span_note(
- sp,
- &format!(
- "Perhaps two different versions \
- of crate `{}` are being used?",
- crate_name
- ),
- );
+ err.note(&format!(
+ "perhaps two different versions of crate `{}` are being used?",
+ crate_name
+ ));
}
}
};
if len > 0 && i != len - 1 {
value.push_normal(", ");
}
- //self.push_comma(&mut value, &mut other_value, len, i);
}
if len > 0 {
value.push_highlighted(">");
substs.truncate_to(self.tcx, &generics)
}
+ /// Given two `fn` signatures highlight only sub-parts that are different.
+ fn cmp_fn_sig(
+ &self,
+ sig1: &ty::PolyFnSig<'tcx>,
+ sig2: &ty::PolyFnSig<'tcx>,
+ ) -> (DiagnosticStyledString, DiagnosticStyledString) {
+ let get_lifetimes = |sig| {
+ use crate::hir::def::Namespace;
+ let mut s = String::new();
+ let (_, (sig, reg)) = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS)
+ .name_all_regions(sig)
+ .unwrap();
+ let lts: Vec<String> = reg.into_iter().map(|(_, kind)| kind.to_string()).collect();
+ (if lts.is_empty() {
+ String::new()
+ } else {
+ format!("for<{}> ", lts.join(", "))
+ }, sig)
+ };
+
+ let (lt1, sig1) = get_lifetimes(sig1);
+ let (lt2, sig2) = get_lifetimes(sig2);
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ let mut values = (
+ DiagnosticStyledString::normal("".to_string()),
+ DiagnosticStyledString::normal("".to_string()),
+ );
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ // ^^^^^^
+ values.0.push(sig1.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety);
+ values.1.push(sig2.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety);
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ // ^^^^^^^^^^
+ if sig1.abi != abi::Abi::Rust {
+ values.0.push(format!("extern {} ", sig1.abi), sig1.abi != sig2.abi);
+ }
+ if sig2.abi != abi::Abi::Rust {
+ values.1.push(format!("extern {} ", sig2.abi), sig1.abi != sig2.abi);
+ }
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ // ^^^^^^^^
+ let lifetime_diff = lt1 != lt2;
+ values.0.push(lt1, lifetime_diff);
+ values.1.push(lt2, lifetime_diff);
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ // ^^^
+ values.0.push_normal("fn(");
+ values.1.push_normal("fn(");
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ // ^^^^^
+ let len1 = sig1.inputs().len();
+ let len2 = sig2.inputs().len();
+ if len1 == len2 {
+ for (i, (l, r)) in sig1.inputs().iter().zip(sig2.inputs().iter()).enumerate() {
+ let (x1, x2) = self.cmp(l, r);
+ (values.0).0.extend(x1.0);
+ (values.1).0.extend(x2.0);
+ self.push_comma(&mut values.0, &mut values.1, len1, i);
+ }
+ } else {
+ for (i, l) in sig1.inputs().iter().enumerate() {
+ values.0.push_highlighted(l.to_string());
+ if i != len1 - 1 {
+ values.0.push_highlighted(", ");
+ }
+ }
+ for (i, r) in sig2.inputs().iter().enumerate() {
+ values.1.push_highlighted(r.to_string());
+ if i != len2 - 1 {
+ values.1.push_highlighted(", ");
+ }
+ }
+ }
+
+ if sig1.c_variadic {
+ if len1 > 0 {
+ values.0.push_normal(", ");
+ }
+ values.0.push("...", !sig2.c_variadic);
+ }
+ if sig2.c_variadic {
+ if len2 > 0 {
+ values.1.push_normal(", ");
+ }
+ values.1.push("...", !sig1.c_variadic);
+ }
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ // ^
+ values.0.push_normal(")");
+ values.1.push_normal(")");
+
+ // unsafe extern "C" for<'a> fn(&'a T) -> &'a T
+ // ^^^^^^^^
+ let output1 = sig1.output();
+ let output2 = sig2.output();
+ let (x1, x2) = self.cmp(output1, output2);
+ if !output1.is_unit() {
+ values.0.push_normal(" -> ");
+ (values.0).0.extend(x1.0);
+ }
+ if !output2.is_unit() {
+ values.1.push_normal(" -> ");
+ (values.1).0.extend(x2.0);
+ }
+ values
+ }
+
/// Compares two given types, eliding parts that are the same between them and highlighting
/// relevant differences, and return two representation of those types for highlighted printing.
fn cmp(&self, t1: Ty<'tcx>, t2: Ty<'tcx>) -> (DiagnosticStyledString, DiagnosticStyledString) {
} else {
r.push(' ');
}
- s.push_highlighted(format!(
- "&{}{}",
- r,
- if mutbl == hir::MutMutable { "mut " } else { "" }
- ));
+ s.push_highlighted(format!("&{}{}", r, mutbl.prefix_str()));
s.push_normal(ty.to_string());
}
for (i, lifetimes) in lifetimes.enumerate() {
let l1 = lifetime_display(lifetimes.0);
let l2 = lifetime_display(lifetimes.1);
- if l1 == l2 {
+ if lifetimes.0 == lifetimes.1 {
values.0.push_normal("'_");
values.1.push_normal("'_");
} else {
values
}
+ // When encountering tuples of the same size, highlight only the differing types
+ (&ty::Tuple(substs1), &ty::Tuple(substs2)) if substs1.len() == substs2.len() => {
+ let mut values = (
+ DiagnosticStyledString::normal("("),
+ DiagnosticStyledString::normal("("),
+ );
+ let len = substs1.len();
+ for (i, (left, right)) in substs1.types().zip(substs2.types()).enumerate() {
+ let (x1, x2) = self.cmp(left, right);
+ (values.0).0.extend(x1.0);
+ (values.1).0.extend(x2.0);
+ self.push_comma(&mut values.0, &mut values.1, len, i);
+ }
+ if len == 1 { // Keep the output for single element tuples as `(ty,)`.
+ values.0.push_normal(",");
+ values.1.push_normal(",");
+ }
+ values.0.push_normal(")");
+ values.1.push_normal(")");
+ values
+ }
+
+ (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
+ let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+ let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+ let mut values = self.cmp_fn_sig(&sig1, &sig2);
+ let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1));
+ let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2));
+ let same_path = path1 == path2;
+ values.0.push(path1, !same_path);
+ values.1.push(path2, !same_path);
+ values
+ }
+
+ (ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => {
+ let sig1 = self.tcx.fn_sig(*did1).subst(self.tcx, substs1);
+ let mut values = self.cmp_fn_sig(&sig1, sig2);
+ values.0.push_normal(format!(
+ " {{{}}}",
+ self.tcx.def_path_str_with_substs(*did1, substs1)),
+ );
+ values
+ }
+
+ (ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => {
+ let sig2 = self.tcx.fn_sig(*did2).subst(self.tcx, substs2);
+ let mut values = self.cmp_fn_sig(sig1, &sig2);
+ values.1.push_normal(format!(
+ " {{{}}}",
+ self.tcx.def_path_str_with_substs(*did2, substs2)),
+ );
+ values
+ }
+
+ (ty::FnPtr(sig1), ty::FnPtr(sig2)) => {
+ self.cmp_fn_sig(sig1, sig2)
+ }
+
_ => {
if t1 == t2 {
// The two types are the same, elide and don't highlight.
Some(values) => {
let (is_simple_error, exp_found) = match values {
ValuePairs::Types(exp_found) => {
- let is_simple_err =
- exp_found.expected.is_primitive() && exp_found.found.is_primitive();
+ let is_simple_err = exp_found.expected.is_simple_text()
+ && exp_found.found.is_simple_text();
(is_simple_err, Some(exp_found))
}
};
if let Some((expected, found)) = expected_found {
- match (terr, is_simple_error, expected == found) {
- (&TypeError::Sorts(ref values), false, true) => {
- let sort_string = | a_type: Ty<'tcx> |
- if let ty::Opaque(def_id, _) = a_type.kind {
- format!(" (opaque type at {})", self.tcx.sess.source_map()
- .mk_substr_filename(self.tcx.def_span(def_id)))
- } else {
- format!(" ({})", a_type.sort_string(self.tcx))
- };
- diag.note_expected_found_extra(
- &"type",
- expected,
- found,
- &sort_string(values.expected),
- &sort_string(values.found),
- );
+ let expected_label = exp_found.map_or("type".into(), |ef| ef.expected.prefix_string());
+ let found_label = exp_found.map_or("type".into(), |ef| ef.found.prefix_string());
+ match (&terr, expected == found) {
+ (TypeError::Sorts(values), extra) => {
+ let sort_string = |ty: Ty<'tcx>| match (extra, &ty.kind) {
+ (true, ty::Opaque(def_id, _)) => format!(
+ " (opaque type at {})",
+ self.tcx.sess.source_map()
+ .mk_substr_filename(self.tcx.def_span(*def_id)),
+ ),
+ (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
+ (false, _) => "".to_string(),
+ };
+ if !(values.expected.is_simple_text() && values.found.is_simple_text()) || (
+ exp_found.map_or(false, |ef| {
+ // This happens when the type error is a subset of the expectation,
+ // like when you have two references but one is `usize` and the other
+ // is `f32`. In those cases we still want to show the `note`. If the
+ // value from `ef` is `Infer(_)`, then we ignore it.
+ if !ef.expected.is_ty_infer() {
+ ef.expected != values.expected
+ } else if !ef.found.is_ty_infer() {
+ ef.found != values.found
+ } else {
+ false
+ }
+ })
+ ) {
+ diag.note_expected_found_extra(
+ &expected_label,
+ expected,
+ &found_label,
+ found,
+ &sort_string(values.expected),
+ &sort_string(values.found),
+ );
+ }
}
- (TypeError::ObjectUnsafeCoercion(_), ..) => {
+ (TypeError::ObjectUnsafeCoercion(_), _) => {
diag.note_unsuccessfull_coercion(found, expected);
}
- (_, false, _) => {
+ (_, _) => {
debug!(
"note_type_err: exp_found={:?}, expected={:?} found={:?}",
exp_found, expected, found
);
- if let Some(exp_found) = exp_found {
- self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
+ if !is_simple_error || terr.must_include_note() {
+ diag.note_expected_found(&expected_label, expected, &found_label, found);
}
-
- diag.note_expected_found(&"type", expected, found);
}
- _ => (),
}
}
+ if let Some(exp_found) = exp_found {
+ self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
+ }
- self.check_and_note_conflicting_crates(diag, terr, span);
- self.tcx.note_and_explain_type_err(diag, terr, span);
+ // In some (most?) cases cause.body_id points to actual body, but in some cases
+ // it's a actual definition. According to the comments (e.g. in
+ // librustc_typeck/check/compare_method.rs:compare_predicate_entailment) the latter
+ // is relied upon by some other code. This might (or might not) need cleanup.
+ let body_owner_def_id = self.tcx.hir().opt_local_def_id(cause.body_id)
+ .unwrap_or_else(|| {
+ self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id })
+ });
+ self.check_and_note_conflicting_crates(diag, terr);
+ self.tcx.note_and_explain_type_err(diag, terr, span, body_owner_def_id);
// It reads better to have the error origin as the final
// thing.
}
/// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
- /// suggest it.
+ /// suggests it.
fn suggest_as_ref_where_appropriate(
&self,
span: Span,
infer::Types(ref exp_found) => self.expected_found_str_ty(exp_found),
infer::Regions(ref exp_found) => self.expected_found_str(exp_found),
infer::Consts(ref exp_found) => self.expected_found_str(exp_found),
- infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
- infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found),
+ infer::TraitRefs(ref exp_found) => {
+ let pretty_exp_found = ty::error::ExpectedFound {
+ expected: exp_found.expected.print_only_trait_path(),
+ found: exp_found.found.print_only_trait_path()
+ };
+ self.expected_found_str(&pretty_exp_found)
+ },
+ infer::PolyTraitRefs(ref exp_found) => {
+ let pretty_exp_found = ty::error::ExpectedFound {
+ expected: exp_found.expected.print_only_trait_path(),
+ found: exp_found.found.print_only_trait_path()
+ };
+ self.expected_found_str(&pretty_exp_found)
+ },
}
}
sub_region,
"...",
);
- err.note(&format!(
- "...so that the {}:\nexpected {}\n found {}",
- sup_trace.cause.as_requirement_str(),
- sup_expected.content(),
- sup_found.content()
+ err.span_note(sup_trace.cause.span, &format!(
+ "...so that the {}",
+ sup_trace.cause.as_requirement_str()
));
+
+ err.note_expected_found(
+ &"",
+ sup_expected,
+ &"",
+ sup_found
+ );
err.emit();
return;
}