From 5579f831c290c49725114ef0650bb84e97f8dc65 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 17 Aug 2026 21:40:31 -0700 Subject: [PATCH 1/3] Update `webbrowser` to 1.2.2 to fix RUSTSEC-2026-0257 (#8431) Co-authored-by: Claude Opus 5 (1M context) --- Cargo.lock | 30 ++++++++++-------------------- Cargo.toml | 2 +- deny.toml | 1 - 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a35154eee..71ed40f62 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -907,16 +907,6 @@ dependencies = [ "libc", ] -[[package]] -name = "core-foundation" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -930,7 +920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "core-graphics-types", "foreign-types", "libc", @@ -943,7 +933,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation 0.9.4", + "core-foundation", "libc", ] @@ -1655,7 +1645,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4098,7 +4088,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4558,7 +4548,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5324,15 +5314,15 @@ dependencies = [ [[package]] name = "webbrowser" -version = "1.2.1" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +checksum = "62c35be770821a214dbc362fc26908c853e776c0004294d0b10b8a6bad582f94" dependencies = [ - "core-foundation 0.10.1", "jni", "log", "ndk-context", "objc2 0.6.4", + "objc2-app-kit 0.3.2", "objc2-foundation 0.3.2", "url", "web-sys", @@ -5558,7 +5548,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -5774,7 +5764,7 @@ dependencies = [ "calloop", "cfg_aliases", "concurrent-queue", - "core-foundation 0.9.4", + "core-foundation", "core-graphics", "cursor-icon", "dpi", diff --git a/Cargo.toml b/Cargo.toml index dd12fe9f0..26a60c57b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -156,7 +156,7 @@ wasm-bindgen-futures = "0.4.76" wayland-cursor = { version = "0.31.14", default-features = false } web-sys = "0.3.103" web-time = "1.1" # Timekeeping for native and web -webbrowser = "1.2" +webbrowser = "1.2.2" # 1.2.2 fixes RUSTSEC-2026-0257 (`BROWSER` argument injection) wgpu = { version = "30.0", default-features = false, features = ["std"] } windows-sys = "0.61.2" winit = { version = "0.30.13", default-features = false } diff --git a/deny.toml b/deny.toml index cdc81d99d..01faf237a 100644 --- a/deny.toml +++ b/deny.toml @@ -50,7 +50,6 @@ skip = [ { name = "bit-set" }, # wgpu's naga depends on 0.8, syntect's (used by egui_extras) fancy-regex depends on 0.5 { name = "bit-vec" }, # dependency of bit-set in turn, different between 0.6 and 0.5 { name = "bitflags" }, # old 1.0 version via glutin, png, spirv, … - { name = "core-foundation" }, # version conflict between winit and wgpu ecosystems { name = "env_filter" }, # 0.1.4 only used by android_logger, which is only used by the hello_android example { name = "foldhash" }, # pulled by the duplicated hashbrown versions { name = "getrandom" }, # ring / rustls (and thus ehttp) still depend on getrandom 0.2 From 9bb36b0ac2b64ac94d4cb7bef5d8e3e28e87a6c5 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 17 Aug 2026 21:41:09 -0700 Subject: [PATCH 2/3] Match image file extensions case-insensitively (#8430) `image.PNG` and `cat.SVG` were not recognized as images. Adds `egui::load::has_extension(uri, extension)`, which ignores ASCII case and any `#fragment`, and uses it for the `.svg`, `.gif`, `.webp` and `.png` checks. Note: gif/webp URIs like `a#b.gif` no longer match, since the fragment is now excluded. * [x] I have followed the instructions in the PR template Co-authored-by: Claude Opus 5 (1M context) --- crates/eframe/src/native/glow_integration.rs | 2 +- crates/egui/src/load.rs | 24 ++++++++++++++++++++ crates/egui/src/load/texture_loader.rs | 2 +- crates/egui/src/widgets/image.rs | 4 ++-- crates/egui_extras/src/loaders/svg_loader.rs | 2 +- 5 files changed, 29 insertions(+), 5 deletions(-) diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 1b9469cfd..cca1d22c6 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -1720,7 +1720,7 @@ fn save_screenshot_and_exit( screen_size_in_pixels: [u32; 2], ) { assert!( - path.ends_with(".png"), + egui::load::has_extension(path, "png"), "Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}" ); let screenshot = painter.read_screen_rgba(screen_size_in_pixels); diff --git a/crates/egui/src/load.rs b/crates/egui/src/load.rs index f3acbf6af..df390dc64 100644 --- a/crates/egui/src/load.rs +++ b/crates/egui/src/load.rs @@ -306,6 +306,19 @@ macro_rules! generate_loader_id { } pub use crate::generate_loader_id; +/// Does the given URI end with the given file extension? +/// +/// The comparison ignores ASCII case and any `#fragment` at the end of the URI, +/// so `has_extension("cat.GIF#frame=2", "gif")` is `true`. +/// +/// This is useful when implementing an [`ImageLoader`]. +pub fn has_extension(uri: &str, extension: &str) -> bool { + let path = uri.split('#').next().unwrap_or(uri); + std::path::Path::new(path) + .extension() + .is_some_and(|found| found.eq_ignore_ascii_case(extension)) +} + pub type BytesLoadResult = Result; /// Represents a loader capable of loading raw unstructured bytes from somewhere, @@ -639,3 +652,14 @@ impl Loaders { } } } + +#[test] +fn test_has_extension() { + assert!(has_extension("cat.svg", "svg")); + assert!(has_extension("cat.SVG", "svg")); + assert!(has_extension("http://example.com/cat.gif#frame=2", "gif")); + assert!(!has_extension("cat.svg.png", "svg")); + assert!(!has_extension("svg", "svg")); + assert!(!has_extension("cat.jpeg", "jpg")); + assert!(!has_extension("cat.svg?v=1", "svg")); +} diff --git a/crates/egui/src/load/texture_loader.rs b/crates/egui/src/load/texture_loader.rs index 2ab4d8a57..eb9b4503b 100644 --- a/crates/egui/src/load/texture_loader.rs +++ b/crates/egui/src/load/texture_loader.rs @@ -150,5 +150,5 @@ impl TextureLoader for DefaultTextureLoader { } fn is_svg(uri: &str) -> bool { - uri.ends_with(".svg") + super::has_extension(uri, "svg") } diff --git a/crates/egui/src/widgets/image.rs b/crates/egui/src/widgets/image.rs index 0618e8661..9bce8c4d3 100644 --- a/crates/egui/src/widgets/image.rs +++ b/crates/egui/src/widgets/image.rs @@ -934,7 +934,7 @@ fn animated_image_frame_index(ctx: &Context, uri: &str) -> usize { /// Checks if uri is a gif file fn is_gif_uri(uri: &str) -> bool { - uri.ends_with(".gif") || uri.contains(".gif#") + crate::load::has_extension(uri, "gif") } /// Checks if bytes are gifs @@ -944,7 +944,7 @@ pub fn has_gif_magic_header(bytes: &[u8]) -> bool { /// Checks if uri is a webp file fn is_webp_uri(uri: &str) -> bool { - uri.ends_with(".webp") || uri.contains(".webp#") + crate::load::has_extension(uri, "webp") } /// Checks if bytes are webp diff --git a/crates/egui_extras/src/loaders/svg_loader.rs b/crates/egui_extras/src/loaders/svg_loader.rs index 91063f6b4..53c879056 100644 --- a/crates/egui_extras/src/loaders/svg_loader.rs +++ b/crates/egui_extras/src/loaders/svg_loader.rs @@ -29,7 +29,7 @@ impl SvgLoader { } fn is_supported(uri: &str) -> bool { - uri.ends_with(".svg") + egui::load::has_extension(uri, "svg") } impl Default for SvgLoader { From 34b39d564b8810dc8bc1bc8975f32bb0315bdf23 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 17 Aug 2026 21:55:53 -0700 Subject: [PATCH 3/3] Enable the `clippy::pedantic` lint group (#8429) Instead of opting in to pedantic lints one by one, enable the whole group and opt out of the noisy ones. 64% of the pedantic lints were already listed individually. This deletes 90 explicit lint lines, enables 51 pedantic lints we never listed, and picks up new pedantic lints for free. Each opt-out carries its hit count, so the cost of turning one back on is visible. `restriction` and `nursery` stay opt-in per lint. Stacked on top of #8430, which fixes the one real bug the new lints found. * [x] I have followed the instructions in the PR template --------- Co-authored-by: Claude Opus 5 (1M context) --- Cargo.toml | 126 ++++-------------- crates/ecolor/src/hsva.rs | 4 - crates/eframe/src/native/app_icon.rs | 40 +++--- crates/eframe/src/native/file_storage.rs | 2 +- crates/egui/src/context.rs | 10 +- crates/egui/src/id.rs | 7 + .../src/easy_mark/easy_mark_parser.rs | 16 +-- crates/egui_extras/src/syntax_highlighting.rs | 7 + crates/egui_kittest/src/lib.rs | 14 +- crates/egui_kittest/src/snapshot.rs | 20 +-- crates/epaint/src/shapes/bezier_shape.rs | 2 - crates/epaint/src/tessellator.rs | 8 +- 12 files changed, 97 insertions(+), 159 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 26a60c57b..b6534d2d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -180,138 +180,62 @@ all = "warn" # See also clippy.toml [workspace.lints.clippy] all = { level = "warn", priority = -1 } +pedantic = { level = "warn", priority = -1 } allow_attributes = "warn" as_ptr_cast_mut = "warn" -bool_to_int_with_if = "warn" branches_sharing_code = "warn" -checked_conversions = "warn" clear_with_drain = "warn" clone_on_ref_ptr = "warn" -cloned_instead_of_copied = "warn" coerce_container_to_any = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" -decimal_bitwise_operands = "warn" default_union_representation = "warn" derive_partial_eq_without_eq = "warn" disallowed_script_idents = "warn" # See clippy.toml -doc_broken_link = "warn" -doc_comment_double_space_linebreaks = "warn" doc_include_without_cfg = "warn" -doc_link_with_quotes = "warn" -doc_markdown = "warn" -duration_suboptimal_units = "warn" -elidable_lifetime_names = "warn" empty_enum_variants_with_brackets = "warn" -empty_enums = "warn" -enum_glob_use = "warn" equatable_if_let = "warn" exit = "warn" -expl_impl_clone_on_copy = "warn" -explicit_deref_methods = "warn" -explicit_into_iter_loop = "warn" -explicit_iter_loop = "warn" fallible_impl_from = "warn" -filter_map_next = "warn" -flat_map_option = "warn" float_cmp_const = "warn" -fn_params_excessive_bools = "warn" fn_to_numeric_cast_any = "warn" -format_push_string = "warn" -from_iter_instead_of_collect = "warn" get_unwrap = "warn" -ignore_without_reason = "warn" -ignored_unit_patterns = "warn" -implicit_clone = "warn" imprecise_flops = "warn" -inconsistent_struct_constructor = "warn" -index_refutable_slice = "warn" -inefficient_to_string = "warn" infinite_loop = "warn" -into_iter_without_iter = "warn" -invalid_upcast_comparisons = "warn" -ip_constant = "warn" -iter_filter_is_ok = "warn" -iter_filter_is_some = "warn" -iter_not_returning_iterator = "warn" iter_on_empty_collections = "warn" iter_on_single_items = "warn" iter_over_hash_type = "warn" -iter_without_into_iter = "warn" -large_digit_groups = "warn" -large_futures = "warn" large_include_file = "warn" -large_stack_arrays = "warn" large_stack_frames = "warn" -large_types_passed_by_value = "warn" -linkedlist = "warn" literal_string_with_formatting_args = "warn" lossy_float_literal = "warn" -macro_use_imports = "warn" -manual_assert = "warn" -manual_ilog2 = "warn" -manual_instant_elapsed = "warn" -manual_is_power_of_two = "warn" -manual_is_variant_and = "warn" -manual_let_else = "warn" -manual_midpoint = "warn" # NOTE `midpoint` is often a lot slower for floats, so we have our own `emath::fast_midpoint` function. -manual_string_new = "warn" map_err_ignore = "warn" -match_bool = "warn" -match_same_arms = "warn" -match_wild_err_arm = "warn" -match_wildcard_for_single_variants = "warn" mem_forget = "warn" -mismatching_type_param_order = "warn" missing_assert_message = "warn" -missing_errors_doc = "warn" -missing_fields_in_debug = "warn" -mut_mut = "warn" mutex_integer = "warn" -needless_continue = "warn" -needless_for_each = "warn" needless_pass_by_ref_mut = "warn" -needless_pass_by_value = "warn" -needless_raw_string_hashes = "warn" needless_type_cast = "warn" negative_feature_names = "warn" -non_std_lazy_statics = "warn" non_zero_suggestions = "warn" nonstandard_macro_braces = "warn" -option_as_ref_cloned = "warn" -option_option = "warn" or_fun_call = "warn" path_buf_push_overwrite = "warn" pathbuf_init_then_push = "warn" precedence_bits = "warn" print_stderr = "warn" print_stdout = "warn" -ptr_as_ptr = "warn" -ptr_cast_constness = "warn" -pub_underscore_fields = "warn" pub_without_shorthand = "warn" rc_mutex = "warn" redundant_type_annotations = "warn" -ref_as_ptr = "warn" -ref_option = "warn" -ref_option_ref = "warn" ref_patterns = "warn" rest_pat_in_fully_bound_structs = "warn" return_and_then = "warn" -same_functions_in_if_condition = "warn" -same_length_and_capacity = "warn" -self_only_used_in_recursion = "warn" -semicolon_if_nothing_returned = "warn" set_contains_or_insert = "warn" -single_char_pattern = "warn" -single_match_else = "warn" single_option_map = "warn" std_instead_of_core = "warn" -str_split_at_newline = "warn" str_to_string = "warn" string_add = "warn" -string_add_assign = "warn" string_lit_as_bytes = "warn" string_lit_chars_any = "warn" suspicious_xor_used_as_pow = "warn" @@ -319,52 +243,58 @@ todo = "warn" too_long_first_doc_paragraph = "warn" trailing_empty_array = "warn" trait_duplication_in_bounds = "warn" -transmute_ptr_to_ptr = "warn" tuple_array_conversions = "warn" -unchecked_time_subtraction = "warn" undocumented_unsafe_blocks = "warn" unimplemented = "warn" uninhabited_references = "warn" -uninlined_format_args = "warn" -unnecessary_box_returns = "warn" -unnecessary_debug_formatting = "warn" -unnecessary_literal_bound = "warn" unnecessary_safety_comment = "warn" unnecessary_safety_doc = "warn" unnecessary_self_imports = "warn" -unnecessary_semicolon = "warn" unnecessary_struct_initialization = "warn" -unnecessary_trailing_comma = "warn" -unnecessary_wraps = "warn" -unnested_or_patterns = "warn" -unused_async = "warn" unused_peekable = "warn" unused_rounding = "warn" -unused_self = "warn" unused_trait_names = "warn" unwrap_used = "warn" use_self = "warn" useless_let_if_seq = "warn" verbose_file_reads = "warn" wildcard_dependencies = "warn" -zero_sized_map_values = "warn" -# TODO(emilk): maybe enable more of these lints? -# NOTE: these are all in `pedantic`/`restriction`/`nursery`, so the `allow` is a no-op today. -# We keep them to record our intent in case we ever enable those groups. -cast_possible_wrap = "allow" +# Pedantic lints we opt out of, with the number of hits at the time we enabled `pedantic`: +cast_lossless = "allow" # 204 hits +cast_possible_truncation = "allow" # 287 hits +cast_possible_wrap = "allow" # 43 hits +cast_precision_loss = "allow" # 200 hits +cast_sign_loss = "allow" # 98 hits comparison_chain = "allow" +default_trait_access = "allow" # 278 hits +float_cmp = "allow" # exact float comparisons are usually intentional (`float_cmp_const` is still on) +inline_always = "allow" # 271 hits; we know what we are doing +items_after_statements = "allow" # 82 hits +many_single_char_names = "allow" # `r, g, b, a` and `h, s, v` are fine +missing_panics_doc = "allow" # 68 hits +must_use_candidate = "allow" # 1169 hits +redundant_closure_for_method_calls = "allow" # 89 hits +return_self_not_must_use = "allow" # 246 hits should_panic_without_expect = "allow" +similar_names = "allow" # too many false positives, e.g. `encoder`/`encoded` +struct_excessive_bools = "allow" # 32 hits +struct_field_names = "allow" # 23 hits too_many_lines = "allow" +trivially_copy_pass_by_ref = "allow" # 119 hits +unreadable_literal = "allow" # 513 hits +used_underscore_binding = "allow" # 25 hits # These are meh: assigning_clones = "allow" # No please -cast_possible_truncation = "allow" # too many hits -let_underscore_must_use = "allow" -let_underscore_untyped = "allow" manual_range_contains = "allow" # this one is just worse imho map_unwrap_or = "allow" # so is this one +wildcard_imports = "allow" # `use crate::*` is useful to avoid merge conflicts when adding/removing imports + +# NOTE: these are in `restriction`/`nursery`, so the `allow` is a no-op today. +# We keep them to record our intent in case we ever enable those groups. +let_underscore_must_use = "allow" +let_underscore_untyped = "allow" self_named_module_files = "allow" # Disabled waiting on https://github.com/rust-lang/rust-clippy/issues/9602 significant_drop_tightening = "allow" # Too many false positives -wildcard_imports = "allow" # `use crate::*` is useful to avoid merge conflicts when adding/removing imports diff --git a/crates/ecolor/src/hsva.rs b/crates/ecolor/src/hsva.rs index 17008f5da..8d92d40b8 100644 --- a/crates/ecolor/src/hsva.rs +++ b/crates/ecolor/src/hsva.rs @@ -41,7 +41,6 @@ impl Hsva { /// From linear RGBA with premultiplied alpha #[inline] pub fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { - #![expect(clippy::many_single_char_names)] if a <= 0.0 { if r == 0.0 && b == 0.0 && a == 0.0 { Self::default() @@ -57,7 +56,6 @@ impl Hsva { /// From linear RGBA without premultiplied alpha #[inline] pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { - #![expect(clippy::many_single_char_names)] let (h, s, v) = hsv_from_rgb([r, g, b]); Self { h, s, v, a } } @@ -189,7 +187,6 @@ impl From for Hsva { /// All ranges in 0-1, rgb is linear. #[inline] pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) { - #![expect(clippy::many_single_char_names)] let min = r.min(g.min(b)); let max = r.max(g.max(b)); // value @@ -213,7 +210,6 @@ pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) { /// All ranges in 0-1, rgb is linear. #[inline] pub fn rgb_from_hsv((h, s, v): (f32, f32, f32)) -> [f32; 3] { - #![expect(clippy::many_single_char_names)] let h = (h.fract() + 1.0).fract(); // wrap let s = s.clamp(0.0, 1.0); diff --git a/crates/eframe/src/native/app_icon.rs b/crates/eframe/src/native/app_icon.rs index 9fdeb30e0..ef6c96721 100644 --- a/crates/eframe/src/native/app_icon.rs +++ b/crates/eframe/src/native/app_icon.rs @@ -161,16 +161,16 @@ fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus { if icon_big.is_null() { log::warn!("Failed to create HICON (for big icon) from embedded png data."); return AppIconStatus::NotSetIgnored; // We could try independently with the small icon but what's the point, it would look bad! - } else { - // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior. - unsafe { - SendMessageW( - window_handle, - WM_SETICON, - ICON_BIG as usize, - icon_big as isize, - ); - } + } + + // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior. + unsafe { + SendMessageW( + window_handle, + WM_SETICON, + ICON_BIG as usize, + icon_big as isize, + ); } } { @@ -180,16 +180,16 @@ fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus { if icon_small.is_null() { log::warn!("Failed to create HICON (for small icon) from embedded png data."); return AppIconStatus::NotSetIgnored; - } else { - // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior. - unsafe { - SendMessageW( - window_handle, - WM_SETICON, - ICON_SMALL as usize, - icon_small as isize, - ); - } + } + + // SAFETY: Unsafe WinApi function, takes objects previously created with WinAPI, all checked for null prior. + unsafe { + SendMessageW( + window_handle, + WM_SETICON, + ICON_SMALL as usize, + icon_small as isize, + ); } } diff --git a/crates/eframe/src/native/file_storage.rs b/crates/eframe/src/native/file_storage.rs index f6f4ef477..70b4a3cae 100644 --- a/crates/eframe/src/native/file_storage.rs +++ b/crates/eframe/src/native/file_storage.rs @@ -67,7 +67,7 @@ fn roaming_appdata() -> Option { &FOLDERID_RoamingAppData, KF_FLAG_DONT_VERIFY as u32, core::ptr::null_mut(), - &mut path_raw, + &raw mut path_raw, ) }; diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 45193e15e..25b384c95 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1761,11 +1761,11 @@ impl Context { .get(&id) .map(|v| v.repaint.cumulative_frame_nr) .unwrap_or_else(|| { - if cfg!(debug_assertions) { - panic!("cumulative_frame_nr_for failed to find the viewport {id:?}"); - } else { - 0 - } + debug_assert!( + false, + "cumulative_frame_nr_for failed to find the viewport {id:?}" + ); + 0 }) }) } diff --git a/crates/egui/src/id.rs b/crates/egui/src/id.rs index c9d05465e..6b15fd05d 100644 --- a/crates/egui/src/id.rs +++ b/crates/egui/src/id.rs @@ -41,6 +41,13 @@ impl AsId for T {} /// This is niche-optimized to that `Option` is the same size as `Id`. #[derive(Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr( + feature = "serde", + expect( + clippy::unsafe_derive_deserialize, + reason = "`from_high_entropy_bits` is only `unsafe` about entropy, not memory safety" + ) +)] pub struct Id(NonZeroU64); impl nohash_hasher::IsEnabled for Id {} diff --git a/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs b/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs index 66c0dc04f..0e36844c3 100644 --- a/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs +++ b/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs @@ -118,15 +118,15 @@ impl<'a> Parser<'a> { { let language = &language_start[..newline]; let code_start = &language_start[newline + 1..]; - if let Some(end) = code_start.find("\n```") { + return if let Some(end) = code_start.find("\n```") { let code = &code_start[..end].trim(); self.s = &code_start[end + 4..]; self.start_of_line = false; - return Some(Item::CodeBlock(language, code)); + Some(Item::CodeBlock(language, code)) } else { self.s = ""; - return Some(Item::CodeBlock(language, code_start)); - } + Some(Item::CodeBlock(language, code_start)) + }; } None } @@ -138,18 +138,18 @@ impl<'a> Parser<'a> { self.start_of_line = false; self.style.code = true; let rest_of_line = &self.s[..self.s.find('\n').unwrap_or(self.s.len())]; - if let Some(end) = rest_of_line.find('`') { + return if let Some(end) = rest_of_line.find('`') { let item = Item::Text(self.style, &self.s[..end]); self.s = &self.s[end + 1..]; self.style.code = false; - return Some(item); + Some(item) } else { let end = rest_of_line.len(); let item = Item::Text(self.style, rest_of_line); self.s = &self.s[end..]; self.style.code = false; - return Some(item); - } + Some(item) + }; } None } diff --git a/crates/egui_extras/src/syntax_highlighting.rs b/crates/egui_extras/src/syntax_highlighting.rs index d09151d21..f45b68f2c 100644 --- a/crates/egui_extras/src/syntax_highlighting.rs +++ b/crates/egui_extras/src/syntax_highlighting.rs @@ -210,6 +210,13 @@ impl SyntectTheme { derive(serde::Deserialize, serde::Serialize), serde(default) )] +#[cfg_attr( + all(feature = "serde", not(feature = "syntect")), + expect( + clippy::unsafe_derive_deserialize, + reason = "the `enum_map!` macro expands to `unsafe` code" + ) +)] pub struct CodeTheme { dark_mode: bool, diff --git a/crates/egui_kittest/src/lib.rs b/crates/egui_kittest/src/lib.rs index fa8f26311..8b4ff9ad6 100644 --- a/crates/egui_kittest/src/lib.rs +++ b/crates/egui_kittest/src/lib.rs @@ -247,16 +247,16 @@ impl<'a, State> Harness<'a, State> { pub fn step(&mut self) { let events = core::mem::take(&mut *self.queued_events.lock()); if events.is_empty() { - self._step(false); + self.step_impl(false); } for event in events { self.input.events.push(event); - self._step(false); + self.step_impl(false); } } /// Run a single step. This will not process any events. - fn _step(&mut self, sizing_pass: bool) { + fn step_impl(&mut self, sizing_pass: bool) { self.input.predicted_dt = self.step_dt; let mut output = self.ctx.run_ui(self.input.take(), |ui| { @@ -297,7 +297,7 @@ impl<'a, State> Harness<'a, State> { /// [`Harness::new_ui`] / [`Harness::new_ui_state`] or /// [`HarnessBuilder::build_ui`] / [`HarnessBuilder::build_ui_state`]. pub fn fit_contents(&mut self) { - self._step(true); + self.step_impl(true); // Calculate size including all content (main UI + popups + tooltips) if let Some(rect) = self.compute_total_rect_with_popups() { @@ -333,7 +333,7 @@ impl<'a, State> Harness<'a, State> { } } - fn _try_run(&mut self, sleep: bool) -> Result { + fn try_run_impl(&mut self, sleep: bool) -> Result { let mut steps = 0; loop { steps += 1; @@ -374,7 +374,7 @@ impl<'a, State> Harness<'a, State> { /// - [`Harness::run_steps`]. /// - [`Harness::try_run_realtime`]. pub fn try_run(&mut self) -> Result { - self._try_run(false) + self.try_run_impl(false) } /// Run until @@ -414,7 +414,7 @@ impl<'a, State> Harness<'a, State> { /// - [`Harness::run_steps`]. /// - [`Harness::try_run`]. pub fn try_run_realtime(&mut self) -> Result { - self._try_run(true) + self.try_run_impl(true) } /// Run a number of steps. diff --git a/crates/egui_kittest/src/snapshot.rs b/crates/egui_kittest/src/snapshot.rs index e4472219f..2f709cc3c 100644 --- a/crates/egui_kittest/src/snapshot.rs +++ b/crates/egui_kittest/src/snapshot.rs @@ -535,31 +535,31 @@ fn try_image_snapshot_options_impl( Ok(image) => image.to_rgba8(), Err(err) => { // No previous snapshot - probably a new test. - if mode.is_update() { - return update_snapshot(); + return if mode.is_update() { + update_snapshot() } else { write_new_png()?; - return Err(SnapshotError::OpenSnapshot { + Err(SnapshotError::OpenSnapshot { path: snapshot_path.clone(), err, - }); - } + }) + }; } }; if previous.dimensions() != new.dimensions() { - if mode.is_update() { - return update_snapshot(); + return if mode.is_update() { + update_snapshot() } else { write_new_png()?; - return Err(SnapshotError::SizeMismatch { + Err(SnapshotError::SizeMismatch { name, expected: previous.dimensions(), actual: new.dimensions(), - }); - } + }) + }; } // Compare existing image to the new one: diff --git a/crates/epaint/src/shapes/bezier_shape.rs b/crates/epaint/src/shapes/bezier_shape.rs index caf1094a1..99694f4fb 100644 --- a/crates/epaint/src/shapes/bezier_shape.rs +++ b/crates/epaint/src/shapes/bezier_shape.rs @@ -1,5 +1,3 @@ -#![expect(clippy::many_single_char_names)] - use core::ops::Range; use crate::{Color32, PathShape, PathStroke, Shape}; diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index 7b207abeb..a9784f48b 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1519,11 +1519,11 @@ impl Tessellator { if stroke.is_empty() { return; // we are done - } else { - // we still need to do the stroke - fill = Color32::TRANSPARENT; // don't fill again below - break; } + + // we still need to do the stroke + fill = Color32::TRANSPARENT; // don't fill again below + break; } } }