1
Fork 0

Emits E0599 when meeting MyTrait::missing_method

This commit is contained in:
mu001999 2023-05-17 16:59:39 +08:00
parent bc888958c9
commit db64512422
11 changed files with 147 additions and 47 deletions

View file

@ -478,6 +478,7 @@ pub enum StashKey {
/// FRU syntax /// FRU syntax
MaybeFruTypo, MaybeFruTypo,
CallAssocMethod, CallAssocMethod,
TraitMissingMethod,
} }
fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) { fn default_track_diagnostic(d: &mut Diagnostic, f: &mut dyn FnMut(&mut Diagnostic)) {

View file

@ -19,7 +19,7 @@ use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{ use rustc_errors::{
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError,
MultiSpan, MultiSpan, StashKey,
}; };
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
@ -38,7 +38,6 @@ use rustc_middle::ty::{self, Const, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}
use rustc_middle::ty::{DynKind, ToPredicate}; use rustc_middle::ty::{DynKind, ToPredicate};
use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS}; use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::edition::Edition;
use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, Span, DUMMY_SP}; use rustc_span::{sym, Span, DUMMY_SP};
use rustc_target::spec::abi; use rustc_target::spec::abi;
@ -3718,7 +3717,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
)); ));
} }
if self_ty.span.edition() >= Edition::Edition2021 { if self_ty.span.edition().rust_2021() {
let msg = "trait objects must include the `dyn` keyword"; let msg = "trait objects must include the `dyn` keyword";
let label = "add `dyn` keyword before this trait"; let label = "add `dyn` keyword before this trait";
let mut diag = let mut diag =
@ -3732,7 +3731,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
} }
// check if the impl trait that we are considering is a impl of a local trait // check if the impl trait that we are considering is a impl of a local trait
self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag); self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag);
diag.emit(); diag.stash(self_ty.span, StashKey::TraitMissingMethod);
} else { } else {
let msg = "trait objects without an explicit `dyn` are deprecated"; let msg = "trait objects without an explicit `dyn` are deprecated";
tcx.struct_span_lint_hir( tcx.struct_span_lint_hir(

View file

@ -79,3 +79,14 @@ hir_typeck_arg_mismatch_indeterminate = argument type mismatch was detected, but
hir_typeck_suggest_boxing_note = for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html hir_typeck_suggest_boxing_note = for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html
hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `Box::new` hir_typeck_suggest_boxing_when_appropriate = store this in the heap by calling `Box::new`
hir_typeck_no_associated_item = no {$item_kind} named `{$item_name}` found for {$ty_prefix} `{$ty_str}`{$trait_missing_method ->
[true] {""}
*[other] {" "}in the current scope
}
hir_typeck_candidate_trait_note = `{$trait_name}` defines an item `{$item_name}`{$action_or_ty ->
[NONE] {""}
[implement] , perhaps you need to implement it
*[other] , perhaps you need to restrict type parameter `{$action_or_ty}` with it
}

View file

@ -1,4 +1,6 @@
//! Errors emitted by `rustc_hir_typeck`. //! Errors emitted by `rustc_hir_typeck`.
use std::borrow::Cow;
use crate::fluent_generated as fluent; use crate::fluent_generated as fluent;
use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage}; use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, MultiSpan, SubdiagnosticMessage};
use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic};
@ -295,3 +297,25 @@ pub enum SuggestBoxing {
end: Span, end: Span,
}, },
} }
#[derive(Diagnostic)]
#[diag(hir_typeck_no_associated_item, code = "E0599")]
pub struct NoAssociatedItem {
#[primary_span]
pub span: Span,
pub item_kind: &'static str,
pub item_name: Ident,
pub ty_prefix: Cow<'static, str>,
pub ty_str: String,
pub trait_missing_method: bool,
}
#[derive(Subdiagnostic)]
#[note(hir_typeck_candidate_trait_note)]
pub struct CandidateTraitNote {
#[primary_span]
pub span: Span,
pub trait_name: String,
pub item_name: Ident,
pub action_or_ty: String,
}

View file

@ -1245,6 +1245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
error, error,
Some((rcvr, args)), Some((rcvr, args)),
expected, expected,
false,
) { ) {
err.emit(); err.emit();
} }

View file

@ -4,7 +4,7 @@ use crate::rvalue_scopes;
use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy}; use crate::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy, RawTy};
use rustc_data_structures::captures::Captures; use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan, StashKey};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -853,6 +853,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
let item_name = item_segment.ident; let item_name = item_segment.ident;
let result = self let result = self
.resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id) .resolve_fully_qualified_call(span, item_name, ty.normalized, qself.span, hir_id)
.and_then(|r| {
// lint bare trait if the method is found in the trait
if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) {
diag.emit();
}
Ok(r)
})
.or_else(|error| { .or_else(|error| {
let guar = self let guar = self
.tcx .tcx
@ -863,17 +870,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
_ => Err(guar), _ => Err(guar),
}; };
let trait_missing_method =
matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait();
// If we have a path like `MyTrait::missing_method`, then don't register // If we have a path like `MyTrait::missing_method`, then don't register
// a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise, // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise,
// register a WF obligation so that we can detect any additional // register a WF obligation so that we can detect any additional
// errors in the self type. // errors in the self type.
if !(matches!(error, method::MethodError::NoMatch(_)) && ty.normalized.is_trait()) { if !trait_missing_method {
self.register_wf_obligation( self.register_wf_obligation(
ty.raw.into(), ty.raw.into(),
qself.span, qself.span,
traits::WellFormed(None), traits::WellFormed(None),
); );
} }
// emit or cancel the diagnostic for bare traits
if span.edition().rust_2021() && let Some(mut diag) = self.tcx.sess.diagnostic().steal_diagnostic(qself.span, StashKey::TraitMissingMethod) {
if trait_missing_method {
// cancel the diag for bare traits when meeting `MyTrait::missing_method`
diag.cancel();
} else {
diag.emit();
}
}
if item_name.name != kw::Empty { if item_name.name != kw::Empty {
if let Some(mut e) = self.report_method_error( if let Some(mut e) = self.report_method_error(
span, span,
@ -883,10 +903,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
error, error,
None, None,
Expectation::NoExpectation, Expectation::NoExpectation,
trait_missing_method && span.edition().rust_2021(), // emits missing method for trait only after edition 2021
) { ) {
e.emit(); e.emit();
} }
} }
result result
}); });

View file

@ -2,6 +2,8 @@
//! found or is otherwise invalid. //! found or is otherwise invalid.
use crate::errors; use crate::errors;
use crate::errors::CandidateTraitNote;
use crate::errors::NoAssociatedItem;
use crate::Expectation; use crate::Expectation;
use crate::FnCtxt; use crate::FnCtxt;
use rustc_ast::ast::Mutability; use rustc_ast::ast::Mutability;
@ -38,6 +40,7 @@ use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _
use rustc_trait_selection::traits::{ use rustc_trait_selection::traits::{
FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, FulfillmentError, Obligation, ObligationCause, ObligationCauseCode,
}; };
use std::borrow::Cow;
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope}; use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
use super::{CandidateSource, MethodError, NoMatchData}; use super::{CandidateSource, MethodError, NoMatchData};
@ -112,6 +115,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
error: MethodError<'tcx>, error: MethodError<'tcx>,
args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
expected: Expectation<'tcx>, expected: Expectation<'tcx>,
trait_missing_method: bool,
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
// Avoid suggestions when we don't know what's going on. // Avoid suggestions when we don't know what's going on.
if rcvr_ty.references_error() { if rcvr_ty.references_error() {
@ -136,6 +140,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sugg_span, sugg_span,
&mut no_match_data, &mut no_match_data,
expected, expected,
trait_missing_method,
); );
} }
@ -278,6 +283,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
sugg_span: Span, sugg_span: Span,
no_match_data: &mut NoMatchData<'tcx>, no_match_data: &mut NoMatchData<'tcx>,
expected: Expectation<'tcx>, expected: Expectation<'tcx>,
trait_missing_method: bool,
) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> {
let mode = no_match_data.mode; let mode = no_match_data.mode;
let tcx = self.tcx; let tcx = self.tcx;
@ -323,7 +329,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span = item_name.span; span = item_name.span;
// Don't show generic arguments when the method can't be found in any implementation (#81576). // Don't show generic arguments when the method can't be found in any implementation (#81576).
let mut ty_str_reported = ty_str.clone(); let mut ty_str_reported = if trait_missing_method {
ty_str.strip_prefix("dyn ").expect("Failed to remove the prefix dyn").to_owned()
} else {
ty_str.clone()
};
if let ty::Adt(_, generics) = rcvr_ty.kind() { if let ty::Adt(_, generics) = rcvr_ty.kind() {
if generics.len() > 0 { if generics.len() > 0 {
let mut autoderef = self.autoderef(span, rcvr_ty); let mut autoderef = self.autoderef(span, rcvr_ty);
@ -355,25 +366,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
{ {
self.suggest_missing_writer(rcvr_ty, args) self.suggest_missing_writer(rcvr_ty, args)
} else { } else {
struct_span_err!( tcx.sess.create_err(NoAssociatedItem {
tcx.sess,
span, span,
E0599,
"no {} named `{}` found for {} `{}` in the current scope",
item_kind, item_kind,
item_name, item_name,
rcvr_ty.prefix_string(self.tcx), ty_prefix: if trait_missing_method {
ty_str_reported, // FIXME(mu001999) E0599 maybe not suitable here because it is for types
) Cow::from("trait")
} else {
rcvr_ty.prefix_string(self.tcx)
},
ty_str: ty_str_reported,
trait_missing_method,
})
}; };
if tcx.sess.source_map().is_multiline(sugg_span) { if tcx.sess.source_map().is_multiline(sugg_span) {
err.span_label(sugg_span.with_hi(span.lo()), ""); err.span_label(sugg_span.with_hi(span.lo()), "");
} }
let ty_str = if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 { let mut ty_str = if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 {
short_ty_str short_ty_str
} else { } else {
ty_str ty_str
}; };
if trait_missing_method {
ty_str =
ty_str.strip_prefix("dyn ").expect("Failed to remove the prefix dyn").to_owned();
}
if let Some(file) = ty_file { if let Some(file) = ty_file {
err.note(format!("the full type name has been written to '{}'", file.display(),)); err.note(format!("the full type name has been written to '{}'", file.display(),));
} }
@ -1067,6 +1086,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&static_candidates, &static_candidates,
unsatisfied_bounds, unsatisfied_bounds,
expected.only_has_type(self), expected.only_has_type(self),
trait_missing_method,
); );
} }
@ -2375,6 +2395,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
static_candidates: &[CandidateSource], static_candidates: &[CandidateSource],
unsatisfied_bounds: bool, unsatisfied_bounds: bool,
return_type: Option<Ty<'tcx>>, return_type: Option<Ty<'tcx>>,
trait_missing_method: bool,
) { ) {
let mut alt_rcvr_sugg = false; let mut alt_rcvr_sugg = false;
if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
@ -2598,11 +2619,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}, },
_ => None, _ => None,
}; };
err.help(if param_type.is_some() { if !trait_missing_method {
"items from traits can only be used if the type parameter is bounded by the trait" err.help(if param_type.is_some() {
} else { "items from traits can only be used if the type parameter is bounded by the trait"
"items from traits can only be used if the trait is implemented and in scope" } else {
}); "items from traits can only be used if the trait is implemented and in scope"
});
}
let candidates_len = candidates.len(); let candidates_len = candidates.len();
let message = |action| { let message = |action| {
format!( format!(
@ -2736,27 +2760,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(candidates, Vec::new()) (candidates, Vec::new())
}; };
let action = if let Some(param) = param_type {
format!("restrict type parameter `{}` with", param)
} else {
// FIXME: it might only need to be imported into scope, not implemented.
"implement".to_string()
};
match &potential_candidates[..] { match &potential_candidates[..] {
[] => {} [] => {}
[trait_info] if trait_info.def_id.is_local() => { [trait_info] if trait_info.def_id.is_local() => {
err.span_note( err.subdiagnostic(CandidateTraitNote {
self.tcx.def_span(trait_info.def_id), span: self.tcx.def_span(trait_info.def_id),
format!( trait_name: self.tcx.def_path_str(trait_info.def_id),
"`{}` defines an item `{}`, perhaps you need to {} it", item_name,
self.tcx.def_path_str(trait_info.def_id), action_or_ty: if trait_missing_method {
item_name, "NONE".to_string()
action } else {
), param_type.map_or_else(
); || "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
ToString::to_string,
)
},
});
} }
trait_infos => { trait_infos => {
let mut msg = message(action); let mut msg = message(param_type.map_or_else(
|| "implement".to_string(), // FIXME: it might only need to be imported into scope, not implemented.
|param| format!("restrict type parameter `{}` with", param),
));
for (i, trait_info) in trait_infos.iter().enumerate() { for (i, trait_info) in trait_infos.iter().enumerate() {
msg.push_str(&format!( msg.push_str(&format!(
"\ncandidate #{}: `{}`", "\ncandidate #{}: `{}`",

View file

@ -0,0 +1,11 @@
// edition: 2021
trait Has {
fn has() {}
}
trait HasNot {}
fn main() {
HasNot::has(); //~ ERROR
}

View file

@ -0,0 +1,15 @@
error[E0599]: no function or associated item named `has` found for trait `HasNot`
--> $DIR/issue-111312.rs:10:13
|
LL | HasNot::has();
| ^^^ function or associated item not found in `HasNot`
|
note: `Has` defines an item `has`
--> $DIR/issue-111312.rs:3:1
|
LL | trait Has {
| ^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0599`.

View file

@ -1,5 +1,4 @@
#[derive(Clone)] //~ trait objects must include the `dyn` keyword #[derive(Clone)] //~ trait objects must include the `dyn` keyword
//~| trait objects must include the `dyn` keyword
struct Foo; struct Foo;
trait Foo {} //~ the name `Foo` is defined multiple times trait Foo {} //~ the name `Foo` is defined multiple times
fn main() {} fn main() {}

View file

@ -1,5 +1,5 @@
error[E0428]: the name `Foo` is defined multiple times error[E0428]: the name `Foo` is defined multiple times
--> $DIR/issue-106072.rs:4:1 --> $DIR/issue-106072.rs:3:1
| |
LL | struct Foo; LL | struct Foo;
| ----------- previous definition of the type `Foo` here | ----------- previous definition of the type `Foo` here
@ -16,15 +16,7 @@ LL | #[derive(Clone)]
| |
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0782]: trait objects must include the `dyn` keyword error: aborting due to 2 previous errors
--> $DIR/issue-106072.rs:1:10
|
LL | #[derive(Clone)]
| ^^^^^
|
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 3 previous errors
Some errors have detailed explanations: E0428, E0782. Some errors have detailed explanations: E0428, E0782.
For more information about an error, try `rustc --explain E0428`. For more information about an error, try `rustc --explain E0428`.