Skip to content
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

Emit a warning if a match is too complex #122685

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion compiler/rustc_mir_build/src/thir/pattern/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,10 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
scrut_ty: Ty<'tcx>,
) -> Result<UsefulnessReport<'p, 'tcx>, ErrorGuaranteed> {
let pattern_complexity_limit =
get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity);
get_limit_size(cx.tcx.hir().krate_attrs(), cx.tcx.sess, sym::pattern_complexity)
// Default value to emit the warning for "too complex" match. We picked it to warn
// after a second or two.
.or(Some(10_000_000));
let report =
rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty, pattern_complexity_limit)
.map_err(|err| {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_pattern_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ pattern_analysis_overlapping_range_endpoints = multiple patterns overlap on thei
.label = ... with this range
.note = you likely meant to write mutually exclusive ranges

pattern_analysis_too_complex = this pattern-match expression is taking a long time to analyze
.help = to speed up checking, break it up into smaller `match`es and/or replace some patterns with guards
.note = match checking can take exponential time when many different fields of structs/tuples/arrays are involved

pattern_analysis_uncovered = {$count ->
[1] pattern `{$witness_1}`
[2] patterns `{$witness_1}` and `{$witness_2}`
Expand Down
11 changes: 10 additions & 1 deletion compiler/rustc_pattern_analysis/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use rustc_errors::{Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
use rustc_macros::{LintDiagnostic, Subdiagnostic};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::thir::Pat;
use rustc_middle::ty::Ty;
use rustc_span::Span;
Expand Down Expand Up @@ -148,3 +148,12 @@ pub(crate) struct NonExhaustiveOmittedPatternLintOnArm {
pub lint_level: &'static str,
pub lint_name: &'static str,
}

#[derive(Diagnostic)]
#[diag(pattern_analysis_too_complex)]
#[help]
#[note]
pub(crate) struct TooComplex {
#[primary_span]
pub span: Span,
}
3 changes: 2 additions & 1 deletion compiler/rustc_pattern_analysis/src/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,8 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {

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"))
self.tcx.dcx().emit_warn(errors::TooComplex { span });
Ok(())
}

fn lint_non_contiguous_range_endpoints(
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_pattern_analysis/src/usefulness.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,8 @@ impl<'a, Cx: PatCx> UsefulnessCtxt<'a, Cx> {
.complexity_limit
.is_some_and(|complexity_limit| complexity_limit < self.complexity_level)
{
// We change it to `None` to prevent it from being called more than once.
self.complexity_limit = None;
return self.tycx.complexity_exceeded();
}
Ok(())
Expand Down
44 changes: 6 additions & 38 deletions tests/ui/pattern/complexity_limit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//@ check-pass

#![feature(rustc_attrs)]
#![pattern_complexity = "10000"]
// By default the complexity is 10_000_000, but we reduce it so the test takes
// (much) less time.
#![pattern_complexity = "2000000"]

#[derive(Default)]
struct BaseCommand {
Expand All @@ -21,22 +25,10 @@ struct BaseCommand {
field16: bool,
field17: bool,
field18: bool,
field19: bool,
field20: bool,
field21: bool,
field22: bool,
field23: bool,
field24: bool,
field25: bool,
field26: bool,
field27: bool,
field28: bool,
field29: bool,
field30: bool,
}

fn request_key(command: BaseCommand) {
match command { //~ ERROR: reached pattern complexity limit
match command { //~ WARN: this pattern-match expression is taking a long time to analyze
BaseCommand { field01: true, .. } => {}
BaseCommand { field02: true, .. } => {}
BaseCommand { field03: true, .. } => {}
Expand All @@ -55,18 +47,6 @@ fn request_key(command: BaseCommand) {
BaseCommand { field16: true, .. } => {}
BaseCommand { field17: true, .. } => {}
BaseCommand { field18: true, .. } => {}
BaseCommand { field19: true, .. } => {}
BaseCommand { field20: true, .. } => {}
BaseCommand { field21: true, .. } => {}
BaseCommand { field22: true, .. } => {}
BaseCommand { field23: true, .. } => {}
BaseCommand { field24: true, .. } => {}
BaseCommand { field25: true, .. } => {}
BaseCommand { field26: true, .. } => {}
BaseCommand { field27: true, .. } => {}
BaseCommand { field28: true, .. } => {}
BaseCommand { field29: true, .. } => {}
BaseCommand { field30: true, .. } => {}

BaseCommand { field01: false, .. } => {}
BaseCommand { field02: false, .. } => {}
Expand All @@ -86,18 +66,6 @@ fn request_key(command: BaseCommand) {
BaseCommand { field16: false, .. } => {}
BaseCommand { field17: false, .. } => {}
BaseCommand { field18: false, .. } => {}
BaseCommand { field19: false, .. } => {}
BaseCommand { field20: false, .. } => {}
BaseCommand { field21: false, .. } => {}
BaseCommand { field22: false, .. } => {}
BaseCommand { field23: false, .. } => {}
BaseCommand { field24: false, .. } => {}
BaseCommand { field25: false, .. } => {}
BaseCommand { field26: false, .. } => {}
BaseCommand { field27: false, .. } => {}
BaseCommand { field28: false, .. } => {}
BaseCommand { field29: false, .. } => {}
BaseCommand { field30: false, .. } => {}
}
}

Expand Down
11 changes: 7 additions & 4 deletions tests/ui/pattern/complexity_limit.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
error: reached pattern complexity limit
--> $DIR/complexity_limit.rs:39:5
warning: this pattern-match expression is taking a long time to analyze
--> $DIR/complexity_limit.rs:31:5
|
LL | / match command {
LL | | BaseCommand { field01: true, .. } => {}
LL | | BaseCommand { field02: true, .. } => {}
LL | | BaseCommand { field03: true, .. } => {}
... |
LL | | BaseCommand { field30: false, .. } => {}
LL | | BaseCommand { field18: false, .. } => {}
LL | | }
| |_____^
|
= help: to speed up checking, break it up into smaller `match`es and/or replace some patterns with guards
= note: match checking can take exponential time when many different fields of structs/tuples/arrays are involved

error: aborting due to 1 previous error
warning: 1 warning emitted

Loading