diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 470f318eb33..0aaa8ae69e1 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -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, diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 8126b451181..3542fc7cd33 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -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, diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6c99044d0aa..d9b27fc1318 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -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::>(); + items.sort_by_key(|(order, _, _)| *order); + let suggestion = |name, args| { + format!( + "::{name}({})", + std::iter::repeat("_").take(args).collect::>().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::>(), + 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::>(); - 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::() }) .collect::>(); + candidate_fields.sort(); let len = candidate_fields.len(); if len > 0 { diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 6956778f974..db434636f59 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -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( diff --git a/compiler/rustc_parse/src/validate_attr.rs b/compiler/rustc_parse/src/validate_attr.rs index f7396598220..e6c46fc2528 100644 --- a/compiler/rustc_parse/src/validate_attr.rs +++ b/compiler/rustc_parse/src/validate_attr.rs @@ -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 { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 28e6fe9b4b7..84225bbe39b 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -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) = diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 6aecd3610c6..d62d7fdcae0 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -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::>>(), ), ), _ => (": 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::>() - } else { - fields.map(|f| format!("{f}{tail}")).collect::>() - }; + 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::>() + } else { + fields.map(|f| format!("{f}{tail}")).collect::>() + }; + + (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], + ) { + 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::>(); + items.sort_by_key(|(order, _, _)| *order); + let suggestion = |name, args| { + format!( + "::{name}({})", + std::iter::repeat("_").take(args).collect::>().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::>(), + 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::>(); + 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::>(); + 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::>(); + suggestable_variants_with_placeholders.sort(); if !suggestable_variants_with_placeholders.is_empty() { let msg = diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index d4f2e316890..2cb599964d2 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -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, diff --git a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr index 7ed15e89caa..78e6376bca2 100644 --- a/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr +++ b/tests/ui/hygiene/extern-prelude-from-opaque-fail-2018.stderr @@ -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` diff --git a/tests/ui/parser/bad-pointer-type.stderr b/tests/ui/parser/bad-pointer-type.stderr index b7225ca887d..e843c49886b 100644 --- a/tests/ui/parser/bad-pointer-type.stderr +++ b/tests/ui/parser/bad-pointer-type.stderr @@ -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 diff --git a/tests/ui/parser/double-pointer.stderr b/tests/ui/parser/double-pointer.stderr index 28037f93265..10aedbb92a1 100644 --- a/tests/ui/parser/double-pointer.stderr +++ b/tests/ui/parser/double-pointer.stderr @@ -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 diff --git a/tests/ui/privacy/suggest-box-new.fixed b/tests/ui/privacy/suggest-box-new.fixed deleted file mode 100644 index f5ae5c2abfd..00000000000 --- a/tests/ui/privacy/suggest-box-new.fixed +++ /dev/null @@ -1,15 +0,0 @@ -// run-rustfix -#![allow(dead_code)] -struct U { - wtf: Option>>, - x: T, -} -fn main() { - U { - wtf: Some(Box::new(U { //~ ERROR cannot initialize a tuple struct which contains private fields - wtf: None, - x: (), - })), - x: () - }; -} diff --git a/tests/ui/privacy/suggest-box-new.rs b/tests/ui/privacy/suggest-box-new.rs index 2e18dba8b9f..7125285fc77 100644 --- a/tests/ui/privacy/suggest-box-new.rs +++ b/tests/ui/privacy/suggest-box-new.rs @@ -1,4 +1,3 @@ -// run-rustfix #![allow(dead_code)] struct U { wtf: Option>>, @@ -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 } diff --git a/tests/ui/privacy/suggest-box-new.stderr b/tests/ui/privacy/suggest-box-new.stderr index ed7fa036408..2224f1c60d6 100644 --- a/tests/ui/privacy/suggest-box-new.stderr +++ b/tests/ui/privacy/suggest-box-new.stderr @@ -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 _ = ::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(::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 _ = ::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 _ = ::default(); + | + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0423`. diff --git a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr index 671f5efddd9..057e37dbe10 100644 --- a/tests/ui/suggestions/suggest-tryinto-edition-change.stderr +++ b/tests/ui/suggestions/suggest-tryinto-edition-change.stderr @@ -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]); diff --git a/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs b/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs index 69ed498e7e1..4f8b3508398 100644 --- a/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs +++ b/tests/ui/xcrate/auxiliary/xcrate_unit_struct.rs @@ -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, } diff --git a/tests/ui/xcrate/xcrate-unit-struct.rs b/tests/ui/xcrate/xcrate-unit-struct.rs index c99cf77ce7a..bc14cd8d4c0 100644 --- a/tests/ui/xcrate/xcrate-unit-struct.rs +++ b/tests/ui/xcrate/xcrate-unit-struct.rs @@ -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; } diff --git a/tests/ui/xcrate/xcrate-unit-struct.stderr b/tests/ui/xcrate/xcrate-unit-struct.stderr index cee31456888..7365170b69e 100644 --- a/tests/ui/xcrate/xcrate-unit-struct.stderr +++ b/tests/ui/xcrate/xcrate-unit-struct.stderr @@ -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`.