Skip to content

Commit 3898c33

Browse files
Emit a warning if a match is too complex and set the default trigger when complexity reaches 10_000_000
1 parent a42873e commit 3898c33

File tree

5 files changed

+22
-3
lines changed

5 files changed

+22
-3
lines changed

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
405405
scrut_ty: Ty<'tcx>,
406406
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
407407
let pattern_complexity_limit =
408-
get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity);
408+
get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity)
409+
// Default value to emit the warning for "too complex" match. We picked it based on
410+
// this crater run: <https://github.com/rust-lang/rust/pull/121979>.
411+
.or(Some(10_000_000));
409412
let report =
410413
rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty, pattern_complexity_limit)
411414
.map_err(|err| {

compiler/rustc_pattern_analysis/messages.ftl

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ pattern_analysis_overlapping_range_endpoints = multiple patterns overlap on thei
1919
.label = ... with this range
2020
.note = you likely meant to write mutually exclusive ranges
2121
22+
pattern_analysis_too_complex = this pattern-match expression is taking too long to analyze
23+
.help = break it up into smaller `match`es and/or replace some patterns with guards
24+
.note = match checking can take exponential time when many different fields of structs/tuples/arrays are involved
25+
2226
pattern_analysis_uncovered = {$count ->
2327
[1] pattern `{$witness_1}`
2428
[2] patterns `{$witness_1}` and `{$witness_2}`

compiler/rustc_pattern_analysis/src/errors.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use rustc_errors::{Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
2-
use rustc_macros::{LintDiagnostic, Subdiagnostic};
2+
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
33
use rustc_middle::thir::Pat;
44
use rustc_middle::ty::Ty;
55
use rustc_span::Span;
@@ -148,3 +148,12 @@ pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
148148
pub lint_level: &'static str,
149149
pub lint_name: &'static str,
150150
}
151+
152+
#[derive(Diagnostic)]
153+
#[diag(pattern_analysis_too_complex)]
154+
#[help]
155+
#[note]
156+
pub(crate) struct TooComplex {
157+
#[primary_span]
158+
pub span: Span,
159+
}

compiler/rustc_pattern_analysis/src/rustc.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,8 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
913913

914914
fn complexity_exceeded(&self) -> Result<(), Self::Error> {
915915
let span = self.whole_match_span.unwrap_or(self.scrut_span);
916-
Err(self.tcx.dcx().span_err(span, "reached pattern complexity limit"))
916+
self.tcx.dcx().emit_warn(errors::TooComplex { span });
917+
Ok(())
917918
}
918919

919920
fn lint_non_contiguous_range_endpoints(

compiler/rustc_pattern_analysis/src/usefulness.rs

+2
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,8 @@ impl<'a, Cx: PatCx> UsefulnessCtxt<'a, Cx> {
745745
.complexity_limit
746746
.is_some_and(|complexity_limit| complexity_limit < self.complexity_level)
747747
{
748+
// We change it to `None` to prevent it from being called more than once.
749+
self.complexity_limit = None;
748750
return self.tycx.complexity_exceeded();
749751
}
750752
Ok(())

0 commit comments

Comments
 (0)