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 <lucasmeurer96@gmail.com>
This commit is contained in:
Nicolas 2025-03-25 09:20:29 +01:00 committed by GitHub
parent 903bd81313
commit 58b2ac88c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 331 additions and 108 deletions

View File

@ -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

View File

@ -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()

View File

@ -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])
}

View File

@ -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());
}

View File

@ -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`].

View File

@ -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"
);
}
});

View File

@ -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);
}
}

View File

@ -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

View File

@ -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 {

View File

@ -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"

View File

@ -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<usize>) -> &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]

View File

@ -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 = [

View File

@ -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<dyn FnOnce(&mut Self) -> R + 'c>,
) -> InnerResponse<R> {
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);

View File

@ -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();

View File

@ -1065,7 +1065,10 @@ fn value_from_normalized(normalized: f64, range: RangeInclusive<f64>, 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<f64>, 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<f64>, 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

View File

@ -194,7 +194,10 @@ impl TextBuffer for String {
}
fn delete_char_range(&mut self, char_range: Range<usize>) {
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);

View File

@ -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(

View File

@ -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:?}");
}

View File

@ -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 { .. } => {

View File

@ -490,8 +490,15 @@ impl Highlighter {
fn as_byte_range(whole: &str, range: &str) -> std::ops::Range<usize> {
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())
}

View File

@ -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 {}",

View File

@ -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<usize>) -> 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);

View File

@ -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
}
}

View File

@ -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 {

View File

@ -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();

View File

@ -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<Item = u8>) -> 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]
}
}

View File

@ -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<Mesh16> {
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

View File

@ -339,7 +339,7 @@ impl Shape {
#[inline]
pub fn mesh(mesh: impl Into<Arc<Mesh>>) -> 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<Shape>,
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;

View File

@ -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
}

View File

@ -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();

View File

@ -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(

View File

@ -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);

View File

@ -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 {

View File

@ -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:

View File

@ -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