Add new pattern_complexity
attribute to add possibility to limit and check recursion in pattern matching
This commit is contained in:
parent
5257aee7dd
commit
be31b6b6cd
8 changed files with 56 additions and 6 deletions
|
@ -142,6 +142,9 @@ pub trait TypeCx: Sized + fmt::Debug {
|
|||
_overlaps_with: &[&DeconstructedPat<Self>],
|
||||
) {
|
||||
}
|
||||
|
||||
/// The maximum pattern complexity limit was reached.
|
||||
fn complexity_exceeded(&self) -> Result<(), Self::Error>;
|
||||
}
|
||||
|
||||
/// The arm of a match expression.
|
||||
|
@ -167,10 +170,12 @@ pub fn analyze_match<'p, 'tcx>(
|
|||
tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
||||
arms: &[rustc::MatchArm<'p, 'tcx>],
|
||||
scrut_ty: Ty<'tcx>,
|
||||
pattern_complexity_limit: Option<usize>,
|
||||
) -> Result<rustc::UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
|
||||
let scrut_ty = tycx.reveal_opaque_ty(scrut_ty);
|
||||
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
|
||||
let report = compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity)?;
|
||||
let report =
|
||||
compute_match_usefulness(tycx, arms, scrut_ty, scrut_validity, pattern_complexity_limit)?;
|
||||
|
||||
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
|
||||
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
|
||||
|
|
|
@ -895,6 +895,11 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
|||
errors::OverlappingRangeEndpoints { overlap: overlaps, range: pat_span },
|
||||
);
|
||||
}
|
||||
|
||||
fn complexity_exceeded(&self) -> Result<(), Self::Error> {
|
||||
let span = self.whole_match_span.unwrap_or(self.scrut_span);
|
||||
Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
|
||||
|
|
|
@ -734,6 +734,21 @@ struct UsefulnessCtxt<'a, Cx: TypeCx> {
|
|||
/// Collect the patterns found useful during usefulness checking. This is used to lint
|
||||
/// unreachable (sub)patterns.
|
||||
useful_subpatterns: FxHashSet<PatId>,
|
||||
complexity_limit: Option<usize>,
|
||||
complexity_level: usize,
|
||||
}
|
||||
|
||||
impl<'a, Cx: TypeCx> UsefulnessCtxt<'a, Cx> {
|
||||
fn increase_complexity_level(&mut self, complexity_add: usize) -> Result<(), Cx::Error> {
|
||||
self.complexity_level += complexity_add;
|
||||
if self
|
||||
.complexity_limit
|
||||
.is_some_and(|complexity_limit| complexity_limit < self.complexity_level)
|
||||
{
|
||||
return self.tycx.complexity_exceeded();
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Context that provides information local to a place under investigation.
|
||||
|
@ -1552,6 +1567,7 @@ fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
|
|||
}
|
||||
|
||||
let Some(place) = matrix.head_place() else {
|
||||
mcx.increase_complexity_level(matrix.rows().len())?;
|
||||
// The base case: there are no columns in the matrix. We are morally pattern-matching on ().
|
||||
// A row is useful iff it has no (unguarded) rows above it.
|
||||
let mut useful = true; // Whether the next row is useful.
|
||||
|
@ -1690,8 +1706,14 @@ pub fn compute_match_usefulness<'p, Cx: TypeCx>(
|
|||
arms: &[MatchArm<'p, Cx>],
|
||||
scrut_ty: Cx::Ty,
|
||||
scrut_validity: ValidityConstraint,
|
||||
complexity_limit: Option<usize>,
|
||||
) -> Result<UsefulnessReport<'p, Cx>, Cx::Error> {
|
||||
let mut cx = UsefulnessCtxt { tycx, useful_subpatterns: FxHashSet::default() };
|
||||
let mut cx = UsefulnessCtxt {
|
||||
tycx,
|
||||
useful_subpatterns: FxHashSet::default(),
|
||||
complexity_limit,
|
||||
complexity_level: 0,
|
||||
};
|
||||
let mut matrix = Matrix::new(arms, scrut_ty, scrut_validity);
|
||||
let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(&mut cx, &mut matrix)?;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue