diff --git a/src/accounts.rs b/src/accounts.rs index 5d86668ab..ce3ba0eef 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -586,6 +586,7 @@ impl Config { } #[cfg(not(target_os = "ios"))] + #[expect(clippy::arithmetic_side_effects)] async fn create_lock_task(dir: PathBuf) -> Result>>> { let lockfile = dir.join(LOCKFILE_NAME); let mut lock = fd_lock::RwLock::new(fs::File::create(lockfile).await?); @@ -752,6 +753,7 @@ impl Config { } /// Creates a new account in the account manager directory. + #[expect(clippy::arithmetic_side_effects)] async fn new_account(&mut self) -> Result { let id = { let id = self.inner.next_id; @@ -841,6 +843,7 @@ impl Config { /// /// Without this workaround removing account may fail on Windows with an error /// "The process cannot access the file because it is being used by another process. (os error 32)". +#[expect(clippy::arithmetic_side_effects)] async fn try_many_times(f: F) -> std::result::Result<(), T> where F: Fn() -> Fut, diff --git a/src/aheader.rs b/src/aheader.rs index 65db5248d..c8cde836c 100644 --- a/src/aheader.rs +++ b/src/aheader.rs @@ -73,6 +73,7 @@ impl fmt::Display for Aheader { let keydata = self.public_key.to_base64().chars().enumerate().fold( String::new(), |mut res, (i, c)| { + #[expect(clippy::arithmetic_side_effects)] if i % 78 == 78 - "keydata=".len() { res.push(' ') } diff --git a/src/blob.rs b/src/blob.rs index bd426a35c..27c44f3e3 100644 --- a/src/blob.rs +++ b/src/blob.rs @@ -321,6 +321,7 @@ impl<'a> BlobObject<'a> { /// then the updated user-visible filename will be returned; /// this may be necessary because the format may be changed to JPG, /// i.e. "image.png" -> "image.jpg". + #[expect(clippy::arithmetic_side_effects)] fn check_or_recode_to_size( &mut self, context: &Context, diff --git a/src/calls.rs b/src/calls.rs index 3e0263f0a..f75b9116c 100644 --- a/src/calls.rs +++ b/src/calls.rs @@ -79,6 +79,7 @@ impl CallInfo { } fn remaining_ring_seconds(&self) -> i64 { + #[expect(clippy::arithmetic_side_effects)] let remaining_seconds = self.msg.timestamp_sent + RINGING_SECONDS - time(); remaining_seconds.clamp(0, RINGING_SECONDS) } @@ -175,6 +176,7 @@ impl CallInfo { } /// Returns call duration in seconds. + #[expect(clippy::arithmetic_side_effects)] pub fn duration_seconds(&self) -> i64 { if let (Some(start), Some(end)) = ( self.msg.param.get_i64(CALL_ACCEPTED_TIMESTAMP), diff --git a/src/chat.rs b/src/chat.rs index 06cf1712e..2b20642aa 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -941,6 +941,7 @@ SELECT id, rfc724_mid, pre_rfc724_mid, timestamp, ?, 1 FROM msgs WHERE chat_id=? /// Jaccard similarity coefficient is used to estimate similarity of chat member sets. /// /// Chat is considered active if something was posted there within the last 42 days. + #[expect(clippy::arithmetic_side_effects)] pub async fn get_similar_chat_ids(self, context: &Context) -> Result> { // Count number of common members in this and other chats. let intersection = context @@ -1145,6 +1146,7 @@ SELECT id, rfc724_mid, pre_rfc724_mid, timestamp, ?, 1 FROM msgs WHERE chat_id=? /// prefer plaintext emails. /// /// To get more verbose summary for a contact, including its key fingerprint, use [`Contact::get_encrinfo`]. + #[expect(clippy::arithmetic_side_effects)] pub async fn get_encryption_info(self, context: &Context) -> Result { let chat = Chat::load_from_db(context, self).await?; if !chat.is_encrypted(context).await? { @@ -1730,6 +1732,7 @@ impl Chat { /// /// If `update_msg_id` is set, that record is reused; /// if `update_msg_id` is None, a new record is created. + #[expect(clippy::arithmetic_side_effects)] async fn prepare_msg_raw( &mut self, context: &Context, @@ -2995,6 +2998,7 @@ pub async fn send_text_msg( } /// Sends chat members a request to edit the given message's text. +#[expect(clippy::arithmetic_side_effects)] pub async fn send_edit_request(context: &Context, msg_id: MsgId, new_text: String) -> Result<()> { let mut original_msg = Message::load_from_db(context, msg_id).await?; ensure!( @@ -3100,6 +3104,7 @@ pub async fn get_chat_msgs(context: &Context, chat_id: ChatId) -> Result Result { let timestamp_some_days_ago = time() - DC_RESEND_USER_AVATAR_DAYS * 24 * 60 * 60; let needs_attach = context @@ -4433,6 +4439,7 @@ pub async fn forward_msgs(context: &Context, msg_ids: &[MsgId], chat_id: ChatId) } /// Forwards multiple messages to a chat in another context. +#[expect(clippy::arithmetic_side_effects)] pub async fn forward_msgs_2ctx( ctx_src: &Context, msg_ids: &[MsgId], @@ -4563,6 +4570,7 @@ pub async fn save_msgs(context: &Context, msg_ids: &[MsgId]) -> Result<()> { /// the copy contains a reference to the original message /// as well as to the original chat in case the original message gets deleted. /// Returns data needed to add a `SaveMessage` sync item. +#[expect(clippy::arithmetic_side_effects)] pub(crate) async fn save_copy_in_self_talk( context: &Context, src_msg_id: MsgId, @@ -4741,6 +4749,7 @@ pub(crate) async fn get_chat_id_by_grpid( /// /// Optional `label` can be provided to ensure that message is added only once. /// If `important` is true, a notification will be sent. +#[expect(clippy::arithmetic_side_effects)] pub async fn add_device_msg_with_importance( context: &Context, label: Option<&str>, diff --git a/src/color.rs b/src/color.rs index a84c59f46..f3ab24962 100644 --- a/src/color.rs +++ b/src/color.rs @@ -7,6 +7,7 @@ use colorutils_rs::{Oklch, Rgb, TransferFunction}; use sha1::{Digest, Sha1}; /// Converts an identifier to Hue angle. +#[expect(clippy::arithmetic_side_effects)] fn str_to_angle(s: &str) -> f32 { let bytes = s.as_bytes(); let result = Sha1::digest(bytes); @@ -19,6 +20,7 @@ fn str_to_angle(s: &str) -> f32 { /// /// Returns a 24-bit number with 8 least significant bits corresponding to the blue color and 8 /// most significant bits corresponding to the red color. +#[expect(clippy::arithmetic_side_effects)] fn rgb_to_u32(rgb: Rgb) -> u32 { 65536 * u32::from(rgb.r) + 256 * u32::from(rgb.g) + u32::from(rgb.b) } diff --git a/src/contact.rs b/src/contact.rs index da41033bd..dd22baaae 100644 --- a/src/contact.rs +++ b/src/contact.rs @@ -673,6 +673,7 @@ impl Contact { } /// Returns `true` if this contact was seen recently. + #[expect(clippy::arithmetic_side_effects)] pub fn was_seen_recently(&self) -> bool { time() - self.last_seen <= SEEN_RECENTLY_SECONDS } @@ -1071,6 +1072,7 @@ VALUES (?, ?, ?, ?, ?, ?) /// The `addr_book` is a multiline string in the format `Name one\nAddress one\nName two\nAddress two`. /// /// Returns the number of modified contacts. + #[expect(clippy::arithmetic_side_effects)] pub async fn add_address_book(context: &Context, addr_book: &str) -> Result { let mut modify_cnt = 0; @@ -1909,6 +1911,7 @@ pub(crate) async fn set_status( } /// Updates last seen timestamp of the contact if it is earlier than the given `timestamp`. +#[expect(clippy::arithmetic_side_effects)] pub(crate) async fn update_last_seen( context: &Context, contact_id: ContactId, @@ -2000,6 +2003,7 @@ pub(crate) async fn mark_contact_id_as_verified( Ok(()) } +#[expect(clippy::arithmetic_side_effects)] fn cat_fingerprint(ret: &mut String, name: &str, addr: &str, fingerprint: &str) { *ret += &format!("\n\n{name} ({addr}):\n{fingerprint}"); } @@ -2041,6 +2045,7 @@ impl RecentlySeenLoop { } } + #[expect(clippy::arithmetic_side_effects)] async fn run(context: Context, interrupt: Receiver) { type MyHeapElem = (Reverse, ContactId); diff --git a/src/context.rs b/src/context.rs index 22b12bafa..144b0ac9b 100644 --- a/src/context.rs +++ b/src/context.rs @@ -342,6 +342,7 @@ enum RunningState { /// actual keys and their values which will be present are not /// guaranteed. Calling [Context::get_info] also includes information /// about the context on top of the information here. +#[expect(clippy::arithmetic_side_effects)] pub fn get_info() -> BTreeMap<&'static str, String> { let mut res = BTreeMap::new(); diff --git a/src/dehtml.rs b/src/dehtml.rs index 12cd4e609..4c1b6cc24 100644 --- a/src/dehtml.rs +++ b/src/dehtml.rs @@ -235,6 +235,7 @@ fn str_cb(event_str: &str, dehtml: &mut Dehtml) { } } +#[expect(clippy::arithmetic_side_effects)] fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) { let tag = String::from_utf8_lossy(event.name().as_ref()) .trim() @@ -280,6 +281,7 @@ fn dehtml_endtag_cb(event: &BytesEnd, dehtml: &mut Dehtml) { } } +#[expect(clippy::arithmetic_side_effects)] fn dehtml_starttag_cb( event: &BytesStart, dehtml: &mut Dehtml, @@ -356,6 +358,7 @@ fn dehtml_starttag_cb( /// In order to know when a specific tag is closed, we need to count the opening and closing tags. /// The `counts`s are stored in the `Dehtml` struct. +#[expect(clippy::arithmetic_side_effects)] fn pop_tag(count: &mut u32) { if *count > 0 { *count -= 1; @@ -364,6 +367,7 @@ fn pop_tag(count: &mut u32) { /// In order to know when a specific tag is closed, we need to count the opening and closing tags. /// The `counts`s are stored in the `Dehtml` struct. +#[expect(clippy::arithmetic_side_effects)] fn maybe_push_tag( event: &BytesStart, reader: &Reader, diff --git a/src/ephemeral.rs b/src/ephemeral.rs index 7baf92095..42bf9cb28 100644 --- a/src/ephemeral.rs +++ b/src/ephemeral.rs @@ -593,6 +593,7 @@ async fn next_expiration_timestamp(context: &Context) -> Option { .min() } +#[expect(clippy::arithmetic_side_effects)] pub(crate) async fn ephemeral_loop(context: &Context, interrupt_receiver: Receiver<()>) { loop { let ephemeral_timestamp = next_expiration_timestamp(context).await; @@ -650,6 +651,7 @@ pub(crate) async fn ephemeral_loop(context: &Context, interrupt_receiver: Receiv } /// Schedules expired IMAP messages for deletion. +#[expect(clippy::arithmetic_side_effects)] pub(crate) async fn delete_expired_imap_messages(context: &Context) -> Result<()> { let now = time(); diff --git a/src/html.rs b/src/html.rs index fec82589f..77a0c6739 100644 --- a/src/html.rs +++ b/src/html.rs @@ -86,6 +86,7 @@ impl HtmlMsgParser { /// Function takes a raw mime-message string, /// searches for the main-text part /// and returns that as parser.html + #[expect(clippy::arithmetic_side_effects)] pub async fn from_bytes<'a>( context: &Context, rawmime: &'a [u8], @@ -119,6 +120,7 @@ impl HtmlMsgParser { /// Usually, there is at most one plain-text and one HTML-text part, /// multiple plain-text parts might be used for mailinglist-footers, /// therefore we use the first one. + #[expect(clippy::arithmetic_side_effects)] async fn collect_texts_recursive<'a>( &'a mut self, context: &'a Context, diff --git a/src/imap.rs b/src/imap.rs index 0e759ec0a..0125f5390 100644 --- a/src/imap.rs +++ b/src/imap.rs @@ -208,6 +208,7 @@ impl> Iterator for UidGrouper { // Tuple of folder, row IDs, and UID range as a string. type Item = (String, Vec, String); + #[expect(clippy::arithmetic_side_effects)] fn next(&mut self) -> Option { let (_, _, folder) = self.inner.peek().cloned()?; @@ -543,6 +544,7 @@ impl Imap { /// Fetches new messages. /// /// Returns true if at least one message was fetched. + #[expect(clippy::arithmetic_side_effects)] pub(crate) async fn fetch_new_messages( &mut self, context: &Context, @@ -583,6 +585,7 @@ impl Imap { } /// Returns number of messages processed and whether the function should be called again. + #[expect(clippy::arithmetic_side_effects)] async fn fetch_new_msg_batch( &mut self, context: &Context, @@ -1265,6 +1268,7 @@ impl Session { /// /// If the message is incorrect or there is a failure to write a message to the database, /// it is skipped and the error is logged. + #[expect(clippy::arithmetic_side_effects)] pub(crate) async fn fetch_many_msgs( &mut self, context: &Context, @@ -1429,6 +1433,7 @@ impl Session { /// We get [`/shared/comment`](https://www.rfc-editor.org/rfc/rfc5464#section-6.2.1) /// and [`/shared/admin`](https://www.rfc-editor.org/rfc/rfc5464#section-6.2.2) /// metadata. + #[expect(clippy::arithmetic_side_effects)] pub(crate) async fn update_metadata(&mut self, context: &Context) -> Result<()> { let mut lock = context.metadata.write().await; @@ -2364,6 +2369,7 @@ async fn should_ignore_folder( /// Builds a list of sequence/uid sets. The returned sets have each no more than around 1000 /// characters because according to /// command lines should not be much more than 1000 chars (servers should allow at least 8000 chars) +#[expect(clippy::arithmetic_side_effects)] fn build_sequence_sets(uids: &[u32]) -> Result, String)>> { // first, try to find consecutive ranges: let mut ranges: Vec = vec![]; diff --git a/src/imap/session.rs b/src/imap/session.rs index 583fc51ff..b810feef5 100644 --- a/src/imap/session.rs +++ b/src/imap/session.rs @@ -127,6 +127,7 @@ impl Session { /// Prefetch `n_uids` messages starting from `uid_next`. Returns a list of fetch results in the /// order of ascending delivery time to the server (INTERNALDATE). + #[expect(clippy::arithmetic_side_effects)] pub(crate) async fn prefetch( &mut self, uid_next: u32, diff --git a/src/imex.rs b/src/imex.rs index ab79fc105..d912acefd 100644 --- a/src/imex.rs +++ b/src/imex.rs @@ -293,6 +293,7 @@ impl AsyncRead for ProgressReader where R: AsyncRead, { + #[expect(clippy::arithmetic_side_effects)] fn poll_read( self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -438,6 +439,7 @@ fn get_next_backup_path( /// Exports the database to a separate file with the given passphrase. /// /// Set passphrase to empty string to export the database unencrypted. +#[expect(clippy::arithmetic_side_effects)] async fn export_backup(context: &Context, dir: &Path, passphrase: String) -> Result<()> { // get a fine backup file name (the name includes the date so that multiple backup instances are possible) let now = time(); @@ -511,6 +513,7 @@ impl AsyncWrite for ProgressWriter where W: AsyncWrite, { + #[expect(clippy::arithmetic_side_effects)] fn poll_write( self: Pin<&mut Self>, cx: &mut std::task::Context<'_>, @@ -590,6 +593,7 @@ async fn import_secret_key(context: &Context, path: &Path) -> Result<()> { /// containing secret keys are imported and the last successfully /// imported which does not contain "legacy" in its filename /// is set as the default. +#[expect(clippy::arithmetic_side_effects)] async fn import_self_keys(context: &Context, path: &Path) -> Result<()> { let attr = tokio::fs::metadata(path).await?; @@ -643,6 +647,7 @@ async fn import_self_keys(context: &Context, path: &Path) -> Result<()> { Ok(()) } +#[expect(clippy::arithmetic_side_effects)] async fn export_self_keys(context: &Context, dir: &Path) -> Result<()> { let mut export_errors = 0; diff --git a/src/imex/key_transfer.rs b/src/imex/key_transfer.rs index 017aec3e8..28019365d 100644 --- a/src/imex/key_transfer.rs +++ b/src/imex/key_transfer.rs @@ -129,6 +129,7 @@ pub async fn render_setup_file(context: &Context, passphrase: &str) -> Result String { let mut random_val: u16; let mut ret = String::new(); diff --git a/src/imex/transfer.rs b/src/imex/transfer.rs index ad3647e5d..3fb1d26af 100644 --- a/src/imex/transfer.rs +++ b/src/imex/transfer.rs @@ -166,6 +166,7 @@ impl BackupProvider { }) } + #[expect(clippy::arithmetic_side_effects)] async fn handle_connection( context: Context, conn: iroh::endpoint::Connecting, diff --git a/src/lib.rs b/src/lib.rs index 0494e96d3..3fae87a9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,7 @@ clippy::cloned_instead_of_copied, clippy::manual_is_variant_and )] +#![cfg_attr(not(test), warn(clippy::arithmetic_side_effects))] #![cfg_attr(not(test), forbid(clippy::indexing_slicing))] #![cfg_attr(not(test), forbid(clippy::string_slice))] #![allow( diff --git a/src/location.rs b/src/location.rs index fa80bac00..b6ea6cab3 100644 --- a/src/location.rs +++ b/src/location.rs @@ -263,6 +263,7 @@ impl Kml { } /// Enables location streaming in chat identified by `chat_id` for `seconds` seconds. +#[expect(clippy::arithmetic_side_effects)] pub async fn send_locations_to_chat( context: &Context, chat_id: ChatId, @@ -385,6 +386,7 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64 } /// Searches for locations in the given time range, optionally filtering by chat and contact IDs. +#[expect(clippy::arithmetic_side_effects)] pub async fn get_range( context: &Context, chat_id: Option, @@ -517,6 +519,7 @@ pub(crate) async fn delete_orphaned_poi_locations(context: &Context) -> Result<( } /// Returns `location.kml` contents. +#[expect(clippy::arithmetic_side_effects)] pub async fn get_kml(context: &Context, chat_id: ChatId) -> Result> { let mut last_added_location_id = 0; @@ -752,6 +755,7 @@ pub(crate) async fn location_loop(context: &Context, interrupt_receiver: Receive /// Returns number of seconds until the next time location streaming for some chat ends /// automatically. +#[expect(clippy::arithmetic_side_effects)] async fn maybe_send_locations(context: &Context) -> Result> { let mut next_event: Option = None; diff --git a/src/log/stream.rs b/src/log/stream.rs index dc91407b8..0ecaa7231 100644 --- a/src/log/stream.rs +++ b/src/log/stream.rs @@ -76,6 +76,7 @@ impl LoggingStream { } impl AsyncRead for LoggingStream { + #[expect(clippy::arithmetic_side_effects)] fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/src/message.rs b/src/message.rs index 65c591361..c34a43093 100644 --- a/src/message.rs +++ b/src/message.rs @@ -201,6 +201,7 @@ SELECT ?1, rfc724_mid, pre_rfc724_mid, timestamp, ?, ? FROM msgs WHERE id=?1 } /// Returns detailed message information in a multi-line text form. + #[expect(clippy::arithmetic_side_effects)] pub async fn get_info(self, context: &Context) -> Result { let msg = Message::load_from_db(context, self).await?; @@ -822,6 +823,7 @@ impl Message { /// /// Currently this includes `additional_text`, but this may change in future, when the UIs show /// the necessary info themselves. + #[expect(clippy::arithmetic_side_effects)] pub fn get_text(&self) -> String { self.text.clone() + &self.additional_text } @@ -964,6 +966,7 @@ impl Message { /// /// A message has a deviating timestamp when it is sent on /// another day as received/sorted by. + #[expect(clippy::arithmetic_side_effects)] pub fn has_deviating_timestamp(&self) -> bool { let cnv_to_local = gm2local_offset(); let sort_timestamp = self.get_sort_timestamp() + cnv_to_local; @@ -2119,6 +2122,7 @@ pub async fn get_request_msg_cnt(context: &Context) -> usize { /// Returns the number of messages that are older than the given number of seconds. /// This includes e-mails downloaded due to the `show_emails` option. /// Messages in the "saved messages" folder are not counted as they will not be deleted automatically. +#[expect(clippy::arithmetic_side_effects)] pub async fn estimate_deletion_cnt( context: &Context, from_server: bool, diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 71fdf0d7d..8fa2a5632 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -195,6 +195,7 @@ fn new_address_with_name(name: &str, address: String) -> Address<'static> { } impl MimeFactory { + #[expect(clippy::arithmetic_side_effects)] pub async fn from_msg(context: &Context, msg: Message) -> Result { let now = time(); let chat = Chat::load_from_db(context, msg.chat_id).await?; @@ -726,6 +727,7 @@ impl MimeFactory { /// Consumes a `MimeFactory` and renders it into a message which is then stored in /// `smtp`-table to be used by the SMTP loop + #[expect(clippy::arithmetic_side_effects)] pub async fn render(mut self, context: &Context) -> Result { let mut headers = Vec::<(&'static str, HeaderType<'static>)>::new(); @@ -2069,6 +2071,7 @@ impl MimeFactory { } /// Render an MDN + #[expect(clippy::arithmetic_side_effects)] fn render_mdn(&mut self) -> Result> { // RFC 6522, this also requires the `report-type` parameter which is equal // to the MIME subtype of the second body part of the multipart/report diff --git a/src/mimeparser.rs b/src/mimeparser.rs index a04b5621f..a880ce303 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -266,6 +266,7 @@ impl MimeMessage { /// /// This method has some side-effects, /// such as saving blobs and saving found public keys to the database. + #[expect(clippy::arithmetic_side_effects)] pub(crate) async fn from_bytes(context: &Context, body: &[u8]) -> Result { let mail = mailparse::parse_mail(body)?; @@ -728,6 +729,7 @@ impl MimeMessage { Ok(parser) } + #[expect(clippy::arithmetic_side_effects)] fn get_timestamp_sent( hdrs: &[mailparse::MailHeader<'_>], default: i64, @@ -1005,6 +1007,7 @@ impl MimeMessage { Ok(()) } + #[expect(clippy::arithmetic_side_effects)] fn avatar_action_from_header( &mut self, context: &Context, @@ -1506,6 +1509,7 @@ impl MimeMessage { } #[expect(clippy::too_many_arguments)] + #[expect(clippy::arithmetic_side_effects)] async fn do_add_single_file_part( &mut self, context: &Context, @@ -2057,6 +2061,7 @@ impl MimeMessage { /// Returns parsed `Chat-Group-Member-Timestamps` header contents. /// /// Returns `None` if there is no such header. + #[expect(clippy::arithmetic_side_effects)] pub fn chat_group_member_timestamps(&self) -> Option> { let now = time() + constants::TIMESTAMP_SENT_TOLERANCE; self.get_header(HeaderDef::ChatGroupMemberTimestamps) diff --git a/src/net/http.rs b/src/net/http.rs index 2954c7fc7..847209200 100644 --- a/src/net/http.rs +++ b/src/net/http.rs @@ -115,6 +115,7 @@ where } /// Converts the URL to expiration and stale timestamps. +#[expect(clippy::arithmetic_side_effects)] fn http_url_cache_timestamps(url: &str, mimetype: Option<&str>) -> (i64, i64) { let now = time(); @@ -173,6 +174,7 @@ async fn http_cache_put(context: &Context, url: &str, response: &Response) -> Re /// Retrieves the binary from HTTP cache. /// /// Also returns if the response is stale and should be revalidated in the background. +#[expect(clippy::arithmetic_side_effects)] async fn http_cache_get(context: &Context, url: &str) -> Result> { let now = time(); let Some((blob_name, mimetype, encoding, stale_timestamp)) = context diff --git a/src/net/proxy.rs b/src/net/proxy.rs index dfb019d5c..4a7f03fa9 100644 --- a/src/net/proxy.rs +++ b/src/net/proxy.rs @@ -174,6 +174,7 @@ pub enum ProxyConfig { } /// Constructs HTTP/1.1 `CONNECT` request for HTTP(S) proxy. +#[expect(clippy::arithmetic_side_effects)] fn http_connect_request(host: &str, port: u16, auth: Option<(&str, &str)>) -> String { // According to // clients MUST send `Host:` header in HTTP/1.1 requests, @@ -322,6 +323,7 @@ impl ProxyConfig { /// config into `proxy_url` if `proxy_url` is unset or empty. /// /// Unsets `socks5_host`, `socks5_port`, `socks5_user` and `socks5_password` in any case. + #[expect(clippy::arithmetic_side_effects)] async fn migrate_socks_config(sql: &Sql) -> Result<()> { if sql.get_raw_config("proxy_url").await?.is_none() { // Load legacy SOCKS5 settings. diff --git a/src/oauth2.rs b/src/oauth2.rs index c42fee18a..26b061fea 100644 --- a/src/oauth2.rs +++ b/src/oauth2.rs @@ -67,6 +67,7 @@ pub async fn get_oauth2_url( } } +#[expect(clippy::arithmetic_side_effects)] pub(crate) async fn get_oauth2_access_token( context: &Context, addr: &str, @@ -256,6 +257,7 @@ pub(crate) async fn get_oauth2_addr( } impl Oauth2 { + #[expect(clippy::arithmetic_side_effects)] fn from_address(addr: &str) -> Option { let addr_normalized = normalize_addr(addr); if let Some(domain) = addr_normalized diff --git a/src/peer_channels.rs b/src/peer_channels.rs index 6fa166685..618bf03d0 100644 --- a/src/peer_channels.rs +++ b/src/peer_channels.rs @@ -533,6 +533,7 @@ pub(crate) fn iroh_topic_from_str(topic: &str) -> Result { Ok(topic) } +#[expect(clippy::arithmetic_side_effects)] async fn subscribe_loop( context: &Context, mut stream: iroh_gossip::net::GossipReceiver, diff --git a/src/pgp.rs b/src/pgp.rs index c50bb96f0..9299788f8 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -170,6 +170,7 @@ pub enum SeipdVersion { /// Encrypts `plain` text using `public_keys_for_encryption` /// and signs it using `private_key_for_signing`. +#[expect(clippy::arithmetic_side_effects)] pub async fn pk_encrypt( plain: Vec, public_keys_for_encryption: Vec, diff --git a/src/plaintext.rs b/src/plaintext.rs index 349b02021..f10bd9d72 100644 --- a/src/plaintext.rs +++ b/src/plaintext.rs @@ -24,6 +24,7 @@ pub struct PlainText { impl PlainText { /// Convert plain text to HTML. /// The function handles quotes, links, fixed and floating text paragraphs. + #[expect(clippy::arithmetic_side_effects)] pub fn to_html(&self) -> String { static LINKIFY_MAIL_RE: LazyLock = LazyLock::new(|| regex::Regex::new(r"\b([\w.\-+]+@[\w.\-]+)\b").unwrap()); diff --git a/src/qr.rs b/src/qr.rs index a289d9aaf..4311db02e 100644 --- a/src/qr.rs +++ b/src/qr.rs @@ -680,6 +680,7 @@ fn decode_account(qr: &str) -> Result { } /// scheme: `https://t.me/socks?server=foo&port=123` or `https://t.me/socks?server=1.2.3.4&port=123` +#[expect(clippy::arithmetic_side_effects)] fn decode_tg_socks_proxy(_context: &Context, qr: &str) -> Result { let url = url::Url::parse(qr).context("Invalid t.me/socks url")?; @@ -1021,6 +1022,7 @@ async fn decode_smtp(context: &Context, qr: &str) -> Result { /// Scheme: `MATMSG:TO:addr...;SUB:subject...;BODY:body...;` /// /// There may or may not be linebreaks after the fields. +#[expect(clippy::arithmetic_side_effects)] async fn decode_matmsg(context: &Context, qr: &str) -> Result { // Does not work when the text `TO:` is used in subject/body _and_ TO: is not the first field. // we ignore this case. diff --git a/src/qr_code_generator.rs b/src/qr_code_generator.rs index a8456e04f..5d42d2549 100644 --- a/src/qr_code_generator.rs +++ b/src/qr_code_generator.rs @@ -15,6 +15,7 @@ use crate::securejoin; use crate::stock_str::{self, backup_transfer_qr}; /// Create a QR code from any input data. +#[expect(clippy::arithmetic_side_effects)] pub fn create_qr_svg(qrcode_content: &str) -> Result { let all_size = 512.0; let qr_code_size = 416.0; @@ -175,6 +176,7 @@ async fn self_info(context: &Context) -> Result<(Option>, String, String Ok((avatar, displayname, addr, color)) } +#[expect(clippy::arithmetic_side_effects)] fn inner_generate_secure_join_qr_code( qrcode_description: &str, qrcode_content: &str, diff --git a/src/reaction.rs b/src/reaction.rs index 4736974eb..0782c9ad4 100644 --- a/src/reaction.rs +++ b/src/reaction.rs @@ -122,6 +122,7 @@ impl Reactions { } /// Returns a map from emojis to their frequencies. + #[expect(clippy::arithmetic_side_effects)] pub fn emoji_frequencies(&self) -> BTreeMap { let mut emoji_frequencies: BTreeMap = BTreeMap::new(); for reaction in self.reactions.values() { diff --git a/src/receive_imf.rs b/src/receive_imf.rs index adbc786d1..12115215f 100644 --- a/src/receive_imf.rs +++ b/src/receive_imf.rs @@ -1185,6 +1185,7 @@ pub async fn from_field_to_contact_id( } } +#[expect(clippy::arithmetic_side_effects)] async fn decide_chat_assignment( context: &Context, mime_parser: &MimeMessage, @@ -2919,6 +2920,7 @@ async fn create_group( } } +#[expect(clippy::arithmetic_side_effects)] async fn update_chats_contacts_timestamps( context: &Context, chat_id: ChatId, @@ -3418,6 +3420,7 @@ async fn apply_chat_name_avatar_and_description_changes( } /// Returns a list of strings that should be shown as info messages, informing about group membership changes. +#[expect(clippy::arithmetic_side_effects)] async fn group_changes_msgs( context: &Context, added_ids: &HashSet, diff --git a/src/scheduler/connectivity.rs b/src/scheduler/connectivity.rs index 78cd8f6c9..6b761e5dd 100644 --- a/src/scheduler/connectivity.rs +++ b/src/scheduler/connectivity.rs @@ -303,6 +303,7 @@ impl Context { /// /// This comes as an HTML from the core so that we can easily improve it /// and the improvement instantly reaches all UIs. + #[expect(clippy::arithmetic_side_effects)] pub async fn get_connectivity_html(&self) -> Result { let mut ret = r#" diff --git a/src/securejoin.rs b/src/securejoin.rs index 8bf532d69..d826403c7 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -410,6 +410,7 @@ pub(crate) fn get_secure_join_step(mime_message: &MimeMessage) -> Option String { if let Some(text) = text.strip_prefix("--") { "-\u{200B}-".to_string() + &text.replace("\n--", "\n-\u{200B}-") @@ -21,6 +22,7 @@ pub fn escape_message_footer_marks(text: &str) -> String { /// Returns `(lines, footer_lines)` tuple; /// `footer_lines` is set to `Some` if the footer was actually removed from `lines` /// (which is equal to the input array otherwise). +#[expect(clippy::arithmetic_side_effects)] pub(crate) fn remove_message_footer<'a>( lines: &'a [&str], ) -> (&'a [&'a str], Option<&'a [&'a str]>) { @@ -175,6 +177,7 @@ fn skip_forward_header<'a>(lines: &'a [&str]) -> (&'a [&'a str], bool) { } } +#[expect(clippy::arithmetic_side_effects)] fn remove_bottom_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], Option) { let mut first_quoted_line = lines.len(); let mut last_quoted_line = None; @@ -217,6 +220,7 @@ fn remove_bottom_quote<'a>(lines: &'a [&str]) -> (&'a [&'a str], Option) } } +#[expect(clippy::arithmetic_side_effects)] fn remove_top_quote<'a>( lines: &'a [&str], is_chat_message: bool, @@ -262,6 +266,7 @@ fn remove_top_quote<'a>( } } +#[expect(clippy::arithmetic_side_effects)] fn render_message(lines: &[&str], is_cut_at_end: bool) -> String { let mut ret = String::new(); /* we write empty lines only in case and non-empty line follows */ diff --git a/src/sql.rs b/src/sql.rs index bd610e511..dbd4ffba3 100644 --- a/src/sql.rs +++ b/src/sql.rs @@ -798,6 +798,7 @@ fn new_connection(path: &Path, passphrase: &str) -> Result { // Tries to clear the freelist to free some space on the disk. // // This only works if auto_vacuum is enabled. +#[expect(clippy::arithmetic_side_effects)] async fn incremental_vacuum(context: &Context) -> Result<()> { context .sql @@ -956,6 +957,7 @@ pub fn row_get_vec(row: &Row, idx: usize) -> rusqlite::Result> { } /// Enumerates used files in the blobdir and removes unused ones. +#[expect(clippy::arithmetic_side_effects)] pub async fn remove_unused_files(context: &Context) -> Result<()> { let mut files_in_use = HashSet::new(); let mut unreferenced_count = 0; diff --git a/src/sql/migrations.rs b/src/sql/migrations.rs index 11a2fb48b..3c3ca0030 100644 --- a/src/sql/migrations.rs +++ b/src/sql/migrations.rs @@ -31,6 +31,7 @@ tokio::task_local! { static STOP_MIGRATIONS_AT: i32; } +#[expect(clippy::arithmetic_side_effects)] pub async fn run(context: &Context, sql: &Sql) -> Result { let mut exists_before_update = false; let mut dbversion_before_update = DBVERSION; @@ -2202,6 +2203,7 @@ fn migrate_key_contacts( } /// Rewrite `from_id`, `to_id` in >= 1000 messages starting from the newest ones, to key-contacts. +#[expect(clippy::arithmetic_side_effects)] pub(crate) async fn msgs_to_key_contacts(context: &Context) -> Result<()> { let sql = &context.sql; if sql diff --git a/src/storage_usage.rs b/src/storage_usage.rs index 72e7967d0..a2283bf56 100644 --- a/src/storage_usage.rs +++ b/src/storage_usage.rs @@ -50,6 +50,7 @@ impl std::fmt::Display for StorageUsage { } /// Get storage usage information for the Context's database +#[expect(clippy::arithmetic_side_effects)] pub async fn get_storage_usage(ctx: &Context) -> Result { let context_clone = ctx.clone(); let blobdir_size = @@ -121,6 +122,7 @@ pub async fn get_storage_usage(ctx: &Context) -> Result { } /// Returns storage usage of the blob directory +#[expect(clippy::arithmetic_side_effects)] pub fn get_blobdir_storage_usage(ctx: &Context) -> u64 { WalkDir::new(ctx.get_blobdir()) .max_depth(2) diff --git a/src/timesmearing.rs b/src/timesmearing.rs index 3576dd12d..8d028d3c1 100644 --- a/src/timesmearing.rs +++ b/src/timesmearing.rs @@ -37,6 +37,7 @@ impl SmearedTimestamp { /// Allocates `count` unique timestamps. /// /// Returns the first allocated timestamp. + #[expect(clippy::arithmetic_side_effects)] pub fn create_n(&self, now: i64, count: i64) -> i64 { let mut prev = self.smeared_timestamp.load(Ordering::Relaxed); loop { diff --git a/src/tools.rs b/src/tools.rs index 6ef3406df..30ec3ee08 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -47,6 +47,7 @@ use crate::stock_str; /// Shortens a string to a specified length and adds "[...]" to the /// end of the shortened string. +#[expect(clippy::arithmetic_side_effects)] pub(crate) fn truncate(buf: &str, approx_chars: usize) -> Cow<'_, str> { let count = buf.chars().count(); if count <= approx_chars + DC_ELLIPSIS.len() { @@ -77,6 +78,7 @@ pub(crate) fn truncate(buf: &str, approx_chars: usize) -> Cow<'_, str> { /// end of the shortened string. /// /// returns tuple with the String and a boolean whether is was truncated +#[expect(clippy::arithmetic_side_effects)] pub(crate) fn truncate_by_lines( buf: String, max_lines: usize, @@ -256,6 +258,7 @@ async fn maybe_warn_on_bad_time(context: &Context, now: i64, known_past_timestam false } +#[expect(clippy::arithmetic_side_effects)] async fn maybe_warn_on_outdated(context: &Context, now: i64, approx_compile_time: i64) { if now > approx_compile_time + DC_OUTDATED_WARNING_DAYS * 24 * 60 * 60 { let mut msg = Message::new_text(stock_str::update_reminder_msg_body(context).await); @@ -649,6 +652,7 @@ impl ToOption for Option { } } +#[expect(clippy::arithmetic_side_effects)] pub fn remove_subject_prefix(last_subject: &str) -> String { let subject_start = if last_subject.starts_with("Chat:") { 0 @@ -671,6 +675,7 @@ pub fn remove_subject_prefix(last_subject: &str) -> String { // Types and methods to create hop-info for message-info +#[expect(clippy::arithmetic_side_effects)] fn extract_address_from_receive_header<'a>(header: &'a str, start: &str) -> Option<&'a str> { let header_len = header.len(); header.find(start).and_then(|mut begin| { @@ -683,6 +688,7 @@ fn extract_address_from_receive_header<'a>(header: &'a str, start: &str) -> Opti }) } +#[expect(clippy::arithmetic_side_effects)] pub(crate) fn parse_receive_header(header: &str) -> String { let header = header.replace(&['\r', '\n'][..], ""); let mut hop_info = String::from("Hop: "); @@ -789,6 +795,7 @@ pub(crate) fn normalize_text(text: &str) -> Option { } /// Increments `*t` and checks that it equals to `expected` after that. +#[expect(clippy::arithmetic_side_effects)] pub(crate) fn inc_and_check( t: &mut T, expected: T, diff --git a/src/webxdc.rs b/src/webxdc.rs index ac15583bd..3eabeb3b7 100644 --- a/src/webxdc.rs +++ b/src/webxdc.rs @@ -782,6 +782,7 @@ impl Context { /// {"payload":"another update data"}]}` /// /// * `(first, last)`: range of status update serials to send. + #[expect(clippy::arithmetic_side_effects)] pub(crate) async fn render_webxdc_status_update_object( &self, instance_msg_id: MsgId, diff --git a/src/webxdc/maps_integration.rs b/src/webxdc/maps_integration.rs index 28e3dd9bc..d7fb3c88f 100644 --- a/src/webxdc/maps_integration.rs +++ b/src/webxdc/maps_integration.rs @@ -96,6 +96,7 @@ pub(crate) async fn intercept_send_update( Ok(()) } +#[expect(clippy::arithmetic_side_effects)] pub(crate) async fn intercept_get_updates( context: &Context, chat_id: Option,