1
Fork 0

Auto merge of #127174 - matthiaskrgr:rollup-q87j6cn, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #126018 (Remove the `box_pointers` lint.)
 - #126895 (Fix simd_gather documentation)
 - #126981 (Replace some magic booleans in match-lowering with enums)
 - #127038 (Update test comment)
 - #127053 (Update the LoongArch target documentation)
 - #127069 (small correction to fmt::Pointer impl)
 - #127157 (coverage: Avoid getting extra unexpansion info when we don't need it)
 - #127160 (Add a regression test for #123630)
 - #127161 (Improve `run-make-support` library `args` API)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-06-30 20:14:40 +00:00
commit 6868c831a1
33 changed files with 430 additions and 349 deletions

View file

@ -56,8 +56,6 @@ lint_builtin_asm_labels = avoid using named labels in inline assembly
.help = only local labels of the form `<number>:` should be used in inline asm
.note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty}
lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
.previous_decl_label = `{$orig}` previously declared here
.mismatch_label = this signature doesn't match the previous declaration

View file

@ -24,9 +24,9 @@ use crate::fluent_generated as fluent;
use crate::{
errors::BuiltinEllipsisInclusiveRangePatterns,
lints::{
BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle,
BuiltinDeprecatedAttrLink, BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed,
BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
@ -56,7 +56,6 @@ use rustc_middle::bug;
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::GenericArgKind;
use rustc_middle::ty::TypeVisitableExt;
use rustc_middle::ty::Upcast;
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
@ -134,80 +133,6 @@ impl EarlyLintPass for WhileTrue {
}
}
declare_lint! {
/// The `box_pointers` lints use of the Box type.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(box_pointers)]
/// struct Foo {
/// x: Box<i32>,
/// }
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// This lint is mostly historical, and not particularly useful. `Box<T>`
/// used to be built into the language, and the only way to do heap
/// allocation. Today's Rust can call into other allocators, etc.
BOX_POINTERS,
Allow,
"use of owned (Box type) heap memory"
}
declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
impl BoxPointers {
fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
for leaf in ty.walk() {
if let GenericArgKind::Type(leaf_ty) = leaf.unpack()
&& leaf_ty.is_box()
{
cx.emit_span_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
}
}
}
}
impl<'tcx> LateLintPass<'tcx> for BoxPointers {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
match it.kind {
hir::ItemKind::Fn(..)
| hir::ItemKind::TyAlias(..)
| hir::ItemKind::Enum(..)
| hir::ItemKind::Struct(..)
| hir::ItemKind::Union(..) => self.check_heap_type(
cx,
it.span,
cx.tcx.type_of(it.owner_id).instantiate_identity(),
),
_ => (),
}
// If it's a struct, we also have to check the fields' types
match it.kind {
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
for field in struct_def.fields() {
self.check_heap_type(
cx,
field.span,
cx.tcx.type_of(field.def_id).instantiate_identity(),
);
}
}
_ => (),
}
}
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
let ty = cx.typeck_results().node_type(e.hir_id);
self.check_heap_type(cx, e.span, ty);
}
}
declare_lint! {
/// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }`
/// instead of `Struct { x }` in a pattern.
@ -1640,7 +1565,6 @@ declare_lint_pass!(
/// which are used by other parts of the compiler.
SoftLints => [
WHILE_TRUE,
BOX_POINTERS,
NON_SHORTHAND_FIELD_PATTERNS,
UNSAFE_CODE,
MISSING_DOCS,

View file

@ -187,7 +187,6 @@ late_lint_methods!(
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
InvalidFromUtf8: InvalidFromUtf8,
VariantSizeDifferences: VariantSizeDifferences,
BoxPointers: BoxPointers,
PathStatements: PathStatements,
LetUnderscore: LetUnderscore,
InvalidReferenceCasting: InvalidReferenceCasting,
@ -551,6 +550,10 @@ fn register_builtins(store: &mut LintStore) {
"converted into hard error, see RFC #3535 \
<https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
);
store.register_removed(
"box_pointers",
"it does not detect other kinds of allocations, and existed only for historical reasons",
);
}
fn register_internals(store: &mut LintStore) {

View file

@ -66,12 +66,6 @@ pub struct BuiltinWhileTrue {
pub replace: String,
}
#[derive(LintDiagnostic)]
#[diag(lint_builtin_box_pointers)]
pub struct BuiltinBoxPointers<'a> {
pub ty: Ty<'a>,
}
#[derive(LintDiagnostic)]
#[diag(lint_builtin_non_shorthand_field_patterns)]
pub struct BuiltinNonShorthandFieldPatterns {

View file

@ -1,3 +1,4 @@
use crate::build::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops};
use crate::build::ForGuard::OutsideGuard;
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
use rustc_middle::middle::region::Scope;
@ -201,7 +202,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pattern,
UserTypeProjections::none(),
&mut |this, _, _, node, span, _, _| {
this.storage_live_binding(block, node, span, OutsideGuard, true);
this.storage_live_binding(
block,
node,
span,
OutsideGuard,
ScheduleDrops::Yes,
);
},
);
let else_block_span = this.thir[*else_block].span;
@ -213,8 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pattern,
None,
initializer_span,
false,
true,
DeclareLetBindings::No,
EmitStorageLive::No,
)
});
matching.and(failure)
@ -291,7 +298,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pattern,
UserTypeProjections::none(),
&mut |this, _, _, node, span, _, _| {
this.storage_live_binding(block, node, span, OutsideGuard, true);
this.storage_live_binding(
block,
node,
span,
OutsideGuard,
ScheduleDrops::Yes,
);
this.schedule_drop_for_binding(node, span, OutsideGuard);
},
)

View file

@ -1,6 +1,7 @@
//! See docs in build/expr/mod.rs
use crate::build::expr::category::{Category, RvalueFunc};
use crate::build::matches::DeclareLetBindings;
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
use rustc_ast::InlineAsmOptions;
use rustc_data_structures::fx::FxHashMap;
@ -86,7 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
cond,
Some(condition_scope), // Temp scope
source_info,
true, // Declare `let` bindings normally
DeclareLetBindings::Yes, // Declare `let` bindings normally
));
// Lower the `then` arm into its block.
@ -163,7 +164,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_info,
// This flag controls how inner `let` expressions are lowered,
// but either way there shouldn't be any of those in here.
true,
DeclareLetBindings::LetNotPermitted,
)
});
let (short_circuit, continuation, constant) = match op {

View file

@ -28,6 +28,7 @@ mod simplify;
mod test;
mod util;
use std::assert_matches::assert_matches;
use std::borrow::Borrow;
use std::mem;
@ -39,9 +40,50 @@ struct ThenElseArgs {
/// `self.local_scope()` is used.
temp_scope_override: Option<region::Scope>,
variable_source_info: SourceInfo,
/// Determines how bindings should be handled when lowering `let` expressions.
///
/// Forwarded to [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].
/// When false (for match guards), `let` bindings won't be declared.
declare_let_bindings: bool,
declare_let_bindings: DeclareLetBindings,
}
/// Should lowering a `let` expression also declare its bindings?
///
/// Used by [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].
#[derive(Clone, Copy)]
pub(crate) enum DeclareLetBindings {
/// Yes, declare `let` bindings as normal for `if` conditions.
Yes,
/// No, don't declare `let` bindings, because the caller declares them
/// separately due to special requirements.
///
/// Used for match guards and let-else.
No,
/// Let expressions are not permitted in this context, so it is a bug to
/// try to lower one (e.g inside lazy-boolean-or or boolean-not).
LetNotPermitted,
}
/// Used by [`Builder::bind_matched_candidate_for_arm_body`] to determine
/// whether or not to call [`Builder::storage_live_binding`] to emit
/// [`StatementKind::StorageLive`].
#[derive(Clone, Copy)]
pub(crate) enum EmitStorageLive {
/// Yes, emit `StorageLive` as normal.
Yes,
/// No, don't emit `StorageLive`. The caller has taken responsibility for
/// emitting `StorageLive` as appropriate.
No,
}
/// Used by [`Builder::storage_live_binding`] and [`Builder::bind_matched_candidate_for_arm_body`]
/// to decide whether to schedule drops.
#[derive(Clone, Copy, Debug)]
pub(crate) enum ScheduleDrops {
/// Yes, the relevant functions should also schedule drops as appropriate.
Yes,
/// No, don't schedule drops. The caller has taken responsibility for any
/// appropriate drops.
No,
}
impl<'a, 'tcx> Builder<'a, 'tcx> {
@ -57,7 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
expr_id: ExprId,
temp_scope_override: Option<region::Scope>,
variable_source_info: SourceInfo,
declare_let_bindings: bool,
declare_let_bindings: DeclareLetBindings,
) -> BlockAnd<()> {
self.then_else_break_inner(
block,
@ -91,13 +133,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.then_else_break_inner(
block,
lhs,
ThenElseArgs { declare_let_bindings: true, ..args },
ThenElseArgs {
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
..args
},
)
});
let rhs_success_block = unpack!(this.then_else_break_inner(
failure_block,
rhs,
ThenElseArgs { declare_let_bindings: true, ..args },
ThenElseArgs {
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
..args
},
));
// Make the LHS and RHS success arms converge to a common block.
@ -127,7 +175,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.then_else_break_inner(
block,
arg,
ThenElseArgs { declare_let_bindings: true, ..args },
ThenElseArgs {
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
..args
},
)
});
this.break_for_else(success_block, args.variable_source_info);
@ -147,7 +198,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Some(args.variable_source_info.scope),
args.variable_source_info.span,
args.declare_let_bindings,
false,
EmitStorageLive::Yes,
),
_ => {
let mut block = block;
@ -440,7 +491,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&fake_borrow_temps,
scrutinee_span,
Some((arm, match_scope)),
false,
EmitStorageLive::Yes,
);
this.fixed_temps_scope = old_dedup_scope;
@ -485,7 +536,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
scrutinee_span: Span,
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
storages_alive: bool,
emit_storage_live: EmitStorageLive,
) -> BasicBlock {
if candidate.subcandidates.is_empty() {
// Avoid generating another `BasicBlock` when we only have one
@ -496,8 +547,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fake_borrow_temps,
scrutinee_span,
arm_match_scope,
true,
storages_alive,
ScheduleDrops::Yes,
emit_storage_live,
)
} else {
// It's helpful to avoid scheduling drops multiple times to save
@ -515,7 +566,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// To handle this we instead unschedule it's drop after each time
// we lower the guard.
let target_block = self.cfg.start_new_block();
let mut schedule_drops = true;
let mut schedule_drops = ScheduleDrops::Yes;
let arm = arm_match_scope.unzip().0;
// We keep a stack of all of the bindings and type ascriptions
// from the parent candidates that we visit, that also need to
@ -534,10 +585,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
scrutinee_span,
arm_match_scope,
schedule_drops,
storages_alive,
emit_storage_live,
);
if arm.is_none() {
schedule_drops = false;
schedule_drops = ScheduleDrops::No;
}
self.cfg.goto(binding_end, outer_source_info, target_block);
},
@ -563,8 +614,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match irrefutable_pat.kind {
// Optimize the case of `let x = ...` to write directly into `x`
PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => {
let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
let place = self.storage_live_binding(
block,
var,
irrefutable_pat.span,
OutsideGuard,
ScheduleDrops::Yes,
);
unpack!(block = self.expr_into_dest(place, block, initializer_id));
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
@ -597,8 +653,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
},
ascription: thir::Ascription { ref annotation, variance: _ },
} => {
let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
let place = self.storage_live_binding(
block,
var,
irrefutable_pat.span,
OutsideGuard,
ScheduleDrops::Yes,
);
unpack!(block = self.expr_into_dest(place, block, initializer_id));
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
@ -704,7 +765,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&[],
irrefutable_pat.span,
None,
false,
EmitStorageLive::Yes,
)
.unit()
}
@ -780,13 +841,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
/// Emits a [`StatementKind::StorageLive`] for the given var, and also
/// schedules a drop if requested (and possible).
pub(crate) fn storage_live_binding(
&mut self,
block: BasicBlock,
var: LocalVarId,
span: Span,
for_guard: ForGuard,
schedule_drop: bool,
schedule_drop: ScheduleDrops,
) -> Place<'tcx> {
let local_id = self.var_local_id(var, for_guard);
let source_info = self.source_info(span);
@ -794,7 +857,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Although there is almost always scope for given variable in corner cases
// like #92893 we might get variable with no scope.
if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id)
&& schedule_drop
&& matches!(schedule_drop, ScheduleDrops::Yes)
{
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
}
@ -1991,8 +2054,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Pat binding - used for `let` and function parameters as well.
impl<'a, 'tcx> Builder<'a, 'tcx> {
/// If the bindings have already been declared, set `declare_bindings` to
/// `false` to avoid duplicated bindings declaration; used for if-let guards.
/// Lowers a `let` expression that appears in a suitable context
/// (e.g. an `if` condition or match guard).
///
/// Also used for lowering let-else statements, since they have similar
/// needs despite not actually using `let` expressions.
///
/// Use [`DeclareLetBindings`] to control whether the `let` bindings are
/// declared or not.
pub(crate) fn lower_let_expr(
&mut self,
mut block: BasicBlock,
@ -2000,8 +2069,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pat: &Pat<'tcx>,
source_scope: Option<SourceScope>,
scope_span: Span,
declare_bindings: bool,
storages_alive: bool,
declare_let_bindings: DeclareLetBindings,
emit_storage_live: EmitStorageLive,
) -> BlockAnd<()> {
let expr_span = self.thir[expr_id].span;
let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
@ -2017,10 +2086,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.break_for_else(otherwise_block, self.source_info(expr_span));
if declare_bindings {
let expr_place = scrutinee.try_to_place(self);
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
self.declare_bindings(source_scope, pat.span.to(scope_span), pat, None, opt_expr_place);
match declare_let_bindings {
DeclareLetBindings::Yes => {
let expr_place = scrutinee.try_to_place(self);
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
self.declare_bindings(
source_scope,
pat.span.to(scope_span),
pat,
None,
opt_expr_place,
);
}
DeclareLetBindings::No => {} // Caller is responsible for bindings.
DeclareLetBindings::LetNotPermitted => {
self.tcx.dcx().span_bug(expr_span, "let expression not expected in this context")
}
}
let success = self.bind_pattern(
@ -2029,7 +2110,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&[],
expr_span,
None,
storages_alive,
emit_storage_live,
);
// If branch coverage is enabled, record this branch.
@ -2053,8 +2134,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
scrutinee_span: Span,
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
schedule_drops: bool,
storages_alive: bool,
schedule_drops: ScheduleDrops,
emit_storage_live: EmitStorageLive,
) -> BasicBlock {
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
@ -2203,7 +2284,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
guard,
None, // Use `self.local_scope()` as the temp scope
this.source_info(arm.span),
false, // For guards, `let` bindings are declared separately
DeclareLetBindings::No, // For guards, `let` bindings are declared separately
)
});
@ -2264,12 +2345,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let cause = FakeReadCause::ForGuardBinding;
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
}
assert!(schedule_drops, "patterns with guards must schedule drops");
assert_matches!(
schedule_drops,
ScheduleDrops::Yes,
"patterns with guards must schedule drops"
);
self.bind_matched_candidate_for_arm_body(
post_guard_block,
true,
ScheduleDrops::Yes,
by_value_bindings,
storages_alive,
emit_storage_live,
);
post_guard_block
@ -2281,7 +2366,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block,
schedule_drops,
bindings,
storages_alive,
emit_storage_live,
);
block
}
@ -2317,7 +2402,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn bind_matched_candidate_for_guard<'b>(
&mut self,
block: BasicBlock,
schedule_drops: bool,
schedule_drops: ScheduleDrops,
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
) where
'tcx: 'b,
@ -2370,9 +2455,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn bind_matched_candidate_for_arm_body<'b>(
&mut self,
block: BasicBlock,
schedule_drops: bool,
schedule_drops: ScheduleDrops,
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
storages_alive: bool,
emit_storage_live: EmitStorageLive,
) where
'tcx: 'b,
{
@ -2382,21 +2467,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Assign each of the bindings. This may trigger moves out of the candidate.
for binding in bindings {
let source_info = self.source_info(binding.span);
let local = if storages_alive {
let local = match emit_storage_live {
// Here storages are already alive, probably because this is a binding
// from let-else.
// We just need to schedule drop for the value.
self.var_local_id(binding.var_id, OutsideGuard).into()
} else {
self.storage_live_binding(
EmitStorageLive::No => self.var_local_id(binding.var_id, OutsideGuard).into(),
EmitStorageLive::Yes => self.storage_live_binding(
block,
binding.var_id,
binding.span,
OutsideGuard,
schedule_drops,
)
),
};
if schedule_drops {
if matches!(schedule_drops, ScheduleDrops::Yes) {
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
}
let rvalue = match binding.binding_mode.0 {

View file

@ -9,9 +9,8 @@ use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
use crate::coverage::spans::{
extract_refined_covspans, unexpand_into_body_span_with_visible_macro,
};
use crate::coverage::spans::extract_refined_covspans;
use crate::coverage::unexpand::unexpand_into_body_span;
use crate::coverage::ExtractedHirInfo;
/// Associates an ordinary executable code span with its corresponding BCB.
@ -202,8 +201,7 @@ pub(super) fn extract_branch_pairs(
if !raw_span.ctxt().outer_expn_data().is_root() {
return None;
}
let (span, _) =
unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?;
let span = unexpand_into_body_span(raw_span, hir_info.body_span)?;
let bcb_from_marker =
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
@ -238,7 +236,7 @@ pub(super) fn extract_mcdc_mappings(
if !raw_span.ctxt().outer_expn_data().is_root() {
return None;
}
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
let span = unexpand_into_body_span(raw_span, body_span)?;
let true_bcb = bcb_from_marker(true_marker)?;
let false_bcb = bcb_from_marker(false_marker)?;
@ -261,7 +259,7 @@ pub(super) fn extract_mcdc_mappings(
mcdc_decisions.extend(branch_info.mcdc_decision_spans.iter().filter_map(
|decision: &mir::coverage::MCDCDecisionSpan| {
let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
let span = unexpand_into_body_span(decision.span, body_span)?;
let end_bcbs = decision
.end_markers

View file

@ -6,6 +6,7 @@ mod mappings;
mod spans;
#[cfg(test)]
mod tests;
mod unexpand;
use rustc_middle::mir::coverage::{
CodeRegion, CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind,

View file

@ -14,11 +14,6 @@ use crate::coverage::ExtractedHirInfo;
mod from_mir;
// FIXME(#124545) It's awkward that we have to re-export this, because it's an
// internal detail of `from_mir` that is also needed when handling branch and
// MC/DC spans. Ideally we would find a more natural home for it.
pub(super) use from_mir::unexpand_into_body_span_with_visible_macro;
pub(super) fn extract_refined_covspans(
mir_body: &mir::Body<'_>,
hir_info: &ExtractedHirInfo,

View file

@ -4,12 +4,13 @@ use rustc_middle::mir::{
self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind,
};
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
use rustc_span::{Span, Symbol};
use crate::coverage::graph::{
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
};
use crate::coverage::spans::Covspan;
use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro;
use crate::coverage::ExtractedHirInfo;
pub(crate) struct ExtractedCovspans {
@ -215,59 +216,6 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
}
}
/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
/// within the function's body source. This span is guaranteed to be contained
/// within, or equal to, the `body_span`. If the extrapolated span is not
/// contained within the `body_span`, `None` is returned.
///
/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
/// etc.).
pub(crate) fn unexpand_into_body_span_with_visible_macro(
original_span: Span,
body_span: Span,
) -> Option<(Span, Option<Symbol>)> {
let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
let visible_macro = prev
.map(|prev| match prev.ctxt().outer_expn_data().kind {
ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
_ => None,
})
.flatten();
Some((span, visible_macro))
}
/// Walks through the expansion ancestors of `original_span` to find a span that
/// is contained in `body_span` and has the same [`SyntaxContext`] as `body_span`.
/// The ancestor that was traversed just before the matching span (if any) is
/// also returned.
///
/// For example, a return value of `Some((ancestor, Some(prev))` means that:
/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
/// - `ancestor == prev.parent_callsite()`
///
/// [`SyntaxContext`]: rustc_span::SyntaxContext
fn unexpand_into_body_span_with_prev(
original_span: Span,
body_span: Span,
) -> Option<(Span, Option<Span>)> {
let mut prev = None;
let mut curr = original_span;
while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
prev = Some(curr);
curr = curr.parent_callsite()?;
}
debug_assert_eq!(Some(curr), original_span.find_ancestor_in_same_ctxt(body_span));
if let Some(prev) = prev {
debug_assert_eq!(Some(curr), prev.parent_callsite());
}
Some((curr, prev))
}
#[derive(Debug)]
pub(crate) struct Hole {
pub(crate) span: Span,

View file

@ -0,0 +1,60 @@
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
/// Walks through the expansion ancestors of `original_span` to find a span that
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> Option<Span> {
// Because we don't need to return any extra ancestor information,
// we can just delegate directly to `find_ancestor_inside_same_ctxt`.
original_span.find_ancestor_inside_same_ctxt(body_span)
}
/// Walks through the expansion ancestors of `original_span` to find a span that
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
///
/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
/// the returned symbol will be the name of that macro (e.g. `foo`).
pub(crate) fn unexpand_into_body_span_with_visible_macro(
original_span: Span,
body_span: Span,
) -> Option<(Span, Option<Symbol>)> {
let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
let visible_macro = prev
.map(|prev| match prev.ctxt().outer_expn_data().kind {
ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
_ => None,
})
.flatten();
Some((span, visible_macro))
}
/// Walks through the expansion ancestors of `original_span` to find a span that
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
/// The ancestor that was traversed just before the matching span (if any) is
/// also returned.
///
/// For example, a return value of `Some((ancestor, Some(prev)))` means that:
/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
/// - `prev.parent_callsite() == ancestor`
///
/// [syntax context]: rustc_span::SyntaxContext
fn unexpand_into_body_span_with_prev(
original_span: Span,
body_span: Span,
) -> Option<(Span, Option<Span>)> {
let mut prev = None;
let mut curr = original_span;
while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
prev = Some(curr);
curr = curr.parent_callsite()?;
}
debug_assert_eq!(Some(curr), original_span.find_ancestor_inside_same_ctxt(body_span));
if let Some(prev) = prev {
debug_assert_eq!(Some(curr), prev.parent_callsite());
}
Some((curr, prev))
}