Tweak output
- Only point at a the single expression where the found type was first inferred. - Find method call argument that might have caused the found type to be inferred. - Provide structured suggestion. - Apply some review comments. - Tweak wording.
This commit is contained in:
parent
6b0cce4b50
commit
9cc8d86190
@ -1,5 +1,6 @@
|
|||||||
use crate::FnCtxt;
|
use crate::FnCtxt;
|
||||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||||
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
use rustc_errors::MultiSpan;
|
use rustc_errors::MultiSpan;
|
||||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
@ -14,12 +15,14 @@ use rustc_middle::ty::adjustment::AllowTwoPhase;
|
|||||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||||
use rustc_middle::ty::fold::TypeFolder;
|
use rustc_middle::ty::fold::TypeFolder;
|
||||||
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
|
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
|
||||||
|
use rustc_middle::ty::relate::TypeRelation;
|
||||||
use rustc_middle::ty::{
|
use rustc_middle::ty::{
|
||||||
self, Article, AssocItem, Ty, TyCtxt, TypeAndMut, TypeSuperFoldable, TypeVisitable,
|
self, Article, AssocItem, Ty, TyCtxt, TypeAndMut, TypeSuperFoldable, TypeVisitable,
|
||||||
};
|
};
|
||||||
use rustc_span::symbol::{sym, Symbol};
|
use rustc_span::symbol::{sym, Symbol};
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, Span};
|
||||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||||
|
use rustc_trait_selection::traits::error_reporting::method_chain::CollectAllMismatches;
|
||||||
use rustc_trait_selection::traits::ObligationCause;
|
use rustc_trait_selection::traits::ObligationCause;
|
||||||
|
|
||||||
use super::method::probe;
|
use super::method::probe;
|
||||||
@ -44,7 +47,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
self.annotate_alternative_method_deref(err, expr, error);
|
self.annotate_alternative_method_deref(err, expr, error);
|
||||||
|
|
||||||
// Use `||` to give these suggestions a precedence
|
// Use `||` to give these suggestions a precedence
|
||||||
let _ = self.suggest_missing_parentheses(err, expr)
|
let suggested = self.suggest_missing_parentheses(err, expr)
|
||||||
|| self.suggest_remove_last_method_call(err, expr, expected)
|
|| self.suggest_remove_last_method_call(err, expr, expected)
|
||||||
|| self.suggest_associated_const(err, expr, expected)
|
|| self.suggest_associated_const(err, expr, expected)
|
||||||
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
|
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
|
||||||
@ -57,8 +60,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|
|| self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected)
|
||||||
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|
|| self.suggest_copied_or_cloned(err, expr, expr_ty, expected)
|
||||||
|| self.suggest_into(err, expr, expr_ty, expected)
|
|| self.suggest_into(err, expr, expr_ty, expected)
|
||||||
|| self.suggest_floating_point_literal(err, expr, expected)
|
|| self.suggest_floating_point_literal(err, expr, expected);
|
||||||
|| self.point_inference_types(err, expr);
|
if !suggested {
|
||||||
|
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn emit_coerce_suggestions(
|
pub fn emit_coerce_suggestions(
|
||||||
@ -210,7 +215,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
(expected, Some(err))
|
(expected, Some(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn point_inference_types(&self, err: &mut Diagnostic, expr: &hir::Expr<'_>) -> bool {
|
fn point_at_expr_source_of_inferred_type(
|
||||||
|
&self,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
expr: &hir::Expr<'_>,
|
||||||
|
found: Ty<'tcx>,
|
||||||
|
expected: Ty<'tcx>,
|
||||||
|
) -> bool {
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let map = self.tcx.hir();
|
let map = self.tcx.hir();
|
||||||
|
|
||||||
@ -250,25 +261,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; };
|
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; };
|
||||||
let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; };
|
let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; };
|
||||||
let hir::def::Res::Local(hir_id) = p.res else { return false; };
|
let hir::def::Res::Local(hir_id) = p.res else { return false; };
|
||||||
let Some(node) = map.find(hir_id) else { return false; };
|
let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; };
|
||||||
let hir::Node::Pat(pat) = node else { return false; };
|
|
||||||
let parent = map.get_parent_node(pat.hir_id);
|
let parent = map.get_parent_node(pat.hir_id);
|
||||||
let Some(hir::Node::Local(hir::Local {
|
let Some(hir::Node::Local(hir::Local {
|
||||||
ty: None,
|
ty: None,
|
||||||
init: Some(init),
|
init: Some(init),
|
||||||
..
|
..
|
||||||
})) = map.find(parent) else { return false; };
|
})) = map.find(parent) else { return false; };
|
||||||
|
let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; };
|
||||||
let ty = self.node_ty(init.hir_id);
|
|
||||||
if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() {
|
if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let mut span_labels = vec![(
|
|
||||||
init.span,
|
|
||||||
with_forced_trimmed_paths!(format!(
|
|
||||||
"here the type of `{ident}` is inferred to be `{ty}`",
|
|
||||||
)),
|
|
||||||
)];
|
|
||||||
|
|
||||||
// Locate all the usages of the relevant binding.
|
// Locate all the usages of the relevant binding.
|
||||||
struct FindExprs<'hir> {
|
struct FindExprs<'hir> {
|
||||||
@ -296,71 +299,139 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
expr_finder.visit_expr(body.value);
|
expr_finder.visit_expr(body.value);
|
||||||
let mut eraser = TypeEraser { tcx };
|
let mut eraser = TypeEraser { tcx };
|
||||||
let mut prev = eraser.fold_ty(ty);
|
let mut prev = eraser.fold_ty(ty);
|
||||||
|
let mut prev_span = None;
|
||||||
|
|
||||||
for ex in expr_finder.uses {
|
for binding in expr_finder.uses {
|
||||||
if ex.span.overlaps(expr.span) { break; }
|
// In every expression where the binding is referenced, we will look at that
|
||||||
let parent = map.get_parent_node(ex.hir_id);
|
// expression's type and see if it is where the incorrect found type was fully
|
||||||
|
// "materialized" and point at it. We will also try to provide a suggestion there.
|
||||||
|
let parent = map.get_parent_node(binding.hir_id);
|
||||||
if let Some(hir::Node::Expr(expr))
|
if let Some(hir::Node::Expr(expr))
|
||||||
| Some(hir::Node::Stmt(hir::Stmt {
|
| Some(hir::Node::Stmt(hir::Stmt {
|
||||||
kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr),
|
kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr),
|
||||||
..
|
..
|
||||||
})) = &map.find(parent)
|
})) = &map.find(parent)
|
||||||
&& let hir::ExprKind::MethodCall(s, rcvr, args, span) = expr.kind
|
&& let hir::ExprKind::MethodCall(s, rcvr, args, _span) = expr.kind
|
||||||
&& rcvr.hir_id == ex.hir_id
|
&& rcvr.hir_id == binding.hir_id
|
||||||
|
&& let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
|
||||||
{
|
{
|
||||||
let ty = if let Ok(m) = self.lookup_method(ty, s, span, expr, rcvr, args) {
|
// We special case methods, because they can influence inference through the
|
||||||
// We get the self type from `lookup_method` because the `rcvr` node
|
// call's arguments and we can provide a more explicit span.
|
||||||
// type will not have had any adjustments from the fn arguments.
|
let sig = self.tcx.fn_sig(def_id);
|
||||||
let ty = m.sig.inputs_and_output[0];
|
let def_self_ty = sig.input(0).skip_binder();
|
||||||
match ty.kind() {
|
let rcvr_ty = self.node_ty(rcvr.hir_id);
|
||||||
// Remove one layer of references to account for `&mut self` and
|
// Get the evaluated type *after* calling the method call, so that the influence
|
||||||
// `&self`, so that we can compare it against the binding.
|
// of the arguments can be reflected in the receiver type. The receiver
|
||||||
ty::Ref(_, ty, _) => *ty,
|
// expression has the type *before* theis analysis is done.
|
||||||
_ => ty,
|
let ty = match self.lookup_probe(s.ident, rcvr_ty, expr, probe::ProbeScope::TraitsInScope) {
|
||||||
}
|
Ok(pick) => pick.self_ty,
|
||||||
} else {
|
Err(_) => rcvr_ty,
|
||||||
self.node_ty(rcvr.hir_id)
|
|
||||||
};
|
};
|
||||||
|
// Remove one layer of references to account for `&mut self` and
|
||||||
|
// `&self`, so that we can compare it against the binding.
|
||||||
|
let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) {
|
||||||
|
(ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty),
|
||||||
|
_ => (ty, def_self_ty),
|
||||||
|
};
|
||||||
|
let mut param_args = FxHashMap::default();
|
||||||
|
let mut param_expected = FxHashMap::default();
|
||||||
|
let mut param_found = FxHashMap::default();
|
||||||
|
if self.can_eq(self.param_env, ty, found).is_ok() {
|
||||||
|
// We only point at the first place where the found type was inferred.
|
||||||
|
for (i, param_ty) in sig.inputs().skip_binder().iter().skip(1).enumerate() {
|
||||||
|
if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() {
|
||||||
|
// We found an argument that references a type parameter in `Self`,
|
||||||
|
// so we assume that this is the argument that caused the found
|
||||||
|
// type, which we know already because of `can_eq` above was first
|
||||||
|
// inferred in this method call.
|
||||||
|
let arg = &args[i];
|
||||||
|
let arg_ty = self.node_ty(arg.hir_id);
|
||||||
|
err.span_label(
|
||||||
|
arg.span,
|
||||||
|
&format!(
|
||||||
|
"this is of type `{arg_ty}`, which makes `{ident}` to be \
|
||||||
|
inferred as `{ty}`",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
param_args.insert(param_ty, (arg, arg_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here we find, for a type param `T`, the type that `T` is in the current
|
||||||
|
// method call *and* in the original expected type. That way, we can see if we
|
||||||
|
// can give any structured suggestion for the function argument.
|
||||||
|
let mut c = CollectAllMismatches {
|
||||||
|
infcx: &self.infcx,
|
||||||
|
param_env: self.param_env,
|
||||||
|
errors: vec![],
|
||||||
|
};
|
||||||
|
let _ = c.relate(def_self_ty, ty);
|
||||||
|
for error in c.errors {
|
||||||
|
if let TypeError::Sorts(error) = error {
|
||||||
|
param_found.insert(error.expected, error.found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.errors = vec![];
|
||||||
|
let _ = c.relate(def_self_ty, expected);
|
||||||
|
for error in c.errors {
|
||||||
|
if let TypeError::Sorts(error) = error {
|
||||||
|
param_expected.insert(error.expected, error.found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (param, (arg,arg_ty)) in param_args.iter() {
|
||||||
|
let Some(expected) = param_expected.get(param) else { continue; };
|
||||||
|
let Some(found) = param_found.get(param) else { continue; };
|
||||||
|
if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; }
|
||||||
|
self.suggest_deref_ref_or_into(err, arg, *expected, *found, None);
|
||||||
|
}
|
||||||
|
|
||||||
let ty = eraser.fold_ty(ty);
|
let ty = eraser.fold_ty(ty);
|
||||||
if ty.references_error() {
|
if ty.references_error() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ty != prev {
|
if ty != prev
|
||||||
span_labels.push((
|
&& param_args.is_empty()
|
||||||
|
&& self.can_eq(self.param_env, ty, found).is_ok()
|
||||||
|
{
|
||||||
|
// We only point at the first place where the found type was inferred.
|
||||||
|
err.span_label(
|
||||||
s.ident.span,
|
s.ident.span,
|
||||||
with_forced_trimmed_paths!(format!(
|
with_forced_trimmed_paths!(format!(
|
||||||
"here the type of `{ident}` is inferred to be `{ty}`",
|
"here the type of `{ident}` is inferred to be `{ty}`",
|
||||||
)),
|
)),
|
||||||
));
|
);
|
||||||
prev = ty;
|
break;
|
||||||
}
|
}
|
||||||
|
prev = ty;
|
||||||
} else {
|
} else {
|
||||||
let ty = eraser.fold_ty(self.node_ty(ex.hir_id));
|
let ty = eraser.fold_ty(self.node_ty(binding.hir_id));
|
||||||
if ty.references_error() {
|
if ty.references_error() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if ty != prev {
|
if ty != prev && let Some(span) = prev_span && self.can_eq(self.param_env, ty, found).is_ok() {
|
||||||
span_labels.push((
|
// We only point at the first place where the found type was inferred.
|
||||||
ex.span,
|
// We use the *previous* span because if the type is known *here* it means
|
||||||
|
// it was *evaluated earlier*. We don't do this for method calls because we
|
||||||
|
// evaluate the method's self type eagerly, but not in any other case.
|
||||||
|
err.span_label(
|
||||||
|
span,
|
||||||
with_forced_trimmed_paths!(format!(
|
with_forced_trimmed_paths!(format!(
|
||||||
"here the type of `{ident}` is inferred to be `{ty}`",
|
"here the type of `{ident}` is inferred to be `{ty}`",
|
||||||
)),
|
)),
|
||||||
));
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
prev = ty;
|
prev = ty;
|
||||||
}
|
}
|
||||||
if ex.hir_id == expr.hir_id {
|
if binding.hir_id == expr.hir_id {
|
||||||
// Stop showing spans after the error type was emitted.
|
// Do not look at expressions that come after the expression we were originally
|
||||||
|
// evaluating and had a type error.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
prev_span = Some(binding.span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if span_labels.len() < 2 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (sp, label) in span_labels {
|
|
||||||
err.span_label(sp, &label);
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,10 +67,7 @@ LL | x == 5
|
|||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/assignment-in-if.rs:44:18
|
--> $DIR/assignment-in-if.rs:44:18
|
||||||
|
|
|
|
||||||
LL | let x = 1;
|
LL | if y = (Foo { foo: x }) {
|
||||||
| - here the type of `x` is inferred to be `{integer}`
|
|
||||||
...
|
|
||||||
LL | println!("{}", x);
|
|
||||||
| - here the type of `x` is inferred to be `usize`
|
| - here the type of `x` is inferred to be `usize`
|
||||||
...
|
...
|
||||||
LL | if x == x && x = x && x == x {
|
LL | if x == x && x = x && x == x {
|
||||||
@ -81,10 +78,7 @@ LL | if x == x && x = x && x == x {
|
|||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/assignment-in-if.rs:44:22
|
--> $DIR/assignment-in-if.rs:44:22
|
||||||
|
|
|
|
||||||
LL | let x = 1;
|
LL | if y = (Foo { foo: x }) {
|
||||||
| - here the type of `x` is inferred to be `{integer}`
|
|
||||||
...
|
|
||||||
LL | println!("{}", x);
|
|
||||||
| - here the type of `x` is inferred to be `usize`
|
| - here the type of `x` is inferred to be `usize`
|
||||||
...
|
...
|
||||||
LL | if x == x && x = x && x == x {
|
LL | if x == x && x = x && x == x {
|
||||||
@ -104,10 +98,7 @@ LL | if x == x && x == x && x == x {
|
|||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/assignment-in-if.rs:51:28
|
--> $DIR/assignment-in-if.rs:51:28
|
||||||
|
|
|
|
||||||
LL | let x = 1;
|
LL | if y = (Foo { foo: x }) {
|
||||||
| - here the type of `x` is inferred to be `{integer}`
|
|
||||||
...
|
|
||||||
LL | println!("{}", x);
|
|
||||||
| - here the type of `x` is inferred to be `usize`
|
| - here the type of `x` is inferred to be `usize`
|
||||||
...
|
...
|
||||||
LL | if x == x && x == x && x = x {
|
LL | if x == x && x == x && x = x {
|
||||||
|
13
src/test/ui/type/type-check/point-at-inference-2.rs
Normal file
13
src/test/ui/type/type-check/point-at-inference-2.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
fn bar(_: Vec<i32>) {}
|
||||||
|
fn baz(_: &Vec<&i32>) {}
|
||||||
|
fn main() {
|
||||||
|
let v = vec![&1];
|
||||||
|
bar(v); //~ ERROR E0308
|
||||||
|
let v = vec![];
|
||||||
|
baz(&v);
|
||||||
|
baz(&v);
|
||||||
|
bar(v); //~ ERROR E0308
|
||||||
|
let v = vec![];
|
||||||
|
baz(&v);
|
||||||
|
bar(v); //~ ERROR E0308
|
||||||
|
}
|
56
src/test/ui/type/type-check/point-at-inference-2.stderr
Normal file
56
src/test/ui/type/type-check/point-at-inference-2.stderr
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/point-at-inference-2.rs:5:9
|
||||||
|
|
|
||||||
|
LL | bar(v);
|
||||||
|
| --- ^ expected `i32`, found `&{integer}`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
= note: expected struct `Vec<i32>`
|
||||||
|
found struct `Vec<&{integer}>`
|
||||||
|
note: function defined here
|
||||||
|
--> $DIR/point-at-inference-2.rs:1:4
|
||||||
|
|
|
||||||
|
LL | fn bar(_: Vec<i32>) {}
|
||||||
|
| ^^^ -----------
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/point-at-inference-2.rs:9:9
|
||||||
|
|
|
||||||
|
LL | baz(&v);
|
||||||
|
| - here the type of `v` is inferred to be `Vec<&i32>`
|
||||||
|
LL | baz(&v);
|
||||||
|
LL | bar(v);
|
||||||
|
| --- ^ expected `i32`, found `&i32`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
= note: expected struct `Vec<i32>`
|
||||||
|
found struct `Vec<&i32>`
|
||||||
|
note: function defined here
|
||||||
|
--> $DIR/point-at-inference-2.rs:1:4
|
||||||
|
|
|
||||||
|
LL | fn bar(_: Vec<i32>) {}
|
||||||
|
| ^^^ -----------
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/point-at-inference-2.rs:12:9
|
||||||
|
|
|
||||||
|
LL | baz(&v);
|
||||||
|
| - here the type of `v` is inferred to be `Vec<&i32>`
|
||||||
|
LL | bar(v);
|
||||||
|
| --- ^ expected `i32`, found `&i32`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
= note: expected struct `Vec<i32>`
|
||||||
|
found struct `Vec<&i32>`
|
||||||
|
note: function defined here
|
||||||
|
--> $DIR/point-at-inference-2.rs:1:4
|
||||||
|
|
|
||||||
|
LL | fn bar(_: Vec<i32>) {}
|
||||||
|
| ^^^ -----------
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
13
src/test/ui/type/type-check/point-at-inference.fixed
Normal file
13
src/test/ui/type/type-check/point-at-inference.fixed
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// run-rustfix
|
||||||
|
fn bar(_: Vec<i32>) {}
|
||||||
|
fn baz(_: &impl std::any::Any) {}
|
||||||
|
fn main() {
|
||||||
|
let v = vec![1, 2, 3, 4, 5];
|
||||||
|
let mut foo = vec![];
|
||||||
|
baz(&foo);
|
||||||
|
for i in &v {
|
||||||
|
foo.push(*i);
|
||||||
|
}
|
||||||
|
baz(&foo);
|
||||||
|
bar(foo); //~ ERROR E0308
|
||||||
|
}
|
13
src/test/ui/type/type-check/point-at-inference.rs
Normal file
13
src/test/ui/type/type-check/point-at-inference.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// run-rustfix
|
||||||
|
fn bar(_: Vec<i32>) {}
|
||||||
|
fn baz(_: &impl std::any::Any) {}
|
||||||
|
fn main() {
|
||||||
|
let v = vec![1, 2, 3, 4, 5];
|
||||||
|
let mut foo = vec![];
|
||||||
|
baz(&foo);
|
||||||
|
for i in &v {
|
||||||
|
foo.push(i);
|
||||||
|
}
|
||||||
|
baz(&foo);
|
||||||
|
bar(foo); //~ ERROR E0308
|
||||||
|
}
|
26
src/test/ui/type/type-check/point-at-inference.stderr
Normal file
26
src/test/ui/type/type-check/point-at-inference.stderr
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/point-at-inference.rs:12:9
|
||||||
|
|
|
||||||
|
LL | foo.push(i);
|
||||||
|
| - this is of type `&{integer}`, which makes `foo` to be inferred as `Vec<&{integer}>`
|
||||||
|
...
|
||||||
|
LL | bar(foo);
|
||||||
|
| --- ^^^ expected `i32`, found `&{integer}`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
= note: expected struct `Vec<i32>`
|
||||||
|
found struct `Vec<&{integer}>`
|
||||||
|
note: function defined here
|
||||||
|
--> $DIR/point-at-inference.rs:2:4
|
||||||
|
|
|
||||||
|
LL | fn bar(_: Vec<i32>) {}
|
||||||
|
| ^^^ -----------
|
||||||
|
help: consider dereferencing the borrow
|
||||||
|
|
|
||||||
|
LL | foo.push(*i);
|
||||||
|
| +
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
Loading…
x
Reference in New Issue
Block a user