From 58b2ac88c0b39a48ac2277161ca75df3034b7ae3 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Tue, 25 Mar 2025 09:20:29 +0100 Subject: [PATCH] Add assert messages and print bad argument values in asserts (#5216) Enabled the `missing_assert_message` lint * [x] I have followed the instructions in the PR template --------- Co-authored-by: Lucas Meurer --- Cargo.toml | 2 +- crates/ecolor/src/color32.rs | 10 ++- crates/ecolor/src/rgba.rs | 15 +++- crates/eframe/src/stopwatch.rs | 4 +- crates/egui/src/containers/menu.rs | 1 - crates/egui/src/context.rs | 24 +++++-- crates/egui/src/hit_test.rs | 10 ++- crates/egui/src/layout.rs | 53 +++++++++----- crates/egui/src/placer.rs | 11 +-- crates/egui/src/response.rs | 5 +- .../src/text_selection/text_cursor_state.rs | 7 +- crates/egui/src/text_selection/visuals.rs | 6 +- crates/egui/src/ui.rs | 21 ++++-- crates/egui/src/widgets/label.rs | 5 +- crates/egui/src/widgets/slider.rs | 22 ++++-- .../egui/src/widgets/text_edit/text_buffer.rs | 5 +- crates/egui_demo_lib/src/rendering_test.rs | 15 +++- crates/egui_extras/src/loaders/file_loader.rs | 2 +- crates/egui_extras/src/sizing.rs | 10 ++- crates/egui_extras/src/syntax_highlighting.rs | 11 ++- crates/egui_glow/src/painter.rs | 9 ++- crates/emath/src/lib.rs | 20 ++++-- crates/emath/src/rot2.rs | 5 +- crates/emath/src/smart_aim.rs | 10 ++- crates/epaint/benches/benchmark.rs | 7 +- crates/epaint/src/image.rs | 71 +++++++++++++++---- crates/epaint/src/mesh.rs | 18 +++-- crates/epaint/src/shapes/shape.rs | 10 ++- crates/epaint/src/stats.rs | 5 +- crates/epaint/src/tessellator.rs | 4 +- crates/epaint/src/text/font.rs | 12 +++- crates/epaint/src/text/text_layout.rs | 7 +- crates/epaint/src/text/text_layout_types.rs | 2 +- crates/epaint/src/texture_atlas.rs | 6 +- scripts/check.sh | 14 ++-- 35 files changed, 331 insertions(+), 108 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f58f958b..92f1ce3c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -207,6 +207,7 @@ match_wild_err_arm = "warn" match_wildcard_for_single_variants = "warn" mem_forget = "warn" mismatching_type_param_order = "warn" +missing_assert_message = "warn" missing_enforced_import_renames = "warn" missing_errors_doc = "warn" missing_safety_doc = "warn" @@ -274,7 +275,6 @@ zero_sized_map_values = "warn" # TODO(emilk): maybe enable more of these lints? iter_over_hash_type = "allow" -missing_assert_message = "allow" should_panic_without_expect = "allow" too_many_lines = "allow" unwrap_used = "allow" # TODO(emilk): We really wanna warn on this one diff --git a/crates/ecolor/src/color32.rs b/crates/ecolor/src/color32.rs index b5e05276..72d0496e 100644 --- a/crates/ecolor/src/color32.rs +++ b/crates/ecolor/src/color32.rs @@ -273,7 +273,10 @@ impl Color32 { /// This is perceptually even, and faster that [`Self::linear_multiply`]. #[inline] pub fn gamma_multiply(self, factor: f32) -> Self { - debug_assert!(0.0 <= factor && factor.is_finite()); + debug_assert!( + 0.0 <= factor && factor.is_finite(), + "factor should be finite, but was {factor}" + ); let Self([r, g, b, a]) = self; Self([ (r as f32 * factor + 0.5) as u8, @@ -306,7 +309,10 @@ impl Color32 { /// You likely want to use [`Self::gamma_multiply`] instead. #[inline] pub fn linear_multiply(self, factor: f32) -> Self { - debug_assert!(0.0 <= factor && factor.is_finite()); + debug_assert!( + 0.0 <= factor && factor.is_finite(), + "factor should be finite, but was {factor}" + ); // As an unfortunate side-effect of using premultiplied alpha // we need a somewhat expensive conversion to linear space and back. Rgba::from(self).multiply(factor).into() diff --git a/crates/ecolor/src/rgba.rs b/crates/ecolor/src/rgba.rs index 85535bf3..99fab41c 100644 --- a/crates/ecolor/src/rgba.rs +++ b/crates/ecolor/src/rgba.rs @@ -90,15 +90,24 @@ impl Rgba { #[inline] pub fn from_luminance_alpha(l: f32, a: f32) -> Self { - debug_assert!(0.0 <= l && l <= 1.0); - debug_assert!(0.0 <= a && a <= 1.0); + debug_assert!( + 0.0 <= l && l <= 1.0, + "l should be in the range [0, 1], but was {l}" + ); + debug_assert!( + 0.0 <= a && a <= 1.0, + "a should be in the range [0, 1], but was {a}" + ); Self([l * a, l * a, l * a, a]) } /// Transparent black #[inline] pub fn from_black_alpha(a: f32) -> Self { - debug_assert!(0.0 <= a && a <= 1.0); + debug_assert!( + 0.0 <= a && a <= 1.0, + "a should be in the range [0, 1], but was {a}" + ); Self([0.0, 0.0, 0.0, a]) } diff --git a/crates/eframe/src/stopwatch.rs b/crates/eframe/src/stopwatch.rs index 9b013618..e6eabcbd 100644 --- a/crates/eframe/src/stopwatch.rs +++ b/crates/eframe/src/stopwatch.rs @@ -18,7 +18,7 @@ impl Stopwatch { } pub fn start(&mut self) { - assert!(self.start.is_none()); + assert!(self.start.is_none(), "Stopwatch already running"); self.start = Some(Instant::now()); } @@ -29,7 +29,7 @@ impl Stopwatch { } pub fn resume(&mut self) { - assert!(self.start.is_none()); + assert!(self.start.is_none(), "Stopwatch still running"); self.start = Some(Instant::now()); } diff --git a/crates/egui/src/containers/menu.rs b/crates/egui/src/containers/menu.rs index 26e2fb79..7511d07d 100644 --- a/crates/egui/src/containers/menu.rs +++ b/crates/egui/src/containers/menu.rs @@ -159,7 +159,6 @@ impl MenuState { } /// Horizontal menu bar where you can add [`MenuButton`]s. - /// The menu bar goes well in a [`crate::TopBottomPanel::top`], /// but can also be placed in a [`crate::Window`]. /// In the latter case you may want to wrap it in [`Frame`]. diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 3bdf1507..44af99fe 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -82,7 +82,11 @@ impl Default for WrappedTextureManager { epaint::FontImage::new([0, 0]).into(), Default::default(), ); - assert_eq!(font_id, TextureId::default()); + assert_eq!( + font_id, + TextureId::default(), + "font id should be equal to TextureId::default(), but was {font_id:?}", + ); Self(Arc::new(RwLock::new(tex_mngr))) } @@ -804,7 +808,11 @@ impl Context { let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get()); let mut output = FullOutput::default(); - debug_assert_eq!(output.platform_output.num_completed_passes, 0); + debug_assert_eq!( + output.platform_output.num_completed_passes, 0, + "output must be fresh, but had {} passes", + output.platform_output.num_completed_passes + ); loop { profiling::scope!( @@ -828,7 +836,11 @@ impl Context { self.begin_pass(new_input.take()); run_ui(self); output.append(self.end_pass()); - debug_assert!(0 < output.platform_output.num_completed_passes); + debug_assert!( + 0 < output.platform_output.num_completed_passes, + "Completed passes was lower than 0, was {}", + output.platform_output.num_completed_passes + ); if !output.platform_output.requested_discard() { break; // no need for another pass @@ -3272,7 +3284,11 @@ impl Context { #[cfg(feature = "accesskit")] self.pass_state_mut(|fs| { if let Some(state) = fs.accesskit_state.as_mut() { - assert_eq!(state.parent_stack.pop(), Some(_id)); + assert_eq!( + state.parent_stack.pop(), + Some(_id), + "Mismatched push/pop in with_accessibility_parent" + ); } }); diff --git a/crates/egui/src/hit_test.rs b/crates/egui/src/hit_test.rs index 910e558b..e7917212 100644 --- a/crates/egui/src/hit_test.rs +++ b/crates/egui/src/hit_test.rs @@ -175,11 +175,17 @@ pub fn hit_test( restore_widget_rect(wr); } if let Some(wr) = &mut hits.drag { - debug_assert!(wr.sense.senses_drag()); + debug_assert!( + wr.sense.senses_drag(), + "We should only return drag hits if they sense drag" + ); restore_widget_rect(wr); } if let Some(wr) = &mut hits.click { - debug_assert!(wr.sense.senses_click()); + debug_assert!( + wr.sense.senses_click(), + "We should only return click hits if they sense click" + ); restore_widget_rect(wr); } } diff --git a/crates/egui/src/layout.rs b/crates/egui/src/layout.rs index e967aaa6..91f39fb2 100644 --- a/crates/egui/src/layout.rs +++ b/crates/egui/src/layout.rs @@ -71,9 +71,17 @@ impl Region { } pub fn sanity_check(&self) { - debug_assert!(!self.min_rect.any_nan()); - debug_assert!(!self.max_rect.any_nan()); - debug_assert!(!self.cursor.any_nan()); + debug_assert!( + !self.min_rect.any_nan(), + "min rect has Nan: {:?}", + self.min_rect + ); + debug_assert!( + !self.max_rect.any_nan(), + "max rect has Nan: {:?}", + self.max_rect + ); + debug_assert!(!self.cursor.any_nan(), "cursor has Nan: {:?}", self.cursor); } } @@ -394,8 +402,8 @@ impl Layout { /// ## Doing layout impl Layout { pub fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect { - debug_assert!(size.x >= 0.0 && size.y >= 0.0); - debug_assert!(!outer.is_negative()); + debug_assert!(size.x >= 0.0 && size.y >= 0.0, "Negative size: {size:?}"); + debug_assert!(!outer.is_negative(), "Negative outer: {outer:?}"); self.align2().align_size_within_rect(size, outer).round_ui() } @@ -421,7 +429,7 @@ impl Layout { } pub(crate) fn region_from_max_rect(&self, max_rect: Rect) -> Region { - debug_assert!(!max_rect.any_nan()); + debug_assert!(!max_rect.any_nan(), "max_rect is not NaN: {max_rect:?}"); let mut region = Region { min_rect: Rect::NOTHING, // temporary max_rect, @@ -454,8 +462,8 @@ impl Layout { /// Given the cursor in the region, how much space is available /// for the next widget? fn available_from_cursor_max_rect(&self, cursor: Rect, max_rect: Rect) -> Rect { - debug_assert!(!cursor.any_nan()); - debug_assert!(!max_rect.any_nan()); + debug_assert!(!cursor.any_nan(), "cursor is NaN: {cursor:?}"); + debug_assert!(!max_rect.any_nan(), "max_rect is NaN: {max_rect:?}"); // NOTE: in normal top-down layout the cursor has moved below the current max_rect, // but the available shouldn't be negative. @@ -509,7 +517,7 @@ impl Layout { avail.max.y = y; } - debug_assert!(!avail.any_nan()); + debug_assert!(!avail.any_nan(), "avail is NaN: {avail:?}"); avail } @@ -520,7 +528,10 @@ impl Layout { /// Use `justify_and_align` to get the inner `widget_rect`. pub(crate) fn next_frame(&self, region: &Region, child_size: Vec2, spacing: Vec2) -> Rect { region.sanity_check(); - debug_assert!(child_size.x >= 0.0 && child_size.y >= 0.0); + debug_assert!( + child_size.x >= 0.0 && child_size.y >= 0.0, + "Negative size: {child_size:?}" + ); if self.main_wrap { let available_size = self.available_rect_before_wrap(region).size(); @@ -600,7 +611,10 @@ impl Layout { fn next_frame_ignore_wrap(&self, region: &Region, child_size: Vec2) -> Rect { region.sanity_check(); - debug_assert!(child_size.x >= 0.0 && child_size.y >= 0.0); + debug_assert!( + child_size.x >= 0.0 && child_size.y >= 0.0, + "Negative size: {child_size:?}" + ); let available_rect = self.available_rect_before_wrap(region); @@ -633,16 +647,19 @@ impl Layout { frame_rect = frame_rect.translate(Vec2::Y * (region.cursor.top() - frame_rect.top())); } - debug_assert!(!frame_rect.any_nan()); - debug_assert!(!frame_rect.is_negative()); + debug_assert!(!frame_rect.any_nan(), "frame_rect is NaN: {frame_rect:?}"); + debug_assert!(!frame_rect.is_negative(), "frame_rect is negative"); frame_rect.round_ui() } /// Apply justify (fill width/height) and/or alignment after calling `next_space`. pub(crate) fn justify_and_align(&self, frame: Rect, mut child_size: Vec2) -> Rect { - debug_assert!(child_size.x >= 0.0 && child_size.y >= 0.0); - debug_assert!(!frame.is_negative()); + debug_assert!( + child_size.x >= 0.0 && child_size.y >= 0.0, + "Negative size: {child_size:?}" + ); + debug_assert!(!frame.is_negative(), "frame is negative"); if self.horizontal_justify() { child_size.x = child_size.x.at_least(frame.width()); // fill full width @@ -660,8 +677,8 @@ impl Layout { ) -> Rect { let frame = self.next_frame_ignore_wrap(region, size); let rect = self.align_size_within_rect(size, frame); - debug_assert!(!rect.any_nan()); - debug_assert!(!rect.is_negative()); + debug_assert!(!rect.any_nan(), "rect is NaN: {rect:?}"); + debug_assert!(!rect.is_negative(), "rect is negative: {rect:?}"); rect } @@ -704,7 +721,7 @@ impl Layout { widget_rect: Rect, item_spacing: Vec2, ) { - debug_assert!(!cursor.any_nan()); + debug_assert!(!cursor.any_nan(), "cursor is NaN: {cursor:?}"); if self.main_wrap { if cursor.intersects(frame_rect.shrink(1.0)) { // make row/column larger if necessary diff --git a/crates/egui/src/placer.rs b/crates/egui/src/placer.rs index 2de822c0..6ffe07ed 100644 --- a/crates/egui/src/placer.rs +++ b/crates/egui/src/placer.rs @@ -133,8 +133,8 @@ impl Placer { /// Apply justify or alignment after calling `next_space`. pub(crate) fn justify_and_align(&self, rect: Rect, child_size: Vec2) -> Rect { - debug_assert!(!rect.any_nan()); - debug_assert!(!child_size.any_nan()); + debug_assert!(!rect.any_nan(), "rect: {rect:?}"); + debug_assert!(!child_size.any_nan(), "child_size is NaN: {child_size:?}"); if let Some(grid) = &self.grid { grid.justify_and_align(rect, child_size) @@ -164,8 +164,11 @@ impl Placer { widget_rect: Rect, item_spacing: Vec2, ) { - debug_assert!(!frame_rect.any_nan()); - debug_assert!(!widget_rect.any_nan()); + debug_assert!(!frame_rect.any_nan(), "frame_rect: {frame_rect:?}"); + debug_assert!( + !widget_rect.any_nan(), + "widget_rect is NaN: {widget_rect:?}" + ); self.region.sanity_check(); if let Some(grid) = &mut self.grid { diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index a1b522ef..28f4bde7 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -985,7 +985,10 @@ impl Response { /// /// You may not call [`Self::interact`] on the resulting `Response`. pub fn union(&self, other: Self) -> Self { - assert!(self.ctx == other.ctx); + assert!( + self.ctx == other.ctx, + "Responses must be from the same `Context`" + ); debug_assert!( self.layer_id == other.layer_id, "It makes no sense to combine Responses from two different layers" diff --git a/crates/egui/src/text_selection/text_cursor_state.rs b/crates/egui/src/text_selection/text_cursor_state.rs index 21ebda3d..baf7b046 100644 --- a/crates/egui/src/text_selection/text_cursor_state.rs +++ b/crates/egui/src/text_selection/text_cursor_state.rs @@ -271,7 +271,12 @@ pub fn byte_index_from_char_index(s: &str, char_index: usize) -> usize { } pub fn slice_char_range(s: &str, char_range: std::ops::Range) -> &str { - assert!(char_range.start <= char_range.end); + assert!( + char_range.start <= char_range.end, + "Invalid range, start must be less than end, but start = {}, end = {}", + char_range.start, + char_range.end + ); let start_byte = byte_index_from_char_index(s, char_range.start); let end_byte = byte_index_from_char_index(s, char_range.end); &s[start_byte..end_byte] diff --git a/crates/egui/src/text_selection/visuals.rs b/crates/egui/src/text_selection/visuals.rs index 4025a2d5..32a040a8 100644 --- a/crates/egui/src/text_selection/visuals.rs +++ b/crates/egui/src/text_selection/visuals.rs @@ -59,7 +59,11 @@ pub fn paint_text_selection( // Start by appending the selection rectangle to end of the mesh, as two triangles (= 6 indices): let num_indices_before = mesh.indices.len(); mesh.add_colored_rect(rect, color); - assert_eq!(num_indices_before + 6, mesh.indices.len()); + assert_eq!( + num_indices_before + 6, + mesh.indices.len(), + "We expect exactly 6 new indices" + ); // Copy out the new triangles: let selection_triangles = [ diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index dad03681..0e8509bb 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -286,7 +286,7 @@ impl Ui { } } - debug_assert!(!max_rect.any_nan()); + debug_assert!(!max_rect.any_nan(), "max_rect is NaN: {max_rect:?}"); let stable_id = self.id.with(id_salt); let unique_id = stable_id.with(self.next_auto_id_salt); let next_auto_id_salt = unique_id.value().wrapping_add(1); @@ -914,14 +914,20 @@ impl Ui { /// Set the minimum width of the ui. /// This can't shrink the ui, only make it larger. pub fn set_min_width(&mut self, width: f32) { - debug_assert!(0.0 <= width); + debug_assert!( + 0.0 <= width, + "Negative width makes no sense, but got: {width}" + ); self.placer.set_min_width(width); } /// Set the minimum height of the ui. /// This can't shrink the ui, only make it larger. pub fn set_min_height(&mut self, height: f32) { - debug_assert!(0.0 <= height); + debug_assert!( + 0.0 <= height, + "Negative height makes no sense, but got: {height}" + ); self.placer.set_min_height(height); } @@ -1399,7 +1405,7 @@ impl Ui { fn allocate_space_impl(&mut self, desired_size: Vec2) -> Rect { let item_spacing = self.spacing().item_spacing; let frame_rect = self.placer.next_space(desired_size, item_spacing); - debug_assert!(!frame_rect.any_nan()); + debug_assert!(!frame_rect.any_nan(), "frame_rect is nan in allocate_space"); let widget_rect = self.placer.justify_and_align(frame_rect, desired_size); self.placer @@ -1422,7 +1428,7 @@ impl Ui { /// Allocate a rect without interacting with it. pub fn advance_cursor_after_rect(&mut self, rect: Rect) -> Id { - debug_assert!(!rect.any_nan()); + debug_assert!(!rect.any_nan(), "rect is nan in advance_cursor_after_rect"); let rect = rect.round_ui(); let item_spacing = self.spacing().item_spacing; @@ -1494,7 +1500,10 @@ impl Ui { layout: Layout, add_contents: Box R + 'c>, ) -> InnerResponse { - debug_assert!(desired_size.x >= 0.0 && desired_size.y >= 0.0); + debug_assert!( + desired_size.x >= 0.0 && desired_size.y >= 0.0, + "Negative desired size: {desired_size:?}" + ); let item_spacing = self.spacing().item_spacing; let frame_rect = self.placer.next_space(desired_size, item_spacing); let child_rect = self.placer.justify_and_align(frame_rect, desired_size); diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index 34b684df..c36b9fc6 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -199,7 +199,10 @@ impl Label { let cursor = ui.cursor(); let first_row_indentation = available_width - ui.available_size_before_wrap().x; - debug_assert!(first_row_indentation.is_finite()); + debug_assert!( + first_row_indentation.is_finite(), + "first row indentation is not finite: {first_row_indentation}" + ); layout_job.wrap.max_width = available_width; layout_job.first_row_min_height = cursor.height(); diff --git a/crates/egui/src/widgets/slider.rs b/crates/egui/src/widgets/slider.rs index 59028207..e8b026ff 100644 --- a/crates/egui/src/widgets/slider.rs +++ b/crates/egui/src/widgets/slider.rs @@ -1065,7 +1065,10 @@ fn value_from_normalized(normalized: f64, range: RangeInclusive, spec: &Sli let log = lerp(min_log..=max_log, normalized); 10.0_f64.powf(log) } else { - assert!(min < 0.0 && 0.0 < max); + assert!( + min < 0.0 && 0.0 < max, + "min should be negative and max positive, but got min={min} and max={max}" + ); let zero_cutoff = logarithmic_zero_cutoff(min, max); if normalized < zero_cutoff { // negative @@ -1114,7 +1117,10 @@ fn normalized_from_value(value: f64, range: RangeInclusive, spec: &SliderSp let value_log = value.log10(); remap_clamp(value_log, min_log..=max_log, 0.0..=1.0) } else { - assert!(min < 0.0 && 0.0 < max); + assert!( + min < 0.0 && 0.0 < max, + "min should be negative and max positive, but got min={min} and max={max}" + ); let zero_cutoff = logarithmic_zero_cutoff(min, max); if value < 0.0 { // negative @@ -1142,8 +1148,11 @@ fn normalized_from_value(value: f64, range: RangeInclusive, spec: &SliderSp } fn range_log10(min: f64, max: f64, spec: &SliderSpec) -> (f64, f64) { - assert!(spec.logarithmic); - assert!(min <= max); + assert!(spec.logarithmic, "spec must be logarithmic"); + assert!( + min <= max, + "min must be less than or equal to max, but was min={min} and max={max}" + ); if min == 0.0 && max == INFINITY { (spec.smallest_positive.log10(), INF_RANGE_MAGNITUDE) @@ -1167,7 +1176,10 @@ fn range_log10(min: f64, max: f64, spec: &SliderSpec) -> (f64, f64) { /// where to put the zero cutoff for logarithmic sliders /// that crosses zero ? fn logarithmic_zero_cutoff(min: f64, max: f64) -> f64 { - assert!(min < 0.0 && 0.0 < max); + assert!( + min < 0.0 && 0.0 < max, + "min must be negative and max positive, but got min={min} and max={max}" + ); let min_magnitude = if min == -INFINITY { INF_RANGE_MAGNITUDE diff --git a/crates/egui/src/widgets/text_edit/text_buffer.rs b/crates/egui/src/widgets/text_edit/text_buffer.rs index 9290f1e9..ccf3a095 100644 --- a/crates/egui/src/widgets/text_edit/text_buffer.rs +++ b/crates/egui/src/widgets/text_edit/text_buffer.rs @@ -194,7 +194,10 @@ impl TextBuffer for String { } fn delete_char_range(&mut self, char_range: Range) { - assert!(char_range.start <= char_range.end); + assert!( + char_range.start <= char_range.end, + "start must be <= end, but got {char_range:?}" + ); // Get both byte indices let byte_start = byte_index_from_char_index(self.as_str(), char_range.start); diff --git a/crates/egui_demo_lib/src/rendering_test.rs b/crates/egui_demo_lib/src/rendering_test.rs index 879e2c7a..14714cca 100644 --- a/crates/egui_demo_lib/src/rendering_test.rs +++ b/crates/egui_demo_lib/src/rendering_test.rs @@ -307,7 +307,10 @@ fn vertex_gradient(ui: &mut Ui, bg_fill: Color32, gradient: &Gradient) -> Respon } { let n = gradient.0.len(); - assert!(n >= 2); + assert!( + n >= 2, + "A gradient must have at least two colors, but this had {n}" + ); let mut mesh = Mesh::default(); for (i, &color) in gradient.0.iter().enumerate() { let t = i as f32 / (n as f32 - 1.0); @@ -594,8 +597,14 @@ fn blending_and_feathering_test(ui: &mut Ui) { } fn text_on_bg(ui: &mut egui::Ui, fg: Color32, bg: Color32) { - assert!(fg.is_opaque()); - assert!(bg.is_opaque()); + assert!( + fg.is_opaque(), + "Foreground color must be opaque, but was: {fg:?}", + ); + assert!( + bg.is_opaque(), + "Background color must be opaque, but was: {bg:?}", + ); ui.horizontal(|ui| { ui.label( diff --git a/crates/egui_extras/src/loaders/file_loader.rs b/crates/egui_extras/src/loaders/file_loader.rs index 5d1b45fb..8b762887 100644 --- a/crates/egui_extras/src/loaders/file_loader.rs +++ b/crates/egui_extras/src/loaders/file_loader.rs @@ -96,7 +96,7 @@ impl BytesLoader for FileLoader { Err(err) => Err(err.to_string()), }; let prev = cache.lock().insert(uri.clone(), Poll::Ready(result)); - assert!(matches!(prev, Some(Poll::Pending))); + assert!(matches!(prev, Some(Poll::Pending)), "unexpected state"); ctx.request_repaint(); log::trace!("finished loading {uri:?}"); } diff --git a/crates/egui_extras/src/sizing.rs b/crates/egui_extras/src/sizing.rs index 77030677..7f32a84e 100644 --- a/crates/egui_extras/src/sizing.rs +++ b/crates/egui_extras/src/sizing.rs @@ -32,7 +32,10 @@ impl Size { /// Relative size relative to all available space. Values must be in range `0.0..=1.0`. pub fn relative(fraction: f32) -> Self { - debug_assert!(0.0 <= fraction && fraction <= 1.0); + debug_assert!( + 0.0 <= fraction && fraction <= 1.0, + "fraction should be in the range [0, 1], but was {fraction}" + ); Self::Relative { fraction, range: Rangef::new(0.0, f32::INFINITY), @@ -121,7 +124,10 @@ impl Sizing { .map(|&size| match size { Size::Absolute { initial, .. } => initial, Size::Relative { fraction, range } => { - assert!(0.0 <= fraction && fraction <= 1.0); + assert!( + 0.0 <= fraction && fraction <= 1.0, + "fraction should be in the range [0, 1], but was {fraction}" + ); range.clamp(length * fraction) } Size::Remainder { .. } => { diff --git a/crates/egui_extras/src/syntax_highlighting.rs b/crates/egui_extras/src/syntax_highlighting.rs index 027ba5ee..77ad0cc2 100644 --- a/crates/egui_extras/src/syntax_highlighting.rs +++ b/crates/egui_extras/src/syntax_highlighting.rs @@ -490,8 +490,15 @@ impl Highlighter { fn as_byte_range(whole: &str, range: &str) -> std::ops::Range { let whole_start = whole.as_ptr() as usize; let range_start = range.as_ptr() as usize; - assert!(whole_start <= range_start); - assert!(range_start + range.len() <= whole_start + whole.len()); + assert!( + whole_start <= range_start, + "range must be within whole, but was {range}" + ); + assert!( + range_start + range.len() <= whole_start + whole.len(), + "range_start + range length must be smaller than whole_start + whole length, but was {}", + range_start + range.len() + ); let offset = range_start - whole_start; offset..(offset + range.len()) } diff --git a/crates/egui_glow/src/painter.rs b/crates/egui_glow/src/painter.rs index bec46cf0..1d1322c4 100644 --- a/crates/egui_glow/src/painter.rs +++ b/crates/egui_glow/src/painter.rs @@ -469,7 +469,7 @@ impl Painter { #[inline(never)] // Easier profiling fn paint_mesh(&mut self, mesh: &Mesh) { - debug_assert!(mesh.is_valid()); + debug_assert!(mesh.is_valid(), "Mesh is not valid"); if let Some(texture) = self.texture(mesh.texture_id) { unsafe { self.gl.bind_buffer(glow::ARRAY_BUFFER, Some(self.vbo)); @@ -560,7 +560,12 @@ impl Painter { data: &[u8], ) { profiling::function_scope!(); - assert_eq!(data.len(), w * h * 4); + assert_eq!( + data.len(), + w * h * 4, + "Mismatch between texture size and texel count, by {}", + data.len() % (w * h * 4) + ); assert!( w <= self.max_texture_side && h <= self.max_texture_side, "Got a texture image of size {}x{}, but the maximum supported texture side is only {}", diff --git a/crates/emath/src/lib.rs b/crates/emath/src/lib.rs index ae04f9ec..ad657fcc 100644 --- a/crates/emath/src/lib.rs +++ b/crates/emath/src/lib.rs @@ -149,7 +149,10 @@ where { let from = from.into(); let to = to.into(); - debug_assert!(from.start() != from.end()); + debug_assert!( + from.start() != from.end(), + "from.start() and from.end() should not be equal" + ); let t = (x - *from.start()) / (*from.end() - *from.start()); lerp(to, t) } @@ -173,7 +176,10 @@ where } else if *from.end() <= x { *to.end() } else { - debug_assert!(from.start() != from.end()); + debug_assert!( + from.start() != from.end(), + "from.start() and from.end() should not be equal" + ); let t = (x - *from.start()) / (*from.end() - *from.start()); // Ensure no numerical inaccuracies sneak in: if T::ONE <= t { @@ -200,8 +206,14 @@ pub fn format_with_minimum_decimals(value: f64, decimals: usize) -> String { pub fn format_with_decimals_in_range(value: f64, decimal_range: RangeInclusive) -> String { let min_decimals = *decimal_range.start(); let max_decimals = *decimal_range.end(); - debug_assert!(min_decimals <= max_decimals); - debug_assert!(max_decimals < 100); + debug_assert!( + min_decimals <= max_decimals, + "min_decimals should be <= max_decimals, but got min_decimals: {min_decimals}, max_decimals: {max_decimals}" + ); + debug_assert!( + max_decimals < 100, + "max_decimals should be < 100, but got {max_decimals}" + ); let max_decimals = max_decimals.min(16); let min_decimals = min_decimals.min(max_decimals); diff --git a/crates/emath/src/rot2.rs b/crates/emath/src/rot2.rs index 88c425a5..9af0103a 100644 --- a/crates/emath/src/rot2.rs +++ b/crates/emath/src/rot2.rs @@ -84,7 +84,10 @@ impl Rot2 { c: self.c / l, s: self.s / l, }; - debug_assert!(ret.is_finite()); + debug_assert!( + ret.is_finite(), + "Rot2::normalized produced a non-finite result" + ); ret } } diff --git a/crates/emath/src/smart_aim.rs b/crates/emath/src/smart_aim.rs index 72094706..9ef010d9 100644 --- a/crates/emath/src/smart_aim.rs +++ b/crates/emath/src/smart_aim.rs @@ -33,7 +33,10 @@ pub fn best_in_range_f64(min: f64, max: f64) -> f64 { if !max.is_finite() { return min; } - debug_assert!(min.is_finite() && max.is_finite()); + debug_assert!( + min.is_finite() && max.is_finite(), + "min: {min:?}, max: {max:?}" + ); let min_exponent = min.log10(); let max_exponent = max.log10(); @@ -101,7 +104,10 @@ fn from_decimal_string(s: &[i32]) -> f64 { /// Find the simplest integer in the range [min, max] fn simplest_digit_closed_range(min: i32, max: i32) -> i32 { - debug_assert!(1 <= min && min <= max && max <= 9); + debug_assert!( + 1 <= min && min <= max && max <= 9, + "min should be in [1, 9], but was {min:?} and max should be in [min, 9], but was {max:?}" + ); if min <= 5 && 5 <= max { 5 } else { diff --git a/crates/epaint/benches/benchmark.rs b/crates/epaint/benches/benchmark.rs index e723638b..444f81a1 100644 --- a/crates/epaint/benches/benchmark.rs +++ b/crates/epaint/benches/benchmark.rs @@ -53,7 +53,12 @@ fn tessellate_circles(c: &mut Criterion) { clipped_shapes.push(ClippedShape { clip_rect, shape }); } } - assert_eq!(clipped_shapes.len(), 100_000); + assert_eq!( + clipped_shapes.len(), + 100_000, + "length of clipped shapes should be 100k, but was {}", + clipped_shapes.len() + ); let pixels_per_point = 2.0; let options = TessellationOptions::default(); diff --git a/crates/epaint/src/image.rs b/crates/epaint/src/image.rs index 69844f42..e50e33e6 100644 --- a/crates/epaint/src/image.rs +++ b/crates/epaint/src/image.rs @@ -94,7 +94,13 @@ impl ColorImage { /// } /// ``` pub fn from_rgba_unmultiplied(size: [usize; 2], rgba: &[u8]) -> Self { - assert_eq!(size[0] * size[1] * 4, rgba.len()); + assert_eq!( + size[0] * size[1] * 4, + rgba.len(), + "size: {:?}, rgba.len(): {}", + size, + rgba.len() + ); let pixels = rgba .chunks_exact(4) .map(|p| Color32::from_rgba_unmultiplied(p[0], p[1], p[2], p[3])) @@ -103,7 +109,13 @@ impl ColorImage { } pub fn from_rgba_premultiplied(size: [usize; 2], rgba: &[u8]) -> Self { - assert_eq!(size[0] * size[1] * 4, rgba.len()); + assert_eq!( + size[0] * size[1] * 4, + rgba.len(), + "size: {:?}, rgba.len(): {}", + size, + rgba.len() + ); let pixels = rgba .chunks_exact(4) .map(|p| Color32::from_rgba_premultiplied(p[0], p[1], p[2], p[3])) @@ -115,7 +127,13 @@ impl ColorImage { /// /// Panics if `size[0] * size[1] != gray.len()`. pub fn from_gray(size: [usize; 2], gray: &[u8]) -> Self { - assert_eq!(size[0] * size[1], gray.len()); + assert_eq!( + size[0] * size[1], + gray.len(), + "size: {:?}, gray.len(): {}", + size, + gray.len() + ); let pixels = gray.iter().map(|p| Color32::from_gray(*p)).collect(); Self { size, pixels } } @@ -127,7 +145,13 @@ impl ColorImage { #[doc(alias = "from_grey_iter")] pub fn from_gray_iter(size: [usize; 2], gray_iter: impl Iterator) -> Self { let pixels: Vec<_> = gray_iter.map(Color32::from_gray).collect(); - assert_eq!(size[0] * size[1], pixels.len()); + assert_eq!( + size[0] * size[1], + pixels.len(), + "size: {:?}, pixels.len(): {}", + size, + pixels.len() + ); Self { size, pixels } } @@ -150,7 +174,13 @@ impl ColorImage { /// /// Panics if `size[0] * size[1] * 3 != rgb.len()`. pub fn from_rgb(size: [usize; 2], rgb: &[u8]) -> Self { - assert_eq!(size[0] * size[1] * 3, rgb.len()); + assert_eq!( + size[0] * size[1] * 3, + rgb.len(), + "size: {:?}, rgb.len(): {}", + size, + rgb.len() + ); let pixels = rgb .chunks_exact(3) .map(|p| Color32::from_rgb(p[0], p[1], p[2])) @@ -225,7 +255,7 @@ impl std::ops::Index<(usize, usize)> for ColorImage { #[inline] fn index(&self, (x, y): (usize, usize)) -> &Color32 { let [w, h] = self.size; - assert!(x < w && y < h); + assert!(x < w && y < h, "x: {x}, y: {y}, w: {w}, h: {h}"); &self.pixels[y * w + x] } } @@ -234,7 +264,7 @@ impl std::ops::IndexMut<(usize, usize)> for ColorImage { #[inline] fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut Color32 { let [w, h] = self.size; - assert!(x < w && y < h); + assert!(x < w && y < h, "x: {x}, y: {y}, w: {w}, h: {h}"); &mut self.pixels[y * w + x] } } @@ -328,15 +358,32 @@ impl FontImage { /// Clone a sub-region as a new image. pub fn region(&self, [x, y]: [usize; 2], [w, h]: [usize; 2]) -> Self { - assert!(x + w <= self.width()); - assert!(y + h <= self.height()); + assert!( + x + w <= self.width(), + "x + w should be <= self.width(), but x: {}, w: {}, width: {}", + x, + w, + self.width() + ); + assert!( + y + h <= self.height(), + "y + h should be <= self.height(), but y: {}, h: {}, height: {}", + y, + h, + self.height() + ); let mut pixels = Vec::with_capacity(w * h); for y in y..y + h { let offset = y * self.width() + x; pixels.extend(&self.pixels[offset..(offset + w)]); } - assert_eq!(pixels.len(), w * h); + assert_eq!( + pixels.len(), + w * h, + "pixels.len should be w * h, but got {}", + pixels.len() + ); Self { size: [w, h], pixels, @@ -350,7 +397,7 @@ impl std::ops::Index<(usize, usize)> for FontImage { #[inline] fn index(&self, (x, y): (usize, usize)) -> &f32 { let [w, h] = self.size; - assert!(x < w && y < h); + assert!(x < w && y < h, "x: {x}, y: {y}, w: {w}, h: {h}"); &self.pixels[y * w + x] } } @@ -359,7 +406,7 @@ impl std::ops::IndexMut<(usize, usize)> for FontImage { #[inline] fn index_mut(&mut self, (x, y): (usize, usize)) -> &mut f32 { let [w, h] = self.size; - assert!(x < w && y < h); + assert!(x < w && y < h, "x: {x}, y: {y}, w: {w}, h: {h}"); &mut self.pixels[y * w + x] } } diff --git a/crates/epaint/src/mesh.rs b/crates/epaint/src/mesh.rs index 930cb771..60be2935 100644 --- a/crates/epaint/src/mesh.rs +++ b/crates/epaint/src/mesh.rs @@ -119,7 +119,7 @@ impl Mesh { /// Panics when `other` mesh has a different texture. pub fn append(&mut self, other: Self) { profiling::function_scope!(); - debug_assert!(other.is_valid()); + debug_assert!(other.is_valid(), "Other mesh is invalid"); if self.is_empty() { *self = other; @@ -133,7 +133,7 @@ impl Mesh { /// /// Panics when `other` mesh has a different texture. pub fn append_ref(&mut self, other: &Self) { - debug_assert!(other.is_valid()); + debug_assert!(other.is_valid(), "Other mesh is invalid"); if self.is_empty() { self.texture_id = other.texture_id; @@ -155,7 +155,10 @@ impl Mesh { /// Panics when the mesh has assigned a texture. #[inline(always)] pub fn colored_vertex(&mut self, pos: Pos2, color: Color32) { - debug_assert!(self.texture_id == TextureId::default()); + debug_assert!( + self.texture_id == TextureId::default(), + "Mesh has an assigned texture" + ); self.vertices.push(Vertex { pos, uv: WHITE_UV, @@ -218,7 +221,10 @@ impl Mesh { /// Uniformly colored rectangle. #[inline(always)] pub fn add_colored_rect(&mut self, rect: Rect, color: Color32) { - debug_assert!(self.texture_id == TextureId::default()); + debug_assert!( + self.texture_id == TextureId::default(), + "Mesh has an assigned texture" + ); self.add_rect_with_uv(rect, [WHITE_UV, WHITE_UV].into(), color); } @@ -227,7 +233,7 @@ impl Mesh { /// Splits this mesh into many smaller meshes (if needed) /// where the smaller meshes have 16-bit indices. pub fn split_to_u16(self) -> Vec { - debug_assert!(self.is_valid()); + debug_assert!(self.is_valid(), "Mesh is invalid"); const MAX_SIZE: u32 = u16::MAX as u32; @@ -280,7 +286,7 @@ impl Mesh { vertices: self.vertices[(min_vindex as usize)..=(max_vindex as usize)].to_vec(), texture_id: self.texture_id, }; - debug_assert!(mesh.is_valid()); + debug_assert!(mesh.is_valid(), "Mesh is invalid"); output.push(mesh); } output diff --git a/crates/epaint/src/shapes/shape.rs b/crates/epaint/src/shapes/shape.rs index 6c24881d..d17f528c 100644 --- a/crates/epaint/src/shapes/shape.rs +++ b/crates/epaint/src/shapes/shape.rs @@ -339,7 +339,7 @@ impl Shape { #[inline] pub fn mesh(mesh: impl Into>) -> Self { let mesh = mesh.into(); - debug_assert!(mesh.is_valid()); + debug_assert!(mesh.is_valid(), "Invalid mesh: {mesh:#?}"); Self::Mesh(mesh) } @@ -525,7 +525,13 @@ fn dashes_from_line( shapes: &mut Vec, dash_offset: f32, ) { - assert_eq!(dash_lengths.len(), gap_lengths.len()); + assert_eq!( + dash_lengths.len(), + gap_lengths.len(), + "Mismatched dash and gap lengths, got dash_lengths: {}, gap_lengths: {}", + dash_lengths.len(), + gap_lengths.len() + ); let mut position_on_segment = dash_offset; let mut drawing_dash = false; let mut step = 0; diff --git a/crates/epaint/src/stats.rs b/crates/epaint/src/stats.rs index 68bba622..cb72d90e 100644 --- a/crates/epaint/src/stats.rs +++ b/crates/epaint/src/stats.rs @@ -111,7 +111,10 @@ impl AllocInfo { } pub fn num_elements(&self) -> usize { - assert!(self.element_size != ElementSize::Heterogenous); + assert!( + self.element_size != ElementSize::Heterogenous, + "Heterogenous element size" + ); self.num_elements } diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 6d953f25..bcb13a12 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -382,7 +382,7 @@ impl Path { pub fn add_open_points(&mut self, points: &[Pos2]) { let n = points.len(); - assert!(n >= 2); + assert!(n >= 2, "A path needs at least two points, but got {n}"); if n == 2 { // Common case optimization: @@ -428,7 +428,7 @@ impl Path { pub fn add_line_loop(&mut self, points: &[Pos2]) { let n = points.len(); - assert!(n >= 2); + assert!(n >= 2, "A path needs at least two points, but got {n}"); self.reserve(n); let mut n0 = (points[0] - points[n - 1]).normalized().rot90(); diff --git a/crates/epaint/src/text/font.rs b/crates/epaint/src/text/font.rs index a3994ca4..5415bae0 100644 --- a/crates/epaint/src/text/font.rs +++ b/crates/epaint/src/text/font.rs @@ -91,8 +91,14 @@ impl FontImpl { scale_in_pixels: f32, tweak: FontTweak, ) -> Self { - assert!(scale_in_pixels > 0.0); - assert!(pixels_per_point > 0.0); + assert!( + scale_in_pixels > 0.0, + "scale_in_pixels is smaller than 0, got: {scale_in_pixels:?}" + ); + assert!( + pixels_per_point > 0.0, + "pixels_per_point must be greater than 0, got: {pixels_per_point:?}" + ); use ab_glyph::{Font, ScaleFont}; let scaled = ab_glyph_font.as_scaled(scale_in_pixels); @@ -264,7 +270,7 @@ impl FontImpl { } fn allocate_glyph(&self, glyph_id: ab_glyph::GlyphId) -> GlyphInfo { - assert!(glyph_id.0 != 0); + assert!(glyph_id.0 != 0, "Can't allocate glyph for id 0"); use ab_glyph::{Font as _, ScaleFont}; let glyph = glyph_id.with_scale_and_position( diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index ff973ba4..638b7a70 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -529,7 +529,7 @@ fn halign_and_justify_row( (num_leading_spaces, row.glyphs.len() - num_trailing_spaces) }; let num_glyphs_in_range = glyph_range.1 - glyph_range.0; - assert!(num_glyphs_in_range > 0); + assert!(num_glyphs_in_range > 0, "Should have at least one glyph"); let original_min_x = row.glyphs[glyph_range.0].logical_rect().min.x; let original_max_x = row.glyphs[glyph_range.1 - 1].logical_rect().max.x; @@ -898,7 +898,10 @@ fn add_hline(point_scale: PointScale, [start, stop]: [Pos2; 2], stroke: Stroke, } else { // Thin lines often lost, so this is a bad idea - assert_eq!(start.y, stop.y); + assert_eq!( + start.y, stop.y, + "Horizontal line must be horizontal, but got: {start:?} -> {stop:?}" + ); let min_y = point_scale.round_to_pixel(start.y - 0.5 * stroke.width); let max_y = point_scale.round_to_pixel(min_y + stroke.width); diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 2b8b3e81..6d69045a 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -892,7 +892,7 @@ impl Galley { } ccursor_it.index += row.char_count_including_newline(); } - debug_assert!(ccursor_it == self.end()); + debug_assert!(ccursor_it == self.end(), "Cursor out of bounds"); if let Some(last_row) = self.rows.last() { LayoutCursor { diff --git a/crates/epaint/src/texture_atlas.rs b/crates/epaint/src/texture_atlas.rs index 7ea76f87..79054022 100644 --- a/crates/epaint/src/texture_atlas.rs +++ b/crates/epaint/src/texture_atlas.rs @@ -88,7 +88,11 @@ impl TextureAtlas { // Make the top left pixel fully white for `WHITE_UV`, i.e. painting something with solid color: let (pos, image) = atlas.allocate((1, 1)); - assert_eq!(pos, (0, 0)); + assert_eq!( + pos, + (0, 0), + "Expected the first allocation to be at (0, 0), but was at {pos:?}" + ); image[pos] = 1.0; // Allocate a series of anti-aliased discs used to render small filled circles: diff --git a/scripts/check.sh b/scripts/check.sh index 0d835617..1a4eec5d 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -9,7 +9,7 @@ set -x # Checks all tests, lints etc. # Basically does what the CI does. -cargo +1.81.0 install --quiet typos-cli +# cargo +1.81.0 install --quiet typos-cli export RUSTFLAGS="-D warnings" export RUSTDOCFLAGS="-D warnings" # https://github.com/emilk/egui/pull/1454 @@ -35,20 +35,20 @@ cargo test --quiet --doc # slow - checks all doc-tests cargo check --quiet -p eframe --no-default-features --features "glow" if [[ "$OSTYPE" == "linux-gnu"* ]]; then - cargo check --quiet -p eframe --no-default-features --features "wgpu","x11" - cargo check --quiet -p eframe --no-default-features --features "wgpu","wayland" + cargo check --quiet -p eframe --no-default-features --features "wgpu","x11" + cargo check --quiet -p eframe --no-default-features --features "wgpu","wayland" else - cargo check --quiet -p eframe --no-default-features --features "wgpu" + cargo check --quiet -p eframe --no-default-features --features "wgpu" fi cargo check --quiet -p egui --no-default-features --features "serde" cargo check --quiet -p egui_demo_app --no-default-features --features "glow" if [[ "$OSTYPE" == "linux-gnu"* ]]; then - cargo check --quiet -p egui_demo_app --no-default-features --features "wgpu","x11" - cargo check --quiet -p egui_demo_app --no-default-features --features "wgpu","wayland" + cargo check --quiet -p egui_demo_app --no-default-features --features "wgpu","x11" + cargo check --quiet -p egui_demo_app --no-default-features --features "wgpu","wayland" else - cargo check --quiet -p egui_demo_app --no-default-features --features "wgpu" + cargo check --quiet -p egui_demo_app --no-default-features --features "wgpu" fi cargo check --quiet -p egui_demo_lib --no-default-features