Auto merge of #117683 - estebank:priv-builder-sugg, r=cjgillot
When encountering struct fn call literal with private fields, suggest all builders When encountering code like `Box(42)`, suggest `Box::new(42)` and *all* other associated functions that return `-> Box<T>`. Add a way to give pre-sorted suggestions.
This commit is contained in:
commit
9a66e4471f
@ -777,17 +777,15 @@ impl Diagnostic {
|
||||
applicability: Applicability,
|
||||
style: SuggestionStyle,
|
||||
) -> &mut Self {
|
||||
let mut suggestions: Vec<_> = suggestions.into_iter().collect();
|
||||
suggestions.sort();
|
||||
|
||||
debug_assert!(
|
||||
!(sp.is_empty() && suggestions.iter().any(|suggestion| suggestion.is_empty())),
|
||||
"Span must not be empty and have no suggestion"
|
||||
);
|
||||
|
||||
let substitutions = suggestions
|
||||
.into_iter()
|
||||
.map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
|
||||
.map(|snippet| {
|
||||
debug_assert!(
|
||||
!(sp.is_empty() && snippet.is_empty()),
|
||||
"Span must not be empty and have no suggestion"
|
||||
);
|
||||
Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] }
|
||||
})
|
||||
.collect();
|
||||
self.push_suggestion(CodeSuggestion {
|
||||
substitutions,
|
||||
|
@ -945,7 +945,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
match (types, traits) {
|
||||
let mut types = types.to_vec();
|
||||
types.sort();
|
||||
let mut traits = traits.to_vec();
|
||||
traits.sort();
|
||||
match (&types[..], &traits[..]) {
|
||||
([], []) => {
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
|
@ -1897,7 +1897,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.collect();
|
||||
|
||||
if !private_fields.is_empty() {
|
||||
self.report_private_fields(adt_ty, span, private_fields, ast_fields);
|
||||
self.report_private_fields(adt_ty, span, expr.span, private_fields, ast_fields);
|
||||
} else {
|
||||
self.report_missing_fields(
|
||||
adt_ty,
|
||||
@ -2056,6 +2056,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
&self,
|
||||
adt_ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
expr_span: Span,
|
||||
private_fields: Vec<&ty::FieldDef>,
|
||||
used_fields: &'tcx [hir::ExprField<'tcx>],
|
||||
) {
|
||||
@ -2100,6 +2101,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
were = pluralize!("was", remaining_private_fields_len),
|
||||
));
|
||||
}
|
||||
|
||||
if let ty::Adt(def, _) = adt_ty.kind() {
|
||||
let def_id = def.did();
|
||||
let mut items = self
|
||||
.tcx
|
||||
.inherent_impls(def_id)
|
||||
.iter()
|
||||
.flat_map(|i| self.tcx.associated_items(i).in_definition_order())
|
||||
// Only assoc fn with no receivers.
|
||||
.filter(|item| {
|
||||
matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter
|
||||
})
|
||||
.filter_map(|item| {
|
||||
// Only assoc fns that return `Self`
|
||||
let fn_sig = self.tcx.fn_sig(item.def_id).skip_binder();
|
||||
let ret_ty = fn_sig.output();
|
||||
let ret_ty =
|
||||
self.tcx.normalize_erasing_late_bound_regions(self.param_env, ret_ty);
|
||||
if !self.can_eq(self.param_env, ret_ty, adt_ty) {
|
||||
return None;
|
||||
}
|
||||
let input_len = fn_sig.inputs().skip_binder().len();
|
||||
let order = !item.name.as_str().starts_with("new");
|
||||
Some((order, item.name, input_len))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
items.sort_by_key(|(order, _, _)| *order);
|
||||
let suggestion = |name, args| {
|
||||
format!(
|
||||
"::{name}({})",
|
||||
std::iter::repeat("_").take(args).collect::<Vec<_>>().join(", ")
|
||||
)
|
||||
};
|
||||
match &items[..] {
|
||||
[] => {}
|
||||
[(_, name, args)] => {
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi().with_hi(expr_span.hi()),
|
||||
format!("you might have meant to use the `{name}` associated function"),
|
||||
suggestion(name, *args),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
err.span_suggestions(
|
||||
span.shrink_to_hi().with_hi(expr_span.hi()),
|
||||
"you might have meant to use an associated function to build this type",
|
||||
items
|
||||
.iter()
|
||||
.map(|(_, name, args)| suggestion(name, *args))
|
||||
.collect::<Vec<String>>(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(default_trait) = self.tcx.get_diagnostic_item(sym::Default)
|
||||
&& self
|
||||
.infcx
|
||||
.type_implements_trait(default_trait, [adt_ty], self.param_env)
|
||||
.may_apply()
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"consider using the `Default` trait",
|
||||
vec![
|
||||
(span.shrink_to_lo(), "<".to_string()),
|
||||
(
|
||||
span.shrink_to_hi().with_hi(expr_span.hi()),
|
||||
" as std::default::Default>::default()".to_string(),
|
||||
),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
err.emit();
|
||||
}
|
||||
|
||||
@ -2703,7 +2779,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.get_field_candidates_considering_privacy(span, ty, mod_id, id)
|
||||
{
|
||||
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
|
||||
let candidate_fields: Vec<_> = found_fields
|
||||
let mut candidate_fields: Vec<_> = found_fields
|
||||
.into_iter()
|
||||
.filter_map(|candidate_field| {
|
||||
self.check_for_nested_field_satisfying(
|
||||
@ -2724,6 +2800,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.collect::<String>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
candidate_fields.sort();
|
||||
|
||||
let len = candidate_fields.len();
|
||||
if len > 0 {
|
||||
|
@ -1426,6 +1426,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
if !suggs.is_empty()
|
||||
&& let Some(span) = sugg_span
|
||||
{
|
||||
suggs.sort();
|
||||
err.span_suggestions(
|
||||
span.with_hi(item_name.span.lo()),
|
||||
"use fully-qualified syntax to disambiguate",
|
||||
@ -2000,8 +2001,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.tcx.get_diagnostic_item(sym::Borrow),
|
||||
self.tcx.get_diagnostic_item(sym::BorrowMut),
|
||||
];
|
||||
let candidate_fields: Vec<_> = fields
|
||||
.iter()
|
||||
let mut candidate_fields: Vec<_> = fields
|
||||
.into_iter()
|
||||
.filter_map(|candidate_field| {
|
||||
self.check_for_nested_field_satisfying(
|
||||
span,
|
||||
@ -2035,6 +2036,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
.join(".")
|
||||
})
|
||||
.collect();
|
||||
candidate_fields.sort();
|
||||
|
||||
let len = candidate_fields.len();
|
||||
if len > 0 {
|
||||
@ -2567,13 +2569,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.tcx.item_name(*trait_did),
|
||||
)
|
||||
});
|
||||
let mut sugg: Vec<_> = path_strings.chain(glob_path_strings).collect();
|
||||
sugg.sort();
|
||||
|
||||
err.span_suggestions(
|
||||
span,
|
||||
msg,
|
||||
path_strings.chain(glob_path_strings),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
err.span_suggestions(span, msg, sugg, Applicability::MaybeIncorrect);
|
||||
}
|
||||
|
||||
fn suggest_valid_traits(
|
||||
|
@ -186,6 +186,7 @@ fn emit_malformed_attribute(
|
||||
msg.push_str(&format!("`{code}`"));
|
||||
suggestions.push(code);
|
||||
}
|
||||
suggestions.sort();
|
||||
if should_warn(name) {
|
||||
sess.buffer_lint(&ILL_FORMED_ATTRIBUTE_INPUT, span, ast::CRATE_NODE_ID, msg);
|
||||
} else {
|
||||
|
@ -2608,6 +2608,7 @@ fn show_candidates(
|
||||
path_strings.extend(core_path_strings);
|
||||
path_strings.dedup_by(|a, b| a.0 == b.0);
|
||||
}
|
||||
accessible_path_strings.sort();
|
||||
|
||||
if !accessible_path_strings.is_empty() {
|
||||
let (determiner, kind, name, through) =
|
||||
|
@ -1,11 +1,13 @@
|
||||
use crate::diagnostics::{ImportSuggestion, LabelSuggestion, TypoSuggestion};
|
||||
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
|
||||
use crate::late::{LifetimeBinderKind, LifetimeRes, LifetimeRibKind, LifetimeUseSet};
|
||||
use crate::ty::fast_reject::SimplifiedType;
|
||||
use crate::{errors, path_names_to_string};
|
||||
use crate::{Module, ModuleKind, ModuleOrUniformRoot};
|
||||
use crate::{PathResult, PathSource, Segment};
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt};
|
||||
use rustc_ast::{
|
||||
self as ast, AssocItemKind, Expr, ExprKind, GenericParam, GenericParamKind, Item, ItemKind,
|
||||
@ -15,7 +17,7 @@ use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{
|
||||
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
|
||||
MultiSpan,
|
||||
MultiSpan, SuggestionStyle,
|
||||
};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
|
||||
@ -29,6 +31,8 @@ use rustc_span::hygiene::MacroKind;
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
|
||||
use rustc_middle::ty;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::iter;
|
||||
use std::ops::Deref;
|
||||
@ -1331,7 +1335,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
let ns = source.namespace();
|
||||
let is_expected = &|res| source.is_expected(res);
|
||||
|
||||
let path_sep = |err: &mut Diagnostic, expr: &Expr, kind: DefKind| {
|
||||
let path_sep = |this: &mut Self, err: &mut Diagnostic, expr: &Expr, kind: DefKind| {
|
||||
const MESSAGE: &str = "use the path separator to refer to an item";
|
||||
|
||||
let (lhs_span, rhs_span) = match &expr.kind {
|
||||
@ -1352,7 +1356,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
true
|
||||
} else if kind == DefKind::Struct
|
||||
&& let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span)
|
||||
&& let Ok(snippet) = self.r.tcx.sess.source_map().span_to_snippet(lhs_source_span)
|
||||
&& let Ok(snippet) = this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span)
|
||||
{
|
||||
// The LHS is a type that originates from a macro call.
|
||||
// We have to add angle brackets around it.
|
||||
@ -1387,13 +1391,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut bad_struct_syntax_suggestion = |def_id: DefId| {
|
||||
let (followed_by_brace, closing_brace) = self.followed_by_brace(span);
|
||||
let mut bad_struct_syntax_suggestion = |this: &mut Self, def_id: DefId| {
|
||||
let (followed_by_brace, closing_brace) = this.followed_by_brace(span);
|
||||
|
||||
match source {
|
||||
PathSource::Expr(Some(
|
||||
parent @ Expr { kind: ExprKind::Field(..) | ExprKind::MethodCall(..), .. },
|
||||
)) if path_sep(err, &parent, DefKind::Struct) => {}
|
||||
)) if path_sep(this, err, &parent, DefKind::Struct) => {}
|
||||
PathSource::Expr(
|
||||
None
|
||||
| Some(Expr {
|
||||
@ -1430,7 +1434,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
}
|
||||
PathSource::Expr(_) | PathSource::TupleStruct(..) | PathSource::Pat => {
|
||||
let span = find_span(&source, err);
|
||||
err.span_label(self.r.def_span(def_id), format!("`{path_str}` defined here"));
|
||||
err.span_label(this.r.def_span(def_id), format!("`{path_str}` defined here"));
|
||||
|
||||
let (tail, descr, applicability, old_fields) = match source {
|
||||
PathSource::Pat => ("", "pattern", Applicability::MachineApplicable, None),
|
||||
@ -1440,50 +1444,69 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
Applicability::MachineApplicable,
|
||||
Some(
|
||||
args.iter()
|
||||
.map(|a| self.r.tcx.sess.source_map().span_to_snippet(*a).ok())
|
||||
.map(|a| this.r.tcx.sess.source_map().span_to_snippet(*a).ok())
|
||||
.collect::<Vec<Option<String>>>(),
|
||||
),
|
||||
),
|
||||
_ => (": val", "literal", Applicability::HasPlaceholders, None),
|
||||
};
|
||||
let field_ids = self.r.field_def_ids(def_id);
|
||||
let (fields, applicability) = match field_ids {
|
||||
Some(field_ids) => {
|
||||
let fields = field_ids.iter().map(|&id| self.r.tcx.item_name(id));
|
||||
|
||||
let fields = if let Some(old_fields) = old_fields {
|
||||
fields
|
||||
.enumerate()
|
||||
.map(|(idx, new)| (new, old_fields.get(idx)))
|
||||
.map(|(new, old)| {
|
||||
let new = new.to_ident_string();
|
||||
if let Some(Some(old)) = old
|
||||
&& new != *old
|
||||
{
|
||||
format!("{new}: {old}")
|
||||
} else {
|
||||
new
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
} else {
|
||||
fields.map(|f| format!("{f}{tail}")).collect::<Vec<String>>()
|
||||
};
|
||||
if !this.has_private_fields(def_id) {
|
||||
// If the fields of the type are private, we shouldn't be suggesting using
|
||||
// the struct literal syntax at all, as that will cause a subsequent error.
|
||||
let field_ids = this.r.field_def_ids(def_id);
|
||||
let (fields, applicability) = match field_ids {
|
||||
Some(field_ids) => {
|
||||
let fields = field_ids.iter().map(|&id| this.r.tcx.item_name(id));
|
||||
|
||||
(fields.join(", "), applicability)
|
||||
}
|
||||
None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
|
||||
};
|
||||
let pad = match field_ids {
|
||||
Some(field_ids) if field_ids.is_empty() => "",
|
||||
_ => " ",
|
||||
};
|
||||
err.span_suggestion(
|
||||
span,
|
||||
format!("use struct {descr} syntax instead"),
|
||||
format!("{path_str} {{{pad}{fields}{pad}}}"),
|
||||
applicability,
|
||||
);
|
||||
let fields = if let Some(old_fields) = old_fields {
|
||||
fields
|
||||
.enumerate()
|
||||
.map(|(idx, new)| (new, old_fields.get(idx)))
|
||||
.map(|(new, old)| {
|
||||
let new = new.to_ident_string();
|
||||
if let Some(Some(old)) = old
|
||||
&& new != *old
|
||||
{
|
||||
format!("{new}: {old}")
|
||||
} else {
|
||||
new
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
} else {
|
||||
fields.map(|f| format!("{f}{tail}")).collect::<Vec<String>>()
|
||||
};
|
||||
|
||||
(fields.join(", "), applicability)
|
||||
}
|
||||
None => ("/* fields */".to_string(), Applicability::HasPlaceholders),
|
||||
};
|
||||
let pad = match field_ids {
|
||||
Some(field_ids) if field_ids.is_empty() => "",
|
||||
_ => " ",
|
||||
};
|
||||
err.span_suggestion(
|
||||
span,
|
||||
format!("use struct {descr} syntax instead"),
|
||||
format!("{path_str} {{{pad}{fields}{pad}}}"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
if let PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, ref args),
|
||||
span: call_span,
|
||||
..
|
||||
})) = source
|
||||
{
|
||||
this.suggest_alternative_construction_methods(
|
||||
def_id,
|
||||
err,
|
||||
path.span,
|
||||
*call_span,
|
||||
&args[..],
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
err.span_label(span, fallback_label.to_string());
|
||||
@ -1533,7 +1556,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
Res::Def(kind @ (DefKind::Mod | DefKind::Trait), _),
|
||||
PathSource::Expr(Some(parent)),
|
||||
) => {
|
||||
if !path_sep(err, &parent, kind) {
|
||||
if !path_sep(self, err, &parent, kind) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1567,13 +1590,13 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
let (ctor_def, ctor_vis, fields) = if let Some(struct_ctor) = struct_ctor {
|
||||
if let PathSource::Expr(Some(parent)) = source {
|
||||
if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind {
|
||||
bad_struct_syntax_suggestion(def_id);
|
||||
bad_struct_syntax_suggestion(self, def_id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
struct_ctor
|
||||
} else {
|
||||
bad_struct_syntax_suggestion(def_id);
|
||||
bad_struct_syntax_suggestion(self, def_id);
|
||||
return true;
|
||||
};
|
||||
|
||||
@ -1593,30 +1616,21 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
Some(Vec::from(pattern_spans))
|
||||
}
|
||||
// e.g. `let _ = Enum::TupleVariant(field1, field2);`
|
||||
_ if source.is_call() => {
|
||||
PathSource::Expr(Some(Expr {
|
||||
kind: ExprKind::Call(path, ref args),
|
||||
span: call_span,
|
||||
..
|
||||
})) => {
|
||||
err.set_primary_message(
|
||||
"cannot initialize a tuple struct which contains private fields",
|
||||
);
|
||||
if !def_id.is_local()
|
||||
&& self
|
||||
.r
|
||||
.tcx
|
||||
.inherent_impls(def_id)
|
||||
.iter()
|
||||
.flat_map(|impl_def_id| {
|
||||
self.r.tcx.provided_trait_methods(*impl_def_id)
|
||||
})
|
||||
.any(|assoc| !assoc.fn_has_self_parameter && assoc.name == sym::new)
|
||||
{
|
||||
// FIXME: look for associated functions with Self return type,
|
||||
// instead of relying only on the name and lack of self receiver.
|
||||
err.span_suggestion_verbose(
|
||||
span.shrink_to_hi(),
|
||||
"you might have meant to use the `new` associated function",
|
||||
"::new".to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
self.suggest_alternative_construction_methods(
|
||||
def_id,
|
||||
err,
|
||||
path.span,
|
||||
*call_span,
|
||||
&args[..],
|
||||
);
|
||||
// Use spans of the tuple struct definition.
|
||||
self.r.field_def_ids(def_id).map(|field_ids| {
|
||||
field_ids
|
||||
@ -1663,7 +1677,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
err.span_label(span, "constructor is not visible here due to private fields");
|
||||
}
|
||||
(Res::Def(DefKind::Union | DefKind::Variant, def_id), _) if ns == ValueNS => {
|
||||
bad_struct_syntax_suggestion(def_id);
|
||||
bad_struct_syntax_suggestion(self, def_id);
|
||||
}
|
||||
(Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id), _) if ns == ValueNS => {
|
||||
match source {
|
||||
@ -1709,6 +1723,161 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
true
|
||||
}
|
||||
|
||||
fn suggest_alternative_construction_methods(
|
||||
&mut self,
|
||||
def_id: DefId,
|
||||
err: &mut Diagnostic,
|
||||
path_span: Span,
|
||||
call_span: Span,
|
||||
args: &[P<Expr>],
|
||||
) {
|
||||
if def_id.is_local() {
|
||||
// Doing analysis on local `DefId`s would cause infinite recursion.
|
||||
return;
|
||||
}
|
||||
// Look at all the associated functions without receivers in the type's
|
||||
// inherent impls to look for builders that return `Self`
|
||||
let mut items = self
|
||||
.r
|
||||
.tcx
|
||||
.inherent_impls(def_id)
|
||||
.iter()
|
||||
.flat_map(|i| self.r.tcx.associated_items(i).in_definition_order())
|
||||
// Only assoc fn with no receivers.
|
||||
.filter(|item| matches!(item.kind, ty::AssocKind::Fn) && !item.fn_has_self_parameter)
|
||||
.filter_map(|item| {
|
||||
// Only assoc fns that return `Self`
|
||||
let fn_sig = self.r.tcx.fn_sig(item.def_id).skip_binder();
|
||||
let ret_ty = fn_sig.output();
|
||||
let ret_ty = self
|
||||
.r
|
||||
.tcx
|
||||
.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), ret_ty);
|
||||
let ty::Adt(def, _args) = ret_ty.kind() else {
|
||||
return None;
|
||||
};
|
||||
let input_len = fn_sig.inputs().skip_binder().len();
|
||||
if def.did() != def_id {
|
||||
return None;
|
||||
}
|
||||
let order = !item.name.as_str().starts_with("new");
|
||||
Some((order, item.name, input_len))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
items.sort_by_key(|(order, _, _)| *order);
|
||||
let suggestion = |name, args| {
|
||||
format!(
|
||||
"::{name}({})",
|
||||
std::iter::repeat("_").take(args).collect::<Vec<_>>().join(", ")
|
||||
)
|
||||
};
|
||||
match &items[..] {
|
||||
[] => {}
|
||||
[(_, name, len)] if *len == args.len() => {
|
||||
err.span_suggestion_verbose(
|
||||
path_span.shrink_to_hi(),
|
||||
format!("you might have meant to use the `{name}` associated function",),
|
||||
format!("::{name}"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
[(_, name, len)] => {
|
||||
err.span_suggestion_verbose(
|
||||
path_span.shrink_to_hi().with_hi(call_span.hi()),
|
||||
format!("you might have meant to use the `{name}` associated function",),
|
||||
suggestion(name, *len),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
err.span_suggestions_with_style(
|
||||
path_span.shrink_to_hi().with_hi(call_span.hi()),
|
||||
"you might have meant to use an associated function to build this type",
|
||||
items
|
||||
.iter()
|
||||
.map(|(_, name, len)| suggestion(name, *len))
|
||||
.collect::<Vec<String>>(),
|
||||
Applicability::MaybeIncorrect,
|
||||
SuggestionStyle::ShowAlways,
|
||||
);
|
||||
}
|
||||
}
|
||||
// We'd ideally use `type_implements_trait` but don't have access to
|
||||
// the trait solver here. We can't use `get_diagnostic_item` or
|
||||
// `all_traits` in resolve either. So instead we abuse the import
|
||||
// suggestion machinery to get `std::default::Default` and perform some
|
||||
// checks to confirm that we got *only* that trait. We then see if the
|
||||
// Adt we have has a direct implementation of `Default`. If so, we
|
||||
// provide a structured suggestion.
|
||||
let default_trait = self
|
||||
.r
|
||||
.lookup_import_candidates(
|
||||
Ident::with_dummy_span(sym::Default),
|
||||
Namespace::TypeNS,
|
||||
&self.parent_scope,
|
||||
&|res: Res| matches!(res, Res::Def(DefKind::Trait, _)),
|
||||
)
|
||||
.iter()
|
||||
.filter_map(|candidate| candidate.did)
|
||||
.filter(|did| {
|
||||
self.r
|
||||
.tcx
|
||||
.get_attrs(*did, sym::rustc_diagnostic_item)
|
||||
.any(|attr| attr.value_str() == Some(sym::Default))
|
||||
})
|
||||
.next();
|
||||
let Some(default_trait) = default_trait else {
|
||||
return;
|
||||
};
|
||||
if self
|
||||
.r
|
||||
.extern_crate_map
|
||||
.iter()
|
||||
// FIXME: This doesn't include impls like `impl Default for String`.
|
||||
.flat_map(|(_, crate_)| self.r.tcx.implementations_of_trait((*crate_, default_trait)))
|
||||
.filter_map(|(_, simplified_self_ty)| *simplified_self_ty)
|
||||
.filter_map(|simplified_self_ty| match simplified_self_ty {
|
||||
SimplifiedType::Adt(did) => Some(did),
|
||||
_ => None,
|
||||
})
|
||||
.any(|did| did == def_id)
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"consider using the `Default` trait",
|
||||
vec![
|
||||
(path_span.shrink_to_lo(), "<".to_string()),
|
||||
(
|
||||
path_span.shrink_to_hi().with_hi(call_span.hi()),
|
||||
" as std::default::Default>::default()".to_string(),
|
||||
),
|
||||
],
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_private_fields(&self, def_id: DefId) -> bool {
|
||||
let fields = match def_id.as_local() {
|
||||
Some(def_id) => self.r.struct_constructors.get(&def_id).cloned().map(|(_, _, f)| f),
|
||||
None => Some(
|
||||
self.r
|
||||
.tcx
|
||||
.associated_item_def_ids(def_id)
|
||||
.iter()
|
||||
.map(|field_id| self.r.tcx.visibility(field_id))
|
||||
.collect(),
|
||||
),
|
||||
};
|
||||
|
||||
fields.map_or(false, |fields| {
|
||||
fields
|
||||
.iter()
|
||||
.filter(|vis| !self.r.is_accessible_from(**vis, self.parent_scope.module))
|
||||
.next()
|
||||
.is_some()
|
||||
})
|
||||
}
|
||||
|
||||
/// Given the target `ident` and `kind`, search for the similarly named associated item
|
||||
/// in `self.current_trait_ref`.
|
||||
pub(crate) fn find_similarly_named_assoc_item(
|
||||
@ -2089,11 +2258,12 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
if suggest_only_tuple_variants {
|
||||
// Suggest only tuple variants regardless of whether they have fields and do not
|
||||
// suggest path with added parentheses.
|
||||
let suggestable_variants = variants
|
||||
let mut suggestable_variants = variants
|
||||
.iter()
|
||||
.filter(|(.., kind)| *kind == CtorKind::Fn)
|
||||
.map(|(variant, ..)| path_names_to_string(variant))
|
||||
.collect::<Vec<_>>();
|
||||
suggestable_variants.sort();
|
||||
|
||||
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
|
||||
|
||||
@ -2144,7 +2314,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let suggestable_variants = variants
|
||||
let mut suggestable_variants = variants
|
||||
.iter()
|
||||
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
|
||||
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
||||
@ -2153,6 +2323,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
CtorKind::Fn => format!("({variant}())"),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
suggestable_variants.sort();
|
||||
let no_suggestable_variant = suggestable_variants.is_empty();
|
||||
|
||||
if !no_suggestable_variant {
|
||||
@ -2170,7 +2341,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
let suggestable_variants_with_placeholders = variants
|
||||
let mut suggestable_variants_with_placeholders = variants
|
||||
.iter()
|
||||
.filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind))
|
||||
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
||||
@ -2179,6 +2350,7 @@ impl<'a: 'ast, 'ast, 'tcx> LateResolutionVisitor<'a, '_, 'ast, 'tcx> {
|
||||
_ => None,
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
suggestable_variants_with_placeholders.sort();
|
||||
|
||||
if !suggestable_variants_with_placeholders.is_empty() {
|
||||
let msg =
|
||||
|
@ -424,8 +424,9 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
|
||||
improvements.push(suggestion);
|
||||
}
|
||||
}
|
||||
let nonminimal_bool_lint = |suggestions: Vec<_>| {
|
||||
let nonminimal_bool_lint = |mut suggestions: Vec<_>| {
|
||||
if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow {
|
||||
suggestions.sort();
|
||||
span_lint_hir_and_then(
|
||||
self.cx,
|
||||
NONMINIMAL_BOOL,
|
||||
|
@ -25,8 +25,8 @@ LL | a!();
|
||||
| ---- in this macro invocation
|
||||
|
|
||||
= help: consider importing one of these items:
|
||||
std::mem
|
||||
core::mem
|
||||
std::mem
|
||||
= note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error[E0433]: failed to resolve: use of undeclared crate or module `my_core`
|
||||
|
@ -6,10 +6,10 @@ LL | fn foo(_: *()) {
|
||||
|
|
||||
help: add `mut` or `const` here
|
||||
|
|
||||
LL | fn foo(_: *const ()) {
|
||||
| +++++
|
||||
LL | fn foo(_: *mut ()) {
|
||||
| +++
|
||||
LL | fn foo(_: *const ()) {
|
||||
| +++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -6,10 +6,10 @@ LL | let dptr: **const i32 = &ptr;
|
||||
|
|
||||
help: add `mut` or `const` here
|
||||
|
|
||||
LL | let dptr: *const *const i32 = &ptr;
|
||||
| +++++
|
||||
LL | let dptr: *mut *const i32 = &ptr;
|
||||
| +++
|
||||
LL | let dptr: *const *const i32 = &ptr;
|
||||
| +++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
struct U <T> {
|
||||
wtf: Option<Box<U<T>>>,
|
||||
x: T,
|
||||
}
|
||||
fn main() {
|
||||
U {
|
||||
wtf: Some(Box::new(U { //~ ERROR cannot initialize a tuple struct which contains private fields
|
||||
wtf: None,
|
||||
x: (),
|
||||
})),
|
||||
x: ()
|
||||
};
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
// run-rustfix
|
||||
#![allow(dead_code)]
|
||||
struct U <T> {
|
||||
wtf: Option<Box<U<T>>>,
|
||||
@ -12,4 +11,9 @@ fn main() {
|
||||
})),
|
||||
x: ()
|
||||
};
|
||||
let _ = std::collections::HashMap();
|
||||
//~^ ERROR expected function, tuple struct or tuple variant, found struct `std::collections::HashMap`
|
||||
let _ = std::collections::HashMap {};
|
||||
//~^ ERROR cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields
|
||||
let _ = Box {}; //~ ERROR cannot construct `Box<_, _>` with struct literal syntax due to private fields
|
||||
}
|
||||
|
@ -1,5 +1,29 @@
|
||||
error[E0423]: expected function, tuple struct or tuple variant, found struct `std::collections::HashMap`
|
||||
--> $DIR/suggest-box-new.rs:14:13
|
||||
|
|
||||
LL | let _ = std::collections::HashMap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
--> $SRC_DIR/std/src/collections/hash/map.rs:LL:COL
|
||||
|
|
||||
= note: `std::collections::HashMap` defined here
|
||||
|
|
||||
help: you might have meant to use an associated function to build this type
|
||||
|
|
||||
LL | let _ = std::collections::HashMap::new();
|
||||
| ~~~~~~~
|
||||
LL | let _ = std::collections::HashMap::with_capacity(_);
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
LL | let _ = std::collections::HashMap::with_hasher(_);
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: consider using the `Default` trait
|
||||
|
|
||||
LL | let _ = <std::collections::HashMap as std::default::Default>::default();
|
||||
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0423]: cannot initialize a tuple struct which contains private fields
|
||||
--> $DIR/suggest-box-new.rs:9:19
|
||||
--> $DIR/suggest-box-new.rs:8:19
|
||||
|
|
||||
LL | wtf: Some(Box(U {
|
||||
| ^^^
|
||||
@ -10,11 +34,67 @@ note: constructor is not visible here due to private fields
|
||||
= note: private field
|
||||
|
|
||||
= note: private field
|
||||
help: you might have meant to use the `new` associated function
|
||||
help: you might have meant to use an associated function to build this type
|
||||
|
|
||||
LL | wtf: Some(Box::new(U {
|
||||
| +++++
|
||||
LL | wtf: Some(Box::new(_)),
|
||||
| ~~~~~~~~
|
||||
LL | wtf: Some(Box::new_uninit()),
|
||||
| ~~~~~~~~~~~~~~
|
||||
LL | wtf: Some(Box::new_zeroed()),
|
||||
| ~~~~~~~~~~~~~~
|
||||
LL | wtf: Some(Box::new_in(_, _)),
|
||||
| ~~~~~~~~~~~~~~
|
||||
and 10 other candidates
|
||||
help: consider using the `Default` trait
|
||||
|
|
||||
LL | wtf: Some(<Box as std::default::Default>::default()),
|
||||
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to previous error
|
||||
error: cannot construct `HashMap<_, _, _>` with struct literal syntax due to private fields
|
||||
--> $DIR/suggest-box-new.rs:16:13
|
||||
|
|
||||
LL | let _ = std::collections::HashMap {};
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: ... and other private field `base` that was not provided
|
||||
help: you might have meant to use an associated function to build this type
|
||||
|
|
||||
LL | let _ = std::collections::HashMap::new();
|
||||
| ~~~~~~~
|
||||
LL | let _ = std::collections::HashMap::with_capacity(_);
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
LL | let _ = std::collections::HashMap::with_hasher(_);
|
||||
| ~~~~~~~~~~~~~~~~
|
||||
LL | let _ = std::collections::HashMap::with_capacity_and_hasher(_, _);
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: consider using the `Default` trait
|
||||
|
|
||||
LL | let _ = <std::collections::HashMap as std::default::Default>::default();
|
||||
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: cannot construct `Box<_, _>` with struct literal syntax due to private fields
|
||||
--> $DIR/suggest-box-new.rs:18:13
|
||||
|
|
||||
LL | let _ = Box {};
|
||||
| ^^^
|
||||
|
|
||||
= note: ... and other private fields `0` and `1` that were not provided
|
||||
help: you might have meant to use an associated function to build this type
|
||||
|
|
||||
LL | let _ = Box::new(_);
|
||||
| ~~~~~~~~
|
||||
LL | let _ = Box::new_uninit();
|
||||
| ~~~~~~~~~~~~~~
|
||||
LL | let _ = Box::new_zeroed();
|
||||
| ~~~~~~~~~~~~~~
|
||||
LL | let _ = Box::new_in(_, _);
|
||||
| ~~~~~~~~~~~~~~
|
||||
and 10 other candidates
|
||||
help: consider using the `Default` trait
|
||||
|
|
||||
LL | let _ = <Box as std::default::Default>::default();
|
||||
| + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0423`.
|
||||
|
@ -4,8 +4,8 @@ error[E0433]: failed to resolve: use of undeclared type `TryFrom`
|
||||
LL | let _i: i16 = TryFrom::try_from(0_i32).unwrap();
|
||||
| ^^^^^^^ use of undeclared type `TryFrom`
|
||||
|
|
||||
= note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021
|
||||
= note: 'core::convert::TryFrom' is included in the prelude starting in Edition 2021
|
||||
= note: 'std::convert::TryFrom' is included in the prelude starting in Edition 2021
|
||||
help: consider importing one of these items
|
||||
|
|
||||
LL + use core::convert::TryFrom;
|
||||
@ -19,8 +19,8 @@ error[E0433]: failed to resolve: use of undeclared type `TryInto`
|
||||
LL | let _i: i16 = TryInto::try_into(0_i32).unwrap();
|
||||
| ^^^^^^^ use of undeclared type `TryInto`
|
||||
|
|
||||
= note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021
|
||||
= note: 'core::convert::TryInto' is included in the prelude starting in Edition 2021
|
||||
= note: 'std::convert::TryInto' is included in the prelude starting in Edition 2021
|
||||
help: consider importing one of these items
|
||||
|
|
||||
LL + use core::convert::TryInto;
|
||||
@ -34,8 +34,8 @@ error[E0433]: failed to resolve: use of undeclared type `FromIterator`
|
||||
LL | let _v: Vec<_> = FromIterator::from_iter(&[1]);
|
||||
| ^^^^^^^^^^^^ use of undeclared type `FromIterator`
|
||||
|
|
||||
= note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021
|
||||
= note: 'core::iter::FromIterator' is included in the prelude starting in Edition 2021
|
||||
= note: 'std::iter::FromIterator' is included in the prelude starting in Edition 2021
|
||||
help: a trait with a similar name exists
|
||||
|
|
||||
LL | let _v: Vec<_> = IntoIterator::from_iter(&[1]);
|
||||
|
@ -18,6 +18,11 @@ pub struct TupleStruct(pub usize, pub &'static str);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructWithFields {
|
||||
pub foo: isize,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StructWithPrivFields {
|
||||
foo: isize,
|
||||
}
|
||||
|
||||
|
@ -8,5 +8,7 @@ extern crate xcrate_unit_struct;
|
||||
fn main() {
|
||||
let _ = xcrate_unit_struct::StructWithFields;
|
||||
//~^ ERROR expected value, found struct `xcrate_unit_struct::StructWithFields`
|
||||
let _ = xcrate_unit_struct::StructWithPrivFields;
|
||||
//~^ ERROR expected value, found struct `xcrate_unit_struct::StructWithPrivFields`
|
||||
let _ = xcrate_unit_struct::Struct;
|
||||
}
|
||||
|
@ -9,6 +9,17 @@ LL | let _ = xcrate_unit_struct::StructWithFields;
|
||||
LL | pub struct StructWithFields {
|
||||
| --------------------------- `xcrate_unit_struct::StructWithFields` defined here
|
||||
|
||||
error: aborting due to previous error
|
||||
error[E0423]: expected value, found struct `xcrate_unit_struct::StructWithPrivFields`
|
||||
--> $DIR/xcrate-unit-struct.rs:11:13
|
||||
|
|
||||
LL | let _ = xcrate_unit_struct::StructWithPrivFields;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
::: $DIR/auxiliary/xcrate_unit_struct.rs:25:1
|
||||
|
|
||||
LL | pub struct StructWithPrivFields {
|
||||
| ------------------------------- `xcrate_unit_struct::StructWithPrivFields` defined here
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0423`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user