diff --git a/CHANGELOG.md b/CHANGELOG.md index 6080fd3663b3..c664f66ef8fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5701,6 +5701,7 @@ Released 2018-09-13 [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute [`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice +[`unstable_intrinsics_with_stable_wrapper`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_intrinsics_with_stable_wrapper [`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_enumerate_index`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_enumerate_index diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index b96a7af90700..48e8d2691cf3 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -712,6 +712,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, + crate::unstable_intrinsics_with_stable_wrapper::UNSTABLE_INTRINSICS_WITH_STABLE_WRAPPER_INFO, crate::unused_async::UNUSED_ASYNC_INFO, crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO, crate::unused_peekable::UNUSED_PEEKABLE_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 5636f46b22fe..f9e15edcdc07 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -349,6 +349,7 @@ mod unnecessary_struct_initialization; mod unnecessary_wraps; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unstable_intrinsics_with_stable_wrapper; mod unused_async; mod unused_io_amount; mod unused_peekable; @@ -1111,6 +1112,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv()))); store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl)); + store + .register_late_pass(|_| Box::new(unstable_intrinsics_with_stable_wrapper::UnstableIntrinsicsWithStableWrapper)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/unstable_intrinsics_with_stable_wrapper.rs b/clippy_lints/src/unstable_intrinsics_with_stable_wrapper.rs new file mode 100644 index 000000000000..2767a6930897 --- /dev/null +++ b/clippy_lints/src/unstable_intrinsics_with_stable_wrapper.rs @@ -0,0 +1,126 @@ +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, PathSegment, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TyCtxt; +use rustc_resolve::rustdoc::{add_doc_fragment, attrs_to_doc_fragments}; +use rustc_session::declare_lint_pass; +use rustc_span::def_id::DefId; +use rustc_span::{sym, Span}; + +use clippy_utils::diagnostics::span_lint; +use clippy_utils::{any_parent_has_attr, def_path_def_ids, match_any_def_paths}; + +use std::sync::OnceLock; + +declare_clippy_lint! { + /// ### What it does + /// Detects usage of unstable intrinsics with safe wrappers. + /// + /// ### Why is this bad? + /// It allows to have the same features without requiring to use the `core_intrinsics` + /// feature. + /// + /// ### Example + /// ```no_run + /// # #![feature(core_intrinsics)] + /// use std::intrinsics::add_with_overflow; + /// + /// add_with_overflow(12u32, 14); + /// ``` + /// Use instead: + /// ```no_run + /// 12u32.overflowing_add(14); + /// ``` + #[clippy::version = "1.77.0"] + pub UNSTABLE_INTRINSICS_WITH_STABLE_WRAPPER, + suspicious, + "Detects usage of unstable intrinsics with safe wrappers" +} + +declare_lint_pass!(UnstableIntrinsicsWithStableWrapper => [UNSTABLE_INTRINSICS_WITH_STABLE_WRAPPER]); + +fn get_doc(tcx: TyCtxt<'_>, fn_def_id: DefId) -> String { + let (fragments, _) = + attrs_to_doc_fragments(tcx.get_attrs_unchecked(fn_def_id).iter().map(|attr| (attr, None)), true); + let mut doc = String::new(); + for fragment in &fragments { + add_doc_fragment(&mut doc, fragment); + } + doc +} + +fn emit_if_is_unstable_intrinsic( + cx: &LateContext<'_>, + def_id: DefId, + expr_span: Span, + fn_name: &str, + segments: &[PathSegment<'_>], +) { + static FNS: OnceLock> = OnceLock::new(); + let fns = FNS.get_or_init(|| { + let mod_def_id = def_path_def_ids(cx, &["core", "intrinsics"]).next().unwrap(); + cx.tcx + .module_children(mod_def_id) + .iter() + .filter_map(|child| match child.res { + Res::Def(DefKind::Fn, fn_def_id) => Some(fn_def_id), + _ => None, + }) + .filter(|fn_def_id| !get_doc(cx.tcx, *fn_def_id).contains("does not have a stable counterpart")) + .collect::>() + }); + + if cx.tcx.is_intrinsic(def_id) + // This is to prevent false positives like "transmute". + && segments.len() > 1 + && let Some(mod_def_id) = segments[segments.len() - 2].res.opt_def_id() + && match_any_def_paths(cx, mod_def_id, &[ + &["core", "intrinsics"], + &["std", "intrinsics"], + ]).is_some() + { + for intrinsic in fns { + if *intrinsic == def_id { + span_lint( + cx, + UNSTABLE_INTRINSICS_WITH_STABLE_WRAPPER, + expr_span, + &format!( + "consider using the stable counterpart mentioned in the documentation \ + (https://doc.rust-lang.org/stable/core/intrinsics/fn.{fn_name}.html)" + ), + ); + return; + } + } + } +} + +impl<'tcx> LateLintPass<'tcx> for UnstableIntrinsicsWithStableWrapper { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Use(path, _) = item.kind { + for res in &path.res { + if let Some(use_def_id) = res.opt_def_id() + && cx.tcx.def_kind(use_def_id) == DefKind::Fn + && let Some(fn_name) = cx.tcx.opt_item_name(use_def_id) + { + emit_if_is_unstable_intrinsic(cx, use_def_id, path.span, fn_name.as_str(), path.segments); + } + } + } + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Call(call_expr, _) = expr.kind + && let ExprKind::Path(QPath::Resolved(_, path)) = call_expr.kind + && let Some(fn_def_id) = path.res.opt_def_id() + && !fn_def_id.is_local() + // We get the function name instead from `path` because it could have been renamed. + && let Some(fn_name) = cx.tcx.opt_item_name(fn_def_id) + && let fn_name = fn_name.as_str() + && !any_parent_has_attr(cx.tcx, expr.hir_id, sym::automatically_derived) + { + emit_if_is_unstable_intrinsic(cx, fn_def_id, expr.span, fn_name, path.segments); + } + } +} diff --git a/tests/ui/transmute.rs b/tests/ui/transmute.rs index 1796ccaf28e1..7262533c5e43 100644 --- a/tests/ui/transmute.rs +++ b/tests/ui/transmute.rs @@ -1,4 +1,9 @@ -#![allow(dead_code, clippy::borrow_as_ptr, clippy::needless_lifetimes)] +#![allow( + dead_code, + clippy::borrow_as_ptr, + clippy::needless_lifetimes, + clippy::unstable_intrinsics_with_stable_wrapper +)] //@no-rustfix extern crate core; diff --git a/tests/ui/transmute.stderr b/tests/ui/transmute.stderr index df32d478cbf4..7b6405a261f2 100644 --- a/tests/ui/transmute.stderr +++ b/tests/ui/transmute.stderr @@ -1,5 +1,5 @@ error: transmute from a reference to a pointer - --> $DIR/transmute.rs:24:23 + --> $DIR/transmute.rs:29:23 | LL | let _: *const T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T` @@ -8,61 +8,61 @@ LL | let _: *const T = core::intrinsics::transmute(t); = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:28:21 + --> $DIR/transmute.rs:33:21 | LL | let _: *mut T = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T` error: transmute from a reference to a pointer - --> $DIR/transmute.rs:31:23 + --> $DIR/transmute.rs:36:23 | LL | let _: *const U = core::intrinsics::transmute(t); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U` error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:38:27 + --> $DIR/transmute.rs:43:27 | LL | let _: Vec = core::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:41:27 + --> $DIR/transmute.rs:46:27 | LL | let _: Vec = core::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:44:27 + --> $DIR/transmute.rs:49:27 | LL | let _: Vec = std::intrinsics::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:47:27 + --> $DIR/transmute.rs:52:27 | LL | let _: Vec = std::mem::transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`std::vec::Vec`) to itself - --> $DIR/transmute.rs:50:27 + --> $DIR/transmute.rs:55:27 | LL | let _: Vec = my_transmute(my_vec()); | ^^^^^^^^^^^^^^^^^^^^^^ error: transmute from an integer to a pointer - --> $DIR/transmute.rs:53:31 + --> $DIR/transmute.rs:58:31 | LL | let _: *const usize = std::mem::transmute(5_isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize` error: transmute from an integer to a pointer - --> $DIR/transmute.rs:58:31 + --> $DIR/transmute.rs:63:31 | LL | let _: *const usize = std::mem::transmute(1 + 1usize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize` error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:90:24 + --> $DIR/transmute.rs:95:24 | LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,25 +71,25 @@ LL | let _: Usize = core::intrinsics::transmute(int_const_ptr); = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]` error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`) - --> $DIR/transmute.rs:94:24 + --> $DIR/transmute.rs:99:24 | LL | let _: Usize = core::intrinsics::transmute(int_mut_ptr); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`) - --> $DIR/transmute.rs:97:31 + --> $DIR/transmute.rs:102:31 | LL | let _: *const Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`) - --> $DIR/transmute.rs:100:29 + --> $DIR/transmute.rs:105:29 | LL | let _: *mut Usize = core::intrinsics::transmute(my_int()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: transmute from a `u8` to a `bool` - --> $DIR/transmute.rs:107:28 + --> $DIR/transmute.rs:112:28 | LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0` @@ -98,7 +98,7 @@ LL | let _: bool = unsafe { std::mem::transmute(0_u8) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]` error: transmute from a `u32` to a `f32` - --> $DIR/transmute.rs:115:31 + --> $DIR/transmute.rs:120:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)` @@ -107,25 +107,25 @@ LL | let _: f32 = unsafe { std::mem::transmute(0_u32) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_float)]` error: transmute from a `i32` to a `f32` - --> $DIR/transmute.rs:118:31 + --> $DIR/transmute.rs:123:31 | LL | let _: f32 = unsafe { std::mem::transmute(0_i32) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)` error: transmute from a `u64` to a `f64` - --> $DIR/transmute.rs:120:31 + --> $DIR/transmute.rs:125:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_u64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)` error: transmute from a `i64` to a `f64` - --> $DIR/transmute.rs:122:31 + --> $DIR/transmute.rs:127:31 | LL | let _: f64 = unsafe { std::mem::transmute(0_i64) }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:143:30 + --> $DIR/transmute.rs:148:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` @@ -134,85 +134,85 @@ LL | let _: [u8; 1] = std::mem::transmute(0u8); = help: to override `-D warnings` add `#[allow(clippy::transmute_num_to_bytes)]` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:146:30 + --> $DIR/transmute.rs:151:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:148:31 + --> $DIR/transmute.rs:153:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:150:30 + --> $DIR/transmute.rs:155:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:152:30 + --> $DIR/transmute.rs:157:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:154:31 + --> $DIR/transmute.rs:159:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `f32` to a `[u8; 4]` - --> $DIR/transmute.rs:156:30 + --> $DIR/transmute.rs:161:30 | LL | let _: [u8; 4] = std::mem::transmute(0.0f32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()` error: transmute from a `f64` to a `[u8; 8]` - --> $DIR/transmute.rs:158:30 + --> $DIR/transmute.rs:163:30 | LL | let _: [u8; 8] = std::mem::transmute(0.0f64); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()` error: transmute from a `u8` to a `[u8; 1]` - --> $DIR/transmute.rs:164:30 + --> $DIR/transmute.rs:169:30 | LL | let _: [u8; 1] = std::mem::transmute(0u8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()` error: transmute from a `u32` to a `[u8; 4]` - --> $DIR/transmute.rs:166:30 + --> $DIR/transmute.rs:171:30 | LL | let _: [u8; 4] = std::mem::transmute(0u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()` error: transmute from a `u128` to a `[u8; 16]` - --> $DIR/transmute.rs:168:31 + --> $DIR/transmute.rs:173:31 | LL | let _: [u8; 16] = std::mem::transmute(0u128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()` error: transmute from a `i8` to a `[u8; 1]` - --> $DIR/transmute.rs:170:30 + --> $DIR/transmute.rs:175:30 | LL | let _: [u8; 1] = std::mem::transmute(0i8); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()` error: transmute from a `i32` to a `[u8; 4]` - --> $DIR/transmute.rs:172:30 + --> $DIR/transmute.rs:177:30 | LL | let _: [u8; 4] = std::mem::transmute(0i32); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()` error: transmute from a `i128` to a `[u8; 16]` - --> $DIR/transmute.rs:174:31 + --> $DIR/transmute.rs:179:31 | LL | let _: [u8; 16] = std::mem::transmute(0i128); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:185:28 + --> $DIR/transmute.rs:190:28 | LL | let _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()` @@ -221,13 +221,13 @@ LL | let _: &str = unsafe { std::mem::transmute(B) }; = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]` error: transmute from a `&mut [u8]` to a `&mut str` - --> $DIR/transmute.rs:188:32 + --> $DIR/transmute.rs:193:32 | LL | let _: &mut str = unsafe { std::mem::transmute(mb) }; | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()` error: transmute from a `&[u8]` to a `&str` - --> $DIR/transmute.rs:190:30 + --> $DIR/transmute.rs:195:30 | LL | const _: &str = unsafe { std::mem::transmute(B) }; | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)` diff --git a/tests/ui/unstable_intrinsics_with_stable_wrapper.rs b/tests/ui/unstable_intrinsics_with_stable_wrapper.rs new file mode 100644 index 000000000000..855fdb19004b --- /dev/null +++ b/tests/ui/unstable_intrinsics_with_stable_wrapper.rs @@ -0,0 +1,24 @@ +#![warn(clippy::unstable_intrinsics_with_stable_wrapper)] +#![allow(clippy::invalid_null_ptr_usage)] +#![feature(core_intrinsics)] + +use core::intrinsics::{add_with_overflow, assert_inhabited}; +//~^ ERROR: consider using the stable counterpart +use std::ptr::write_bytes; + +fn main() { + // Shouldn't warn since we can't infer just from its name if it's an intrinsic or not. + add_with_overflow(12, 14); + // There is a stable counterpart so it should warn. + core::intrinsics::add_with_overflow(12, 14); + //~^ ERROR: consider using the stable counterpart + // This one doesn't have a counterpart so should not emit a warning. + core::intrinsics::assert_inhabited::(); + assert_inhabited::(); + // Shouldn't warn because it's the safe version. + unsafe { + std::ptr::write_bytes::(std::ptr::null_mut(), 42, 0); + write_bytes::(std::ptr::null_mut(), 42, 0); + std::intrinsics::write_bytes::(std::ptr::null_mut(), 42, 0); + } +} diff --git a/tests/ui/unstable_intrinsics_with_stable_wrapper.stderr b/tests/ui/unstable_intrinsics_with_stable_wrapper.stderr new file mode 100644 index 000000000000..a4c991e553cc --- /dev/null +++ b/tests/ui/unstable_intrinsics_with_stable_wrapper.stderr @@ -0,0 +1,17 @@ +error: consider using the stable counterpart mentioned in the documentation (https://doc.rust-lang.org/stable/core/intrinsics/fn.add_with_overflow.html) + --> $DIR/unstable_intrinsics_with_stable_wrapper.rs:5:24 + | +LL | use core::intrinsics::{add_with_overflow, assert_inhabited}; + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unstable-intrinsics-with-stable-wrapper` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unstable_intrinsics_with_stable_wrapper)]` + +error: consider using the stable counterpart mentioned in the documentation (https://doc.rust-lang.org/stable/core/intrinsics/fn.add_with_overflow.html) + --> $DIR/unstable_intrinsics_with_stable_wrapper.rs:13:5 + | +LL | core::intrinsics::add_with_overflow(12, 14); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors +