Auto merge of #127956 - tgross35:rollup-8ten7pk, r=tgross35
Rollup of 7 pull requests Successful merges: - #121533 (Handle .init_array link_section specially on wasm) - #127825 (Migrate `macos-fat-archive`, `manual-link` and `archive-duplicate-names` `run-make` tests to rmake) - #127891 (Tweak suggestions when using incorrect type of enum literal) - #127902 (`collect_tokens_trailing_token` cleanups) - #127928 (Migrate `lto-smoke-c` and `link-path-order` `run-make` tests to rmake) - #127935 (Change `binary_asm_labels` to only fire on x86 and x86_64) - #127953 ([compiletest] Search *.a when getting dynamic libraries on AIX) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
11e57241f1
@ -495,8 +495,14 @@ fn codegen_static_item(&self, def_id: DefId) {
|
||||
}
|
||||
|
||||
// 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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1087,7 +1087,7 @@ pub fn lower_assoc_path(
|
||||
);
|
||||
|
||||
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 @@ pub fn lower_assoc_path(
|
||||
.collect::<Vec<Symbol>>(),
|
||||
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::<Vec<_>>()
|
||||
.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::<Vec<_>>()
|
||||
.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(
|
||||
|
@ -519,7 +519,15 @@ pub(crate) fn check_expr_path(
|
||||
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 @@ fn report_unknown_field(
|
||||
);
|
||||
|
||||
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 @@ fn report_unknown_field(
|
||||
),
|
||||
);
|
||||
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::<Vec<_>>().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::<Vec<_>>().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,
|
||||
);
|
||||
}
|
||||
|
@ -53,7 +53,7 @@
|
||||
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 @@ fn opt_find_breakable(&mut self, target_id: HirId) -> Option<&mut BreakableCtxt<
|
||||
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::<Vec<_>>()
|
||||
.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()
|
||||
|
@ -1596,16 +1596,127 @@ fn report_no_match_method_error(
|
||||
// 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::<Vec<_>>(),
|
||||
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::<Vec<_>>()
|
||||
.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::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
));
|
||||
}
|
||||
(_, [arg]) if inputs.len() != args.len() => {
|
||||
// Replace arguments.
|
||||
suggestion.push((
|
||||
arg.span,
|
||||
inputs
|
||||
.iter()
|
||||
.map(|i| format!("/* {i} */"))
|
||||
.collect::<Vec<String>>()
|
||||
.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::<Vec<String>>()
|
||||
.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::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
)];
|
||||
}
|
||||
}
|
||||
}
|
||||
err.multipart_suggestion_verbose(
|
||||
"there is a variant with a similar name",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1023,7 +1023,8 @@ fn check_pat_path(
|
||||
}
|
||||
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 @@ fn check_pat_path(
|
||||
let e = report_unexpected_variant_res(
|
||||
tcx,
|
||||
res,
|
||||
None,
|
||||
qpath,
|
||||
pat.span,
|
||||
E0533,
|
||||
@ -1189,7 +1191,7 @@ fn check_pat_tuple_struct(
|
||||
};
|
||||
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
|
||||
};
|
||||
|
@ -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 <https://bugs.llvm.org/show_bug.cgi?id=36144> 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 <https://github.com/llvm/llvm-project/issues/99547> for more information
|
||||
|
||||
lint_invalid_asm_label_format_arg = avoid using named labels in inline assembly
|
||||
.help = only local labels of the form `<number>:` should be used in inline asm
|
||||
|
@ -66,6 +66,7 @@
|
||||
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 @@ fn is_zero(expr: &hir::Expr<'_>) -> bool {
|
||||
///
|
||||
/// ### 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 @@ fn is_zero(expr: &hir::Expr<'_>) -> bool {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
/// This will produce:
|
||||
///
|
||||
/// ```text
|
||||
/// error: avoid using labels containing only the digits `0` and `1` in inline assembly
|
||||
/// --> <source>: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 <https://github.com/llvm/llvm-project/issues/99547> 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 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
|
||||
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 => (),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -303,17 +303,13 @@ pub fn parse_inner_attributes(&mut self) -> PResult<'a, ast::AttrVec> {
|
||||
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 @@ fn parse_meta_item_inner(&mut self) -> PResult<'a, ast::NestedMetaItem> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
|
@ -17,12 +17,12 @@
|
||||
///
|
||||
/// 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 @@ fn to_attr_token_stream(&self) -> AttrTokenStream {
|
||||
}
|
||||
|
||||
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<R: HasAttrs + HasTokens>(
|
||||
&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 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
|
||||
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 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
|
||||
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 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
|
||||
|
||||
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 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
|
||||
.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 @@ pub fn collect_tokens_trailing_token<R: HasAttrs + HasTokens>(
|
||||
*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();
|
||||
}
|
||||
|
@ -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<ReplaceRange>,
|
||||
inner_attr_ranges: FxHashMap<AttrId, ReplaceRange>,
|
||||
inner_attr_ranges: FxHashMap<AttrId, Range<u32>>,
|
||||
}
|
||||
|
||||
/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
|
||||
@ -425,6 +426,11 @@ pub fn new(
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
..
|
||||
}"
|
||||
);
|
||||
|
@ -94,6 +94,8 @@ fn get_lib_name(lib: &str, aux_type: AuxType) -> Option<String> {
|
||||
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)
|
||||
}),
|
||||
|
@ -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
|
||||
|
@ -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)
|
37
tests/run-make/archive-duplicate-names/rmake.rs
Normal file
37
tests/run-make/archive-duplicate-names/rmake.rs
Normal file
@ -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();
|
||||
};
|
||||
}
|
@ -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)
|
33
tests/run-make/link-path-order/rmake.rs
Normal file
33
tests/run-make/link-path-order/rmake.rs
Normal file
@ -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");
|
||||
}
|
@ -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)
|
20
tests/run-make/lto-smoke-c/rmake.rs
Normal file
20
tests/run-make/lto-smoke-c/rmake.rs
Normal file
@ -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");
|
||||
}
|
@ -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
|
20
tests/run-make/macos-fat-archive/rmake.rs
Normal file
20
tests/run-make/macos-fat-archive/rmake.rs
Normal file
@ -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();
|
||||
}
|
@ -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)
|
16
tests/run-make/manual-link/rmake.rs
Normal file
16
tests/run-make/manual-link/rmake.rs
Normal file
@ -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");
|
||||
}
|
@ -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 <https://github.com/llvm/llvm-project/issues/99547> 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 <https://github.com/llvm/llvm-project/issues/99547> 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 <https://github.com/llvm/llvm-project/issues/99547> 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 <https://github.com/llvm/llvm-project/issues/99547> 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 <https://github.com/llvm/llvm-project/issues/99547> for more information
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
|
17
tests/ui/asm/binary_asm_labels_allowed.rs
Normal file
17
tests/ui/asm/binary_asm_labels_allowed.rs
Normal file
@ -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");
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
55
tests/ui/suggestions/incorrect-variant-literal.rs
Normal file
55
tests/ui/suggestions/incorrect-variant-literal.rs
Normal file
@ -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 };
|
||||
}
|
1028
tests/ui/suggestions/incorrect-variant-literal.svg
Normal file
1028
tests/ui/suggestions/incorrect-variant-literal.svg
Normal file
File diff suppressed because it is too large
Load Diff
After Width: | Height: | Size: 78 KiB |
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user