Skip to content

Rollup of 14 pull requests #139229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 53 commits into from
Apr 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
0ec1d46
Add the new `amx` target features
sayantn Mar 4, 2025
7c2434c
Add the `movrs` target feature and `movrs_target_feature` feature gate
sayantn Mar 4, 2025
6948343
fix: Check empty SIMD vector in inline asm
eyraudh Jan 9, 2025
ff699ce
fix: running the test only on x86_64.
eyraudh Feb 3, 2025
c123adf
rustdoc js: add nonundef and use it to remove a ts-expect-error
lolbinarycat Mar 24, 2025
ccd95ac
search.js: improve typechecking by avoiding Map.has
lolbinarycat Mar 24, 2025
e9a5470
search.js: refactor handling of rawPaths in buildIndex
lolbinarycat Mar 24, 2025
49bf6ca
search.js: add undef2null and eliminate more @ts-expect-error
lolbinarycat Mar 24, 2025
1af16cd
search.js(query parser): rethrow error if it isn't a string array
lolbinarycat Mar 24, 2025
8af647c
search.js: remove another Map.has() and @ts-expect-error
lolbinarycat Mar 24, 2025
714be45
search.js: refactor transformResults
lolbinarycat Mar 24, 2025
ca514c1
search.js: fix signature of pushText
lolbinarycat Mar 24, 2025
7f14198
search.js: use @type instead of @ts-expect-error
lolbinarycat Mar 24, 2025
3245e7b
search.js: fix return type of unifyFunctionTypes
lolbinarycat Mar 24, 2025
4a00a84
search.js: give type to unifiedGenericsMgens
lolbinarycat Mar 24, 2025
523f413
search.js: give type annotation to newSolutions
lolbinarycat Mar 24, 2025
1b8b09a
search.js: fix whitespace
lolbinarycat Mar 25, 2025
74b7599
satisfy eslint
lolbinarycat Mar 27, 2025
65624d9
tester.js: ignore displayTypeSignature if it is null
lolbinarycat Mar 27, 2025
d487087
search.js: revert usage of nonundef for now (not available under test)
lolbinarycat Mar 27, 2025
4f2baaa
Do not mix normalized and unnormalized caller bounds when constructin…
compiler-errors Mar 25, 2025
e80a3e2
coverage: Tweak tests/coverage/assert-ne.rs
Zalathar Mar 29, 2025
577272e
coverage: Shrink call spans to just the function name
Zalathar Mar 29, 2025
62a533c
coverage: Instead of splitting, just discard any span that overlaps a…
Zalathar Mar 25, 2025
26cea8a
coverage: Don't split bang-macro spans, just truncate them
Zalathar Mar 25, 2025
27b866d
Add ui test ui/traits/object/suggestion-trait-object-issue-139174.rs
xizheyin Apr 1, 2025
12604fa
Skip suggest impl or dyn when poly trait is not a real trait
xizheyin Apr 1, 2025
2dc650b
replace commit placeholder in vendor status with actual commit
Shourya742 Mar 28, 2025
624eb85
chore: remove redundant backtick
highcloudwind Apr 1, 2025
d2358f7
fix link in netbsd.md
futreall Apr 1, 2025
6c3be19
Update mdbook to 0.4.48
ehuss Apr 1, 2025
770fcbf
Move test-float-parse to the 2024 edition
bjorn3 Apr 1, 2025
f922e74
Make coroutine_drop_cleanup 2024 edition compatible
bjorn3 Apr 1, 2025
a2f2943
Use the 2024 edition in ./x.py fmt
bjorn3 Apr 1, 2025
2425580
Allow formatting example/gen_block_iterate.rs
bjorn3 Apr 1, 2025
a1d34bc
move autodiff from EnzymeAD/Enzyme to our rust-lang/Enzyme soft-fork
ZuseZ4 Apr 1, 2025
5f74fa0
call `mir_promoted` inside of `do_mir_borrowck`
lcnr Mar 31, 2025
cb275d4
simplify Interner opaque types API
lcnr Mar 31, 2025
6f40f0c
rustc_target: RISC-V: add base "I"-related important extensions
a4lg Apr 2, 2025
bae53a7
Rollup merge of #135295 - eyraudh:master, r=compiler-errors
Zalathar Apr 2, 2025
5b0f658
Rollup merge of #138003 - sayantn:new-amx, r=Amanieu
Zalathar Apr 2, 2025
6aec7de
Rollup merge of #138823 - a4lg:riscv-feature-addition-base-i, r=Amanieu
Zalathar Apr 2, 2025
7f9be72
Rollup merge of #138913 - lolbinarycat:rustdoc-js-less-expect-error-p…
Zalathar Apr 2, 2025
7812409
Rollup merge of #138941 - compiler-errors:receiver-is-dispatchable-bo…
Zalathar Apr 2, 2025
8420a55
Rollup merge of #139060 - Shourya742:2025-03-28-replace-commit-with-a…
Zalathar Apr 2, 2025
1692ebd
Rollup merge of #139102 - Zalathar:no-split, r=oli-obk
Zalathar Apr 2, 2025
2311b34
Rollup merge of #139191 - lcnr:interner-opaques, r=compiler-errors
Zalathar Apr 2, 2025
2d43a8b
Rollup merge of #139200 - xizheyin:issue-139174, r=compiler-errors
Zalathar Apr 2, 2025
e89ba0b
Rollup merge of #139208 - futreall:master, r=jieyouxu
Zalathar Apr 2, 2025
cedb554
Rollup merge of #139210 - highcloudwind:master, r=aDotInTheVoid
Zalathar Apr 2, 2025
5f05d43
Rollup merge of #139212 - ehuss:update-mdbook, r=Mark-Simulacrum
Zalathar Apr 2, 2025
82f0446
Rollup merge of #139214 - bjorn3:edition_2024_rustfmt, r=compiler-errors
Zalathar Apr 2, 2025
ea51a1c
Rollup merge of #139225 - EnzymeAD:rust-lang-enzyme, r=jieyouxu
Zalathar Apr 2, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
shallow = true
[submodule "src/tools/enzyme"]
path = src/tools/enzyme
url = https://github.com/EnzymeAD/Enzyme.git
url = https://github.com/rust-lang/Enzyme.git
shallow = true
[submodule "src/gcc"]
path = src/gcc
Expand Down
7 changes: 2 additions & 5 deletions compiler/rustc_borrowck/src/consumers.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This file provides API for compiler consumers.

use rustc_hir::def_id::LocalDefId;
use rustc_index::{IndexSlice, IndexVec};
use rustc_index::IndexVec;
use rustc_middle::mir::{Body, Promoted};
use rustc_middle::ty::TyCtxt;

Expand Down Expand Up @@ -100,8 +100,5 @@ pub fn get_body_with_borrowck_facts(
def: LocalDefId,
options: ConsumerOptions,
) -> BodyWithBorrowckFacts<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexSlice<_, _> = &promoted.borrow();
*super::do_mir_borrowck(tcx, input_body, promoted, Some(options)).1.unwrap()
*super::do_mir_borrowck(tcx, def, Some(options)).1.unwrap()
}
19 changes: 9 additions & 10 deletions compiler/rustc_borrowck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,8 @@ pub fn provide(providers: &mut Providers) {
}

fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
let (input_body, promoted) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def));

let (input_body, _) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();

if input_body.should_skip() || input_body.tainted_by_errors.is_some() {
debug!("Skipping borrowck because of injected body or tainted body");
// Let's make up a borrowck result! Fun times!
Expand All @@ -120,7 +117,7 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
return tcx.arena.alloc(result);
}

let borrowck_result = do_mir_borrowck(tcx, input_body, &*promoted.borrow(), None).0;
let borrowck_result = do_mir_borrowck(tcx, def, None).0;
debug!("mir_borrowck done");

tcx.arena.alloc(borrowck_result)
Expand All @@ -131,15 +128,16 @@ fn mir_borrowck(tcx: TyCtxt<'_>, def: LocalDefId) -> &BorrowCheckResult<'_> {
/// Use `consumer_options: None` for the default behavior of returning
/// [`BorrowCheckResult`] only. Otherwise, return [`BodyWithBorrowckFacts`] according
/// to the given [`ConsumerOptions`].
#[instrument(skip(tcx, input_body, input_promoted), fields(id=?input_body.source.def_id()), level = "debug")]
#[instrument(skip(tcx), level = "debug")]
fn do_mir_borrowck<'tcx>(
tcx: TyCtxt<'tcx>,
input_body: &Body<'tcx>,
input_promoted: &IndexSlice<Promoted, Body<'tcx>>,
def: LocalDefId,
consumer_options: Option<ConsumerOptions>,
) -> (BorrowCheckResult<'tcx>, Option<Box<BodyWithBorrowckFacts<'tcx>>>) {
let def = input_body.source.def_id().expect_local();
let infcx = BorrowckInferCtxt::new(tcx, def);
let (input_body, promoted) = tcx.mir_promoted(def);
let input_body: &Body<'_> = &input_body.borrow();
let input_promoted: &IndexSlice<_, _> = &promoted.borrow();
if let Some(e) = input_body.tainted_by_errors {
infcx.set_tainted_by_errors(e);
}
Expand Down Expand Up @@ -499,7 +497,8 @@ impl<'tcx> BorrowckInferCtxt<'tcx> {
)
});

self.inject_new_hidden_type_unchecked(key, hidden_ty);
let prev = self.register_hidden_type_in_storage(key, hidden_ty);
assert_eq!(prev, None);
}
}
}
Expand Down
16 changes: 12 additions & 4 deletions compiler/rustc_codegen_cranelift/example/gen_block_iterate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,25 @@
#![feature(gen_blocks)]

fn foo() -> impl Iterator<Item = u32> {
gen { yield 42; for x in 3..6 { yield x } }
gen {
yield 42;
for x in 3..6 {
yield x
}
}
}

fn moved() -> impl Iterator<Item = u32> {
let mut x = "foo".to_string();
gen move {
yield 42;
if x == "foo" { return }
if x == "foo" {
return;
}
x.clear();
for x in 3..6 { yield x }
for x in 3..6 {
yield x
}
}
}

Expand All @@ -32,5 +41,4 @@ fn main() {
let mut iter = moved();
assert_eq!(iter.next(), Some(42));
assert_eq!(iter.next(), None);

}
4 changes: 0 additions & 4 deletions compiler/rustc_codegen_cranelift/rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
ignore = [
"example/gen_block_iterate.rs", # uses edition 2024
]

# Matches rustfmt.toml of rustc
style_edition = "2024"
use_small_heuristics = "Max"
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,13 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
("sparc", "v8plus") if get_version().0 == 19 => Some(LLVMFeature::new("v9")),
("sparc", "v8plus") if get_version().0 < 19 => None,
("powerpc", "power8-crypto") => Some(LLVMFeature::new("crypto")),
// These new `amx` variants and `movrs` were introduced in LLVM20
("x86", "amx-avx512" | "amx-fp8" | "amx-movrs" | "amx-tf32" | "amx-transpose")
if get_version().0 < 20 =>
{
None
}
("x86", "movrs") if get_version().0 < 20 => None,
(_, s) => Some(LLVMFeature::new(s)),
}
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ declare_features! (
(unstable, loongarch_target_feature, "1.73.0", Some(44839)),
(unstable, m68k_target_feature, "1.85.0", Some(134328)),
(unstable, mips_target_feature, "1.27.0", Some(44839)),
(unstable, movrs_target_feature, "CURRENT_RUSTC_VERSION", Some(137976)),
(unstable, powerpc_target_feature, "1.27.0", Some(44839)),
(unstable, prfchw_target_feature, "1.78.0", Some(44839)),
(unstable, riscv_target_feature, "1.45.0", Some(44839)),
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_hir_analysis/src/check/intrinsicck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ enum NonAsmTypeReason<'tcx> {
Invalid(Ty<'tcx>),
InvalidElement(DefId, Ty<'tcx>),
NotSizedPtr(Ty<'tcx>),
EmptySIMDArray(Ty<'tcx>),
}

impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
Expand Down Expand Up @@ -102,6 +103,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
}
ty::Adt(adt, args) if adt.repr().simd() => {
let fields = &adt.non_enum_variant().fields;
if fields.is_empty() {
return Err(NonAsmTypeReason::EmptySIMDArray(ty));
}
let field = &fields[FieldIdx::ZERO];
let elem_ty = field.ty(self.tcx(), args);

Expand Down Expand Up @@ -226,6 +230,10 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
can be used as arguments for inline assembly",
).emit();
}
NonAsmTypeReason::EmptySIMDArray(ty) => {
let msg = format!("use of empty SIMD vector `{ty}`");
self.infcx.dcx().struct_span_err(expr.span, msg).emit();
}
}
return None;
}
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
"expected a type, found a trait"
);
if self_ty.span.can_be_used_for_suggestions()
&& poly_trait_ref.trait_ref.trait_def_id().is_some()
&& !self.maybe_suggest_impl_trait(self_ty, &mut diag)
&& !self.maybe_suggest_dyn_trait(self_ty, sugg, &mut diag)
{
Expand Down
7 changes: 3 additions & 4 deletions compiler/rustc_infer/src/infer/opaque_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,12 @@ impl<'tcx> InferCtxt<'tcx> {
/// it hasn't previously been defined. This does not emit any
/// constraints and it's the responsibility of the caller to make
/// sure that the item bounds of the opaque are checked.
pub fn inject_new_hidden_type_unchecked(
pub fn register_hidden_type_in_storage(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
hidden_ty: OpaqueHiddenType<'tcx>,
) {
let prev = self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty);
assert_eq!(prev, None);
) -> Option<Ty<'tcx>> {
self.inner.borrow_mut().opaque_types().register(opaque_type_key, hidden_ty)
}

/// Insert a hidden type into the opaque type storage, equating it
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_mir_transform/src/coverage/mappings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
}
} else {
// Extract coverage spans from MIR statements/terminators as normal.
extract_refined_covspans(mir_body, hir_info, graph, &mut code_mappings);
extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
}

branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph));
Expand Down
107 changes: 30 additions & 77 deletions compiler/rustc_mir_transform/src/coverage/spans.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::collections::VecDeque;
use std::iter;

use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir;
use rustc_middle::ty::TyCtxt;
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
use tracing::{debug, debug_span, instrument};

Expand All @@ -11,8 +13,9 @@ use crate::coverage::{ExtractedHirInfo, mappings, unexpand};

mod from_mir;

pub(super) fn extract_refined_covspans(
mir_body: &mir::Body<'_>,
pub(super) fn extract_refined_covspans<'tcx>(
tcx: TyCtxt<'tcx>,
mir_body: &mir::Body<'tcx>,
hir_info: &ExtractedHirInfo,
graph: &CoverageGraph,
code_mappings: &mut impl Extend<mappings::CodeMapping>,
Expand Down Expand Up @@ -50,7 +53,7 @@ pub(super) fn extract_refined_covspans(
// First, perform the passes that need macro information.
covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb));
remove_unwanted_expansion_spans(&mut covspans);
split_visible_macro_spans(&mut covspans);
shrink_visible_macro_spans(tcx, &mut covspans);

// We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
Expand Down Expand Up @@ -83,9 +86,7 @@ pub(super) fn extract_refined_covspans(
// Split the covspans into separate buckets that don't overlap any holes.
let buckets = divide_spans_into_buckets(covspans, &holes);

for mut covspans in buckets {
// Make sure each individual bucket is internally sorted.
covspans.sort_by(compare_covspans);
for covspans in buckets {
let _span = debug_span!("processing bucket", ?covspans).entered();

let mut covspans = remove_unwanted_overlapping_spans(covspans);
Expand Down Expand Up @@ -129,82 +130,50 @@ fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
}

/// When a span corresponds to a macro invocation that is visible from the
/// function body, split it into two parts. The first part covers just the
/// macro name plus `!`, and the second part covers the rest of the macro
/// invocation. This seems to give better results for code that uses macros.
fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
let mut extra_spans = vec![];

covspans.retain(|covspan| {
let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else {
return true;
};

let split_len = visible_macro.as_str().len() as u32 + 1;
let (before, after) = covspan.span.split_at(split_len);
if !covspan.span.contains(before) || !covspan.span.contains(after) {
// Something is unexpectedly wrong with the split point.
// The debug assertion in `split_at` will have already caught this,
// but in release builds it's safer to do nothing and maybe get a
// bug report for unexpected coverage, rather than risk an ICE.
return true;
/// function body, truncate it to just the macro name plus `!`.
/// This seems to give better results for code that uses macros.
fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>) {
let source_map = tcx.sess.source_map();

for covspan in covspans {
if matches!(covspan.expn_kind, Some(ExpnKind::Macro(MacroKind::Bang, _))) {
covspan.span = source_map.span_through_char(covspan.span, '!');
}

extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb));
extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb));
false // Discard the original covspan that we just split.
});

// The newly-split spans are added at the end, so any previous sorting
// is not preserved.
covspans.extend(extra_spans);
}
}

/// Uses the holes to divide the given covspans into buckets, such that:
/// - No span in any hole overlaps a bucket (truncating the spans if necessary).
/// - No span in any hole overlaps a bucket (discarding spans if necessary).
/// - The spans in each bucket are strictly after all spans in previous buckets,
/// and strictly before all spans in subsequent buckets.
///
/// The resulting buckets are sorted relative to each other, but might not be
/// internally sorted.
/// The lists of covspans and holes must be sorted.
/// The resulting buckets are sorted relative to each other, and each bucket's
/// contents are sorted.
#[instrument(level = "debug")]
fn divide_spans_into_buckets(input_covspans: Vec<Covspan>, holes: &[Hole]) -> Vec<Vec<Covspan>> {
debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));

// Now we're ready to start carving holes out of the initial coverage spans,
// and grouping them in buckets separated by the holes.
// Now we're ready to start grouping spans into buckets separated by holes.

let mut input_covspans = VecDeque::from(input_covspans);
let mut fragments = vec![];

// For each hole:
// - Identify the spans that are entirely or partly before the hole.
// - Put those spans in a corresponding bucket, truncated to the start of the hole.
// - If one of those spans also extends after the hole, put the rest of it
// in a "fragments" vector that is processed by the next hole.
// - Discard any that overlap with the hole.
// - Add the remaining identified spans to the corresponding bucket.
let mut buckets = (0..holes.len()).map(|_| vec![]).collect::<Vec<_>>();
for (hole, bucket) in holes.iter().zip(&mut buckets) {
let fragments_from_prev = std::mem::take(&mut fragments);

// Only inspect spans that precede or overlap this hole,
// leaving the rest to be inspected by later holes.
// (This relies on the spans and holes both being sorted.)
let relevant_input_covspans =
drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi());

for covspan in fragments_from_prev.into_iter().chain(relevant_input_covspans) {
let (before, after) = covspan.split_around_hole_span(hole.span);
bucket.extend(before);
fragments.extend(after);
}
bucket.extend(
drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi())
.filter(|c| !c.span.overlaps(hole.span)),
);
}

// After finding the spans before each hole, any remaining fragments/spans
// form their own final bucket, after the final hole.
// Any remaining spans form their own final bucket, after the final hole.
// (If there were no holes, this will just be all of the initial spans.)
fragments.extend(input_covspans);
buckets.push(fragments);
buckets.push(Vec::from(input_covspans));

buckets
}
Expand All @@ -215,7 +184,7 @@ fn drain_front_while<'a, T>(
queue: &'a mut VecDeque<T>,
mut pred_fn: impl FnMut(&T) -> bool,
) -> impl Iterator<Item = T> {
std::iter::from_fn(move || if pred_fn(queue.front()?) { queue.pop_front() } else { None })
iter::from_fn(move || queue.pop_front_if(|x| pred_fn(x)))
}

/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
Expand Down Expand Up @@ -258,22 +227,6 @@ struct Covspan {
}

impl Covspan {
/// Splits this covspan into 0-2 parts:
/// - The part that is strictly before the hole span, if any.
/// - The part that is strictly after the hole span, if any.
fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
let before = try {
let span = self.span.trim_end(hole_span)?;
Self { span, ..*self }
};
let after = try {
let span = self.span.trim_start(hole_span)?;
Self { span, ..*self }
};

(before, after)
}

/// If `self` and `other` can be merged (i.e. they have the same BCB),
/// mutates `self.span` to also include `other.span` and returns true.
///
Expand Down
Loading
Loading