Auto merge of #121937 - GuillaumeGomez:rollup-9684vg3, r=GuillaumeGomez
Rollup of 3 pull requests Successful merges: - #121917 (Add new `pattern_complexity` attribute to add possibility to limit and check recursion in pattern matching) - #121933 (Add missing get_name for wasm::thread.) - #121934 (rustc_log: expose tracing-tree "wraparound" in an env var) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
26907374b9
@ -929,6 +929,10 @@ pub struct BuiltinAttribute {
|
||||
omit_gdb_pretty_printer_section, Normal, template!(Word), WarnFollowing,
|
||||
"the `#[omit_gdb_pretty_printer_section]` attribute is just used for the Rust test suite",
|
||||
),
|
||||
rustc_attr!(
|
||||
TEST, pattern_complexity, CrateLevel, template!(NameValueStr: "N"),
|
||||
ErrorFollowing, @only_local: true,
|
||||
),
|
||||
];
|
||||
|
||||
pub fn deprecated_attributes() -> Vec<&'static BuiltinAttribute> {
|
||||
|
@ -213,6 +213,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
||||
(internal, negative_bounds, "1.71.0", None),
|
||||
/// Allows using `#[omit_gdb_pretty_printer_section]`.
|
||||
(internal, omit_gdb_pretty_printer_section, "1.5.0", None),
|
||||
/// Set the maximum pattern complexity allowed (not limited by default).
|
||||
(internal, pattern_complexity, "CURRENT_RUSTC_VERSION", None),
|
||||
/// Allows using `#[prelude_import]` on glob `use` items.
|
||||
(internal, prelude_import, "1.2.0", None),
|
||||
/// Used to identify crates that contain the profiler runtime.
|
||||
|
@ -57,6 +57,7 @@ pub struct LoggerConfig {
|
||||
pub verbose_entry_exit: Result<String, VarError>,
|
||||
pub verbose_thread_ids: Result<String, VarError>,
|
||||
pub backtrace: Result<String, VarError>,
|
||||
pub wraptree: Result<String, VarError>,
|
||||
}
|
||||
|
||||
impl LoggerConfig {
|
||||
@ -67,6 +68,7 @@ pub fn from_env(env: &str) -> Self {
|
||||
verbose_entry_exit: env::var(format!("{env}_ENTRY_EXIT")),
|
||||
verbose_thread_ids: env::var(format!("{env}_THREAD_IDS")),
|
||||
backtrace: env::var(format!("{env}_BACKTRACE")),
|
||||
wraptree: env::var(format!("{env}_WRAPTREE")),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -99,7 +101,7 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
let layer = tracing_tree::HierarchicalLayer::default()
|
||||
let mut layer = tracing_tree::HierarchicalLayer::default()
|
||||
.with_writer(io::stderr)
|
||||
.with_indent_lines(true)
|
||||
.with_ansi(color_logs)
|
||||
@ -110,6 +112,16 @@ pub fn init_logger(cfg: LoggerConfig) -> Result<(), Error> {
|
||||
.with_thread_ids(verbose_thread_ids)
|
||||
.with_thread_names(verbose_thread_ids);
|
||||
|
||||
match cfg.wraptree {
|
||||
Ok(v) => match v.parse::<usize>() {
|
||||
Ok(v) => {
|
||||
layer = layer.with_wraparound(v);
|
||||
}
|
||||
Err(_) => return Err(Error::InvalidWraptree(v)),
|
||||
},
|
||||
Err(_) => {} // no wraptree
|
||||
}
|
||||
|
||||
let subscriber = tracing_subscriber::Registry::default().with(filter).with(layer);
|
||||
match cfg.backtrace {
|
||||
Ok(str) => {
|
||||
@ -164,6 +176,7 @@ pub fn stderr_isatty() -> bool {
|
||||
pub enum Error {
|
||||
InvalidColorValue(String),
|
||||
NonUnicodeColorValue,
|
||||
InvalidWraptree(String),
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
@ -179,6 +192,10 @@ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter,
|
||||
"non-Unicode log color value: expected one of always, never, or auto",
|
||||
),
|
||||
Error::InvalidWraptree(value) => write!(
|
||||
formatter,
|
||||
"invalid log WRAPTREE value '{value}': expected a non-negative integer",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,13 @@ pub fn get_recursion_limit(krate_attrs: &[Attribute], sess: &Session) -> Limit {
|
||||
}
|
||||
|
||||
fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: usize) -> Limit {
|
||||
match get_limit_size(krate_attrs, sess, name) {
|
||||
Some(size) => Limit::new(size),
|
||||
None => Limit::new(default),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_limit_size(krate_attrs: &[Attribute], sess: &Session, name: Symbol) -> Option<usize> {
|
||||
for attr in krate_attrs {
|
||||
if !attr.has_name(name) {
|
||||
continue;
|
||||
@ -47,7 +54,7 @@ fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: u
|
||||
|
||||
if let Some(s) = attr.value_str() {
|
||||
match s.as_str().parse() {
|
||||
Ok(n) => return Limit::new(n),
|
||||
Ok(n) => return Some(n),
|
||||
Err(e) => {
|
||||
let value_span = attr
|
||||
.meta()
|
||||
@ -69,5 +76,5 @@ fn get_limit(krate_attrs: &[Attribute], sess: &Session, name: Symbol, default: u
|
||||
}
|
||||
}
|
||||
}
|
||||
return Limit::new(default);
|
||||
None
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
use rustc_hir::def::*;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_middle::middle::limits::get_limit_size;
|
||||
use rustc_middle::thir::visit::Visitor;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
@ -26,7 +27,7 @@
|
||||
};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{sym, Span};
|
||||
|
||||
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> {
|
||||
let typeck_results = tcx.typeck(def_id);
|
||||
@ -403,8 +404,11 @@ fn analyze_patterns(
|
||||
arms: &[MatchArm<'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);
|
||||
let report =
|
||||
rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty).map_err(|err| {
|
||||
rustc_pattern_analysis::analyze_match(&cx, &arms, scrut_ty, pattern_complexity_limit)
|
||||
.map_err(|err| {
|
||||
self.error = Err(err);
|
||||
err
|
||||
})?;
|
||||
|
@ -142,6 +142,9 @@ fn lint_overlapping_range_endpoints(
|
||||
_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 @@ fn lint_overlapping_range_endpoints(
|
||||
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)?;
|
||||
|
||||
|
@ -1303,6 +1303,7 @@
|
||||
pat,
|
||||
pat_param,
|
||||
path,
|
||||
pattern_complexity,
|
||||
pattern_parentheses,
|
||||
phantom_data,
|
||||
pic,
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::ffi::CStr;
|
||||
use crate::ffi::CString;
|
||||
use crate::io;
|
||||
use crate::num::NonZero;
|
||||
use crate::sys::unsupported;
|
||||
@ -17,6 +18,9 @@ pub unsafe fn new(_stack: usize, _p: Box<dyn FnOnce()>) -> io::Result<Thread> {
|
||||
pub fn yield_now() {}
|
||||
|
||||
pub fn set_name(_name: &CStr) {}
|
||||
pub fn get_name() -> Option<CString> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn sleep(dur: Duration) {
|
||||
use crate::arch::wasm32;
|
||||
|
@ -0,0 +1,6 @@
|
||||
// check that `pattern_complexity` is feature-gated
|
||||
|
||||
#![pattern_complexity = "42"]
|
||||
//~^ ERROR: the `#[pattern_complexity]` attribute is just used for rustc unit tests
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,12 @@
|
||||
error[E0658]: the `#[pattern_complexity]` attribute is just used for rustc unit tests and will never be stable
|
||||
--> $DIR/feature-gate-pattern-complexity.rs:3:1
|
||||
|
|
||||
LL | #![pattern_complexity = "42"]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: add `#![feature(rustc_attrs)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
106
tests/ui/pattern/complexity_limit.rs
Normal file
106
tests/ui/pattern/complexity_limit.rs
Normal file
@ -0,0 +1,106 @@
|
||||
#![feature(rustc_attrs)]
|
||||
#![pattern_complexity = "10000"]
|
||||
|
||||
#[derive(Default)]
|
||||
struct BaseCommand {
|
||||
field01: bool,
|
||||
field02: bool,
|
||||
field03: bool,
|
||||
field04: bool,
|
||||
field05: bool,
|
||||
field06: bool,
|
||||
field07: bool,
|
||||
field08: bool,
|
||||
field09: bool,
|
||||
field10: bool,
|
||||
field11: bool,
|
||||
field12: bool,
|
||||
field13: bool,
|
||||
field14: bool,
|
||||
field15: bool,
|
||||
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
|
||||
BaseCommand { field01: true, .. } => {}
|
||||
BaseCommand { field02: true, .. } => {}
|
||||
BaseCommand { field03: true, .. } => {}
|
||||
BaseCommand { field04: true, .. } => {}
|
||||
BaseCommand { field05: true, .. } => {}
|
||||
BaseCommand { field06: true, .. } => {}
|
||||
BaseCommand { field07: true, .. } => {}
|
||||
BaseCommand { field08: true, .. } => {}
|
||||
BaseCommand { field09: true, .. } => {}
|
||||
BaseCommand { field10: true, .. } => {}
|
||||
BaseCommand { field11: true, .. } => {}
|
||||
BaseCommand { field12: true, .. } => {}
|
||||
BaseCommand { field13: true, .. } => {}
|
||||
BaseCommand { field14: true, .. } => {}
|
||||
BaseCommand { field15: true, .. } => {}
|
||||
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, .. } => {}
|
||||
BaseCommand { field03: false, .. } => {}
|
||||
BaseCommand { field04: false, .. } => {}
|
||||
BaseCommand { field05: false, .. } => {}
|
||||
BaseCommand { field06: false, .. } => {}
|
||||
BaseCommand { field07: false, .. } => {}
|
||||
BaseCommand { field08: false, .. } => {}
|
||||
BaseCommand { field09: false, .. } => {}
|
||||
BaseCommand { field10: false, .. } => {}
|
||||
BaseCommand { field11: false, .. } => {}
|
||||
BaseCommand { field12: false, .. } => {}
|
||||
BaseCommand { field13: false, .. } => {}
|
||||
BaseCommand { field14: false, .. } => {}
|
||||
BaseCommand { field15: false, .. } => {}
|
||||
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, .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
request_key(BaseCommand::default());
|
||||
}
|
14
tests/ui/pattern/complexity_limit.stderr
Normal file
14
tests/ui/pattern/complexity_limit.stderr
Normal file
@ -0,0 +1,14 @@
|
||||
error: reached pattern complexity limit
|
||||
--> $DIR/complexity_limit.rs:39:5
|
||||
|
|
||||
LL | / match command {
|
||||
LL | | BaseCommand { field01: true, .. } => {}
|
||||
LL | | BaseCommand { field02: true, .. } => {}
|
||||
LL | | BaseCommand { field03: true, .. } => {}
|
||||
... |
|
||||
LL | | BaseCommand { field30: false, .. } => {}
|
||||
LL | | }
|
||||
| |_____^
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
@ -1,3 +1,6 @@
|
||||
#![feature(rustc_attrs)]
|
||||
#![pattern_complexity = "61"]
|
||||
|
||||
//@ check-pass
|
||||
struct BaseCommand {
|
||||
field01: bool,
|
||||
|
Loading…
Reference in New Issue
Block a user