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:
bors 2023-11-19 20:58:16 +00:00
commit 9a66e4471f
18 changed files with 463 additions and 123 deletions

View File

@ -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,

View File

@ -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,

View File

@ -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 {

View File

@ -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(

View File

@ -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 {

View File

@ -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) =

View File

@ -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 =

View File

@ -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,

View File

@ -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`

View File

@ -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

View File

@ -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

View File

@ -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: ()
};
}

View File

@ -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
}

View File

@ -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`.

View File

@ -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]);

View File

@ -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,
}

View File

@ -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;
}

View File

@ -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`.