1
Fork 0

Rollup merge of #101100 - compiler-errors:generalize-call-suggestions, r=petrochenkov

Make call suggestions more general and more accurate

Cleans up some suggestions that have to do with adding `()` to make typeck happy.

1. Drive-by rename of `expr_t` to `base_ty` since it's the type of the `base_expr`
1. Autoderef until we get to a callable type in `suggest_fn_call`.
1. Don't erroneously suggest calling constructor when a method/field does not exist on it.
1. Suggest calling a method receiver if its function output has a method (e.g. `fn.method()` => `fn().method()`)
1. Extend call suggestions to type parameters, fn pointers, trait objects where possible
1. Suggest calling in operators too (fixes #101054)
1. Use `/* {ty} */` as argument placeholder instead of just `_`, which is confusing and makes suggestions look less like `if let` syntax.
This commit is contained in:
Matthias Krüger 2022-08-31 07:58:00 +02:00 committed by GitHub
commit b8b2f88a04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 609 additions and 416 deletions

View file

@ -1249,9 +1249,13 @@ impl HandlerInner {
}
fn treat_err_as_bug(&self) -> bool {
self.flags
.treat_err_as_bug
.map_or(false, |c| self.err_count() + self.lint_err_count >= c.get())
self.flags.treat_err_as_bug.map_or(false, |c| {
self.err_count()
+ self.lint_err_count
+ self.delayed_span_bugs.len()
+ self.delayed_good_path_bugs.len()
>= c.get()
})
}
fn print_error_count(&mut self, registry: &Registry) {
@ -1407,7 +1411,14 @@ impl HandlerInner {
// This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
// incrementing `err_count` by one, so we need to +1 the comparing.
// FIXME: Would be nice to increment err_count in a more coherent way.
if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) {
if self.flags.treat_err_as_bug.map_or(false, |c| {
self.err_count()
+ self.lint_err_count
+ self.delayed_span_bugs.len()
+ self.delayed_good_path_bugs.len()
+ 1
>= c.get()
}) {
// FIXME: don't abort here if report_delayed_bugs is off
self.span_bug(sp, msg);
}

View file

@ -41,7 +41,8 @@ macro_rules! pluralize {
/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
/// to determine whether it should be automatically applied or if the user should be consulted
/// before applying the suggestion.
#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
#[derive(PartialEq, Eq, PartialOrd, Ord)]
pub enum Applicability {
/// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
/// This suggestion should be automatically applied.

View file

@ -844,6 +844,12 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Vec<T> {
}
}
impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &[T] {
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
self.iter().try_for_each(|t| t.visit_with(visitor))
}
}
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> {
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
self.try_map_id(|t| t.try_fold_with(folder))

View file

@ -21,7 +21,6 @@ use crate::errors::{
};
use crate::type_error_struct;
use super::suggest_call_constructor;
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
@ -44,7 +43,7 @@ use rustc_middle::middle::stability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
use rustc_middle::ty::error::TypeError::FieldMisMatch;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
use rustc_session::parse::feature_err;
use rustc_span::hygiene::DesugaringKind;
use rustc_span::lev_distance::find_best_match_for_name;
@ -2141,15 +2140,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
field: Ident,
) -> Ty<'tcx> {
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
let expr_t = self.check_expr(base);
let expr_t = self.structurally_resolved_type(base.span, expr_t);
let base_ty = self.check_expr(base);
let base_ty = self.structurally_resolved_type(base.span, base_ty);
let mut private_candidate = None;
let mut autoderef = self.autoderef(expr.span, expr_t);
while let Some((base_t, _)) = autoderef.next() {
debug!("base_t: {:?}", base_t);
match base_t.kind() {
let mut autoderef = self.autoderef(expr.span, base_ty);
while let Some((deref_base_ty, _)) = autoderef.next() {
debug!("deref_base_ty: {:?}", deref_base_ty);
match deref_base_ty.kind() {
ty::Adt(base_def, substs) if !base_def.is_enum() => {
debug!("struct named {:?}", base_t);
debug!("struct named {:?}", deref_base_ty);
let (ident, def_scope) =
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
let fields = &base_def.non_enum_variant().fields;
@ -2197,23 +2196,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// (#90483) apply adjustments to avoid ExprUseVisitor from
// creating erroneous projection.
self.apply_adjustments(base, adjustments);
self.ban_private_field_access(expr, expr_t, field, did);
self.ban_private_field_access(expr, base_ty, field, did);
return field_ty;
}
if field.name == kw::Empty {
} else if self.method_exists(field, expr_t, expr.hir_id, true) {
self.ban_take_value_of_method(expr, expr_t, field);
} else if !expr_t.is_primitive_ty() {
self.ban_nonexisting_field(field, base, expr, expr_t);
} else if self.method_exists(field, base_ty, expr.hir_id, true) {
self.ban_take_value_of_method(expr, base_ty, field);
} else if !base_ty.is_primitive_ty() {
self.ban_nonexisting_field(field, base, expr, base_ty);
} else {
let field_name = field.to_string();
let mut err = type_error_struct!(
self.tcx().sess,
field.span,
expr_t,
base_ty,
E0610,
"`{expr_t}` is a primitive type and therefore doesn't have fields",
"`{base_ty}` is a primitive type and therefore doesn't have fields",
);
let is_valid_suffix = |field: &str| {
if field == "f32" || field == "f64" {
@ -2251,7 +2250,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None
}
};
if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
if let ty::Infer(ty::IntVar(_)) = base_ty.kind()
&& let ExprKind::Lit(Spanned {
node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
..
@ -2280,35 +2279,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.tcx().ty_error()
}
fn check_call_constructor(
&self,
err: &mut Diagnostic,
base: &'tcx hir::Expr<'tcx>,
def_id: DefId,
) {
if let Some(local_id) = def_id.as_local() {
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
let node = self.tcx.hir().get(hir_id);
if let Some(fields) = node.tuple_fields() {
let kind = match self.tcx.opt_def_kind(local_id) {
Some(DefKind::Ctor(of, _)) => of,
_ => return,
};
suggest_call_constructor(base.span, kind, fields.len(), err);
}
} else {
// The logic here isn't smart but `associated_item_def_ids`
// doesn't work nicely on local.
if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
let parent_def_id = self.tcx.parent(def_id);
let fields = self.tcx.associated_item_def_ids(parent_def_id);
suggest_call_constructor(base.span, of, fields.len(), err);
}
}
}
fn suggest_await_on_field_access(
&self,
err: &mut Diagnostic,
@ -2351,40 +2321,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn ban_nonexisting_field(
&self,
field: Ident,
ident: Ident,
base: &'tcx hir::Expr<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
expr_t: Ty<'tcx>,
base_ty: Ty<'tcx>,
) {
debug!(
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
field, base, expr, expr_t
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
ident, base, expr, base_ty
);
let mut err = self.no_such_field_err(field, expr_t, base.hir_id);
let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
match *expr_t.peel_refs().kind() {
match *base_ty.peel_refs().kind() {
ty::Array(_, len) => {
self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len);
}
ty::RawPtr(..) => {
self.suggest_first_deref_field(&mut err, expr, base, field);
self.suggest_first_deref_field(&mut err, expr, base, ident);
}
ty::Adt(def, _) if !def.is_enum() => {
self.suggest_fields_on_recordish(&mut err, def, field, expr.span);
self.suggest_fields_on_recordish(&mut err, def, ident, expr.span);
}
ty::Param(param_ty) => {
self.point_at_param_definition(&mut err, param_ty);
}
ty::Opaque(_, _) => {
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
}
ty::FnDef(def_id, _) => {
self.check_call_constructor(&mut err, base, def_id);
self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
}
_ => {}
}
if field.name == kw::Await {
self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() {
def.non_enum_variant().fields.iter().any(|field| {
field.ident(self.tcx) == ident
&& field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
})
} else if let ty::Tuple(tys) = output_ty.kind()
&& let Ok(idx) = ident.as_str().parse::<usize>()
{
idx < tys.len()
} else {
false
}
});
if ident.name == kw::Await {
// We know by construction that `<expr>.await` is either on Rust 2015
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
err.note("to `.await` a `Future`, switch to Rust 2018 or later");

View file

@ -2,6 +2,7 @@ use super::FnCtxt;
use crate::astconv::AstConv;
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
use hir::def_id::DefId;
use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
@ -61,70 +62,51 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pointing_at_return_type
}
/// When encountering an fn-like ctor that needs to unify with a value, check whether calling
/// the ctor would successfully solve the type mismatch and if so, suggest it:
/// When encountering an fn-like type, try accessing the output of the type
/// // and suggesting calling it if it satisfies a predicate (i.e. if the
/// output has a method or a field):
/// ```compile_fail,E0308
/// fn foo(x: usize) -> usize { x }
/// let x: usize = foo; // suggest calling the `foo` function: `foo(42)`
/// ```
fn suggest_fn_call(
pub(crate) fn suggest_fn_call(
&self,
err: &mut Diagnostic,
expr: &hir::Expr<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>) -> bool,
) -> bool {
let (def_id, output, inputs) = match *found.kind() {
ty::FnDef(def_id, _) => {
let fn_sig = found.fn_sig(self.tcx);
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len())
}
ty::Closure(def_id, substs) => {
let fn_sig = substs.as_closure().sig();
(def_id, fn_sig.output(), fn_sig.inputs().skip_binder().len() - 1)
}
ty::Opaque(def_id, substs) => {
let sig = self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
// args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
{
Some((
pred.kind().rebind(proj.term.ty().unwrap()),
args.len(),
))
} else {
None
}
});
if let Some((output, inputs)) = sig {
(def_id, output, inputs)
} else {
return false;
}
}
_ => return false,
};
let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
let output = self.normalize_associated_types_in(expr.span, output);
if !output.is_ty_var() && self.can_coerce(output, expected) {
let (sugg_call, mut applicability) = match inputs {
let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found)
else { return false; };
if can_satisfy(output) {
let (sugg_call, mut applicability) = match inputs.len() {
0 => ("".to_string(), Applicability::MachineApplicable),
1..=4 => (
(0..inputs).map(|_| "_").collect::<Vec<_>>().join(", "),
Applicability::MachineApplicable,
inputs
.iter()
.map(|ty| {
if ty.is_suggestable(self.tcx, false) {
format!("/* {ty} */")
} else {
"".to_string()
}
})
.collect::<Vec<_>>()
.join(", "),
Applicability::HasPlaceholders,
),
_ => ("...".to_string(), Applicability::HasPlaceholders),
_ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
};
let msg = match self.tcx.def_kind(def_id) {
DefKind::Fn => "call this function",
DefKind::Closure | DefKind::OpaqueTy => "call this closure",
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct",
DefKind::Ctor(CtorOf::Variant, _) => "instantiate this tuple variant",
_ => "call this function",
let msg = match def_id_or_name {
DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {
DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(),
DefKind::Ctor(CtorOf::Variant, _) => {
"instantiate this tuple variant".to_string()
}
kind => format!("call this {}", kind.descr(def_id)),
},
DefIdOrName::Name(name) => format!("call this {name}"),
};
let sugg = match expr.kind {
@ -161,6 +143,179 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}
fn extract_callable_info(
&self,
expr: &Expr<'_>,
found: Ty<'tcx>,
) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
// Autoderef is useful here because sometimes we box callables, etc.
let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| {
match *found.kind() {
ty::FnPtr(fn_sig) =>
Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
ty::FnDef(def_id, _) => {
let fn_sig = found.fn_sig(self.tcx);
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
}
ty::Closure(def_id, substs) => {
let fn_sig = substs.as_closure().sig();
Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
}
ty::Opaque(def_id, substs) => {
self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
// args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
{
Some((
DefIdOrName::DefId(def_id),
pred.kind().rebind(proj.term.ty().unwrap()),
pred.kind().rebind(args.as_slice()),
))
} else {
None
}
})
}
ty::Dynamic(data, _) => {
data.iter().find_map(|pred| {
if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
&& Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
// for existential projection, substs are shifted over by 1
&& let ty::Tuple(args) = proj.substs.type_at(0).kind()
{
Some((
DefIdOrName::Name("trait object"),
pred.rebind(proj.term.ty().unwrap()),
pred.rebind(args.as_slice()),
))
} else {
None
}
})
}
ty::Param(param) => {
let def_id = self.tcx.generics_of(self.body_id.owner).type_param(&param, self.tcx).def_id;
self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| {
if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder()
&& Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
&& proj.projection_ty.self_ty() == found
// args tuple will always be substs[1]
&& let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
{
Some((
DefIdOrName::DefId(def_id),
pred.kind().rebind(proj.term.ty().unwrap()),
pred.kind().rebind(args.as_slice()),
))
} else {
None
}
})
}
_ => None,
}
}) else { return None; };
let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output);
let inputs = inputs
.skip_binder()
.iter()
.map(|ty| {
self.replace_bound_vars_with_fresh_vars(
expr.span,
infer::FnCall,
inputs.rebind(*ty),
)
})
.collect();
// We don't want to register any extra obligations, which should be
// implied by wf, but also because that would possibly result in
// erroneous errors later on.
let infer::InferOk { value: output, obligations: _ } =
self.normalize_associated_types_in_as_infer_ok(expr.span, output);
if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
}
pub fn suggest_two_fn_call(
&self,
err: &mut Diagnostic,
lhs_expr: &'tcx hir::Expr<'tcx>,
lhs_ty: Ty<'tcx>,
rhs_expr: &'tcx hir::Expr<'tcx>,
rhs_ty: Ty<'tcx>,
can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool,
) -> bool {
let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty)
else { return false; };
let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty)
else { return false; };
if can_satisfy(lhs_output_ty, rhs_output_ty) {
let mut sugg = vec![];
let mut applicability = Applicability::MachineApplicable;
for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] {
let (sugg_call, this_applicability) = match inputs.len() {
0 => ("".to_string(), Applicability::MachineApplicable),
1..=4 => (
inputs
.iter()
.map(|ty| {
if ty.is_suggestable(self.tcx, false) {
format!("/* {ty} */")
} else {
"/* value */".to_string()
}
})
.collect::<Vec<_>>()
.join(", "),
Applicability::HasPlaceholders,
),
_ => ("/* ... */".to_string(), Applicability::HasPlaceholders),
};
applicability = applicability.max(this_applicability);
match expr.kind {
hir::ExprKind::Call(..)
| hir::ExprKind::Path(..)
| hir::ExprKind::Index(..)
| hir::ExprKind::Lit(..) => {
sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]);
}
hir::ExprKind::Closure { .. } => {
// Might be `{ expr } || { bool }`
applicability = Applicability::MaybeIncorrect;
sugg.extend([
(expr.span.shrink_to_lo(), "(".to_string()),
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
]);
}
_ => {
sugg.extend([
(expr.span.shrink_to_lo(), "(".to_string()),
(expr.span.shrink_to_hi(), format!(")({sugg_call})")),
]);
}
}
}
err.multipart_suggestion_verbose(
format!("use parentheses to call these"),
sugg,
applicability,
);
true
} else {
false
}
}
pub fn suggest_deref_ref_or_into(
&self,
err: &mut Diagnostic,
@ -178,12 +333,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
} else {
err.span_suggestion(sp, &msg, suggestion, applicability);
}
} else if let (ty::FnDef(def_id, ..), true) =
(&found.kind(), self.suggest_fn_call(err, expr, expected, found))
} else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected))
&& let ty::FnDef(def_id, ..) = &found.kind()
&& let Some(sp) = self.tcx.hir().span_if_local(*def_id)
{
if let Some(sp) = self.tcx.hir().span_if_local(*def_id) {
err.span_label(sp, format!("{found} defined here"));
}
} else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) {
let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id);
if !methods.is_empty() {
@ -911,3 +1065,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
}
enum DefIdOrName {
DefId(DefId),
Name(&'static str),
}

View file

@ -31,7 +31,7 @@ use std::cmp::Ordering;
use std::iter;
use super::probe::{Mode, ProbeScope};
use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
use super::{CandidateSource, MethodError, NoMatchData};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@ -363,44 +363,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
);
}
if self.is_fn_ty(rcvr_ty, span) {
if let SelfSource::MethodCall(expr) = source {
let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
if let Some(local_id) = def_id.as_local() {
let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
let node = tcx.hir().get(hir_id);
let fields = node.tuple_fields();
if let Some(fields) = fields
&& let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
Some((fields.len(), of))
} else {
None
}
} else {
// The logic here isn't smart but `associated_item_def_ids`
// doesn't work nicely on local.
if let DefKind::Ctor(of, _) = tcx.def_kind(def_id) {
let parent_def_id = tcx.parent(*def_id);
Some((tcx.associated_item_def_ids(parent_def_id).len(), of))
} else {
None
}
}
} else {
None
};
// If the function is a tuple constructor, we recommend that they call it
if let Some((fields, kind)) = suggest {
suggest_call_constructor(expr.span, kind, fields, &mut err);
} else {
// General case
err.span_label(
expr.span,
"this is a function, perhaps you wish to call it",
if let SelfSource::MethodCall(rcvr_expr) = source {
self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| {
let call_expr = self
.tcx
.hir()
.expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id));
let probe = self.lookup_probe(
span,
item_name,
output_ty,
call_expr,
ProbeScope::AllTraits,
);
}
}
probe.is_ok()
});
}
let mut custom_span_label = false;

View file

@ -96,7 +96,6 @@ use check::{check_abi, check_fn, check_mod_item_types};
pub use diverges::Diverges;
pub use expectation::Expectation;
pub use fn_ctxt::*;
use hir::def::CtorOf;
pub use inherited::{Inherited, InheritedBuilder};
use crate::astconv::AstConv;
@ -960,31 +959,3 @@ fn has_expected_num_generic_args<'tcx>(
generics.count() == expected + if generics.has_self { 1 } else { 0 }
})
}
/// Suggests calling the constructor of a tuple struct or enum variant
///
/// * `snippet` - The snippet of code that references the constructor
/// * `span` - The span of the snippet
/// * `params` - The number of parameters the constructor accepts
/// * `err` - A mutable diagnostic builder to add the suggestion to
fn suggest_call_constructor(span: Span, kind: CtorOf, params: usize, err: &mut Diagnostic) {
// Note: tuple-structs don't have named fields, so just use placeholders
let args = vec!["_"; params].join(", ");
let applicable = if params > 0 {
Applicability::HasPlaceholders
} else {
// When n = 0, it's an empty-tuple struct/enum variant
// so we trivially know how to construct it
Applicability::MachineApplicable
};
let kind = match kind {
CtorOf::Struct => "a struct",
CtorOf::Variant => "an enum variant",
};
err.span_label(span, &format!("this is the constructor of {kind}"));
err.multipart_suggestion(
"call the constructor",
vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
applicable,
);
}

View file

@ -410,26 +410,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
};
let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}");
if !lhs_expr.span.eq(&rhs_expr.span) {
self.add_type_neq_err_label(
&mut err,
lhs_expr.span,
lhs_ty,
rhs_ty,
rhs_expr,
op,
is_assign,
expected,
);
self.add_type_neq_err_label(
&mut err,
rhs_expr.span,
rhs_ty,
lhs_ty,
lhs_expr,
op,
is_assign,
expected,
);
err.span_label(lhs_expr.span, lhs_ty.to_string());
err.span_label(rhs_expr.span, rhs_ty.to_string());
}
self.note_unmet_impls_on_type(&mut err, errors);
(err, missing_trait, use_output)
@ -468,17 +450,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
};
let is_compatible = |lhs_ty, rhs_ty| {
self.lookup_op_method(
lhs_ty,
Some(rhs_ty),
Some(rhs_expr),
Op::Binary(op, is_assign),
expected,
)
.is_ok()
};
// We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
// `a += b` => `*a += b` if a is a mut ref.
if is_assign == IsAssign::Yes
&& let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
if !op.span.can_be_used_for_suggestions() {
// Suppress suggestions when lhs and rhs are not in the same span as the error
} else if is_assign == IsAssign::Yes
&& let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
{
suggest_deref_binop(lhs_deref_ty);
} else if is_assign == IsAssign::No
&& let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() {
if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty, lhs_expr.span) {
&& let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
{
if self.type_is_copy_modulo_regions(
self.param_env,
*lhs_deref_ty,
lhs_expr.span,
) {
suggest_deref_binop(*lhs_deref_ty);
}
} else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
is_compatible(lhs_ty, rhs_ty)
}) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| {
is_compatible(lhs_ty, rhs_ty)
}) || self.suggest_two_fn_call(
&mut err,
rhs_expr,
rhs_ty,
lhs_expr,
lhs_ty,
|lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty),
) {
// Cool
}
if let Some(missing_trait) = missing_trait {
let mut visitor = TypeParamVisitor(vec![]);
visitor.visit_ty(lhs_ty);
@ -548,69 +563,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(lhs_ty, rhs_ty, return_ty)
}
/// If one of the types is an uncalled function and calling it would yield the other type,
/// suggest calling the function. Returns `true` if suggestion would apply (even if not given).
fn add_type_neq_err_label(
&self,
err: &mut Diagnostic,
span: Span,
ty: Ty<'tcx>,
other_ty: Ty<'tcx>,
other_expr: &'tcx hir::Expr<'tcx>,
op: hir::BinOp,
is_assign: IsAssign,
expected: Expectation<'tcx>,
) -> bool /* did we suggest to call a function because of missing parentheses? */ {
err.span_label(span, ty.to_string());
if let FnDef(def_id, _) = *ty.kind() {
if !self.tcx.has_typeck_results(def_id) {
return false;
}
// FIXME: Instead of exiting early when encountering bound vars in
// the function signature, consider keeping the binder here and
// propagating it downwards.
let Some(fn_sig) = self.tcx.fn_sig(def_id).no_bound_vars() else {
return false;
};
let other_ty = if let FnDef(def_id, _) = *other_ty.kind() {
if !self.tcx.has_typeck_results(def_id) {
return false;
}
// We're emitting a suggestion, so we can just ignore regions
self.tcx.fn_sig(def_id).skip_binder().output()
} else {
other_ty
};
if self
.lookup_op_method(
fn_sig.output(),
Some(other_ty),
Some(other_expr),
Op::Binary(op, is_assign),
expected,
)
.is_ok()
{
let (variable_snippet, applicability) = if !fn_sig.inputs().is_empty() {
("( /* arguments */ )", Applicability::HasPlaceholders)
} else {
("()", Applicability::MaybeIncorrect)
};
err.span_suggestion_verbose(
span.shrink_to_hi(),
"you might have forgotten to call this function",
variable_snippet,
applicability,
);
return true;
}
}
false
}
/// Provide actionable suggestions when trying to add two strings with incorrect types,
/// like `&str + &str`, `String + String` and `&str + &String`.
///

View file

@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::bar::<'static, char>}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
| ++
@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static>>::bar::<'static, char>}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
| ++
@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<'static, 'static, u8>>::baz}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
| ++

View file

@ -11,7 +11,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>;
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::bar::<ReStatic, char>}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::bar::<'static, char>();
| ++
@ -29,7 +29,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>;
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic>>::bar::<ReStatic, char>}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u32>>::bar::<'static, char>();
| ++
@ -47,7 +47,7 @@ LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz;
|
= note: expected unit type `()`
found fn item `fn() {<i8 as Foo<ReStatic, ReStatic, u8>>::baz}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let x: () = <i8 as Foo<'static, 'static, u8>>::baz();
| ++

View file

@ -5,6 +5,11 @@ LL | if foo == y {}
| --- ^^ - _
| |
| for<'r> fn(&'r i32) -> &'r i32 {foo}
|
help: use parentheses to call this function
|
LL | if foo(/* &i32 */) == y {}
| ++++++++++++
error: aborting due to previous error

View file

@ -6,14 +6,10 @@ LL | let x = f == g;
| |
| fn() {f}
|
help: you might have forgotten to call this function
help: use parentheses to call these
|
LL | let x = f() == g;
| ++
help: you might have forgotten to call this function
|
LL | let x = f == g();
| ++
LL | let x = f() == g();
| ++ ++
error[E0308]: mismatched types
--> $DIR/fn-compare-mismatch.rs:4:18

View file

@ -8,6 +8,10 @@ LL | let _: () = Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>;
|
= note: expected unit type `()`
found struct `Box<dyn FnOnce(isize)>`
help: use parentheses to call this trait object
|
LL | let _: () = (Box::new(|_: isize| {}) as Box<dyn FnOnce(isize)>)(/* isize */);
| + ++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-trait-formatting.rs:10:17
@ -19,6 +23,10 @@ LL | let _: () = Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>
|
= note: expected unit type `()`
found struct `Box<dyn Fn(isize, isize)>`
help: use parentheses to call this trait object
|
LL | let _: () = (Box::new(|_: isize, isize| {}) as Box<dyn Fn(isize, isize)>)(/* isize */, /* isize */);
| + +++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-trait-formatting.rs:14:17

View file

@ -1,16 +1,28 @@
// This test case checks the behavior of typeck::check::method::suggest::is_fn on Ty::Error.
struct Foo;
trait Bar {
//~^ NOTE `Bar` defines an item `bar`, perhaps you need to implement it
//~| NOTE `Bar` defines an item `bar`, perhaps you need to implement it
fn bar(&self) {}
}
impl Bar for Foo {}
fn main() {
let arc = std::sync::Arc::new(oops);
//~^ ERROR cannot find value `oops` in this scope
//~| NOTE not found
// The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
arc.blablabla();
//~^ ERROR no method named `blablabla`
arc.bar();
//~^ ERROR no method named `bar`
//~| NOTE method not found
let arc2 = std::sync::Arc::new(|| 1);
// The error "note: this is a function, perhaps you wish to call it" SHOULD appear
arc2.blablabla();
//~^ ERROR no method named `blablabla`
//~| HELP items from traits can only be used if the trait is implemented and in scope
let arc2 = std::sync::Arc::new(|| Foo);
arc2.bar();
//~^ ERROR no method named `bar`
//~| NOTE method not found
//~| NOTE this is a function, perhaps you wish to call it
//~| HELP items from traits can only be used if the trait is implemented and in scope
//~| HELP use parentheses to call this closure
}

View file

@ -1,22 +1,38 @@
error[E0425]: cannot find value `oops` in this scope
--> $DIR/fn-help-with-err.rs:3:35
--> $DIR/fn-help-with-err.rs:14:35
|
LL | let arc = std::sync::Arc::new(oops);
| ^^^^ not found in this scope
error[E0599]: no method named `blablabla` found for struct `Arc<_>` in the current scope
--> $DIR/fn-help-with-err.rs:7:9
error[E0599]: no method named `bar` found for struct `Arc<_>` in the current scope
--> $DIR/fn-help-with-err.rs:17:9
|
LL | arc.blablabla();
| ^^^^^^^^^ method not found in `Arc<_>`
LL | arc.bar();
| ^^^ method not found in `Arc<_>`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `Bar` defines an item `bar`, perhaps you need to implement it
--> $DIR/fn-help-with-err.rs:5:1
|
LL | trait Bar {
| ^^^^^^^^^
error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>` in the current scope
--> $DIR/fn-help-with-err.rs:12:10
error[E0599]: no method named `bar` found for struct `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>` in the current scope
--> $DIR/fn-help-with-err.rs:23:10
|
LL | arc2.blablabla();
| ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:38]>`
| |
| this is a function, perhaps you wish to call it
LL | arc2.bar();
| ^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:22:36: 22:38]>`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `Bar` defines an item `bar`, perhaps you need to implement it
--> $DIR/fn-help-with-err.rs:5:1
|
LL | trait Bar {
| ^^^^^^^^^
help: use parentheses to call this closure
|
LL | arc2().bar();
| ++
error: aborting due to 3 previous errors

View file

@ -11,7 +11,7 @@ LL | fn opaque() -> impl Fn() -> i32 {
|
= note: expected type `i32`
found opaque type `impl Fn() -> i32`
help: use parentheses to call this closure
help: use parentheses to call this opaque type
|
LL | opaque()()
| ++

View file

@ -13,8 +13,8 @@ LL | fn test() -> Foo { Foo }
found fn item `fn(u32) -> Foo {Foo}`
help: use parentheses to instantiate this tuple struct
|
LL | fn test() -> Foo { Foo(_) }
| +++
LL | fn test() -> Foo { Foo(/* u32 */) }
| +++++++++++
error: aborting due to previous error

View file

@ -2,9 +2,7 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current
--> $DIR/issue-57362-1.rs:20:7
|
LL | a.f();
| - ^ method not found in `fn(&u8)`
| |
| this is a function, perhaps you wish to call it
| ^ method not found in `fn(&u8)`
|
= help: items from traits can only be used if the trait is implemented and in scope
note: `Trait` defines an item `f`, perhaps you need to implement it

View file

@ -6,7 +6,7 @@ LL | foo > 12;
| |
| fn() -> i32 {foo}
|
help: you might have forgotten to call this function
help: use parentheses to call this function
|
LL | foo() > 12;
| ++
@ -28,10 +28,10 @@ LL | bar > 13;
| |
| fn(i64) -> i64 {bar}
|
help: you might have forgotten to call this function
help: use parentheses to call this function
|
LL | bar( /* arguments */ ) > 13;
| +++++++++++++++++++
LL | bar(/* i64 */) > 13;
| +++++++++++
error[E0308]: mismatched types
--> $DIR/issue-59488.rs:18:11
@ -50,14 +50,10 @@ LL | foo > foo;
| |
| fn() -> i32 {foo}
|
help: you might have forgotten to call this function
help: use parentheses to call these
|
LL | foo() > foo;
| ++
help: you might have forgotten to call this function
|
LL | foo > foo();
| ++
LL | foo() > foo();
| ++ ++
error[E0369]: binary operation `>` cannot be applied to type `fn() -> i32 {foo}`
--> $DIR/issue-59488.rs:25:9

View file

@ -8,11 +8,6 @@ LL | assert_eq!(a, 0);
| {integer}
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might have forgotten to call this function
--> $SRC_DIR/core/src/macros/mod.rs:LL:COL
|
LL | if !(*left_val() == *right_val) {
| ++
error[E0308]: mismatched types
--> $DIR/issue-70724-add_type_neq_err_label-unwrap.rs:6:5
@ -21,7 +16,7 @@ LL | assert_eq!(a, 0);
| ^^^^^^^^^^^^^^^^ expected fn item, found integer
|
= note: expected fn item `fn() -> i32 {a}`
found type `i32`
found type `{integer}`
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: `fn() -> i32 {a}` doesn't implement `Debug`

View file

@ -329,8 +329,8 @@ LL | let _: Z = Z::Fn;
found fn item `fn(u8) -> Z {Z::Fn}`
help: use parentheses to instantiate this tuple variant
|
LL | let _: Z = Z::Fn(_);
| +++
LL | let _: Z = Z::Fn(/* u8 */);
| ++++++++++
error[E0618]: expected function, found enum variant `Z::Unit`
--> $DIR/privacy-enum-ctor.rs:31:17
@ -364,8 +364,8 @@ LL | let _: E = m::E::Fn;
found fn item `fn(u8) -> E {E::Fn}`
help: use parentheses to instantiate this tuple variant
|
LL | let _: E = m::E::Fn(_);
| +++
LL | let _: E = m::E::Fn(/* u8 */);
| ++++++++++
error[E0618]: expected function, found enum variant `m::E::Unit`
--> $DIR/privacy-enum-ctor.rs:47:16
@ -399,8 +399,8 @@ LL | let _: E = E::Fn;
found fn item `fn(u8) -> E {E::Fn}`
help: use parentheses to instantiate this tuple variant
|
LL | let _: E = E::Fn(_);
| +++
LL | let _: E = E::Fn(/* u8 */);
| ++++++++++
error[E0618]: expected function, found enum variant `E::Unit`
--> $DIR/privacy-enum-ctor.rs:55:16

View file

@ -0,0 +1,7 @@
fn main() {
let mut x = 1i32;
let y = Box::new(|| 1);
x = y;
//~^ ERROR mismatched types
//~| HELP use parentheses to call this closure
}

View file

@ -0,0 +1,20 @@
error[E0308]: mismatched types
--> $DIR/call-boxed.rs:4:9
|
LL | let mut x = 1i32;
| ---- expected due to this value
LL | let y = Box::new(|| 1);
| -- the found closure
LL | x = y;
| ^ expected `i32`, found struct `Box`
|
= note: expected type `i32`
found struct `Box<[closure@$DIR/call-boxed.rs:3:22: 3:24]>`
help: use parentheses to call this closure
|
LL | x = y();
| ++
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.

View file

@ -0,0 +1,39 @@
struct Foo { i: i32 }
impl Foo {
fn bar(&self) {}
}
fn foo() -> Foo {
Foo { i: 1 }
}
fn main() {
foo.bar();
//~^ ERROR no method named `bar`
//~| HELP use parentheses to call this function
foo.i;
//~^ ERROR no field `i`
//~| HELP use parentheses to call this function
let callable = Box::new(|| Foo { i: 1 }) as Box<dyn Fn() -> Foo>;
callable.bar();
//~^ ERROR no method named `bar`
//~| HELP use parentheses to call this trait object
callable.i;
//~^ ERROR no field `i`
//~| HELP use parentheses to call this trait object
}
fn type_param<T: Fn() -> Foo>(t: T) {
t.bar();
//~^ ERROR no method named `bar`
//~| HELP use parentheses to call this type parameter
t.i;
//~^ ERROR no field `i`
//~| HELP use parentheses to call this type parameter
}

View file

@ -0,0 +1,75 @@
error[E0599]: no method named `bar` found for fn item `fn() -> Foo {foo}` in the current scope
--> $DIR/call-on-missing.rs:12:9
|
LL | foo.bar();
| ^^^ method not found in `fn() -> Foo {foo}`
|
help: use parentheses to call this function
|
LL | foo().bar();
| ++
error[E0609]: no field `i` on type `fn() -> Foo {foo}`
--> $DIR/call-on-missing.rs:16:9
|
LL | foo.i;
| ^
|
help: use parentheses to call this function
|
LL | foo().i;
| ++
error[E0599]: no method named `bar` found for struct `Box<dyn Fn() -> Foo>` in the current scope
--> $DIR/call-on-missing.rs:22:14
|
LL | callable.bar();
| ^^^ method not found in `Box<dyn Fn() -> Foo>`
|
help: use parentheses to call this trait object
|
LL | callable().bar();
| ++
error[E0609]: no field `i` on type `Box<dyn Fn() -> Foo>`
--> $DIR/call-on-missing.rs:26:14
|
LL | callable.i;
| ^ unknown field
|
help: use parentheses to call this trait object
|
LL | callable().i;
| ++
error[E0599]: no method named `bar` found for type parameter `T` in the current scope
--> $DIR/call-on-missing.rs:32:7
|
LL | fn type_param<T: Fn() -> Foo>(t: T) {
| - method `bar` not found for this type parameter
LL | t.bar();
| ^^^ method not found in `T`
|
help: use parentheses to call this type parameter
|
LL | t().bar();
| ++
error[E0609]: no field `i` on type `T`
--> $DIR/call-on-missing.rs:36:7
|
LL | fn type_param<T: Fn() -> Foo>(t: T) {
| - type parameter 'T' declared here
...
LL | t.i;
| ^
|
help: use parentheses to call this type parameter
|
LL | t().i;
| ++
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0599, E0609.
For more information about an error, try `rustc --explain E0599`.

View file

@ -33,8 +33,8 @@ LL | let _: usize = foo;
found fn item `fn(usize, usize) -> usize {foo}`
help: use parentheses to call this function
|
LL | let _: usize = foo(_, _);
| ++++++
LL | let _: usize = foo(/* usize */, /* usize */);
| ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:30:16
@ -51,8 +51,8 @@ LL | let _: S = S;
found fn item `fn(usize, usize) -> S {S}`
help: use parentheses to instantiate this tuple struct
|
LL | let _: S = S(_, _);
| ++++++
LL | let _: S = S(/* usize */, /* usize */);
| ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:31:20
@ -103,10 +103,10 @@ LL | let _: usize = T::baz;
|
= note: expected type `usize`
found fn item `fn(usize, usize) -> usize {<_ as T>::baz}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = T::baz(_, _);
| ++++++
LL | let _: usize = T::baz(/* usize */, /* usize */);
| ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:34:20
@ -121,10 +121,10 @@ LL | let _: usize = T::bat;
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<_ as T>::bat}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = T::bat(_);
| +++
LL | let _: usize = T::bat(/* usize */);
| +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:35:16
@ -141,8 +141,8 @@ LL | let _: E = E::A;
found fn item `fn(usize) -> E {E::A}`
help: use parentheses to instantiate this tuple variant
|
LL | let _: E = E::A(_);
| +++
LL | let _: E = E::A(/* usize */);
| +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:37:20
@ -157,10 +157,10 @@ LL | let _: usize = X::baz;
|
= note: expected type `usize`
found fn item `fn(usize, usize) -> usize {<X as T>::baz}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = X::baz(_, _);
| ++++++
LL | let _: usize = X::baz(/* usize */, /* usize */);
| ++++++++++++++++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:38:20
@ -175,10 +175,10 @@ LL | let _: usize = X::bat;
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bat}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = X::bat(_);
| +++
LL | let _: usize = X::bat(/* usize */);
| +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:39:20
@ -193,10 +193,10 @@ LL | let _: usize = X::bax;
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bax}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = X::bax(_);
| +++
LL | let _: usize = X::bax(/* usize */);
| +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:40:20
@ -211,10 +211,10 @@ LL | let _: usize = X::bach;
|
= note: expected type `usize`
found fn item `fn(usize) -> usize {<X as T>::bach}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = X::bach(_);
| +++
LL | let _: usize = X::bach(/* usize */);
| +++++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:41:20
@ -229,10 +229,10 @@ LL | let _: usize = X::ban;
|
= note: expected type `usize`
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::ban}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = X::ban(_);
| +++
LL | let _: usize = X::ban(/* &X */);
| ++++++++++
error[E0308]: mismatched types
--> $DIR/fn-or-tuple-struct-without-args.rs:42:20
@ -247,10 +247,10 @@ LL | let _: usize = X::bal;
|
= note: expected type `usize`
found fn item `for<'r> fn(&'r X) -> usize {<X as T>::bal}`
help: use parentheses to call this function
help: use parentheses to call this associated function
|
LL | let _: usize = X::bal(_);
| +++
LL | let _: usize = X::bal(/* &X */);
| ++++++++++
error[E0615]: attempted to take value of method `ban` on type `X`
--> $DIR/fn-or-tuple-struct-without-args.rs:43:22

View file

@ -2,17 +2,13 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in
--> $DIR/issue-29124.rs:15:15
|
LL | Obj::func.x();
| --------- ^ method not found in `fn() -> Ret {Obj::func}`
| |
| this is a function, perhaps you wish to call it
| ^ method not found in `fn() -> Ret {Obj::func}`
error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
--> $DIR/issue-29124.rs:17:10
|
LL | func.x();
| ---- ^ method not found in `fn() -> Ret {func}`
| |
| this is a function, perhaps you wish to call it
| ^ method not found in `fn() -> Ret {func}`
error: aborting due to 2 previous errors

View file

@ -4,7 +4,7 @@ struct Bar<T> {
struct Foo();
impl Foo {
fn foo() { }
fn foo(&self) { }
}
fn main() {

View file

@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the
--> $DIR/empty-tuple-method.rs:12:15
|
LL | thing.bar.foo();
| --------- ^^^ method not found in `fn() -> Foo {Foo}`
| |
| this is the constructor of a struct
| ^^^ method not found in `fn() -> Foo {Foo}`
|
help: call the constructor
help: use parentheses to instantiate this tuple struct
|
LL | (thing.bar)().foo();
| + +++

View file

@ -6,7 +6,7 @@ enum Foo{
Tup()
}
impl Foo {
fn foo() { }
fn foo(&self) { }
}
fn main() {

View file

@ -2,11 +2,9 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` i
--> $DIR/enum-variant.rs:14:15
|
LL | thing.bar.foo();
| --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
| |
| this is the constructor of an enum variant
| ^^^ method not found in `fn() -> Foo {Foo::Tup}`
|
help: call the constructor
help: use parentheses to instantiate this tuple variant
|
LL | (thing.bar)().foo();
| + +++

View file

@ -2,14 +2,12 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
--> $DIR/tuple-field.rs:12:15
|
LL | thing.bar.0;
| --------- ^
| |
| this is the constructor of a struct
| ^
|
help: call the constructor
help: use parentheses to instantiate this tuple struct
|
LL | (thing.bar)(_, _).0;
| + +++++++
LL | (thing.bar)(/* char */, /* u16 */).0;
| + ++++++++++++++++++++++++
error: aborting due to previous error

View file

@ -2,14 +2,7 @@ error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}`
--> $DIR/tuple-method.rs:12:15
|
LL | thing.bar.foo();
| --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
| |
| this is the constructor of a struct
|
help: call the constructor
|
LL | (thing.bar)(_, _).foo();
| + +++++++
| ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
error: aborting due to previous error

View file

@ -2,27 +2,13 @@ error[E0599]: no method named `nonexistent_method` found for fn item `fn(_) -> O
--> $DIR/issue-96738.rs:2:10
|
LL | Some.nonexistent_method();
| ---- ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
| |
| this is the constructor of an enum variant
|
help: call the constructor
|
LL | (Some)(_).nonexistent_method();
| + ++++
| ^^^^^^^^^^^^^^^^^^ method not found in `fn(_) -> Option<_> {Option::<_>::Some}`
error[E0609]: no field `nonexistent_field` on type `fn(_) -> Option<_> {Option::<_>::Some}`
--> $DIR/issue-96738.rs:3:10
|
LL | Some.nonexistent_field;
| ---- ^^^^^^^^^^^^^^^^^
| |
| this is the constructor of an enum variant
|
help: call the constructor
|
LL | (Some)(_).nonexistent_field;
| + ++++
| ^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -2,9 +2,7 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl
--> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
|
LL | mut_.call((0, ));
| ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
| |
| this is a function, perhaps you wish to call it
| ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:29]`
error: aborting due to previous error