diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a2314f4850c7c..164d1681a3670 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -495,8 +495,14 @@ impl<'ll> CodegenCx<'ll, '_> { } // Wasm statics with custom link sections get special treatment as they - // go into custom sections of the wasm executable. - if self.tcx.sess.target.is_like_wasm { + // go into custom sections of the wasm executable. The exception to this + // is the `.init_array` section which are treated specially by the wasm linker. + if self.tcx.sess.target.is_like_wasm + && attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap_or(true) + { if let Some(section) = attrs.link_section { let section = llvm::LLVMMDStringInContext2( self.llcx, diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 9fef31acef8a8..24aeb02446194 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -166,21 +166,42 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { return; } - // For the wasm32 target statics with `#[link_section]` are placed into custom - // sections of the final output file, but this isn't link custom sections of - // other executable formats. Namely we can only embed a list of bytes, - // nothing with provenance (pointers to anything else). If any provenance - // show up, reject it here. + // For the wasm32 target statics with `#[link_section]` other than `.init_array` + // are placed into custom sections of the final output file, but this isn't like + // custom sections of other executable formats. Namely we can only embed a list + // of bytes, nothing with provenance (pointers to anything else). If any + // provenance show up, reject it here. // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. + // + // The `.init_array` section is left to go through the normal custom section code path. + // When dealing with `.init_array` wasm-ld currently has several limitations. This manifests + // in workarounds in user-code. + // + // * The linker fails to merge multiple items in a crate into the .init_array section. + // To work around this, a single array can be used placing multiple items in the array. + // #[link_section = ".init_array"] + // static FOO: [unsafe extern "C" fn(); 2] = [ctor, ctor]; + // * Even symbols marked used get gc'd from dependant crates unless at least one symbol + // in the crate is marked with an `#[export_name]` + // + // Once `.init_array` support in wasm-ld is complete, the user code workarounds should + // continue to work, but would no longer be necessary. + if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) && alloc.inner().provenance().ptrs().len() != 0 { - let msg = "statics with a custom `#[link_section]` must be a \ + if attrs + .link_section + .map(|link_section| !link_section.as_str().starts_with(".init_array")) + .unwrap() + { + let msg = "statics with a custom `#[link_section]` must be a \ simple list of bytes on the wasm target with no \ extra levels of indirection such as references"; - tcx.dcx().span_err(tcx.def_span(id), msg); + tcx.dcx().span_err(tcx.def_span(id), msg); + } } } diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 7b8a03def86bc..7d1ab232c717b 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1087,7 +1087,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ); let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggested_name) = find_best_match_for_name( + if let Some(variant_name) = find_best_match_for_name( &adt_def .variants() .iter() @@ -1095,12 +1095,66 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { .collect::>(), assoc_ident.name, None, - ) { - err.span_suggestion( - assoc_ident.span, + ) && let Some(variant) = + adt_def.variants().iter().find(|s| s.name == variant_name) + { + let mut suggestion = vec![(assoc_ident.span, variant_name.to_string())]; + if let hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Semi(ref expr), + .. + }) + | hir::Node::Expr(ref expr) = tcx.parent_hir_node(hir_ref_id) + && let hir::ExprKind::Struct(..) = expr.kind + { + match variant.ctor { + None => { + // struct + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + if variant.fields.is_empty() { + format!("{variant_name} {{}}") + } else { + format!( + "{variant_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + Some((hir::def::CtorKind::Fn, def_id)) => { + // tuple + let fn_sig = tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + format!( + "{variant_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + Some((hir::def::CtorKind::Const, _)) => { + // unit + suggestion = vec![( + assoc_ident.span.with_hi(expr.span.hi()), + variant_name.to_string(), + )]; + } + } + } + err.multipart_suggestion_verbose( "there is a variant with a similar name", - suggested_name, - Applicability::MaybeIncorrect, + suggestion, + Applicability::HasPlaceholders, ); } else { err.span_label( diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index e957330d9a1c5..d708269f1f538 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -519,7 +519,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_error(tcx, e) } Res::Def(DefKind::Variant, _) => { - let e = report_unexpected_variant_res(tcx, res, qpath, expr.span, E0533, "value"); + let e = report_unexpected_variant_res( + tcx, + res, + Some(expr), + qpath, + expr.span, + E0533, + "value", + ); Ty::new_error(tcx, e) } _ => { @@ -2210,8 +2218,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); - match variant.ctor_kind() { - Some(CtorKind::Fn) => match ty.kind() { + match variant.ctor { + Some((CtorKind::Fn, def_id)) => match ty.kind() { ty::Adt(adt, ..) if adt.is_enum() => { err.span_label( variant_ident_span, @@ -2222,28 +2230,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ), ); err.span_label(field.ident.span, "field does not exist"); + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + let fields = format!( + "({})", + inputs.iter().map(|i| format!("/* {i} */")).collect::>().join(", ") + ); + let (replace_span, sugg) = match expr.kind { + hir::ExprKind::Struct(qpath, ..) => { + (qpath.span().shrink_to_hi().with_hi(expr.span.hi()), fields) + } + _ => { + (expr.span, format!("{ty}::{variant}{fields}", variant = variant.name)) + } + }; err.span_suggestion_verbose( - expr.span, + replace_span, format!( "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", adt = ty, variant = variant.name, ), - format!( - "{adt}::{variant}(/* fields */)", - adt = ty, - variant = variant.name, - ), + sugg, Applicability::HasPlaceholders, ); } _ => { err.span_label(variant_ident_span, format!("`{ty}` defined here")); err.span_label(field.ident.span, "field does not exist"); + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + let fields = format!( + "({})", + inputs.iter().map(|i| format!("/* {i} */")).collect::>().join(", ") + ); err.span_suggestion_verbose( expr.span, format!("`{ty}` is a tuple {kind_name}, use the appropriate syntax",), - format!("{ty}(/* fields */)"), + format!("{ty}{fields}"), Applicability::HasPlaceholders, ); } diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index bdbdcee6446dc..2c79366450909 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -53,7 +53,7 @@ use crate::expectation::Expectation; use crate::fn_ctxt::LoweredTy; use crate::gather_locals::GatherLocalsVisitor; use rustc_data_structures::unord::UnordSet; -use rustc_errors::{codes::*, struct_span_code_err, ErrorGuaranteed}; +use rustc_errors::{codes::*, struct_span_code_err, Applicability, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::Visitor; @@ -346,6 +346,7 @@ impl<'tcx> EnclosingBreakables<'tcx> { fn report_unexpected_variant_res( tcx: TyCtxt<'_>, res: Res, + expr: Option<&hir::Expr<'_>>, qpath: &hir::QPath<'_>, span: Span, err_code: ErrCode, @@ -356,7 +357,7 @@ fn report_unexpected_variant_res( _ => res.descr(), }; let path_str = rustc_hir_pretty::qpath_to_string(&tcx, qpath); - let err = tcx + let mut err = tcx .dcx() .struct_span_err(span, format!("expected {expected}, found {res_descr} `{path_str}`")) .with_code(err_code); @@ -366,6 +367,61 @@ fn report_unexpected_variant_res( err.with_span_label(span, "`fn` calls are not allowed in patterns") .with_help(format!("for more information, visit {patterns_url}")) } + Res::Def(DefKind::Variant, _) if let Some(expr) = expr => { + err.span_label(span, format!("not a {expected}")); + let variant = tcx.expect_variant_res(res); + let sugg = if variant.fields.is_empty() { + " {}".to_string() + } else { + format!( + " {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }; + let descr = "you might have meant to create a new value of the struct"; + let mut suggestion = vec![]; + match tcx.parent_hir_node(expr.hir_id) { + hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Call(..), + span: call_span, + .. + }) => { + suggestion.push((span.shrink_to_hi().with_hi(call_span.hi()), sugg)); + } + hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Binary(..), hir_id, .. }) => { + suggestion.push((expr.span.shrink_to_lo(), "(".to_string())); + if let hir::Node::Expr(drop_temps) = tcx.parent_hir_node(*hir_id) + && let hir::ExprKind::DropTemps(_) = drop_temps.kind + && let hir::Node::Expr(parent) = tcx.parent_hir_node(drop_temps.hir_id) + && let hir::ExprKind::If(condition, block, None) = parent.kind + && condition.hir_id == drop_temps.hir_id + && let hir::ExprKind::Block(block, _) = block.kind + && block.stmts.is_empty() + && let Some(expr) = block.expr + && let hir::ExprKind::Path(..) = expr.kind + { + // Special case: you can incorrectly write an equality condition: + // if foo == Struct { field } { /* if body */ } + // which should have been written + // if foo == (Struct { field }) { /* if body */ } + suggestion.push((block.span.shrink_to_hi(), ")".to_string())); + } else { + suggestion.push((span.shrink_to_hi().with_hi(expr.span.hi()), sugg)); + } + } + _ => { + suggestion.push((span.shrink_to_hi(), sugg)); + } + } + + err.multipart_suggestion_verbose(descr, suggestion, Applicability::MaybeIncorrect); + err + } _ => err.with_span_label(span, format!("not a {expected}")), } .emit() diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 425289ce3c526..1cc7cf67ee31e 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -1596,16 +1596,127 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // that had unsatisfied trait bounds if unsatisfied_predicates.is_empty() && rcvr_ty.is_enum() { let adt_def = rcvr_ty.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggestion) = edit_distance::find_best_match_for_name( + if let Some(var_name) = edit_distance::find_best_match_for_name( &adt_def.variants().iter().map(|s| s.name).collect::>(), item_name.name, None, - ) { - err.span_suggestion( - span, + ) && let Some(variant) = adt_def.variants().iter().find(|s| s.name == var_name) + { + let mut suggestion = vec![(span, var_name.to_string())]; + if let SelfSource::QPath(ty) = source + && let hir::Node::Expr(ref path_expr) = self.tcx.parent_hir_node(ty.hir_id) + && let hir::ExprKind::Path(_) = path_expr.kind + && let hir::Node::Stmt(hir::Stmt { + kind: hir::StmtKind::Semi(ref parent), .. + }) + | hir::Node::Expr(ref parent) = self.tcx.parent_hir_node(path_expr.hir_id) + { + let replacement_span = + if let hir::ExprKind::Call(..) | hir::ExprKind::Struct(..) = parent.kind { + // We want to replace the parts that need to go, like `()` and `{}`. + span.with_hi(parent.span.hi()) + } else { + span + }; + match (variant.ctor, parent.kind) { + (None, hir::ExprKind::Struct(..)) => { + // We want a struct and we have a struct. We won't suggest changing + // the fields (at least for now). + suggestion = vec![(span, var_name.to_string())]; + } + (None, _) => { + // struct + suggestion = vec![( + replacement_span, + if variant.fields.is_empty() { + format!("{var_name} {{}}") + } else { + format!( + "{var_name} {{ {} }}", + variant + .fields + .iter() + .map(|f| format!("{}: /* value */", f.name)) + .collect::>() + .join(", ") + ) + }, + )]; + } + (Some((hir::def::CtorKind::Const, _)), _) => { + // unit, remove the `()`. + suggestion = vec![(replacement_span, var_name.to_string())]; + } + ( + Some((hir::def::CtorKind::Fn, def_id)), + hir::ExprKind::Call(rcvr, args), + ) => { + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + // FIXME: reuse the logic for "change args" suggestion to account for types + // involved and detect things like substitution. + match (inputs, args) { + (inputs, []) => { + // Add arguments. + suggestion.push(( + rcvr.span.shrink_to_hi().with_hi(parent.span.hi()), + format!( + "({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )); + } + (_, [arg]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg.span, + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + (_, [arg_start, .., arg_end]) if inputs.len() != args.len() => { + // Replace arguments. + suggestion.push(( + arg_start.span.to(arg_end.span), + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", "), + )); + } + // Argument count is the same, keep as is. + _ => {} + } + } + (Some((hir::def::CtorKind::Fn, def_id)), _) => { + let fn_sig = self.tcx.fn_sig(def_id).instantiate_identity(); + let inputs = fn_sig.inputs().skip_binder(); + suggestion = vec![( + replacement_span, + format!( + "{var_name}({})", + inputs + .iter() + .map(|i| format!("/* {i} */")) + .collect::>() + .join(", ") + ), + )]; + } + } + } + err.multipart_suggestion_verbose( "there is a variant with a similar name", suggestion, - Applicability::MaybeIncorrect, + Applicability::HasPlaceholders, ); } } diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index 6d1e9ff1f9527..8afc6a48dfc5c 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -1023,7 +1023,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::Variant, _) => { let expected = "unit struct, unit variant or constant"; - let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, E0533, expected); + let e = + report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0533, expected); return Ty::new_error(tcx, e); } Res::SelfCtor(def_id) => { @@ -1036,6 +1037,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let e = report_unexpected_variant_res( tcx, res, + None, qpath, pat.span, E0533, @@ -1189,7 +1191,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; let report_unexpected_res = |res: Res| { let expected = "tuple struct or tuple variant"; - let e = report_unexpected_variant_res(tcx, res, qpath, pat.span, E0164, expected); + let e = report_unexpected_variant_res(tcx, res, None, qpath, pat.span, E0164, expected); on_error(e); e }; diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 79d529143811e..7d7b97e2eb1a3 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -403,8 +403,9 @@ lint_inner_macro_attribute_unstable = inner macro attributes are unstable lint_invalid_asm_label_binary = avoid using labels containing only the digits `0` and `1` in inline assembly .label = use a different label that doesn't start with `0` or `1` - .note = an LLVM bug makes these labels ambiguous with a binary literal number - .note = see for more information + .help = start numbering with `2` instead + .note1 = an LLVM bug makes these labels ambiguous with a binary literal number on x86 + .note2 = see for more information lint_invalid_asm_label_format_arg = avoid using named labels in inline assembly .help = only local labels of the form `:` should be used in inline asm diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 485c214ac9def..9ebada0fff377 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -66,6 +66,7 @@ use rustc_span::source_map::Spanned; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{BytePos, InnerSpan, Span}; use rustc_target::abi::Abi; +use rustc_target::asm::InlineAsmArch; use rustc_trait_selection::infer::{InferCtxtExt, TyCtxtInferExt}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{self, misc::type_allowed_to_implement_copy}; @@ -2739,8 +2740,9 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail - /// # #![feature(asm_experimental_arch)] + /// ```rust,ignore (fails on non-x86_64) + /// #![cfg(target_arch = "x86_64")] + /// /// use std::arch::asm; /// /// fn main() { @@ -2750,19 +2752,32 @@ declare_lint! { /// } /// ``` /// - /// {{produces}} + /// This will produce: + /// + /// ```text + /// error: avoid using labels containing only the digits `0` and `1` in inline assembly + /// --> :7:15 + /// | + /// 7 | asm!("0: jmp 0b"); + /// | ^ use a different label that doesn't start with `0` or `1` + /// | + /// = help: start numbering with `2` instead + /// = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + /// = note: see for more information + /// = note: `#[deny(binary_asm_labels)]` on by default + /// ``` /// /// ### Explanation /// - /// A [LLVM bug] causes this code to fail to compile because it interprets the `0b` as a binary - /// literal instead of a reference to the previous local label `0`. Note that even though the - /// bug is marked as fixed, it only fixes a specific usage of intel syntax within standalone - /// files, not inline assembly. To work around this bug, don't use labels that could be - /// confused with a binary literal. + /// An [LLVM bug] causes this code to fail to compile because it interprets the `0b` as a binary + /// literal instead of a reference to the previous local label `0`. To work around this bug, + /// don't use labels that could be confused with a binary literal. + /// + /// This behavior is platform-specific to x86 and x86-64. /// /// See the explanation in [Rust By Example] for more details. /// - /// [LLVM bug]: https://bugs.llvm.org/show_bug.cgi?id=36144 + /// [LLVM bug]: https://github.com/llvm/llvm-project/issues/99547 /// [Rust By Example]: https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels pub BINARY_ASM_LABELS, Deny, @@ -2908,16 +2923,22 @@ impl<'tcx> LateLintPass<'tcx> for AsmLabels { InvalidAsmLabel::FormatArg { missing_precise_span }, ); } - AsmLabelKind::Binary => { - // the binary asm issue only occurs when using intel syntax - if !options.contains(InlineAsmOptions::ATT_SYNTAX) { - cx.emit_span_lint( - BINARY_ASM_LABELS, - span, - InvalidAsmLabel::Binary { missing_precise_span, span }, - ) - } + // the binary asm issue only occurs when using intel syntax on x86 targets + AsmLabelKind::Binary + if !options.contains(InlineAsmOptions::ATT_SYNTAX) + && matches!( + cx.tcx.sess.asm_arch, + Some(InlineAsmArch::X86 | InlineAsmArch::X86_64) | None + ) => + { + cx.emit_span_lint( + BINARY_ASM_LABELS, + span, + InvalidAsmLabel::Binary { missing_precise_span, span }, + ) } + // No lint on anything other than x86 + AsmLabelKind::Binary => (), }; } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index ac5511faf1961..6c5f366727f95 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2074,7 +2074,9 @@ pub enum InvalidAsmLabel { missing_precise_span: bool, }, #[diag(lint_invalid_asm_label_binary)] - #[note] + #[help] + #[note(lint_note1)] + #[note(lint_note2)] Binary { #[note(lint_invalid_asm_label_no_span)] missing_precise_span: bool, diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs index a8fe35f45b31e..a2e40d3398a9a 100644 --- a/compiler/rustc_parse/src/parser/attr.rs +++ b/compiler/rustc_parse/src/parser/attr.rs @@ -303,17 +303,13 @@ impl<'a> Parser<'a> { None }; if let Some(attr) = attr { - let end_pos = self.num_bump_calls; - // If we are currently capturing tokens, mark the location of this inner attribute. - // If capturing ends up creating a `LazyAttrTokenStream`, we will include - // this replace range with it, removing the inner attribute from the final - // `AttrTokenStream`. Inner attributes are stored in the parsed AST note. - // During macro expansion, they are selectively inserted back into the - // token stream (the first inner attribute is removed each time we invoke the - // corresponding macro). - let range = start_pos..end_pos; + // If we are currently capturing tokens (i.e. we are within a call to + // `Parser::collect_tokens_trailing_tokens`) record the token positions of this + // inner attribute, for possible later processing in a `LazyAttrTokenStream`. if let Capturing::Yes = self.capture_state.capturing { - self.capture_state.inner_attr_ranges.insert(attr.id, (range, None)); + let end_pos = self.num_bump_calls; + let range = start_pos..end_pos; + self.capture_state.inner_attr_ranges.insert(attr.id, range); } attrs.push(attr); } else { @@ -463,7 +459,8 @@ impl<'a> Parser<'a> { } } -/// The attributes are complete if all attributes are either a doc comment or a builtin attribute other than `cfg_attr` +/// The attributes are complete if all attributes are either a doc comment or a +/// builtin attribute other than `cfg_attr`. pub fn is_complete(attrs: &[ast::Attribute]) -> bool { attrs.iter().all(|attr| { attr.is_doc_comment() diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index 7e56e92f87b31..dc5f98f7be8b6 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -17,12 +17,12 @@ use std::{iter, mem}; /// /// This wrapper prevents direct access to the underlying `ast::AttrVec`. /// Parsing code can only get access to the underlying attributes -/// by passing an `AttrWrapper` to `collect_tokens_trailing_tokens`. +/// by passing an `AttrWrapper` to `collect_tokens_trailing_token`. /// This makes it difficult to accidentally construct an AST node /// (which stores an `ast::AttrVec`) without first collecting tokens. /// /// This struct has its own module, to ensure that the parser code -/// cannot directly access the `attrs` field +/// cannot directly access the `attrs` field. #[derive(Debug, Clone)] pub struct AttrWrapper { attrs: AttrVec, @@ -76,14 +76,13 @@ fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool { }) } -// Produces a `TokenStream` on-demand. Using `cursor_snapshot` -// and `num_calls`, we can reconstruct the `TokenStream` seen -// by the callback. This allows us to avoid producing a `TokenStream` -// if it is never needed - for example, a captured `macro_rules!` -// argument that is never passed to a proc macro. -// In practice token stream creation happens rarely compared to -// calls to `collect_tokens` (see some statistics in #78736), -// so we are doing as little up-front work as possible. +// From a value of this type we can reconstruct the `TokenStream` seen by the +// `f` callback passed to a call to `Parser::collect_tokens_trailing_token`, by +// replaying the getting of the tokens. This saves us producing a `TokenStream` +// if it is never needed, e.g. a captured `macro_rules!` argument that is never +// passed to a proc macro. In practice, token stream creation happens rarely +// compared to calls to `collect_tokens` (see some statistics in #78736) so we +// are doing as little up-front work as possible. // // This also makes `Parser` very cheap to clone, since // there is no intermediate collection buffer to clone. @@ -163,46 +162,55 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl { } impl<'a> Parser<'a> { - /// Records all tokens consumed by the provided callback, - /// including the current token. These tokens are collected - /// into a `LazyAttrTokenStream`, and returned along with the first part of - /// the callback's result. The second (bool) part of the callback's result - /// indicates if an extra token should be captured, e.g. a comma or + /// Parses code with `f`. If appropriate, it records the tokens (in + /// `LazyAttrTokenStream` form) that were parsed in the result, accessible + /// via the `HasTokens` trait. The second (bool) part of the callback's + /// result indicates if an extra token should be captured, e.g. a comma or /// semicolon. /// /// The `attrs` passed in are in `AttrWrapper` form, which is opaque. The /// `AttrVec` within is passed to `f`. See the comment on `AttrWrapper` for /// details. /// - /// Note: If your callback consumes an opening delimiter - /// (including the case where you call `collect_tokens` - /// when the current token is an opening delimiter), - /// you must also consume the corresponding closing delimiter. + /// Note: If your callback consumes an opening delimiter (including the + /// case where `self.token` is an opening delimiter on entry to this + /// function), you must also consume the corresponding closing delimiter. + /// E.g. you can consume `something ([{ }])` or `([{}])`, but not `([{}]`. + /// This restriction isn't a problem in practice, because parsed AST items + /// always have matching delimiters. /// - /// That is, you can consume - /// `something ([{ }])` or `([{}])`, but not `([{}]` - /// - /// This restriction shouldn't be an issue in practice, - /// since this function is used to record the tokens for - /// a parsed AST item, which always has matching delimiters. + /// The following example code will be used to explain things in comments + /// below. It has an outer attribute and an inner attribute. Parsing it + /// involves two calls to this method, one of which is indirectly + /// recursive. + /// ```ignore (fake attributes) + /// #[cfg_eval] // token pos + /// mod m { // 0.. 3 + /// #[cfg_attr(cond1, attr1)] // 3..12 + /// fn g() { // 12..17 + /// #![cfg_attr(cond2, attr2)] // 17..27 + /// let _x = 3; // 27..32 + /// } // 32..33 + /// } // 33..34 + /// ``` pub fn collect_tokens_trailing_token( &mut self, attrs: AttrWrapper, force_collect: ForceCollect, f: impl FnOnce(&mut Self, ast::AttrVec) -> PResult<'a, (R, bool)>, ) -> PResult<'a, R> { - // We only bail out when nothing could possibly observe the collected tokens: - // 1. We cannot be force collecting tokens (since force-collecting requires tokens - // by definition + // Skip collection when nothing could observe the collected tokens, i.e. + // all of the following conditions hold. + // - We are not force collecting tokens (because force collection + // requires tokens by definition). if matches!(force_collect, ForceCollect::No) - // None of our outer attributes can require tokens (e.g. a proc-macro) + // - None of our outer attributes require tokens. && attrs.is_complete() - // If our target supports custom inner attributes, then we cannot bail - // out early, since we may need to capture tokens for a custom inner attribute - // invocation. + // - Our target doesn't support custom inner attributes (custom + // inner attribute invocation might require token capturing). && !R::SUPPORTS_CUSTOM_INNER_ATTRS - // Never bail out early in `capture_cfg` mode, since there might be `#[cfg]` - // or `#[cfg_attr]` attributes. + // - We are not in `capture_cfg` mode (which requires tokens if + // the parsed node has `#[cfg]` or `#[cfg_attr]` attributes). && !self.capture_cfg { return Ok(f(self, attrs.attrs)?.0); @@ -214,6 +222,12 @@ impl<'a> Parser<'a> { let has_outer_attrs = !attrs.attrs.is_empty(); let replace_ranges_start = self.capture_state.replace_ranges.len(); + // We set and restore `Capturing::Yes` on either side of the call to + // `f`, so we can distinguish the outermost call to + // `collect_tokens_trailing_token` (e.g. parsing `m` in the example + // above) from any inner (indirectly recursive) calls (e.g. parsing `g` + // in the example above). This distinction is used below and in + // `Parser::parse_inner_attributes`. let (mut ret, capture_trailing) = { let prev_capturing = mem::replace(&mut self.capture_state.capturing, Capturing::Yes); let ret_and_trailing = f(self, attrs.attrs); @@ -221,51 +235,46 @@ impl<'a> Parser<'a> { ret_and_trailing? }; - // When we're not in `capture-cfg` mode, then bail out early if: - // 1. Our target doesn't support tokens at all (e.g we're parsing an `NtIdent`) - // so there's nothing for us to do. - // 2. Our target already has tokens set (e.g. we've parsed something - // like `#[my_attr] $item`). The actual parsing code takes care of - // prepending any attributes to the nonterminal, so we don't need to - // modify the already captured tokens. - // Note that this check is independent of `force_collect`- if we already - // have tokens, or can't even store them, then there's never a need to - // force collection of new tokens. + // When we're not in `capture_cfg` mode, then skip collecting and + // return early if either of the following conditions hold. + // - `None`: Our target doesn't support tokens at all (e.g. `NtIdent`). + // - `Some(Some(_))`: Our target already has tokens set (e.g. we've + // parsed something like `#[my_attr] $item`). The actual parsing code + // takes care of prepending any attributes to the nonterminal, so we + // don't need to modify the already captured tokens. + // + // Note that this check is independent of `force_collect`. There's no + // need to collect tokens when we don't support tokens or already have + // tokens. if !self.capture_cfg && matches!(ret.tokens_mut(), None | Some(Some(_))) { return Ok(ret); } - // This is very similar to the bail out check at the start of this function. - // Now that we've parsed an AST node, we have more information available. + // This is similar to the "skip collection" check at the start of this + // function, but now that we've parsed an AST node we have more + // information available. (If we return early here that means the + // setup, such as cloning the token cursor, was unnecessary. That's + // hard to avoid.) + // + // Skip collection when nothing could observe the collected tokens, i.e. + // all of the following conditions hold. + // - We are not force collecting tokens. if matches!(force_collect, ForceCollect::No) - // We now have inner attributes available, so this check is more precise - // than `attrs.is_complete()` at the start of the function. - // As a result, we don't need to check `R::SUPPORTS_CUSTOM_INNER_ATTRS` + // - None of our outer *or* inner attributes require tokens. + // (`attrs` was just outer attributes, but `ret.attrs()` is outer + // and inner attributes. That makes this check more precise than + // `attrs.is_complete()` at the start of the function, and we can + // skip the subsequent check on `R::SUPPORTS_CUSTOM_INNER_ATTRS`. && crate::parser::attr::is_complete(ret.attrs()) - // Subtle: We call `has_cfg_or_cfg_attr` with the attrs from `ret`. - // This ensures that we consider inner attributes (e.g. `#![cfg]`), - // which require us to have tokens available - // We also call `has_cfg_or_cfg_attr` at the beginning of this function, - // but we only bail out if there's no possibility of inner attributes - // (!R::SUPPORTS_CUSTOM_INNER_ATTRS) - // We only capture about `#[cfg]` or `#[cfg_attr]` in `capture_cfg` - // mode - during normal parsing, we don't need any special capturing - // for those attributes, since they're builtin. - && !(self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs())) + // - We are not in `capture_cfg` mode, or we are but there are no + // `#[cfg]` or `#[cfg_attr]` attributes. (During normal + // non-`capture_cfg` parsing, we don't need any special capturing + // for those attributes, because they're builtin.) + && (!self.capture_cfg || !has_cfg_or_cfg_attr(ret.attrs())) { return Ok(ret); } - let mut inner_attr_replace_ranges = Vec::new(); - // Take the captured ranges for any inner attributes that we parsed. - for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { - if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { - inner_attr_replace_ranges.push(attr_range); - } else { - self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute"); - } - } - let replace_ranges_end = self.capture_state.replace_ranges.len(); assert!( @@ -283,15 +292,28 @@ impl<'a> Parser<'a> { let num_calls = end_pos - start_pos; + // Take the captured ranges for any inner attributes that we parsed in + // `Parser::parse_inner_attributes`, and pair them in a `ReplaceRange` + // with `None`, which means the relevant tokens will be removed. (More + // details below.) + let mut inner_attr_replace_ranges = Vec::new(); + for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) { + if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) { + inner_attr_replace_ranges.push((attr_range, None)); + } else { + self.dcx().span_delayed_bug(inner_attr.span, "Missing token range for attribute"); + } + } + // This is hot enough for `deep-vector` that checking the conditions for an empty iterator // is measurably faster than actually executing the iterator. let replace_ranges: Box<[ReplaceRange]> = if replace_ranges_start == replace_ranges_end && inner_attr_replace_ranges.is_empty() { Box::new([]) } else { - // Grab any replace ranges that occur *inside* the current AST node. - // We will perform the actual replacement when we convert the `LazyAttrTokenStream` - // to an `AttrTokenStream`. + // Grab any replace ranges that occur *inside* the current AST node. We will + // perform the actual replacement only when we convert the `LazyAttrTokenStream` to + // an `AttrTokenStream`. self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end] .iter() .cloned() @@ -300,6 +322,28 @@ impl<'a> Parser<'a> { .collect() }; + // What is the status here when parsing the example code at the top of this method? + // + // When parsing `g`: + // - `start_pos..end_pos` is `12..33` (`fn g { ... }`, excluding the outer attr). + // - `inner_attr_replace_ranges` has one entry (`5..15`, when counting from `fn`), to + // delete the inner attr's tokens. + // - This entry is put into the lazy tokens for `g`, i.e. deleting the inner attr from + // those tokens (if they get evaluated). + // - Those lazy tokens are also put into an `AttrsTarget` that is appended to `self`'s + // replace ranges at the bottom of this function, for processing when parsing `m`. + // - `replace_ranges_start..replace_ranges_end` is empty. + // + // When parsing `m`: + // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute). + // - `inner_attr_replace_ranges` is empty. + // - `replace_range_start..replace_ranges_end` has two entries. + // - One to delete the inner attribute (`17..27`), obtained when parsing `g` (see above). + // - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`, + // including its outer attribute), with: + // - `attrs`: includes the outer and the inner attr. + // - `tokens`: lazy tokens for `g` (with its inner attr deleted). + let tokens = LazyAttrTokenStream::new(LazyAttrTokenStreamImpl { start_token, num_calls, @@ -313,27 +357,37 @@ impl<'a> Parser<'a> { *target_tokens = Some(tokens.clone()); } - let final_attrs = ret.attrs(); - // If `capture_cfg` is set and we're inside a recursive call to // `collect_tokens_trailing_token`, then we need to register a replace range // if we have `#[cfg]` or `#[cfg_attr]`. This allows us to run eager cfg-expansion // on the captured token stream. if self.capture_cfg && matches!(self.capture_state.capturing, Capturing::Yes) - && has_cfg_or_cfg_attr(final_attrs) + && has_cfg_or_cfg_attr(ret.attrs()) { assert!(!self.break_last_token, "Should not have unglued last token with cfg attr"); - // Replace the entire AST node that we just parsed, including attributes, with - // `target`. If this AST node is inside an item that has `#[derive]`, then this will - // allow us to cfg-expand this AST node. + // What is the status here when parsing the example code at the top of this method? + // + // When parsing `g`, we add two entries: + // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with: + // - `attrs`: includes the outer and the inner attr. + // - `tokens`: lazy tokens for `g` (with its inner attr deleted). + // - `inner_attr_replace_ranges` contains the one entry to delete the inner attr's + // tokens (`17..27`). + // + // When parsing `m`, we do nothing here. + + // Set things up so that the entire AST node that we just parsed, including attributes, + // will be replaced with `target` in the lazy token stream. This will allow us to + // cfg-expand this AST node. let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos }; - let target = AttrsTarget { attrs: final_attrs.iter().cloned().collect(), tokens }; + let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens }; self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target))); self.capture_state.replace_ranges.extend(inner_attr_replace_ranges); } else if matches!(self.capture_state.capturing, Capturing::No) { - // Only clear the ranges once we've finished capturing entirely. + // Only clear the ranges once we've finished capturing entirely, i.e. we've finished + // the outermost call to this method. self.capture_state.replace_ranges.clear(); self.capture_state.inner_attr_ranges.clear(); } diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index c03527acb2cfe..06545e85dd161 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -221,11 +221,12 @@ enum Capturing { Yes, } +// This state is used by `Parser::collect_tokens_trailing_token`. #[derive(Clone, Debug)] struct CaptureState { capturing: Capturing, replace_ranges: Vec, - inner_attr_ranges: FxHashMap, + inner_attr_ranges: FxHashMap>, } /// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that @@ -425,6 +426,11 @@ impl<'a> Parser<'a> { // Make parser point to the first token. parser.bump(); + // Change this from 1 back to 0 after the bump. This eases debugging of + // `Parser::collect_tokens_trailing_token` nicer because it makes the + // token positions 0-indexed which is nicer than 1-indexed. + parser.num_bump_calls = 0; + parser } diff --git a/compiler/rustc_parse/src/parser/tests.rs b/compiler/rustc_parse/src/parser/tests.rs index cf791d332a2fc..491aa71155af2 100644 --- a/compiler/rustc_parse/src/parser/tests.rs +++ b/compiler/rustc_parse/src/parser/tests.rs @@ -1522,7 +1522,7 @@ fn debug_lookahead() { }, }, tokens: [], - approx_token_stream_pos: 1, + approx_token_stream_pos: 0, .. }" ); @@ -1566,7 +1566,7 @@ fn debug_lookahead() { Parenthesis, ), ], - approx_token_stream_pos: 1, + approx_token_stream_pos: 0, .. }" ); @@ -1631,7 +1631,7 @@ fn debug_lookahead() { Semi, Eof, ], - approx_token_stream_pos: 1, + approx_token_stream_pos: 0, .. }" ); @@ -1663,7 +1663,7 @@ fn debug_lookahead() { No, ), ], - approx_token_stream_pos: 9, + approx_token_stream_pos: 8, .. }" ); @@ -1701,7 +1701,7 @@ fn debug_lookahead() { No, ), ], - approx_token_stream_pos: 9, + approx_token_stream_pos: 8, .. }" ); @@ -1728,7 +1728,7 @@ fn debug_lookahead() { tokens: [ Eof, ], - approx_token_stream_pos: 15, + approx_token_stream_pos: 14, .. }" ); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 1a4101e4de80b..0122886961737 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -94,6 +94,8 @@ fn get_lib_name(lib: &str, aux_type: AuxType) -> Option { format!("{}.dll", lib) } else if cfg!(target_vendor = "apple") { format!("lib{}.dylib", lib) + } else if cfg!(target_os = "aix") { + format!("lib{}.a", lib) } else { format!("lib{}.so", lib) }), diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 745f00c4f5276..2211795b0380f 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -1,4 +1,3 @@ -run-make/archive-duplicate-names/Makefile run-make/branch-protection-check-IBT/Makefile run-make/c-dynamic-dylib/Makefile run-make/c-dynamic-rlib/Makefile @@ -56,16 +55,12 @@ run-make/libtest-junit/Makefile run-make/libtest-thread-limit/Makefile run-make/link-cfg/Makefile run-make/link-framework/Makefile -run-make/link-path-order/Makefile run-make/linkage-attr-on-static/Makefile run-make/long-linker-command-lines-cmd-exe/Makefile run-make/long-linker-command-lines/Makefile run-make/lto-linkage-used-attr/Makefile run-make/lto-no-link-whole-rlib/Makefile -run-make/lto-smoke-c/Makefile run-make/macos-deployment-target/Makefile -run-make/macos-fat-archive/Makefile -run-make/manual-link/Makefile run-make/min-global-align/Makefile run-make/native-link-modifier-bundle/Makefile run-make/native-link-modifier-whole-archive/Makefile diff --git a/tests/run-make/archive-duplicate-names/Makefile b/tests/run-make/archive-duplicate-names/Makefile deleted file mode 100644 index 207eee392993b..0000000000000 --- a/tests/run-make/archive-duplicate-names/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# When two object archives with the same filename are present, an iterator is supposed to inspect each object, recognize the duplication and extract each one to a different directory. -# This test checks that this duplicate handling behaviour has not been broken. -# See https://github.com/rust-lang/rust/pull/24439 - -# ignore-cross-compile -include ../tools.mk - -all: - mkdir $(TMPDIR)/a - mkdir $(TMPDIR)/b - $(call COMPILE_OBJ,$(TMPDIR)/a/foo.o,foo.c) - $(call COMPILE_OBJ,$(TMPDIR)/b/foo.o,bar.c) - $(AR) crus $(TMPDIR)/libfoo.a $(TMPDIR)/a/foo.o $(TMPDIR)/b/foo.o - $(RUSTC) foo.rs - $(RUSTC) bar.rs - $(call RUN,bar) diff --git a/tests/run-make/archive-duplicate-names/rmake.rs b/tests/run-make/archive-duplicate-names/rmake.rs new file mode 100644 index 0000000000000..62a3556619984 --- /dev/null +++ b/tests/run-make/archive-duplicate-names/rmake.rs @@ -0,0 +1,37 @@ +// When two object archives with the same filename are present, an iterator is supposed to +// inspect each object, recognize the duplication and extract each one to a different directory. +// This test checks that this duplicate handling behaviour has not been broken. +// See https://github.com/rust-lang/rust/pull/24439 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{cc, is_msvc, llvm_ar, rfs, run, rustc}; + +fn main() { + rfs::create_dir("a"); + rfs::create_dir("b"); + compile_obj_force_foo("a", "foo"); + compile_obj_force_foo("b", "bar"); + let mut ar = llvm_ar(); + ar.obj_to_ar().arg("libfoo.a"); + if is_msvc() { + ar.arg("a/foo.obj").arg("b/foo.obj").run(); + } else { + ar.arg("a/foo.o").arg("b/foo.o").run(); + } + rustc().input("foo.rs").run(); + rustc().input("bar.rs").run(); + run("bar"); +} + +#[track_caller] +pub fn compile_obj_force_foo(dir: &str, lib_name: &str) { + let obj_file = if is_msvc() { format!("{dir}/foo") } else { format!("{dir}/foo.o") }; + let src = format!("{lib_name}.c"); + if is_msvc() { + cc().arg("-c").out_exe(&obj_file).input(src).run(); + } else { + cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run(); + }; +} diff --git a/tests/run-make/link-path-order/Makefile b/tests/run-make/link-path-order/Makefile deleted file mode 100644 index a3831a63ac7e5..0000000000000 --- a/tests/run-make/link-path-order/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Verifies that the -L arguments given to the linker is in the same order -# as the -L arguments on the rustc command line. - -CORRECT_DIR=$(TMPDIR)/correct -WRONG_DIR=$(TMPDIR)/wrong - -F := $(call NATIVE_STATICLIB_FILE,foo) - -all: $(call NATIVE_STATICLIB,correct) $(call NATIVE_STATICLIB,wrong) - mkdir -p $(CORRECT_DIR) $(WRONG_DIR) - mv $(call NATIVE_STATICLIB,correct) $(CORRECT_DIR)/$(F) - mv $(call NATIVE_STATICLIB,wrong) $(WRONG_DIR)/$(F) - $(RUSTC) main.rs -o $(TMPDIR)/should_succeed -L $(CORRECT_DIR) -L $(WRONG_DIR) - $(call RUN,should_succeed) - $(RUSTC) main.rs -o $(TMPDIR)/should_fail -L $(WRONG_DIR) -L $(CORRECT_DIR) - $(call FAIL,should_fail) diff --git a/tests/run-make/link-path-order/rmake.rs b/tests/run-make/link-path-order/rmake.rs new file mode 100644 index 0000000000000..c8e41b2bcf8ac --- /dev/null +++ b/tests/run-make/link-path-order/rmake.rs @@ -0,0 +1,33 @@ +// The order in which "library search path" `-L` arguments are given to the command line rustc +// is important. These arguments must match the order of the linker's arguments. In this test, +// fetching the Wrong library before the Correct one causes a function to return 0 instead of the +// expected 1, causing a runtime panic, as expected. +// See https://github.com/rust-lang/rust/pull/16904 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib, path, rfs, run, run_fail, rustc, static_lib_name}; + +fn main() { + build_native_static_lib("correct"); + build_native_static_lib("wrong"); + rfs::create_dir("correct"); + rfs::create_dir("wrong"); + rfs::rename(static_lib_name("correct"), path("correct").join(static_lib_name("foo"))); + rfs::rename(static_lib_name("wrong"), path("wrong").join(static_lib_name("foo"))); + rustc() + .input("main.rs") + .output("should_succeed") + .library_search_path("correct") + .library_search_path("wrong") + .run(); + run("should_succeed"); + rustc() + .input("main.rs") + .output("should_fail") + .library_search_path("wrong") + .library_search_path("correct") + .run(); + run_fail("should_fail"); +} diff --git a/tests/run-make/lto-smoke-c/Makefile b/tests/run-make/lto-smoke-c/Makefile deleted file mode 100644 index f1ba3d95da292..0000000000000 --- a/tests/run-make/lto-smoke-c/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -# Apparently older versions of GCC segfault if -g is passed... -CC := $(CC:-g=) - -all: - $(RUSTC) foo.rs -C lto - $(CC) bar.c $(call STATICLIB,foo) \ - $(call OUT_EXE,bar) \ - $(EXTRACFLAGS) $(EXTRACXXFLAGS) - $(call RUN,bar) diff --git a/tests/run-make/lto-smoke-c/rmake.rs b/tests/run-make/lto-smoke-c/rmake.rs new file mode 100644 index 0000000000000..70760f730c05f --- /dev/null +++ b/tests/run-make/lto-smoke-c/rmake.rs @@ -0,0 +1,20 @@ +// LLVM's link-time-optimization (LTO) is a useful feature added to Rust in response +// to #10741. This test uses this feature with `-C lto` alongside a native C library, +// and checks that compilation and execution is successful. +// See https://github.com/rust-lang/rust/issues/10741 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{cc, extra_c_flags, extra_cxx_flags, run, rustc, static_lib_name}; + +fn main() { + rustc().input("foo.rs").arg("-Clto").run(); + cc().input("bar.c") + .arg(static_lib_name("foo")) + .out_exe("bar") + .args(extra_c_flags()) + .args(extra_cxx_flags()) + .run(); + run("bar"); +} diff --git a/tests/run-make/macos-fat-archive/Makefile b/tests/run-make/macos-fat-archive/Makefile deleted file mode 100644 index 0feb39a23cb54..0000000000000 --- a/tests/run-make/macos-fat-archive/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# only-apple - -include ../tools.mk - -"$(TMPDIR)"/libnative-library.a: native-library.c - $(CC) -arch arm64 -arch x86_64 native-library.c -c -o "$(TMPDIR)"/native-library.o - $(AR) crs "$(TMPDIR)"/libnative-library.a "$(TMPDIR)"/native-library.o - -all: "$(TMPDIR)"/libnative-library.a - $(RUSTC) lib.rs --crate-type=lib -L "native=$(TMPDIR)" -l static=native-library diff --git a/tests/run-make/macos-fat-archive/rmake.rs b/tests/run-make/macos-fat-archive/rmake.rs new file mode 100644 index 0000000000000..c9f0fa0769306 --- /dev/null +++ b/tests/run-make/macos-fat-archive/rmake.rs @@ -0,0 +1,20 @@ +// macOS (and iOS) has a concept of universal (fat) binaries which contain code for multiple CPU +// architectures in the same file. Apple is migrating from x86_64 to aarch64 CPUs, +// so for the next few years it will be important for macOS developers to +// build "fat" binaries (executables and cdylibs). + +// Rustc used to be unable to handle these special libraries, which was fixed in #98736. If +// compilation in this test is successful, the native fat library was successfully linked to. +// See https://github.com/rust-lang/rust/issues/55235 + +//@ only-apple + +use run_make_support::{cc, llvm_ar, rustc}; + +fn main() { + cc().args(&["-arch", "arm64", "-arch", "x86_64", "native-library.c", "-c"]) + .out_exe("native-library.o") + .run(); + llvm_ar().obj_to_ar().output_input("libnative-library.a", "native-library.o").run(); + rustc().input("lib.rs").crate_type("lib").arg("-lstatic=native-library").run(); +} diff --git a/tests/run-make/manual-link/Makefile b/tests/run-make/manual-link/Makefile deleted file mode 100644 index 8dbf0460fff50..0000000000000 --- a/tests/run-make/manual-link/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# ignore-cross-compile -include ../tools.mk - -all: $(TMPDIR)/libbar.a - $(RUSTC) foo.rs -lstatic=bar - $(RUSTC) main.rs - $(call RUN,main) diff --git a/tests/run-make/manual-link/rmake.rs b/tests/run-make/manual-link/rmake.rs new file mode 100644 index 0000000000000..1d3621722630a --- /dev/null +++ b/tests/run-make/manual-link/rmake.rs @@ -0,0 +1,16 @@ +// A smoke test for the `-l` command line rustc flag, which manually links to the selected +// library. Useful for native libraries, this is roughly equivalent to `#[link]` in Rust code. +// If compilation succeeds, the flag successfully linked the native library. +// See https://github.com/rust-lang/rust/pull/18470 + +//@ ignore-cross-compile +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib, run, rustc}; + +fn main() { + build_native_static_lib("bar"); + rustc().input("foo.rs").arg("-lstatic=bar").run(); + rustc().input("main.rs").run(); + run("main"); +} diff --git a/tests/ui/asm/binary_asm_labels.stderr b/tests/ui/asm/binary_asm_labels.stderr index 1f2943084f1eb..206d2da177982 100644 --- a/tests/ui/asm/binary_asm_labels.stderr +++ b/tests/ui/asm/binary_asm_labels.stderr @@ -4,7 +4,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("0: jmp 0b"); | ^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information = note: `#[deny(binary_asm_labels)]` on by default error: avoid using labels containing only the digits `0` and `1` in inline assembly @@ -13,7 +15,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("1: jmp 1b"); | ^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: avoid using labels containing only the digits `0` and `1` in inline assembly --> $DIR/binary_asm_labels.rs:13:15 @@ -21,7 +25,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("10: jmp 10b"); | ^^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: avoid using labels containing only the digits `0` and `1` in inline assembly --> $DIR/binary_asm_labels.rs:14:15 @@ -29,7 +35,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("01: jmp 01b"); | ^^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: avoid using labels containing only the digits `0` and `1` in inline assembly --> $DIR/binary_asm_labels.rs:15:15 @@ -37,7 +45,9 @@ error: avoid using labels containing only the digits `0` and `1` in inline assem LL | asm!("1001101: jmp 1001101b"); | ^^^^^^^ use a different label that doesn't start with `0` or `1` | - = note: an LLVM bug makes these labels ambiguous with a binary literal number + = help: start numbering with `2` instead + = note: an LLVM bug makes these labels ambiguous with a binary literal number on x86 + = note: see for more information error: aborting due to 5 previous errors diff --git a/tests/ui/asm/binary_asm_labels_allowed.rs b/tests/ui/asm/binary_asm_labels_allowed.rs new file mode 100644 index 0000000000000..a21ab8d70f5b4 --- /dev/null +++ b/tests/ui/asm/binary_asm_labels_allowed.rs @@ -0,0 +1,17 @@ +//@ build-pass +//@ only-aarch64 + +// The `binary_asm_labels` lint should only be raised on `x86`. Make sure it +// doesn't get raised on other platforms. + +use std::arch::asm; + +fn main() { + unsafe { + asm!("0: bl 0b"); + asm!("1: bl 1b"); + asm!("10: bl 10b"); + asm!("01: bl 01b"); + asm!("1001101: bl 1001101b"); + } +} diff --git a/tests/ui/empty/empty-struct-braces-expr.stderr b/tests/ui/empty/empty-struct-braces-expr.stderr index 4604ebeaa8b98..28c701443de4e 100644 --- a/tests/ui/empty/empty-struct-braces-expr.stderr +++ b/tests/ui/empty/empty-struct-braces-expr.stderr @@ -71,12 +71,22 @@ error[E0533]: expected value, found struct variant `E::Empty3` | LL | let e3 = E::Empty3; | ^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let e3 = E::Empty3 {}; + | ++ error[E0533]: expected value, found struct variant `E::Empty3` --> $DIR/empty-struct-braces-expr.rs:19:14 | LL | let e3 = E::Empty3(); | ^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let e3 = E::Empty3 {}; + | ~~ error[E0423]: expected function, tuple struct or tuple variant, found struct `XEmpty1` --> $DIR/empty-struct-braces-expr.rs:23:15 @@ -104,25 +114,34 @@ error[E0599]: no variant or associated item named `Empty3` found for enum `empty --> $DIR/empty-struct-braces-expr.rs:25:19 | LL | let xe3 = XE::Empty3; - | ^^^^^^ - | | - | variant or associated item not found in `XE` - | help: there is a variant with a similar name: `XEmpty3` + | ^^^^^^ variant or associated item not found in `XE` + | +help: there is a variant with a similar name + | +LL | let xe3 = XE::XEmpty3; + | ~~~~~~~ error[E0599]: no variant or associated item named `Empty3` found for enum `empty_struct::XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:26:19 | LL | let xe3 = XE::Empty3(); - | ^^^^^^ - | | - | variant or associated item not found in `XE` - | help: there is a variant with a similar name: `XEmpty3` + | ^^^^^^ variant or associated item not found in `XE` + | +help: there is a variant with a similar name + | +LL | let xe3 = XE::XEmpty3 {}; + | ~~~~~~~~~~ error[E0599]: no variant named `Empty1` found for enum `empty_struct::XE` --> $DIR/empty-struct-braces-expr.rs:28:9 | LL | XE::Empty1 {}; - | ^^^^^^ help: there is a variant with a similar name: `XEmpty3` + | ^^^^^^ + | +help: there is a variant with a similar name + | +LL | XE::XEmpty3 {}; + | ~~~~~~~~~~ error: aborting due to 9 previous errors diff --git a/tests/ui/enum/error-variant-with-turbofishes.stderr b/tests/ui/enum/error-variant-with-turbofishes.stderr index 66bed1c0d8505..ffc2862bfe02d 100644 --- a/tests/ui/enum/error-variant-with-turbofishes.stderr +++ b/tests/ui/enum/error-variant-with-turbofishes.stderr @@ -3,6 +3,11 @@ error[E0533]: expected value, found struct variant `Struct<0>::Variant` | LL | let x = Struct::<0>::Variant; | ^^^^^^^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let x = Struct::<0>::Variant { x: /* value */ }; + | ++++++++++++++++++ error: aborting due to 1 previous error diff --git a/tests/ui/expr/issue-22933-2.stderr b/tests/ui/expr/issue-22933-2.stderr index 8cda8598f3c4f..dadc31213621c 100644 --- a/tests/ui/expr/issue-22933-2.stderr +++ b/tests/ui/expr/issue-22933-2.stderr @@ -5,10 +5,12 @@ LL | enum Delicious { | -------------- variant or associated item `PIE` not found for this enum ... LL | ApplePie = Delicious::Apple as isize | Delicious::PIE as isize, - | ^^^ - | | - | variant or associated item not found in `Delicious` - | help: there is a variant with a similar name: `Pie` + | ^^^ variant or associated item not found in `Delicious` + | +help: there is a variant with a similar name + | +LL | ApplePie = Delicious::Apple as isize | Delicious::Pie as isize, + | ~~~ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-23217.stderr b/tests/ui/issues/issue-23217.stderr index 05ee0474c7838..d14da75ab72e6 100644 --- a/tests/ui/issues/issue-23217.stderr +++ b/tests/ui/issues/issue-23217.stderr @@ -4,10 +4,12 @@ error[E0599]: no variant or associated item named `A` found for enum `SomeEnum` LL | pub enum SomeEnum { | ----------------- variant or associated item `A` not found for this enum LL | B = SomeEnum::A, - | ^ - | | - | variant or associated item not found in `SomeEnum` - | help: there is a variant with a similar name: `B` + | ^ variant or associated item not found in `SomeEnum` + | +help: there is a variant with a similar name + | +LL | B = SomeEnum::B, + | ~ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-28971.stderr b/tests/ui/issues/issue-28971.stderr index 26057cbc2d1d8..7ca57d6b99817 100644 --- a/tests/ui/issues/issue-28971.stderr +++ b/tests/ui/issues/issue-28971.stderr @@ -5,10 +5,12 @@ LL | enum Foo { | -------- variant or associated item `Baz` not found for this enum ... LL | Foo::Baz(..) => (), - | ^^^ - | | - | variant or associated item not found in `Foo` - | help: there is a variant with a similar name: `Bar` + | ^^^ variant or associated item not found in `Foo` + | +help: there is a variant with a similar name + | +LL | Foo::Bar(..) => (), + | ~~~ error[E0596]: cannot borrow `f` as mutable, as it is not declared as mutable --> $DIR/issue-28971.rs:15:5 diff --git a/tests/ui/issues/issue-34209.stderr b/tests/ui/issues/issue-34209.stderr index 41bc60d03dd81..4c61d250f52d1 100644 --- a/tests/ui/issues/issue-34209.stderr +++ b/tests/ui/issues/issue-34209.stderr @@ -5,7 +5,12 @@ LL | enum S { | ------ variant `B` not found here ... LL | S::B {} => {}, - | ^ help: there is a variant with a similar name: `A` + | ^ + | +help: there is a variant with a similar name + | +LL | S::A {} => {}, + | ~ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-4736.stderr b/tests/ui/issues/issue-4736.stderr index 146dd1d57ce33..c1ae2c47b43a7 100644 --- a/tests/ui/issues/issue-4736.stderr +++ b/tests/ui/issues/issue-4736.stderr @@ -9,8 +9,8 @@ LL | let z = NonCopyable{ p: () }; | help: `NonCopyable` is a tuple struct, use the appropriate syntax | -LL | let z = NonCopyable(/* fields */); - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let z = NonCopyable(/* () */); + | ~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/issues/issue-80607.stderr b/tests/ui/issues/issue-80607.stderr index 20494f319ddd0..d096910297b79 100644 --- a/tests/ui/issues/issue-80607.stderr +++ b/tests/ui/issues/issue-80607.stderr @@ -9,8 +9,8 @@ LL | Enum::V1 { x } | help: `Enum::V1` is a tuple variant, use the appropriate syntax | -LL | Enum::V1(/* fields */) - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | Enum::V1(/* i32 */) + | ~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/numeric/numeric-fields.stderr b/tests/ui/numeric/numeric-fields.stderr index 668405ed638c1..8ab1718ff5e8f 100644 --- a/tests/ui/numeric/numeric-fields.stderr +++ b/tests/ui/numeric/numeric-fields.stderr @@ -9,8 +9,8 @@ LL | let s = S{0b1: 10, 0: 11}; | help: `S` is a tuple struct, use the appropriate syntax | -LL | let s = S(/* fields */); - | ~~~~~~~~~~~~~~~ +LL | let s = S(/* u8 */, /* u16 */); + | ~~~~~~~~~~~~~~~~~~~~~~ error[E0026]: struct `S` does not have a field named `0x1` --> $DIR/numeric-fields.rs:7:17 diff --git a/tests/ui/parser/struct-literal-variant-in-if.stderr b/tests/ui/parser/struct-literal-variant-in-if.stderr index 9f0c0074d674c..15f059f145bbb 100644 --- a/tests/ui/parser/struct-literal-variant-in-if.stderr +++ b/tests/ui/parser/struct-literal-variant-in-if.stderr @@ -47,6 +47,11 @@ error[E0533]: expected value, found struct variant `E::V` | LL | if x == E::V { field } {} | ^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | if x == (E::V { field }) {} + | + + error[E0308]: mismatched types --> $DIR/struct-literal-variant-in-if.rs:10:20 diff --git a/tests/ui/resolve/issue-18252.stderr b/tests/ui/resolve/issue-18252.stderr index 511b8da716fd4..6cb9c1f1dd211 100644 --- a/tests/ui/resolve/issue-18252.stderr +++ b/tests/ui/resolve/issue-18252.stderr @@ -3,6 +3,11 @@ error[E0533]: expected value, found struct variant `Foo::Variant` | LL | let f = Foo::Variant(42); | ^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let f = Foo::Variant { x: /* value */ }; + | ~~~~~~~~~~~~~~~~~~ error: aborting due to 1 previous error diff --git a/tests/ui/resolve/issue-19452.stderr b/tests/ui/resolve/issue-19452.stderr index eff89241fd218..aa7b752ca50b1 100644 --- a/tests/ui/resolve/issue-19452.stderr +++ b/tests/ui/resolve/issue-19452.stderr @@ -3,12 +3,22 @@ error[E0533]: expected value, found struct variant `Homura::Madoka` | LL | let homura = Homura::Madoka; | ^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let homura = Homura::Madoka { age: /* value */ }; + | ++++++++++++++++++++ error[E0533]: expected value, found struct variant `issue_19452_aux::Homura::Madoka` --> $DIR/issue-19452.rs:13:18 | LL | let homura = issue_19452_aux::Homura::Madoka; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let homura = issue_19452_aux::Homura::Madoka { age: /* value */ }; + | ++++++++++++++++++++ error: aborting due to 2 previous errors diff --git a/tests/ui/resolve/privacy-enum-ctor.stderr b/tests/ui/resolve/privacy-enum-ctor.stderr index ee3aecddcc342..12a6580048e17 100644 --- a/tests/ui/resolve/privacy-enum-ctor.stderr +++ b/tests/ui/resolve/privacy-enum-ctor.stderr @@ -295,6 +295,11 @@ error[E0533]: expected value, found struct variant `Z::Struct` | LL | let _: Z = Z::Struct; | ^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: Z = Z::Struct { s: /* value */ }; + | ++++++++++++++++++ error[E0618]: expected function, found enum variant `Z::Unit` --> $DIR/privacy-enum-ctor.rs:31:17 @@ -336,6 +341,11 @@ error[E0533]: expected value, found struct variant `m::E::Struct` | LL | let _: E = m::E::Struct; | ^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: E = m::E::Struct { s: /* value */ }; + | ++++++++++++++++++ error[E0618]: expected function, found enum variant `m::E::Unit` --> $DIR/privacy-enum-ctor.rs:47:16 @@ -377,6 +387,11 @@ error[E0533]: expected value, found struct variant `E::Struct` | LL | let _: E = E::Struct; | ^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: E = E::Struct { s: /* value */ }; + | ++++++++++++++++++ error[E0618]: expected function, found enum variant `E::Unit` --> $DIR/privacy-enum-ctor.rs:55:16 @@ -400,6 +415,11 @@ error[E0533]: expected value, found struct variant `m::n::Z::Struct` | LL | let _: Z = m::n::Z::Struct; | ^^^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: Z = m::n::Z::Struct { s: /* value */ }; + | ++++++++++++++++++ error: aborting due to 23 previous errors diff --git a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr index 40bb87c8a4066..1af86860ce1a6 100644 --- a/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/tests/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -129,6 +129,11 @@ error[E0533]: expected value, found struct variant `E::B` | LL | let _: E = E::B; | ^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | let _: E = E::B { a: /* value */ }; + | ++++++++++++++++++ error[E0308]: mismatched types --> $DIR/fn-or-tuple-struct-without-args.rs:37:20 diff --git a/tests/ui/suggestions/incorrect-variant-literal.rs b/tests/ui/suggestions/incorrect-variant-literal.rs new file mode 100644 index 0000000000000..aac2cc549044f --- /dev/null +++ b/tests/ui/suggestions/incorrect-variant-literal.rs @@ -0,0 +1,55 @@ +//@ only-linux +//@ compile-flags: --error-format=human --color=always + +enum Enum { + Unit, + Tuple(i32), + Struct { x: i32 }, +} + +fn main() { + Enum::Unit; + Enum::Tuple; + Enum::Struct; + Enum::Unit(); + Enum::Tuple(); + Enum::Struct(); + Enum::Unit {}; + Enum::Tuple {}; + Enum::Struct {}; + Enum::Unit(0); + Enum::Tuple(0); + Enum::Struct(0); + Enum::Unit { x: 0 }; + Enum::Tuple { x: 0 }; + Enum::Struct { x: 0 }; // ok + Enum::Unit(0, 0); + Enum::Tuple(0, 0); + Enum::Struct(0, 0); + Enum::Unit { x: 0, y: 0 }; + + Enum::Tuple { x: 0, y: 0 }; + + Enum::Struct { x: 0, y: 0 }; + Enum::unit; + Enum::tuple; + Enum::r#struct; + Enum::unit(); + Enum::tuple(); + Enum::r#struct(); + Enum::unit {}; + Enum::tuple {}; + Enum::r#struct {}; + Enum::unit(0); + Enum::tuple(0); + Enum::r#struct(0); + Enum::unit { x: 0 }; + Enum::tuple { x: 0 }; + Enum::r#struct { x: 0 }; + Enum::unit(0, 0); + Enum::tuple(0, 0); + Enum::r#struct(0, 0); + Enum::unit { x: 0, y: 0 }; + Enum::tuple { x: 0, y: 0 }; + Enum::r#struct { x: 0, y: 0 }; +} diff --git a/tests/ui/suggestions/incorrect-variant-literal.svg b/tests/ui/suggestions/incorrect-variant-literal.svg new file mode 100644 index 0000000000000..980a7b29a0080 --- /dev/null +++ b/tests/ui/suggestions/incorrect-variant-literal.svg @@ -0,0 +1,1028 @@ + + + + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:13:5 + + | + + LL | Enum::Struct; + + | ^^^^^^^^^^^^ not a value + + | + + help: you might have meant to create a new value of the struct + + | + + LL | Enum::Struct { x: /* value */ }; + + | ++++++++++++++++++ + + + + error[E0618]: expected function, found enum variant `Enum::Unit` + + --> $DIR/incorrect-variant-literal.rs:14:5 + + | + + LL | Unit, + + | ---- enum variant `Enum::Unit` defined here + + ... + + LL | Enum::Unit(); + + | ^^^^^^^^^^-- + + | | + + | call expression requires function + + | + + help: `Enum::Unit` is a unit enum variant, and does not take parentheses to be constructed + + | + + LL - Enum::Unit(); + + LL + Enum::Unit; + + | + + + + error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied + + --> $DIR/incorrect-variant-literal.rs:15:5 + + | + + LL | Enum::Tuple(); + + | ^^^^^^^^^^^-- argument #1 of type `i32` is missing + + | + + note: tuple variant defined here + + --> $DIR/incorrect-variant-literal.rs:6:5 + + | + + LL | Tuple(i32), + + | ^^^^^ + + help: provide the argument + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~ + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:16:5 + + | + + LL | Enum::Struct(); + + | ^^^^^^^^^^^^ not a value + + | + + help: you might have meant to create a new value of the struct + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~ + + + + error[E0063]: missing field `0` in initializer of `Enum` + + --> $DIR/incorrect-variant-literal.rs:18:5 + + | + + LL | Enum::Tuple {}; + + | ^^^^^^^^^^^ missing `0` + + + + error[E0063]: missing field `x` in initializer of `Enum` + + --> $DIR/incorrect-variant-literal.rs:19:5 + + | + + LL | Enum::Struct {}; + + | ^^^^^^^^^^^^ missing `x` + + + + error[E0618]: expected function, found `Enum` + + --> $DIR/incorrect-variant-literal.rs:20:5 + + | + + LL | Unit, + + | ---- `Enum::Unit` defined here + + ... + + LL | Enum::Unit(0); + + | ^^^^^^^^^^--- + + | | + + | call expression requires function + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:22:5 + + | + + LL | Enum::Struct(0); + + | ^^^^^^^^^^^^ not a value + + | + + help: you might have meant to create a new value of the struct + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~ + + + + error[E0559]: variant `Enum::Unit` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:23:18 + + | + + LL | Enum::Unit { x: 0 }; + + | ^ `Enum::Unit` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0559]: variant `Enum::Tuple` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:24:19 + + | + + LL | Tuple(i32), + + | ----- `Enum::Tuple` defined here + + ... + + LL | Enum::Tuple { x: 0 }; + + | ^ field does not exist + + | + + help: `Enum::Tuple` is a tuple variant, use the appropriate syntax + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~ + + + + error[E0618]: expected function, found `Enum` + + --> $DIR/incorrect-variant-literal.rs:26:5 + + | + + LL | Unit, + + | ---- `Enum::Unit` defined here + + ... + + LL | Enum::Unit(0, 0); + + | ^^^^^^^^^^------ + + | | + + | call expression requires function + + + + error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied + + --> $DIR/incorrect-variant-literal.rs:27:5 + + | + + LL | Enum::Tuple(0, 0); + + | ^^^^^^^^^^^ - unexpected argument #2 of type `{integer}` + + | + + note: tuple variant defined here + + --> $DIR/incorrect-variant-literal.rs:6:5 + + | + + LL | Tuple(i32), + + | ^^^^^ + + help: remove the extra argument + + | + + LL - Enum::Tuple(0, 0); + + LL + Enum::Tuple(0); + + | + + + + error[E0533]: expected value, found struct variant `Enum::Struct` + + --> $DIR/incorrect-variant-literal.rs:28:5 + + | + + LL | Enum::Struct(0, 0); + + | ^^^^^^^^^^^^ not a value + + | + + help: you might have meant to create a new value of the struct + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~ + + + + error[E0559]: variant `Enum::Unit` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:29:18 + + | + + LL | Enum::Unit { x: 0, y: 0 }; + + | ^ `Enum::Unit` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0559]: variant `Enum::Unit` has no field named `y` + + --> $DIR/incorrect-variant-literal.rs:29:24 + + | + + LL | Enum::Unit { x: 0, y: 0 }; + + | ^ `Enum::Unit` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0559]: variant `Enum::Tuple` has no field named `x` + + --> $DIR/incorrect-variant-literal.rs:31:19 + + | + + LL | Tuple(i32), + + | ----- `Enum::Tuple` defined here + + ... + + LL | Enum::Tuple { x: 0, y: 0 }; + + | ^ field does not exist + + | + + help: `Enum::Tuple` is a tuple variant, use the appropriate syntax + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~ + + + + error[E0559]: variant `Enum::Tuple` has no field named `y` + + --> $DIR/incorrect-variant-literal.rs:31:25 + + | + + LL | Tuple(i32), + + | ----- `Enum::Tuple` defined here + + ... + + LL | Enum::Tuple { x: 0, y: 0 }; + + | ^ field does not exist + + | + + help: `Enum::Tuple` is a tuple variant, use the appropriate syntax + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~ + + + + error[E0559]: variant `Enum::Struct` has no field named `y` + + --> $DIR/incorrect-variant-literal.rs:33:26 + + | + + LL | Enum::Struct { x: 0, y: 0 }; + + | ^ `Enum::Struct` does not have this field + + | + + = note: all struct fields are already assigned + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:34:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit; + + | ^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name (notice the capitalization difference) + + | + + LL | Enum::Unit; + + | ~~~~ + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:35:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `tuple` not found for this enum + + ... + + LL | Enum::tuple; + + | ^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:36:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct; + + | ^^^^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:37:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit(); + + | ^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Unit; + + | ~~~~ + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:38:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `tuple` not found for this enum + + ... + + LL | Enum::tuple(); + + | ^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:39:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct(); + + | ^^^^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant named `unit` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:40:11 + + | + + LL | enum Enum { + + | --------- variant `unit` not found here + + ... + + LL | Enum::unit {}; + + | ^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Unit; + + | ~~~~ + + + + error[E0599]: no variant named `tuple` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:41:11 + + | + + LL | enum Enum { + + | --------- variant `tuple` not found here + + ... + + LL | Enum::tuple {}; + + | ^^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant named `r#struct` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:42:11 + + | + + LL | enum Enum { + + | --------- variant `r#struct` not found here + + ... + + LL | Enum::r#struct {}; + + | ^^^^^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:43:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit(0); + + | ^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Unit; + + | ~~~~ + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:44:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `tuple` not found for this enum + + ... + + LL | Enum::tuple(0); + + | ^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Tuple(0); + + | ~~~~~ + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:45:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct(0); + + | ^^^^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant named `unit` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:46:11 + + | + + LL | enum Enum { + + | --------- variant `unit` not found here + + ... + + LL | Enum::unit { x: 0 }; + + | ^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Unit; + + | ~~~~ + + + + error[E0599]: no variant named `tuple` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:47:11 + + | + + LL | enum Enum { + + | --------- variant `tuple` not found here + + ... + + LL | Enum::tuple { x: 0 }; + + | ^^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant named `r#struct` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:48:11 + + | + + LL | enum Enum { + + | --------- variant `r#struct` not found here + + ... + + LL | Enum::r#struct { x: 0 }; + + | ^^^^^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant or associated item named `unit` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:49:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `unit` not found for this enum + + ... + + LL | Enum::unit(0, 0); + + | ^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Unit; + + | ~~~~ + + + + error[E0599]: no variant or associated item named `tuple` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:50:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `tuple` not found for this enum + + ... + + LL | Enum::tuple(0, 0); + + | ^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~ ~~~~~~~~~ + + + + error[E0599]: no variant or associated item named `r#struct` found for enum `Enum` in the current scope + + --> $DIR/incorrect-variant-literal.rs:51:11 + + | + + LL | enum Enum { + + | --------- variant or associated item `r#struct` not found for this enum + + ... + + LL | Enum::r#struct(0, 0); + + | ^^^^^^^^ variant or associated item not found in `Enum` + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant named `unit` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:52:11 + + | + + LL | enum Enum { + + | --------- variant `unit` not found here + + ... + + LL | Enum::unit { x: 0, y: 0 }; + + | ^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Unit; + + | ~~~~ + + + + error[E0599]: no variant named `tuple` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:53:11 + + | + + LL | enum Enum { + + | --------- variant `tuple` not found here + + ... + + LL | Enum::tuple { x: 0, y: 0 }; + + | ^^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Tuple(/* i32 */); + + | ~~~~~~~~~~~~~~~~ + + + + error[E0599]: no variant named `r#struct` found for enum `Enum` + + --> $DIR/incorrect-variant-literal.rs:54:11 + + | + + LL | enum Enum { + + | --------- variant `r#struct` not found here + + ... + + LL | Enum::r#struct { x: 0, y: 0 }; + + | ^^^^^^^^ + + | + + help: there is a variant with a similar name + + | + + LL | Enum::Struct { x: /* value */ }; + + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + error: aborting due to 39 previous errors + + + + Some errors have detailed explanations: E0061, E0063, E0533, E0559, E0599, E0618. + + For more information about an error, try `rustc --explain E0061`. + + + + + + diff --git a/tests/ui/suggestions/nested-non-tuple-tuple-struct.stderr b/tests/ui/suggestions/nested-non-tuple-tuple-struct.stderr index 948f09fc3fab7..4523333850c06 100644 --- a/tests/ui/suggestions/nested-non-tuple-tuple-struct.stderr +++ b/tests/ui/suggestions/nested-non-tuple-tuple-struct.stderr @@ -9,8 +9,8 @@ LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); | help: `S` is a tuple struct, use the appropriate syntax | -LL | let _x = (S(/* fields */), S { x: 3.0, y: 4.0 }); - | ~~~~~~~~~~~~~~~ +LL | let _x = (S(/* f32 */, /* f32 */), S { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~~~~~~~~~ error[E0560]: struct `S` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:8:27 @@ -23,8 +23,8 @@ LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); | help: `S` is a tuple struct, use the appropriate syntax | -LL | let _x = (S(/* fields */), S { x: 3.0, y: 4.0 }); - | ~~~~~~~~~~~~~~~ +LL | let _x = (S(/* f32 */, /* f32 */), S { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~~~~~~~~~ error[E0560]: struct `S` has no field named `x` --> $DIR/nested-non-tuple-tuple-struct.rs:8:41 @@ -37,8 +37,8 @@ LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); | help: `S` is a tuple struct, use the appropriate syntax | -LL | let _x = (S { x: 1.0, y: 2.0 }, S(/* fields */)); - | ~~~~~~~~~~~~~~~ +LL | let _x = (S { x: 1.0, y: 2.0 }, S(/* f32 */, /* f32 */)); + | ~~~~~~~~~~~~~~~~~~~~~~~ error[E0560]: struct `S` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:8:49 @@ -51,8 +51,8 @@ LL | let _x = (S { x: 1.0, y: 2.0 }, S { x: 3.0, y: 4.0 }); | help: `S` is a tuple struct, use the appropriate syntax | -LL | let _x = (S { x: 1.0, y: 2.0 }, S(/* fields */)); - | ~~~~~~~~~~~~~~~ +LL | let _x = (S { x: 1.0, y: 2.0 }, S(/* f32 */, /* f32 */)); + | ~~~~~~~~~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `x` --> $DIR/nested-non-tuple-tuple-struct.rs:13:22 @@ -65,8 +65,8 @@ LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); | help: `E::V` is a tuple variant, use the appropriate syntax | -LL | let _y = (E::V(/* fields */), E::V { x: 3.0, y: 4.0 }); - | ~~~~~~~~~~~~~~~~~~ +LL | let _y = (E::V(/* f32 */, /* f32 */), E::V { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:13:30 @@ -79,8 +79,8 @@ LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); | help: `E::V` is a tuple variant, use the appropriate syntax | -LL | let _y = (E::V(/* fields */), E::V { x: 3.0, y: 4.0 }); - | ~~~~~~~~~~~~~~~~~~ +LL | let _y = (E::V(/* f32 */, /* f32 */), E::V { x: 3.0, y: 4.0 }); + | ~~~~~~~~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `x` --> $DIR/nested-non-tuple-tuple-struct.rs:13:47 @@ -93,8 +93,8 @@ LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); | help: `E::V` is a tuple variant, use the appropriate syntax | -LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V(/* fields */)); - | ~~~~~~~~~~~~~~~~~~ +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V(/* f32 */, /* f32 */)); + | ~~~~~~~~~~~~~~~~~~~~~~ error[E0559]: variant `E::V` has no field named `y` --> $DIR/nested-non-tuple-tuple-struct.rs:13:55 @@ -107,8 +107,8 @@ LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V { x: 3.0, y: 4.0 }); | help: `E::V` is a tuple variant, use the appropriate syntax | -LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V(/* fields */)); - | ~~~~~~~~~~~~~~~~~~ +LL | let _y = (E::V { x: 1.0, y: 2.0 }, E::V(/* f32 */, /* f32 */)); + | ~~~~~~~~~~~~~~~~~~~~~~ error: aborting due to 8 previous errors diff --git a/tests/ui/suggestions/suggest-variants.stderr b/tests/ui/suggestions/suggest-variants.stderr index a422bc6563541..d93bf2d8cd72c 100644 --- a/tests/ui/suggestions/suggest-variants.stderr +++ b/tests/ui/suggestions/suggest-variants.stderr @@ -5,7 +5,12 @@ LL | enum Shape { | ---------- variant `Squareee` not found here ... LL | println!("My shape is {:?}", Shape::Squareee { size: 5}); - | ^^^^^^^^ help: there is a variant with a similar name: `Square` + | ^^^^^^^^ + | +help: there is a variant with a similar name + | +LL | println!("My shape is {:?}", Shape::Square { size: 5}); + | ~~~~~~ error[E0599]: no variant named `Circl` found for enum `Shape` --> $DIR/suggest-variants.rs:13:41 @@ -14,7 +19,12 @@ LL | enum Shape { | ---------- variant `Circl` not found here ... LL | println!("My shape is {:?}", Shape::Circl { size: 5}); - | ^^^^^ help: there is a variant with a similar name: `Circle` + | ^^^^^ + | +help: there is a variant with a similar name + | +LL | println!("My shape is {:?}", Shape::Circle { size: 5}); + | ~~~~~~ error[E0599]: no variant named `Rombus` found for enum `Shape` --> $DIR/suggest-variants.rs:14:41 @@ -32,10 +42,12 @@ LL | enum Shape { | ---------- variant or associated item `Squareee` not found for this enum ... LL | Shape::Squareee; - | ^^^^^^^^ - | | - | variant or associated item not found in `Shape` - | help: there is a variant with a similar name: `Square` + | ^^^^^^^^ variant or associated item not found in `Shape` + | +help: there is a variant with a similar name + | +LL | Shape::Square { size: /* value */ }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0599]: no variant or associated item named `Circl` found for enum `Shape` in the current scope --> $DIR/suggest-variants.rs:16:12 @@ -44,10 +56,12 @@ LL | enum Shape { | ---------- variant or associated item `Circl` not found for this enum ... LL | Shape::Circl; - | ^^^^^ - | | - | variant or associated item not found in `Shape` - | help: there is a variant with a similar name: `Circle` + | ^^^^^ variant or associated item not found in `Shape` + | +help: there is a variant with a similar name + | +LL | Shape::Circle { radius: /* value */ }; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0599]: no variant or associated item named `Rombus` found for enum `Shape` in the current scope --> $DIR/suggest-variants.rs:17:12 diff --git a/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr b/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr index c9ac99ede6f31..c21c59397c79e 100644 --- a/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr +++ b/tests/ui/type-alias-enum-variants/incorrect-variant-form-through-alias-caught.stderr @@ -3,6 +3,11 @@ error[E0533]: expected value, found struct variant `Alias::Braced` | LL | Alias::Braced; | ^^^^^^^^^^^^^ not a value + | +help: you might have meant to create a new value of the struct + | +LL | Alias::Braced {}; + | ++ error[E0533]: expected unit struct, unit variant or constant, found struct variant `Alias::Braced` --> $DIR/incorrect-variant-form-through-alias-caught.rs:10:9