Auto merge of #138974 - Zalathar:rollup-568cpmy, r=Zalathar

Rollup of 7 pull requests

Successful merges:

 - #138483 (Target modifiers fix for bool flags without value)
 - #138818 (Don't produce debug information for compiler-introduced-vars when desugaring assignments.)
 - #138898 (Mostly parser: Eliminate code that's been dead / semi-dead since the removal of type ascription syntax)
 - #138930 (Add bootstrap step diff to CI job analysis)
 - #138954 (Ensure `define_opaque` attrs are accounted for in HIR hash)
 - #138959 (Revert "Make MatchPairTree::place non-optional")
 - #138967 (Fix typo in error message)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-03-26 11:03:12 +00:00
commit f1bc669636
63 changed files with 895 additions and 744 deletions

View file

@ -545,14 +545,6 @@ pub struct Block {
pub rules: BlockCheckMode,
pub span: Span,
pub tokens: Option<LazyAttrTokenStream>,
/// The following *isn't* a parse error, but will cause multiple errors in following stages.
/// ```compile_fail
/// let x = {
/// foo: var
/// };
/// ```
/// #34255
pub could_be_bare_literal: bool,
}
/// A match pattern.

View file

@ -1222,7 +1222,7 @@ fn walk_mt<T: MutVisitor>(vis: &mut T, MutTy { ty, mutbl: _ }: &mut MutTy) {
}
pub fn walk_block<T: MutVisitor>(vis: &mut T, block: &mut P<Block>) {
let Block { id, stmts, rules: _, span, tokens, could_be_bare_literal: _ } = block.deref_mut();
let Block { id, stmts, rules: _, span, tokens } = block.deref_mut();
vis.visit_id(id);
stmts.flat_map_in_place(|stmt| vis.flat_map_stmt(stmt));
visit_lazy_tts(vis, tokens);

View file

@ -1067,7 +1067,7 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef)
}
pub fn walk_block<'a, V: Visitor<'a>>(visitor: &mut V, block: &'a Block) -> V::Result {
let Block { stmts, id: _, rules: _, span: _, tokens: _, could_be_bare_literal: _ } = block;
let Block { stmts, id: _, rules: _, span: _, tokens: _ } = block;
walk_list!(visitor, visit_stmt, stmts);
V::Result::output()
}

View file

@ -623,7 +623,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
// Don't hash unless necessary, because it's expensive.
let (opt_hash_including_bodies, attrs_hash) =
self.tcx.hash_owner_nodes(node, &bodies, &attrs);
self.tcx.hash_owner_nodes(node, &bodies, &attrs, define_opaque);
let num_nodes = self.item_local_id_counter.as_usize();
let (nodes, parenting) = index::index_hir(self.tcx, node, &bodies, num_nodes);
let nodes = hir::OwnerNodes { opt_hash_including_bodies, nodes, bodies };

View file

@ -395,7 +395,6 @@ mod llvm_enzyme {
tokens: None,
rules: unsf,
span,
could_be_bare_literal: false,
};
let unsf_expr = ecx.expr_block(P(unsf_block));
let blackbox_call_expr = ecx.expr_path(ecx.path(span, blackbox_path));

View file

@ -110,7 +110,6 @@ fn call_unreachable(cx: &ExtCtxt<'_>, span: Span) -> P<ast::Expr> {
rules: ast::BlockCheckMode::Unsafe(ast::CompilerGenerated),
span,
tokens: None,
could_be_bare_literal: false,
}))
}

View file

@ -286,7 +286,6 @@ impl<'a> ExtCtxt<'a> {
rules: BlockCheckMode::Default,
span,
tokens: None,
could_be_bare_literal: false,
})
}

View file

@ -118,12 +118,23 @@ metadata_incompatible_rustc =
metadata_incompatible_target_modifiers =
mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}`
.note = `{$flag_name_prefixed}={$flag_local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$flag_extern_value}` in dependency `{$extern_crate}`
.note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}`
.help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
metadata_incompatible_target_modifiers_help_allow = if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch={$flag_name}` to silence this error
metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$flag_extern_value}` in this crate or `{$flag_name_prefixed}={$flag_local_value}` in `{$extern_crate}`
metadata_incompatible_target_modifiers_help_fix = set `{$flag_name_prefixed}={$extern_value}` in this crate or `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}`
metadata_incompatible_target_modifiers_help_fix_l_missed = set `{$flag_name_prefixed}={$extern_value}` in this crate or unset `{$flag_name_prefixed}` in `{$extern_crate}`
metadata_incompatible_target_modifiers_help_fix_r_missed = unset `{$flag_name_prefixed}` in this crate or set `{$flag_name_prefixed}={$local_value}` in `{$extern_crate}`
metadata_incompatible_target_modifiers_l_missed =
mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}`
.note = unset `{$flag_name_prefixed}` in this crate is incompatible with `{$flag_name_prefixed}={$extern_value}` in dependency `{$extern_crate}`
.help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
metadata_incompatible_target_modifiers_r_missed =
mixing `{$flag_name_prefixed}` will cause an ABI mismatch in crate `{$local_crate}`
.note = `{$flag_name_prefixed}={$local_value}` in this crate is incompatible with unset `{$flag_name_prefixed}` in dependency `{$extern_crate}`
.help = the `{$flag_name_prefixed}` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
metadata_incompatible_wasm_link =
`wasm_import_module` is incompatible with other arguments in `#[link]` attributes

View file

@ -358,30 +358,58 @@ impl CStore {
) {
let span = krate.spans.inner_span.shrink_to_lo();
let allowed_flag_mismatches = &tcx.sess.opts.cg.unsafe_allow_abi_mismatch;
let name = tcx.crate_name(LOCAL_CRATE);
let local_crate = tcx.crate_name(LOCAL_CRATE);
let tmod_extender = |tmod: &TargetModifier| (tmod.extend(), tmod.clone());
let report_diff = |prefix: &String,
opt_name: &String,
flag_local_value: &String,
flag_extern_value: &String| {
flag_local_value: Option<&String>,
flag_extern_value: Option<&String>| {
if allowed_flag_mismatches.contains(&opt_name) {
return;
}
tcx.dcx().emit_err(errors::IncompatibleTargetModifiers {
span,
extern_crate: data.name(),
local_crate: name,
flag_name: opt_name.clone(),
flag_name_prefixed: format!("-{}{}", prefix, opt_name),
flag_local_value: flag_local_value.to_string(),
flag_extern_value: flag_extern_value.to_string(),
});
let extern_crate = data.name();
let flag_name = opt_name.clone();
let flag_name_prefixed = format!("-{}{}", prefix, opt_name);
match (flag_local_value, flag_extern_value) {
(Some(local_value), Some(extern_value)) => {
tcx.dcx().emit_err(errors::IncompatibleTargetModifiers {
span,
extern_crate,
local_crate,
flag_name,
flag_name_prefixed,
local_value: local_value.to_string(),
extern_value: extern_value.to_string(),
})
}
(None, Some(extern_value)) => {
tcx.dcx().emit_err(errors::IncompatibleTargetModifiersLMissed {
span,
extern_crate,
local_crate,
flag_name,
flag_name_prefixed,
extern_value: extern_value.to_string(),
})
}
(Some(local_value), None) => {
tcx.dcx().emit_err(errors::IncompatibleTargetModifiersRMissed {
span,
extern_crate,
local_crate,
flag_name,
flag_name_prefixed,
local_value: local_value.to_string(),
})
}
(None, None) => panic!("Incorrect target modifiers report_diff(None, None)"),
};
};
let mut it1 = mods.iter().map(tmod_extender);
let mut it2 = dep_mods.iter().map(tmod_extender);
let mut left_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;
let mut right_name_val: Option<(ExtendedTargetModifierInfo, TargetModifier)> = None;
let no_val = "*".to_string();
loop {
left_name_val = left_name_val.or_else(|| it1.next());
right_name_val = right_name_val.or_else(|| it2.next());
@ -389,26 +417,31 @@ impl CStore {
(Some(l), Some(r)) => match l.1.opt.cmp(&r.1.opt) {
cmp::Ordering::Equal => {
if l.0.tech_value != r.0.tech_value {
report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &r.1.value_name);
report_diff(
&l.0.prefix,
&l.0.name,
Some(&l.1.value_name),
Some(&r.1.value_name),
);
}
left_name_val = None;
right_name_val = None;
}
cmp::Ordering::Greater => {
report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name);
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
right_name_val = None;
}
cmp::Ordering::Less => {
report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val);
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
left_name_val = None;
}
},
(Some(l), None) => {
report_diff(&l.0.prefix, &l.0.name, &l.1.value_name, &no_val);
report_diff(&l.0.prefix, &l.0.name, Some(&l.1.value_name), None);
left_name_val = None;
}
(None, Some(r)) => {
report_diff(&r.0.prefix, &r.0.name, &no_val, &r.1.value_name);
report_diff(&r.0.prefix, &r.0.name, None, Some(&r.1.value_name));
right_name_val = None;
}
(None, None) => break,

View file

@ -759,8 +759,40 @@ pub struct IncompatibleTargetModifiers {
pub local_crate: Symbol,
pub flag_name: String,
pub flag_name_prefixed: String,
pub flag_local_value: String,
pub flag_extern_value: String,
pub local_value: String,
pub extern_value: String,
}
#[derive(Diagnostic)]
#[diag(metadata_incompatible_target_modifiers_l_missed)]
#[help]
#[note]
#[help(metadata_incompatible_target_modifiers_help_fix_l_missed)]
#[help(metadata_incompatible_target_modifiers_help_allow)]
pub struct IncompatibleTargetModifiersLMissed {
#[primary_span]
pub span: Span,
pub extern_crate: Symbol,
pub local_crate: Symbol,
pub flag_name: String,
pub flag_name_prefixed: String,
pub extern_value: String,
}
#[derive(Diagnostic)]
#[diag(metadata_incompatible_target_modifiers_r_missed)]
#[help]
#[note]
#[help(metadata_incompatible_target_modifiers_help_fix_r_missed)]
#[help(metadata_incompatible_target_modifiers_help_allow)]
pub struct IncompatibleTargetModifiersRMissed {
#[primary_span]
pub span: Span,
pub extern_crate: Symbol,
pub local_crate: Symbol,
pub flag_name: String,
pub flag_name_prefixed: String,
pub local_value: String,
}
#[derive(Diagnostic)]

View file

@ -14,7 +14,7 @@ use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId};
use rustc_hir::*;
use rustc_macros::{Decodable, Encodable, HashStable};
use rustc_span::{ErrorGuaranteed, ExpnId};
use rustc_span::{ErrorGuaranteed, ExpnId, Span};
use crate::query::Providers;
use crate::ty::{EarlyBinder, ImplSubject, TyCtxt};
@ -157,6 +157,7 @@ impl<'tcx> TyCtxt<'tcx> {
node: OwnerNode<'_>,
bodies: &SortedMap<ItemLocalId, &Body<'_>>,
attrs: &SortedMap<ItemLocalId, &[Attribute]>,
define_opaque: Option<&[(Span, LocalDefId)]>,
) -> (Option<Fingerprint>, Option<Fingerprint>) {
if self.needs_crate_hash() {
self.with_stable_hashing_context(|mut hcx| {
@ -168,6 +169,10 @@ impl<'tcx> TyCtxt<'tcx> {
let mut stable_hasher = StableHasher::new();
attrs.hash_stable(&mut hcx, &mut stable_hasher);
// Hash the defined opaque types, which are not present in the attrs.
define_opaque.hash_stable(&mut hcx, &mut stable_hasher);
let h2 = stable_hasher.finish();
(Some(h1), Some(h2))
})

View file

@ -1288,7 +1288,8 @@ impl<'tcx> TyCtxtFeed<'tcx, LocalDefId> {
let bodies = Default::default();
let attrs = hir::AttributeMap::EMPTY;
let (opt_hash_including_bodies, _) = self.tcx.hash_owner_nodes(node, &bodies, &attrs.map);
let (opt_hash_including_bodies, _) =
self.tcx.hash_owner_nodes(node, &bodies, &attrs.map, attrs.define_opaque);
let node = node.into();
self.opt_hir_owner_nodes(Some(self.tcx.arena.alloc(hir::OwnerNodes {
opt_hash_including_bodies,

View file

@ -115,7 +115,6 @@ impl<'tcx> MatchPairTree<'tcx> {
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
}
// Place can be none if the pattern refers to a non-captured place in a closure.
let place = place_builder.try_to_place(cx);
let mut subpairs = Vec::new();
let test_case = match pattern.kind {
@ -321,7 +320,7 @@ impl<'tcx> MatchPairTree<'tcx> {
if let Some(test_case) = test_case {
// This pattern is refutable, so push a new match-pair node.
match_pairs.push(MatchPairTree {
place: place.expect("refutable patterns should always have a place to inspect"),
place,
test_case,
subpairs,
pattern_ty: pattern.ty,

View file

@ -13,13 +13,13 @@ use std::sync::Arc;
use rustc_abi::VariantIdx;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir::{BindingMode, ByRef};
use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node};
use rustc_middle::bug;
use rustc_middle::middle::region;
use rustc_middle::mir::{self, *};
use rustc_middle::thir::{self, *};
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty};
use rustc_span::{BytePos, Pos, Span, Symbol};
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
use tracing::{debug, instrument};
use crate::builder::ForGuard::{self, OutsideGuard, RefWithinGuard};
@ -1279,7 +1279,13 @@ impl<'tcx> TestCase<'tcx> {
#[derive(Debug, Clone)]
pub(crate) struct MatchPairTree<'tcx> {
/// This place...
place: Place<'tcx>,
///
/// ---
/// This can be `None` if it referred to a non-captured place in a closure.
///
/// Invariant: Can only be `None` when `test_case` is `Or`.
/// Therefore this must be `Some(_)` after or-pattern expansion.
place: Option<Place<'tcx>>,
/// ... must pass this test...
test_case: TestCase<'tcx>,
@ -2099,9 +2105,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Extract the match-pair from the highest priority candidate
let match_pair = &candidates[0].match_pairs[0];
let test = self.pick_test_for_match_pair(match_pair);
// Unwrap is ok after simplification.
let match_place = match_pair.place.unwrap();
debug!(?test, ?match_pair);
(match_pair.place, test)
(match_place, test)
}
/// Given a test, we partition the input candidates into several buckets.
@ -2796,13 +2804,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
)))),
};
let for_arm_body = self.local_decls.push(local);
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
value: VarDebugInfoContents::Place(for_arm_body.into()),
composite: None,
argument_index: None,
});
if self.should_emit_debug_info_for_binding(name, var_id) {
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
value: VarDebugInfoContents::Place(for_arm_body.into()),
composite: None,
argument_index: None,
});
}
let locals = if has_guard.0 {
let ref_for_guard = self.local_decls.push(LocalDecl::<'tcx> {
// This variable isn't mutated but has a name, so has to be
@ -2815,13 +2825,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
BindingForm::RefForGuard,
))),
});
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
value: VarDebugInfoContents::Place(ref_for_guard.into()),
composite: None,
argument_index: None,
});
if self.should_emit_debug_info_for_binding(name, var_id) {
self.var_debug_info.push(VarDebugInfo {
name,
source_info: debug_source_info,
value: VarDebugInfoContents::Place(ref_for_guard.into()),
composite: None,
argument_index: None,
});
}
LocalsForNode::ForGuard { ref_for_guard, for_arm_body }
} else {
LocalsForNode::One(for_arm_body)
@ -2829,4 +2841,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
debug!(?locals);
self.var_indices.insert(var_id, locals);
}
/// Some bindings are introduced when producing HIR from the AST and don't
/// actually exist in the source. Skip producing debug info for those when
/// we can recognize them.
fn should_emit_debug_info_for_binding(&self, name: Symbol, var_id: LocalVarId) -> bool {
// For now we only recognize the output of desugaring assigns.
if name != sym::lhs {
return true;
}
let tcx = self.tcx;
for (_, node) in tcx.hir_parent_iter(var_id.0) {
// FIXME(khuey) at what point is it safe to bail on the iterator?
// Can we stop at the first non-Pat node?
if matches!(node, Node::LetStmt(&LetStmt { source: LocalSource::AssignDesugar(_), .. }))
{
return false;
}
}
true
}
}

View file

@ -470,8 +470,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// than one, but it'd be very unusual to have two sides that
// both require tests; you'd expect one side to be simplified
// away.)
let (match_pair_index, match_pair) =
candidate.match_pairs.iter().enumerate().find(|&(_, mp)| mp.place == test_place)?;
let (match_pair_index, match_pair) = candidate
.match_pairs
.iter()
.enumerate()
.find(|&(_, mp)| mp.place == Some(test_place))?;
// If true, the match pair is completely entailed by its corresponding test
// branch, so it can be removed. If false, the match pair is _compatible_
@ -514,7 +517,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
candidate
.match_pairs
.iter()
.any(|mp| mp.place == test_place && is_covering_range(&mp.test_case))
.any(|mp| mp.place == Some(test_place) && is_covering_range(&mp.test_case))
};
if sorted_candidates
.get(&TestBranch::Failure)

View file

@ -173,10 +173,14 @@ impl<'a, 'b, 'tcx> FakeBorrowCollector<'a, 'b, 'tcx> {
// }
// ```
// Hence we fake borrow using a deep borrow.
self.fake_borrow(match_pair.place, FakeBorrowKind::Deep);
if let Some(place) = match_pair.place {
self.fake_borrow(place, FakeBorrowKind::Deep);
}
} else {
// Insert a Shallow borrow of any place that is switched on.
self.fake_borrow(match_pair.place, FakeBorrowKind::Shallow);
if let Some(place) = match_pair.place {
self.fake_borrow(place, FakeBorrowKind::Shallow);
}
for subpair in &match_pair.subpairs {
self.visit_match_pair(subpair);

View file

@ -757,10 +757,6 @@ parse_struct_literal_body_without_path =
struct literal body without path
.suggestion = you might have forgotten to add the struct literal inside the block
parse_struct_literal_needing_parens =
invalid struct literal
.suggestion = you might need to surround the struct literal with parentheses
parse_struct_literal_not_allowed_here = struct literals are not allowed here
.suggestion = surround the struct literal with parentheses

View file

@ -1272,24 +1272,6 @@ pub(crate) struct StructLiteralBodyWithoutPathSugg {
pub after: Span,
}
#[derive(Diagnostic)]
#[diag(parse_struct_literal_needing_parens)]
pub(crate) struct StructLiteralNeedingParens {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub sugg: StructLiteralNeedingParensSugg,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
pub(crate) struct StructLiteralNeedingParensSugg {
#[suggestion_part(code = "(")]
pub before: Span,
#[suggestion_part(code = ")")]
pub after: Span,
}
#[derive(Diagnostic)]
#[diag(parse_unmatched_angle_brackets)]
pub(crate) struct UnmatchedAngleBrackets {

View file

@ -40,9 +40,8 @@ use crate::errors::{
HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,
IncorrectSemicolon, IncorrectUseOfAwait, IncorrectUseOfUse, PatternMethodParamWithoutBody,
QuestionMarkInType, QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator,
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
StructLiteralBodyWithoutPathSugg, SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma,
TernaryOperator, UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
};
use crate::parser::attr::InnerAttrPolicy;
@ -949,7 +948,6 @@ impl<'a> Parser<'a> {
lo: Span,
s: BlockCheckMode,
maybe_struct_name: token::Token,
can_be_struct_literal: bool,
) -> Option<PResult<'a, P<Block>>> {
if self.token.is_ident() && self.look_ahead(1, |t| t == &token::Colon) {
// We might be having a struct literal where people forgot to include the path:
@ -975,47 +973,23 @@ impl<'a> Parser<'a> {
// fn foo() -> Foo { Path {
// field: value,
// } }
let guar = err.delay_as_bug();
err.cancel();
self.restore_snapshot(snapshot);
let mut tail = self.mk_block(
let guar = self.dcx().emit_err(StructLiteralBodyWithoutPath {
span: expr.span,
sugg: StructLiteralBodyWithoutPathSugg {
before: expr.span.shrink_to_lo(),
after: expr.span.shrink_to_hi(),
},
});
Ok(self.mk_block(
thin_vec![self.mk_stmt_err(expr.span, guar)],
s,
lo.to(self.prev_token.span),
);
tail.could_be_bare_literal = true;
if maybe_struct_name.is_ident() && can_be_struct_literal {
// Account for `if Example { a: one(), }.is_pos() {}`.
// expand `before` so that we take care of module path such as:
// `foo::Bar { ... } `
// we expect to suggest `(foo::Bar { ... })` instead of `foo::(Bar { ... })`
let sm = self.psess.source_map();
let before = maybe_struct_name.span.shrink_to_lo();
if let Ok(extend_before) = sm.span_extend_prev_while(before, |t| {
t.is_alphanumeric() || t == ':' || t == '_'
}) {
Err(self.dcx().create_err(StructLiteralNeedingParens {
span: maybe_struct_name.span.to(expr.span),
sugg: StructLiteralNeedingParensSugg {
before: extend_before.shrink_to_lo(),
after: expr.span.shrink_to_hi(),
},
}))
} else {
return None;
}
} else {
self.dcx().emit_err(StructLiteralBodyWithoutPath {
span: expr.span,
sugg: StructLiteralBodyWithoutPathSugg {
before: expr.span.shrink_to_lo(),
after: expr.span.shrink_to_hi(),
},
});
Ok(tail)
}
))
}
(Err(err), Ok(tail)) => {
// We have a block tail that contains a somehow valid type ascription expr.
// We have a block tail that contains a somehow valid expr.
err.cancel();
Ok(tail)
}
@ -1025,10 +999,7 @@ impl<'a> Parser<'a> {
self.consume_block(exp!(OpenBrace), exp!(CloseBrace), ConsumeClosingDelim::Yes);
Err(err)
}
(Ok(_), Ok(mut tail)) => {
tail.could_be_bare_literal = true;
Ok(tail)
}
(Ok(_), Ok(tail)) => Ok(tail),
});
}
None

View file

@ -2296,7 +2296,7 @@ impl<'a> Parser<'a> {
});
}
let (attrs, blk) = self.parse_block_common(lo, blk_mode, true, None)?;
let (attrs, blk) = self.parse_block_common(lo, blk_mode, None)?;
Ok(self.mk_expr_with_attrs(blk.span, ExprKind::Block(blk, opt_label), attrs))
}
@ -3474,19 +3474,9 @@ impl<'a> Parser<'a> {
}
fn is_certainly_not_a_block(&self) -> bool {
// `{ ident, ` and `{ ident: ` cannot start a block.
self.look_ahead(1, |t| t.is_ident())
&& (
// `{ ident, ` cannot start a block.
self.look_ahead(2, |t| t == &token::Comma)
|| self.look_ahead(2, |t| t == &token::Colon)
&& (
// `{ ident: token, ` cannot start a block.
self.look_ahead(4, |t| t == &token::Comma)
// `{ ident: ` cannot start a block unless it's a type ascription
// `ident: Type`.
|| self.look_ahead(3, |t| !t.can_begin_type())
)
)
&& self.look_ahead(2, |t| t == &token::Comma || t == &token::Colon)
}
fn maybe_parse_struct_expr(

View file

@ -2538,7 +2538,7 @@ impl<'a> Parser<'a> {
*sig_hi = self.prev_token.span;
(AttrVec::new(), None)
} else if self.check(exp!(OpenBrace)) || self.token.is_whole_block() {
self.parse_block_common(self.token.span, BlockCheckMode::Default, false, None)
self.parse_block_common(self.token.span, BlockCheckMode::Default, None)
.map(|(attrs, body)| (attrs, Some(body)))?
} else if self.token == token::Eq {
// Recover `fn foo() = $expr;`.

View file

@ -668,7 +668,7 @@ impl<'a> Parser<'a> {
&mut self,
loop_header: Option<Span>,
) -> PResult<'a, (AttrVec, P<Block>)> {
self.parse_block_common(self.token.span, BlockCheckMode::Default, true, loop_header)
self.parse_block_common(self.token.span, BlockCheckMode::Default, loop_header)
}
/// Parses a block. Inner attributes are allowed, block labels are not.
@ -679,7 +679,6 @@ impl<'a> Parser<'a> {
&mut self,
lo: Span,
blk_mode: BlockCheckMode,
can_be_struct_literal: bool,
loop_header: Option<Span>,
) -> PResult<'a, (AttrVec, P<Block>)> {
maybe_whole!(self, NtBlock, |block| (AttrVec::new(), block));
@ -691,12 +690,7 @@ impl<'a> Parser<'a> {
}
let attrs = self.parse_inner_attributes()?;
let tail = match self.maybe_suggest_struct_literal(
lo,
blk_mode,
maybe_ident,
can_be_struct_literal,
) {
let tail = match self.maybe_suggest_struct_literal(lo, blk_mode, maybe_ident) {
Some(tail) => tail?,
None => self.parse_block_tail(lo, blk_mode, AttemptLocalParseRecovery::Yes)?,
};
@ -1043,14 +1037,7 @@ impl<'a> Parser<'a> {
rules: BlockCheckMode,
span: Span,
) -> P<Block> {
P(Block {
stmts,
id: DUMMY_NODE_ID,
rules,
span,
tokens: None,
could_be_bare_literal: false,
})
P(Block { stmts, id: DUMMY_NODE_ID, rules, span, tokens: None })
}
pub(super) fn mk_stmt(&self, span: Span, kind: StmtKind) -> Stmt {

View file

@ -675,11 +675,6 @@ struct DiagMetadata<'ast> {
/// they are used (in a `break` or `continue` statement)
unused_labels: FxIndexMap<NodeId, Span>,
/// Only used for better errors on `let x = { foo: bar };`.
/// In the case of a parse error with `let x = { foo: bar, };`, this isn't needed, it's only
/// needed for cases where this parses as a correct type ascription.
current_block_could_be_bare_struct_literal: Option<Span>,
/// Only used for better errors on `let <pat>: <expr, not type>;`.
current_let_binding: Option<(Span, Option<Span>, Option<Span>)>,
@ -4661,13 +4656,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.ribs[ValueNS].push(Rib::new(RibKind::Normal));
}
let prev = self.diag_metadata.current_block_could_be_bare_struct_literal.take();
if let (true, [Stmt { kind: StmtKind::Expr(expr), .. }]) =
(block.could_be_bare_literal, &block.stmts[..])
&& let ExprKind::Type(..) = expr.kind
{
self.diag_metadata.current_block_could_be_bare_struct_literal = Some(block.span);
}
// Descend into the block.
for stmt in &block.stmts {
if let StmtKind::Item(ref item) = stmt.kind
@ -4681,7 +4669,6 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.visit_stmt(stmt);
}
self.diag_metadata.current_block_could_be_bare_struct_literal = prev;
// Move back up.
self.parent_scope.module = orig_module;

View file

@ -450,7 +450,6 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
err.span_suggestion_verbose(sugg.0, sugg.1, &sugg.2, Applicability::MaybeIncorrect);
}
self.suggest_bare_struct_literal(&mut err);
self.suggest_changing_type_to_const_param(&mut err, res, source, span);
self.explain_functions_in_pattern(&mut err, res, source);
@ -1279,19 +1278,6 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
}
}
fn suggest_bare_struct_literal(&mut self, err: &mut Diag<'_>) {
if let Some(span) = self.diag_metadata.current_block_could_be_bare_struct_literal {
err.multipart_suggestion(
"you might have meant to write a `struct` literal",
vec![
(span.shrink_to_lo(), "{ SomeStruct ".to_string()),
(span.shrink_to_hi(), "}".to_string()),
],
Applicability::HasPlaceholders,
);
}
}
fn explain_functions_in_pattern(
&mut self,
err: &mut Diag<'_>,

View file

@ -94,45 +94,49 @@ fn tmod_push_impl(
tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
tmods: &mut Vec<TargetModifier>,
) {
tmods.push(TargetModifier { opt, value_name: tmod_vals.get(&opt).cloned().unwrap_or_default() })
if let Some(v) = tmod_vals.get(&opt) {
tmods.push(TargetModifier { opt, value_name: v.clone() })
}
}
macro_rules! tmod_push {
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $mods:expr, $tmod_vals:expr) => {
tmod_push_impl(
OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name),
$tmod_vals,
$mods,
);
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr) => {
if *$opt_expr != $init {
tmod_push_impl(
OptionsTargetModifiers::$struct_name($tmod_enum_name::$opt_name),
$tmod_vals,
$mods,
);
}
};
}
macro_rules! gather_tmods {
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[SUBSTRUCT], [TARGET_MODIFIER]) => {
compile_error!("SUBSTRUCT can't be target modifier");
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[UNTRACKED], [TARGET_MODIFIER]) => {
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals)
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED], [TARGET_MODIFIER]) => {
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals)
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED_NO_CRATE_HASH], [TARGET_MODIFIER]) => {
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $mods, $tmod_vals)
tmod_push!($struct_name, $tmod_enum_name, $opt_name, $opt_expr, $init, $mods, $tmod_vals)
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[SUBSTRUCT], []) => {
$opt_expr.gather_target_modifiers($mods, $tmod_vals);
};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[UNTRACKED], []) => {{}};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED], []) => {{}};
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $mods:expr, $tmod_vals:expr,
($struct_name:ident, $tmod_enum_name:ident, $opt_name:ident, $opt_expr:expr, $init:expr, $mods:expr, $tmod_vals:expr,
[TRACKED_NO_CRATE_HASH], []) => {{}};
}
@ -474,7 +478,8 @@ macro_rules! tmod_enum {
$($pout)*
Self::$opt => {
let mut parsed : $t = Default::default();
parse::$parse(&mut parsed, Some($puser_value));
let val = if $puser_value.is_empty() { None } else { Some($puser_value) };
parse::$parse(&mut parsed, val);
ExtendedTargetModifierInfo {
prefix: $prefix.to_string(),
name: stringify!($opt).to_string().replace('_', "-"),
@ -569,7 +574,7 @@ macro_rules! options {
_tmod_vals: &BTreeMap<OptionsTargetModifiers, String>,
) {
$({
gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, _mods, _tmod_vals,
gather_tmods!($struct_name, $tmod_enum_name, $opt, &self.$opt, $init, _mods, _tmod_vals,
[$dep_tracking_marker], [$($tmod),*]);
})*
}
@ -681,10 +686,9 @@ fn build_options<O: Default>(
),
}
}
if let Some(tmod) = *tmod
&& let Some(value) = value
{
target_modifiers.insert(tmod, value.to_string());
if let Some(tmod) = *tmod {
let v = value.map_or(String::new(), ToOwned::to_owned);
target_modifiers.insert(tmod, v);
}
}
None => early_dcx.early_fatal(format!("unknown {outputname} option: `{key}`")),

View file

@ -324,7 +324,7 @@ impl TcpStream {
}
Ok(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.local_port, 0, 0)))
}
_ => Err(io::const_error!(io::ErrorKind::InvalidInput, "tnternal error")),
_ => Err(io::const_error!(io::ErrorKind::InvalidInput, "internal error")),
}
}

View file

@ -109,6 +109,8 @@ pub struct BuildStep {
pub r#type: String,
pub children: Vec<BuildStep>,
pub duration: Duration,
// Full name of the step, including all parent names
pub full_name: String,
}
impl BuildStep {
@ -116,7 +118,7 @@ impl BuildStep {
/// The most important thing is that the build step aggregates the
/// durations of all children, so that it can be easily accessed.
pub fn from_invocation(invocation: &JsonInvocation) -> Self {
fn parse(node: &JsonNode) -> Option<BuildStep> {
fn parse(node: &JsonNode, parent_name: &str) -> Option<BuildStep> {
match node {
JsonNode::RustbuildStep {
type_: kind,
@ -124,11 +126,14 @@ impl BuildStep {
duration_excluding_children_sec,
..
} => {
let children: Vec<_> = children.into_iter().filter_map(parse).collect();
let full_name = format!("{parent_name}-{kind}");
let children: Vec<_> =
children.into_iter().filter_map(|s| parse(s, &full_name)).collect();
let children_duration = children.iter().map(|c| c.duration).sum::<Duration>();
Some(BuildStep {
r#type: kind.to_string(),
children,
full_name,
duration: children_duration
+ Duration::from_secs_f64(*duration_excluding_children_sec),
})
@ -138,8 +143,13 @@ impl BuildStep {
}
let duration = Duration::from_secs_f64(invocation.duration_including_children_sec);
let children: Vec<_> = invocation.children.iter().filter_map(parse).collect();
Self { r#type: "total".to_string(), children, duration }
// The root "total" step is kind of a virtual step that encompasses all other steps,
// but it is not a real parent of the other steps.
// We thus start the parent name hierarchy here and use an empty string
// as the parent name of the top-level steps.
let children: Vec<_> = invocation.children.iter().filter_map(|s| parse(s, "")).collect();
Self { r#type: "total".to_string(), children, duration, full_name: "total".to_string() }
}
pub fn find_all_by_type(&self, r#type: &str) -> Vec<&Self> {
@ -156,33 +166,38 @@ impl BuildStep {
child.find_by_type(r#type, result);
}
}
/// Returns a Vec with all substeps, ordered by their hierarchical order.
/// The first element of the tuple is the depth of a given step.
pub fn linearize_steps(&self) -> Vec<(u32, &BuildStep)> {
let mut substeps: Vec<(u32, &BuildStep)> = Vec::new();
fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) {
substeps.push((level, step));
for child in &step.children {
visit(child, level + 1, substeps);
}
}
visit(self, 0, &mut substeps);
substeps
}
}
/// Writes build steps into a nice indented table.
pub fn format_build_steps(root: &BuildStep) -> String {
use std::fmt::Write;
let mut substeps: Vec<(u32, &BuildStep)> = Vec::new();
fn visit<'a>(step: &'a BuildStep, level: u32, substeps: &mut Vec<(u32, &'a BuildStep)>) {
substeps.push((level, step));
for child in &step.children {
visit(child, level + 1, substeps);
}
}
visit(root, 0, &mut substeps);
let mut output = String::new();
for (level, step) in substeps {
let label = format!(
"{}{}",
".".repeat(level as usize),
// Bootstrap steps can be generic and thus contain angle brackets (<...>).
// However, Markdown interprets these as HTML, so we need to escap ethem.
step.r#type.replace('<', "&lt;").replace('>', "&gt;")
);
for (level, step) in root.linearize_steps() {
let label = format!("{}{}", ".".repeat(level as usize), escape_step_name(step));
writeln!(output, "{label:.<65}{:>8.2}s", step.duration.as_secs_f64()).unwrap();
}
output
}
/// Bootstrap steps can be generic and thus contain angle brackets (<...>).
/// However, Markdown interprets these as HTML, so we need to escap ethem.
pub fn escape_step_name(step: &BuildStep) -> String {
step.r#type.replace('<', "&lt;").replace('>', "&gt;")
}

View file

@ -107,6 +107,7 @@ dependencies = [
"build_helper",
"clap",
"csv",
"diff",
"glob-match",
"insta",
"serde",
@ -241,6 +242,12 @@ dependencies = [
"powerfmt",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "displaydoc"
version = "0.2.5"

View file

@ -7,6 +7,7 @@ edition = "2021"
anyhow = "1"
clap = { version = "4.5", features = ["derive"] }
csv = "1"
diff = "0.1"
glob-match = "0.2"
serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9"

View file

@ -1,33 +1,134 @@
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt::Debug;
use build_helper::metrics::{
BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, format_build_steps,
BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name,
format_build_steps,
};
use crate::metrics;
use crate::metrics::{JobMetrics, JobName, get_test_suites};
use crate::utils::{output_details, pluralize};
pub fn output_bootstrap_stats(metrics: &JsonRoot) {
/// Outputs durations of individual bootstrap steps from the gathered bootstrap invocations,
/// and also a table with summarized information about executed tests.
pub fn output_bootstrap_stats(metrics: &JsonRoot, parent_metrics: Option<&JsonRoot>) {
if !metrics.invocations.is_empty() {
println!("# Bootstrap steps");
record_bootstrap_step_durations(&metrics);
record_bootstrap_step_durations(&metrics, parent_metrics);
record_test_suites(&metrics);
}
}
fn record_bootstrap_step_durations(metrics: &JsonRoot) {
fn record_bootstrap_step_durations(metrics: &JsonRoot, parent_metrics: Option<&JsonRoot>) {
let parent_steps: HashMap<String, BuildStep> = parent_metrics
.map(|metrics| {
metrics
.invocations
.iter()
.map(|invocation| {
(invocation.cmdline.clone(), BuildStep::from_invocation(invocation))
})
.collect()
})
.unwrap_or_default();
for invocation in &metrics.invocations {
let step = BuildStep::from_invocation(invocation);
let table = format_build_steps(&step);
eprintln!("Step `{}`\n{table}\n", invocation.cmdline);
output_details(&invocation.cmdline, || {
output_details(&format!("{} (steps)", invocation.cmdline), || {
println!("<pre><code>{table}</code></pre>");
});
// If there was a parent bootstrap invocation with the same cmdline, diff it
if let Some(parent_step) = parent_steps.get(&invocation.cmdline) {
let table = format_build_step_diffs(&step, parent_step);
let duration_before = parent_step.duration.as_secs();
let duration_after = step.duration.as_secs();
output_details(
&format!("{} (diff) ({duration_before}s -> {duration_after}s)", invocation.cmdline),
|| {
println!("{table}");
},
);
}
}
eprintln!("Recorded {} bootstrap invocation(s)", metrics.invocations.len());
}
/// Creates a table that displays a diff between the durations of steps between
/// two bootstrap invocations.
/// It also shows steps that were missing before/after.
fn format_build_step_diffs(current: &BuildStep, parent: &BuildStep) -> String {
use std::fmt::Write;
// Helper struct that compares steps by their full name
struct StepByName<'a>((u32, &'a BuildStep));
impl<'a> PartialEq for StepByName<'a> {
fn eq(&self, other: &Self) -> bool {
self.0.1.full_name.eq(&other.0.1.full_name)
}
}
fn get_steps(step: &BuildStep) -> Vec<StepByName> {
step.linearize_steps().into_iter().map(|v| StepByName(v)).collect()
}
let steps_before = get_steps(parent);
let steps_after = get_steps(current);
let mut table = "| Step | Before | After | Change |\n".to_string();
writeln!(table, "|:-----|-------:|------:|-------:|").unwrap();
// Try to match removed, added and same steps using a classic diff algorithm
for result in diff::slice(&steps_before, &steps_after) {
let (step, before, after, change) = match result {
// The step was found both before and after
diff::Result::Both(before, after) => {
let duration_before = before.0.1.duration;
let duration_after = after.0.1.duration;
let pct_change = duration_after.as_secs_f64() / duration_before.as_secs_f64();
let pct_change = pct_change * 100.0;
// Normalize around 100, to get + for regression and - for improvements
let pct_change = pct_change - 100.0;
(
before,
format!("{:.2}s", duration_before.as_secs_f64()),
format!("{:.2}s", duration_after.as_secs_f64()),
format!("{pct_change:.1}%"),
)
}
// The step was only found in the parent, so it was likely removed
diff::Result::Left(removed) => (
removed,
format!("{:.2}s", removed.0.1.duration.as_secs_f64()),
"".to_string(),
"(removed)".to_string(),
),
// The step was only found in the current commit, so it was likely added
diff::Result::Right(added) => (
added,
"".to_string(),
format!("{:.2}s", added.0.1.duration.as_secs_f64()),
"(added)".to_string(),
),
};
let prefix = ".".repeat(step.0.0 as usize);
writeln!(
table,
"| {prefix}{} | {before} | {after} | {change} |",
escape_step_name(step.0.1),
)
.unwrap();
}
table
}
fn record_test_suites(metrics: &JsonRoot) {
let suites = metrics::get_test_suites(&metrics);
@ -82,8 +183,7 @@ fn render_table(suites: BTreeMap<String, TestSuiteRecord>) -> String {
table
}
/// Computes a post merge CI analysis report of test differences
/// between the `parent` and `current` commits.
/// Outputs a report of test differences between the `parent` and `current` commits.
pub fn output_test_diffs(job_metrics: HashMap<JobName, JobMetrics>) {
let aggregated_test_diffs = aggregate_test_diffs(&job_metrics);
report_test_diffs(aggregated_test_diffs);

View file

@ -144,31 +144,35 @@ fn postprocess_metrics(
job_name: Option<String>,
) -> anyhow::Result<()> {
let metrics = load_metrics(&metrics_path)?;
output_bootstrap_stats(&metrics);
let (Some(parent), Some(job_name)) = (parent, job_name) else {
return Ok(());
};
if let (Some(parent), Some(job_name)) = (parent, job_name) {
// This command is executed also on PR builds, which might not have parent metrics
// available, because some PR jobs don't run on auto builds, and PR jobs do not upload metrics
// due to missing permissions.
// To avoid having to detect if this is a PR job, and to avoid having failed steps in PR jobs,
// we simply print an error if the parent metrics were not found, but otherwise exit
// successfully.
match download_job_metrics(&job_name, &parent).context("cannot download parent metrics") {
Ok(parent_metrics) => {
output_bootstrap_stats(&metrics, Some(&parent_metrics));
// This command is executed also on PR builds, which might not have parent metrics
// available, because some PR jobs don't run on auto builds, and PR jobs do not upload metrics
// due to missing permissions.
// To avoid having to detect if this is a PR job, and to avoid having failed steps in PR jobs,
// we simply print an error if the parent metrics were not found, but otherwise exit
// successfully.
match download_job_metrics(&job_name, &parent).context("cannot download parent metrics") {
Ok(parent_metrics) => {
let job_metrics = HashMap::from([(
job_name,
JobMetrics { parent: Some(parent_metrics), current: metrics },
)]);
output_test_diffs(job_metrics);
}
Err(error) => {
eprintln!("Metrics for job `{job_name}` and commit `{parent}` not found: {error:?}");
let job_metrics = HashMap::from([(
job_name,
JobMetrics { parent: Some(parent_metrics), current: metrics },
)]);
output_test_diffs(job_metrics);
return Ok(());
}
Err(error) => {
eprintln!(
"Metrics for job `{job_name}` and commit `{parent}` not found: {error:?}"
);
}
}
}
output_bootstrap_stats(&metrics, None);
Ok(())
}

View file

@ -176,7 +176,6 @@ fn rewrite_closure_with_block(
.first()
.map(|attr| attr.span.to(body.span))
.unwrap_or(body.span),
could_be_bare_literal: false,
};
let block = crate::expr::rewrite_block_with_visitor(
context,

View file

@ -423,7 +423,6 @@ fn rewrite_empty_macro_def_body(
rules: ast::BlockCheckMode::Default,
span,
tokens: None,
could_be_bare_literal: false,
};
block.rewrite_result(context, shape)
}

View file

@ -3189,7 +3189,6 @@ ui/parser/issues/issue-108495-dec.rs
ui/parser/issues/issue-110014.rs
ui/parser/issues/issue-111148.rs
ui/parser/issues/issue-111416.rs
ui/parser/issues/issue-111692.rs
ui/parser/issues/issue-112188.rs
ui/parser/issues/issue-112458.rs
ui/parser/issues/issue-113110-non-item-at-module-root.rs

View file

@ -0,0 +1,18 @@
//@ compile-flags: -g -Zmir-opt-level=0
#![crate_type = "lib"]
#[inline(never)]
fn swizzle(a: u32, b: u32, c: u32) -> (u32, (u32, u32)) {
(b, (c, a))
}
pub fn work() {
let mut a = 1;
let mut b = 2;
let mut c = 3;
(a, (b, c)) = swizzle(a, b, c);
println!("{a} {b} {c}");
}
// CHECK-NOT: !DILocalVariable(name: "lhs",

View file

@ -0,0 +1,19 @@
//@ revisions: rpass1 cfail2
#![feature(type_alias_impl_trait)]
pub type Foo = impl Sized;
#[cfg_attr(rpass1, define_opaque())]
#[cfg_attr(cfail2, define_opaque(Foo))]
fn a() {
//[cfail2]~^ ERROR item does not constrain `Foo::{opaque#0}`
let _: Foo = b();
}
#[define_opaque(Foo)]
fn b() -> Foo {
()
}
fn main() {}

View file

@ -114,7 +114,6 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
rules: BlockCheckMode::Default,
span: DUMMY_SP,
tokens: None,
could_be_bare_literal: false,
});
iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None)));
}

View file

@ -0,0 +1,11 @@
//@ edition:2024
//@ check-pass
pub fn f(x: (u32, u32)) {
let _ = || {
let ((0, a) | (a, _)) = x;
a
};
}
fn main() {}

View file

@ -1,32 +0,0 @@
mod module {
#[derive(Eq, PartialEq)]
pub struct Type {
pub x: u8,
pub y: u8,
}
pub const C: u8 = 32u8;
}
fn test(x: module::Type) {
if x == module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
}
}
fn test2(x: module::Type) {
if x ==module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
}
}
fn test3(x: module::Type) {
if x == Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
}
}
fn test4(x: module::Type) {
if x == demo_module::Type { x: module::C, y: 1 } { //~ ERROR invalid struct literal
}
}
fn main() { }

View file

@ -1,46 +0,0 @@
error: invalid struct literal
--> $DIR/issue-111692.rs:12:21
|
LL | if x == module::Type { x: module::C, y: 1 } {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: you might need to surround the struct literal with parentheses
|
LL | if x == (module::Type { x: module::C, y: 1 }) {
| + +
error: invalid struct literal
--> $DIR/issue-111692.rs:17:20
|
LL | if x ==module::Type { x: module::C, y: 1 } {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: you might need to surround the struct literal with parentheses
|
LL | if x ==(module::Type { x: module::C, y: 1 }) {
| + +
error: invalid struct literal
--> $DIR/issue-111692.rs:23:13
|
LL | if x == Type { x: module::C, y: 1 } {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: you might need to surround the struct literal with parentheses
|
LL | if x == (Type { x: module::C, y: 1 }) {
| + +
error: invalid struct literal
--> $DIR/issue-111692.rs:28:26
|
LL | if x == demo_module::Type { x: module::C, y: 1 } {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: you might need to surround the struct literal with parentheses
|
LL | if x == (demo_module::Type { x: module::C, y: 1 }) {
| + +
error: aborting due to 4 previous errors

View file

@ -1,13 +0,0 @@
pub struct Example { a: i32 }
impl Example {
fn is_pos(&self) -> bool { self.a > 0 }
}
fn one() -> i32 { 1 }
fn main() {
if Example { a: one(), }.is_pos() { //~ ERROR invalid struct literal
println!("Positive!");
}
}

View file

@ -1,13 +0,0 @@
error: invalid struct literal
--> $DIR/method-call-on-struct-literal-in-if-condition.rs:10:8
|
LL | if Example { a: one(), }.is_pos() {
| ^^^^^^^^^^^^^^^^^^^^^
|
help: you might need to surround the struct literal with parentheses
|
LL | if (Example { a: one(), }).is_pos() {
| + +
error: aborting due to 1 previous error

View file

@ -1,17 +0,0 @@
struct Foo {
x: isize,
}
impl Foo {
fn hi(&self) -> bool {
true
}
}
fn main() {
for x in Foo { //~ ERROR struct literals are not allowed here
x: 3 //~^ ERROR `bool` is not an iterator
}.hi() {
println!("yo");
}
}

View file

@ -1,31 +0,0 @@
error: struct literals are not allowed here
--> $DIR/struct-literal-in-for.rs:12:14
|
LL | for x in Foo {
| ______________^
LL | | x: 3
LL | | }.hi() {
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ for x in (Foo {
LL | x: 3
LL ~ }).hi() {
|
error[E0277]: `bool` is not an iterator
--> $DIR/struct-literal-in-for.rs:12:14
|
LL | for x in Foo {
| ______________^
LL | | x: 3
LL | | }.hi() {
| |__________^ `bool` is not an iterator
|
= help: the trait `Iterator` is not implemented for `bool`
= note: required for `bool` to implement `IntoIterator`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.

View file

@ -1,22 +0,0 @@
struct Foo {
x: isize,
}
impl Foo {
fn hi(&self) -> bool {
true
}
}
fn main() {
if Foo { //~ ERROR struct literals are not allowed here
x: 3
}.hi() {
println!("yo");
}
if let true = Foo { //~ ERROR struct literals are not allowed here
x: 3
}.hi() {
println!("yo");
}
}

View file

@ -1,34 +0,0 @@
error: struct literals are not allowed here
--> $DIR/struct-literal-in-if.rs:12:8
|
LL | if Foo {
| ________^
LL | | x: 3
LL | | }.hi() {
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ if (Foo {
LL | x: 3
LL ~ }).hi() {
|
error: struct literals are not allowed here
--> $DIR/struct-literal-in-if.rs:17:19
|
LL | if let true = Foo {
| ___________________^
LL | | x: 3
LL | | }.hi() {
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ if let true = (Foo {
LL | x: 3
LL ~ }).hi() {
|
error: aborting due to 2 previous errors

View file

@ -1,13 +0,0 @@
struct Foo {
x: isize,
}
fn main() {
match Foo { //~ ERROR struct literals are not allowed here
x: 3
} {
Foo {
x: x
} => {}
}
}

View file

@ -1,18 +0,0 @@
error: struct literals are not allowed here
--> $DIR/struct-literal-in-match-discriminant.rs:6:11
|
LL | match Foo {
| ___________^
LL | | x: 3
LL | | } {
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ match (Foo {
LL | x: 3
LL ~ }) {
|
error: aborting due to 1 previous error

View file

@ -1,22 +0,0 @@
struct Foo {
x: isize,
}
impl Foo {
fn hi(&self) -> bool {
true
}
}
fn main() {
while Foo { //~ ERROR struct literals are not allowed here
x: 3
}.hi() {
println!("yo");
}
while let true = Foo { //~ ERROR struct literals are not allowed here
x: 3
}.hi() {
println!("yo");
}
}

View file

@ -1,34 +0,0 @@
error: struct literals are not allowed here
--> $DIR/struct-literal-in-while.rs:12:11
|
LL | while Foo {
| ___________^
LL | | x: 3
LL | | }.hi() {
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ while (Foo {
LL | x: 3
LL ~ }).hi() {
|
error: struct literals are not allowed here
--> $DIR/struct-literal-in-while.rs:17:22
|
LL | while let true = Foo {
| ______________________^
LL | | x: 3
LL | | }.hi() {
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ while let true = (Foo {
LL | x: 3
LL ~ }).hi() {
|
error: aborting due to 2 previous errors

View file

@ -1,17 +0,0 @@
struct Foo {
x: isize,
}
impl Foo {
fn hi(&self) -> bool {
true
}
}
fn main() {
while || Foo { //~ ERROR struct literals are not allowed here
x: 3 //~^ ERROR mismatched types
}.hi() {
println!("yo");
}
}

View file

@ -1,37 +0,0 @@
error: struct literals are not allowed here
--> $DIR/struct-literal-restrictions-in-lamda.rs:12:14
|
LL | while || Foo {
| ______________^
LL | | x: 3
LL | | }.hi() {
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ while || (Foo {
LL | x: 3
LL ~ }).hi() {
|
error[E0308]: mismatched types
--> $DIR/struct-literal-restrictions-in-lamda.rs:12:11
|
LL | while || Foo {
| ___________^
LL | | x: 3
LL | | }.hi() {
| |__________^ expected `bool`, found closure
|
= note: expected type `bool`
found closure `{closure@$DIR/struct-literal-restrictions-in-lamda.rs:12:11: 12:13}`
help: use parentheses to call this closure
|
LL ~ while (|| Foo {
LL | x: 3
LL ~ }.hi())() {
|
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -1,25 +0,0 @@
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
enum E {
V { field: bool },
I { field1: bool, field2: usize },
J { field: isize },
K { field: &'static str},
}
fn test_E(x: E) {
let field = true;
if x == E::V { field } {}
//~^ ERROR expected value, found struct variant `E::V`
//~| ERROR mismatched types
if x == E::I { field1: true, field2: 42 } {}
//~^ ERROR struct literals are not allowed here
if x == E::V { field: false } {}
//~^ ERROR struct literals are not allowed here
if x == E::J { field: -42 } {}
//~^ ERROR struct literals are not allowed here
if x == E::K { field: "" } {}
//~^ ERROR struct literals are not allowed here
let y: usize = ();
//~^ ERROR mismatched types
}
fn main() {}

View file

@ -1,76 +0,0 @@
error: struct literals are not allowed here
--> $DIR/struct-literal-variant-in-if.rs:13:13
|
LL | if x == E::I { field1: true, field2: 42 } {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::I { field1: true, field2: 42 }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literal-variant-in-if.rs:15:13
|
LL | if x == E::V { field: false } {}
| ^^^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::V { field: false }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literal-variant-in-if.rs:17:13
|
LL | if x == E::J { field: -42 } {}
| ^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::J { field: -42 }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literal-variant-in-if.rs:19:13
|
LL | if x == E::K { field: "" } {}
| ^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::K { field: "" }) {}
| + +
error[E0533]: expected value, found struct variant `E::V`
--> $DIR/struct-literal-variant-in-if.rs:10:13
|
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
|
LL | if x == E::V { field } {}
| ---------------^^^^^--
| | |
| | expected `()`, found `bool`
| expected this to be `()`
error[E0308]: mismatched types
--> $DIR/struct-literal-variant-in-if.rs:21:20
|
LL | let y: usize = ();
| ----- ^^ expected `usize`, found `()`
| |
| expected due to this
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0308, E0533.
For more information about an error, try `rustc --explain E0308`.

View file

@ -0,0 +1,92 @@
fn main() {
if Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here
println!("yo");
}
if let true = Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here
println!("yo");
}
for x in Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here
//~^ ERROR `bool` is not an iterator
println!("yo");
}
while Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here
println!("yo");
}
while let true = Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here
println!("yo");
}
match Foo { x: 3 } { //~ ERROR struct literals are not allowed here
Foo { x: x } => {}
}
let _ = |x: E| {
let field = true;
if x == E::V { field } {}
//~^ ERROR expected value, found struct variant `E::V`
//~| ERROR mismatched types
if x == E::I { field1: true, field2: 42 } {}
//~^ ERROR struct literals are not allowed here
if x == E::V { field: false } {}
//~^ ERROR struct literals are not allowed here
if x == E::J { field: -42 } {}
//~^ ERROR struct literals are not allowed here
if x == E::K { field: "" } {}
//~^ ERROR struct literals are not allowed here
let y: usize = ();
//~^ ERROR mismatched types
};
// Regression test for <https://github.com/rust-lang/rust/issues/43412>.
while || Foo { x: 3 }.hi() { //~ ERROR struct literals are not allowed here
//~^ ERROR mismatched types
println!("yo");
}
// This uses `one()` over `1` as token `one` may begin a type and thus back when type ascription
// `$expr : $ty` still existed, `{ x: one` could've been the start of a block expr which used to
// make the compiler take a different execution path. Now it no longer makes a difference tho.
// Regression test for <https://github.com/rust-lang/rust/issues/82051>.
if Foo { x: one(), }.hi() { //~ ERROR struct literals are not allowed here
println!("Positive!");
}
const FOO: Foo = Foo { x: 1 };
// Below, test that we correctly parenthesize the struct literals.
// Regression test for <https://github.com/rust-lang/rust/issues/112278>.
if FOO == self::Foo { x: one() } {} //~ ERROR struct literals are not allowed here
if FOO == Foo::<> { x: one() } {} //~ ERROR struct literals are not allowed here
fn env<T: Trait<Out = Foo>>() {
if FOO == <T as Trait>::Out { x: one() } {} //~ ERROR struct literals are not allowed here
//~^ ERROR usage of qualified paths in this context is experimental
}
}
#[derive(PartialEq, Eq)]
struct Foo {
x: isize,
}
impl Foo {
fn hi(&self) -> bool {
true
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
enum E {
V { field: bool },
I { field1: bool, field2: usize },
J { field: isize },
K { field: &'static str},
}
fn one() -> isize { 1 }
trait Trait { type Out; }

View file

@ -0,0 +1,234 @@
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:2:8
|
LL | if Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if (Foo { x: 3 }).hi() {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:5:19
|
LL | if let true = Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if let true = (Foo { x: 3 }).hi() {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:9:14
|
LL | for x in Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | for x in (Foo { x: 3 }).hi() {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:14:11
|
LL | while Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | while (Foo { x: 3 }).hi() {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:17:22
|
LL | while let true = Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | while let true = (Foo { x: 3 }).hi() {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:21:11
|
LL | match Foo { x: 3 } {
| ^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | match (Foo { x: 3 }) {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:30:17
|
LL | if x == E::I { field1: true, field2: 42 } {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::I { field1: true, field2: 42 }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:32:17
|
LL | if x == E::V { field: false } {}
| ^^^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::V { field: false }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:34:17
|
LL | if x == E::J { field: -42 } {}
| ^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::J { field: -42 }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:36:17
|
LL | if x == E::K { field: "" } {}
| ^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if x == (E::K { field: "" }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:43:14
|
LL | while || Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | while || (Foo { x: 3 }).hi() {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:53:8
|
LL | if Foo { x: one(), }.hi() {
| ^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if (Foo { x: one(), }).hi() {
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:61:15
|
LL | if FOO == self::Foo { x: one() } {}
| ^^^^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if FOO == (self::Foo { x: one() }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:63:15
|
LL | if FOO == Foo::<> { x: one() } {}
| ^^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if FOO == (Foo::<> { x: one() }) {}
| + +
error: struct literals are not allowed here
--> $DIR/struct-literals-in-invalid-places.rs:66:19
|
LL | if FOO == <T as Trait>::Out { x: one() } {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: surround the struct literal with parentheses
|
LL | if FOO == (<T as Trait>::Out { x: one() }) {}
| + +
error[E0658]: usage of qualified paths in this context is experimental
--> $DIR/struct-literals-in-invalid-places.rs:66:19
|
LL | if FOO == <T as Trait>::Out { x: one() } {}
| ^^^^^^^^^^^^^^^^^
|
= note: see issue #86935 <https://github.com/rust-lang/rust/issues/86935> for more information
= help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error[E0277]: `bool` is not an iterator
--> $DIR/struct-literals-in-invalid-places.rs:9:14
|
LL | for x in Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^^^^^^ `bool` is not an iterator
|
= help: the trait `Iterator` is not implemented for `bool`
= note: required for `bool` to implement `IntoIterator`
error[E0533]: expected value, found struct variant `E::V`
--> $DIR/struct-literals-in-invalid-places.rs:27:17
|
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-literals-in-invalid-places.rs:27:24
|
LL | if x == E::V { field } {}
| ---------------^^^^^--
| | |
| | expected `()`, found `bool`
| expected this to be `()`
|
help: you might have meant to return this value
|
LL | if x == E::V { return field; } {}
| ++++++ +
error[E0308]: mismatched types
--> $DIR/struct-literals-in-invalid-places.rs:38:24
|
LL | let y: usize = ();
| ----- ^^ expected `usize`, found `()`
| |
| expected due to this
error[E0308]: mismatched types
--> $DIR/struct-literals-in-invalid-places.rs:43:11
|
LL | while || Foo { x: 3 }.hi() {
| ^^^^^^^^^^^^^^^^^^^^ expected `bool`, found closure
|
= note: expected type `bool`
found closure `{closure@$DIR/struct-literals-in-invalid-places.rs:43:11: 43:13}`
help: use parentheses to call this closure
|
LL | while (|| Foo { x: 3 }.hi())() {
| + +++
error: aborting due to 21 previous errors
Some errors have detailed explanations: E0277, E0308, E0533, E0658.
For more information about an error, try `rustc --explain E0277`.

View file

@ -1,11 +1,10 @@
fn foo(x: bool) -> i32 {
match x {
match x { //~ ERROR struct literals are not allowed here
x: i32 => x, //~ ERROR expected
//~^ ERROR mismatched types
true => 42.,
false => 0.333,
true => 42., //~ ERROR expected identifier
false => 0.333, //~ ERROR expected identifier
}
}
} //~ ERROR expected one of
fn main() {
match foo(true) {

View file

@ -1,18 +1,64 @@
error: expected one of `@` or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:3:10
error: expected one of `!`, `,`, `.`, `::`, `?`, `{`, `}`, or an operator, found `=>`
--> $DIR/type-ascription-in-pattern.rs:3:16
|
LL | match x {
| - while parsing this struct
LL | x: i32 => x,
| ^ --- specifying the type of a pattern isn't supported
| |
| expected one of `@` or `|`
| -^^ expected one of 8 possible tokens
| |
| help: try adding a comma: `,`
error: expected identifier, found keyword `true`
--> $DIR/type-ascription-in-pattern.rs:4:9
|
help: maybe write a path separator here
LL | match x {
| - while parsing this struct
LL | x: i32 => x,
LL | true => 42.,
| ^^^^ expected identifier, found keyword
error: expected identifier, found keyword `false`
--> $DIR/type-ascription-in-pattern.rs:5:9
|
LL | x::i32 => x,
| ~~
LL | match x {
| - while parsing this struct
...
LL | false => 0.333,
| ^^^^^ expected identifier, found keyword
error: struct literals are not allowed here
--> $DIR/type-ascription-in-pattern.rs:2:11
|
LL | match x {
| ___________^
LL | | x: i32 => x,
LL | | true => 42.,
LL | | false => 0.333,
LL | | }
| |_____^
|
help: surround the struct literal with parentheses
|
LL ~ match (x {
LL | x: i32 => x,
LL | true => 42.,
LL | false => 0.333,
LL ~ })
|
error: expected one of `.`, `?`, `{`, or an operator, found `}`
--> $DIR/type-ascription-in-pattern.rs:7:1
|
LL | match x {
| ----- while parsing this `match` expression
...
LL | }
| - expected one of `.`, `?`, `{`, or an operator
LL | }
| ^ unexpected token
error: expected one of `...`, `..=`, `..`, or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:12:11
--> $DIR/type-ascription-in-pattern.rs:11:11
|
LL | 42: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
@ -20,7 +66,7 @@ LL | 42: i32 => (),
| expected one of `...`, `..=`, `..`, or `|`
error: expected `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:13:10
--> $DIR/type-ascription-in-pattern.rs:12:10
|
LL | _: f64 => (),
| ^ --- specifying the type of a pattern isn't supported
@ -28,7 +74,7 @@ LL | _: f64 => (),
| expected `|`
error: expected one of `@` or `|`, found `:`
--> $DIR/type-ascription-in-pattern.rs:14:10
--> $DIR/type-ascription-in-pattern.rs:13:10
|
LL | x: i32 => (),
| ^ --- specifying the type of a pattern isn't supported
@ -40,15 +86,5 @@ help: maybe write a path separator here
LL | x::i32 => (),
| ~~
error[E0308]: mismatched types
--> $DIR/type-ascription-in-pattern.rs:3:19
|
LL | fn foo(x: bool) -> i32 {
| --- expected `i32` because of return type
LL | match x {
LL | x: i32 => x,
| ^ expected `i32`, found `bool`
error: aborting due to 8 previous errors
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0308`.

View file

@ -0,0 +1,7 @@
//@ no-prefer-dynamic
//@ compile-flags: --target i686-unknown-linux-gnu -Zreg-struct-return=true
//@ needs-llvm-components: x86
#![feature(no_core)]
#![crate_type = "rlib"]
#![no_core]

View file

@ -5,8 +5,8 @@ LL | #![feature(no_core)]
| ^
|
= help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
= note: `-Zreg-struct-return=true` in this crate is incompatible with `-Zreg-struct-return=` in dependency `default_reg_struct_return`
= help: set `-Zreg-struct-return=` in this crate or `-Zreg-struct-return=true` in `default_reg_struct_return`
= note: `-Zreg-struct-return=true` in this crate is incompatible with unset `-Zreg-struct-return` in dependency `default_reg_struct_return`
= help: unset `-Zreg-struct-return` in this crate or set `-Zreg-struct-return=true` in `default_reg_struct_return`
= help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error
error: aborting due to 1 previous error

View file

@ -0,0 +1,13 @@
error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool`
--> $DIR/no_value_bool.rs:16:1
|
LL | #![feature(no_core)]
| ^
|
= help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
= note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return`
= help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return`
= help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error
error: aborting due to 1 previous error

View file

@ -0,0 +1,13 @@
error: mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool`
--> $DIR/no_value_bool.rs:16:1
|
LL | #![feature(no_core)]
| ^
|
= help: the `-Zreg-struct-return` flag modifies the ABI so Rust crates compiled with different values of this flag cannot be used together safely
= note: unset `-Zreg-struct-return` in this crate is incompatible with `-Zreg-struct-return=true` in dependency `enabled_reg_struct_return`
= help: set `-Zreg-struct-return=true` in this crate or unset `-Zreg-struct-return` in `enabled_reg_struct_return`
= help: if you are sure this will not cause problems, you may use `-Cunsafe-allow-abi-mismatch=reg-struct-return` to silence this error
error: aborting due to 1 previous error

View file

@ -0,0 +1,22 @@
// Tests that bool target modifier value (true) in dependency crate is ok linked
// with the -Zflag specified without value (-Zflag=true is consistent with -Zflag)
//@ aux-build:enabled_reg_struct_return.rs
//@ compile-flags: --target i686-unknown-linux-gnu -Cpanic=abort
//@ needs-llvm-components: x86
//@ revisions: ok ok_explicit error error_explicit
//@[ok] compile-flags: -Zreg-struct-return
//@[ok_explicit] compile-flags: -Zreg-struct-return=true
//@[error] compile-flags:
//@[error_explicit] compile-flags: -Zreg-struct-return=false
//@[ok] check-pass
//@[ok_explicit] check-pass
#![feature(no_core)]
//[error]~^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool`
//[error_explicit]~^^ ERROR mixing `-Zreg-struct-return` will cause an ABI mismatch in crate `no_value_bool`
#![crate_type = "rlib"]
#![no_core]
extern crate enabled_reg_struct_return;