diff --git a/Cargo.toml b/Cargo.toml index 4916b466b..58cc0f287 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,6 +185,7 @@ rustdoc.private_intra_doc_links = "allow" clippy.len_without_is_empty = "allow" # we're not a library crate clippy.let_and_return = "allow" clippy.manual_range_contains = "allow" +clippy.map_unwrap_or = "warn" clippy.needless_lifetimes = "allow" clippy.needless_return = "allow" clippy.new_without_default = "allow" diff --git a/crates/fallback/src/lib.rs b/crates/fallback/src/lib.rs index 3dbcc3323..e55de22a6 100644 --- a/crates/fallback/src/lib.rs +++ b/crates/fallback/src/lib.rs @@ -146,12 +146,15 @@ fn next(&mut self) -> Option { impl<'a, Canonicalize: Fn(char) -> char> ToLowerBuffer<'a, Canonicalize> { pub fn new(mut chars: std::iter::Map, Canonicalize>) -> Self { Self { - current: chars.next().map(|c| c.to_lowercase()).unwrap_or_else(|| { - let mut empty = 'a'.to_lowercase(); - let _ = empty.next(); - debug_assert!(empty.next().is_none()); - empty - }), + current: chars.next().map_or_else( + || { + let mut empty = 'a'.to_lowercase(); + let _ = empty.next(); + debug_assert!(empty.next().is_none()); + empty + }, + |c| c.to_lowercase(), + ), chars, } } diff --git a/src/builtins/abbr.rs b/src/builtins/abbr.rs index 7f1e3ab1b..5a515b8d1 100644 --- a/src/builtins/abbr.rs +++ b/src/builtins/abbr.rs @@ -90,8 +90,7 @@ fn validate(&mut self, streams: &mut IoStreams) -> bool { if self .set_cursor_marker .as_ref() - .map(|m| m.is_empty()) - .unwrap_or(false) + .is_some_and(|m| m.is_empty()) { streams.err.append(wgettext_fmt!( "%s: --set-cursor argument cannot be empty\n", diff --git a/src/builtins/set.rs b/src/builtins/set.rs index 6c86cad59..a7675c0d9 100644 --- a/src/builtins/set.rs +++ b/src/builtins/set.rs @@ -447,7 +447,7 @@ fn split_var_and_indexes_internal<'a>( ) -> Result, EnvArrayParseError> { let mut res = SplitVar::default(); let open_bracket = arg.find_char('['); - res.varname = open_bracket.map(|b| &arg[..b]).unwrap_or(arg); + res.varname = open_bracket.map_or(arg, |b| &arg[..b]); res.var = vars.getf(res.varname, mode); let Some(open_bracket) = open_bracket else { // Common case of no bracket diff --git a/src/common.rs b/src/common.rs index dceb9f552..5d71ae005 100644 --- a/src/common.rs +++ b/src/common.rs @@ -607,7 +607,7 @@ enum Mode { // HACK: To reduce accidental use of brace expansion, treat a brace // with zero or one items as literal input. See #4632. (The hack is // doing it here and like this.) - if vars_or_seps.last().map(|i| *i < brace).unwrap_or(true) { + if vars_or_seps.last().is_none_or(|i| *i < brace) { result.as_char_slice_mut()[brace] = '{'; // We also need to turn all spaces back. for i in brace + 1..result.len() { diff --git a/src/editable_line.rs b/src/editable_line.rs index 9050d976c..4118ba811 100644 --- a/src/editable_line.rs +++ b/src/editable_line.rs @@ -346,14 +346,12 @@ pub fn range_of_line_at_cursor(buffer: &wstr, cursor: usize) -> Range { .as_char_slice() .iter() .rposition(|&c| c == '\n') - .map(|newline| newline + 1) - .unwrap_or(0); + .map_or(0, |newline| newline + 1); let mut end = buffer[cursor..] .as_char_slice() .iter() .position(|&c| c == '\n') - .map(|pos| cursor + pos) - .unwrap_or(buffer.len()); + .map_or(buffer.len(), |pos| cursor + pos); // Remove any trailing newline if end != start && buffer.char_at(end - 1) == '\n' { end -= 1; diff --git a/src/env_dispatch.rs b/src/env_dispatch.rs index e5d0739ed..9f0a3b09e 100644 --- a/src/env_dispatch.rs +++ b/src/env_dispatch.rs @@ -173,8 +173,7 @@ pub fn guess_emoji_width(vars: &EnvStack) { let term_program = vars .get(L!("TERM_PROGRAM")) - .map(|v| v.as_string()) - .unwrap_or_else(WString::new); + .map_or_else(WString::new, |v| v.as_string()); // TODO(term-workaround) if xtversion().unwrap_or(L!("")).starts_with(L!("iTerm2 ")) { @@ -257,8 +256,7 @@ fn handle_fish_cursor_selection_mode_change(vars: &EnvStack) { .get(L!("fish_cursor_selection_mode")) .as_ref() .map(|v| v.as_string()) - .map(|v| v == "inclusive") - .unwrap_or(false); + .is_some_and(|v| v == "inclusive"); let mode = if inclusive { CursorSelectionMode::Inclusive } else { @@ -275,8 +273,7 @@ fn handle_fish_cursor_end_mode_change(vars: &EnvStack) { .get(L!("fish_cursor_end_mode")) .as_ref() .map(|v| v.as_string()) - .map(|v| v == "inclusive") - .unwrap_or(false); + .is_some_and(|v| v == "inclusive"); let mode = if inclusive { CursorEndMode::Inclusive } else { diff --git a/src/exec.rs b/src/exec.rs index 7b540baed..ed07c6b8b 100644 --- a/src/exec.rs +++ b/src/exec.rs @@ -1445,7 +1445,7 @@ fn populate_subshell_output(lst: &mut Vec, buffer: &SeparatedBuffer, sp let stop = data[cursor..].iter().position(|c| *c == b'\n'); let hit_separator = stop.is_some(); // If it's not found, just use the end. - let stop = stop.map(|rel| cursor + rel).unwrap_or(data.len()); + let stop = stop.map_or(data.len(), |rel| cursor + rel); // Stop now points at the first character we do not want to copy. lst.push(bytes2wcstring(&data[cursor..stop])); diff --git a/src/fd_monitor.rs b/src/fd_monitor.rs index e1378ad72..e4c95408a 100644 --- a/src/fd_monitor.rs +++ b/src/fd_monitor.rs @@ -399,8 +399,7 @@ fn run(self) { // // Note that WSLv1 doesn't throw EBADF if the fd is closed is mid-select. drop(data); - let ret = - fds.check_readable(timeout.map(Timeout::Duration).unwrap_or(Timeout::Forever)); + let ret = fds.check_readable(timeout.map_or(Timeout::Forever, Timeout::Duration)); // Cygwin reports ret < 0 && errno == 0 as success. let err = errno().0; if ret < 0 && !matches!(err, libc::EINTR | libc::EBADF) && !(cfg!(cygwin) && err == 0) { diff --git a/src/function.rs b/src/function.rs index fbe1693e6..371033135 100644 --- a/src/function.rs +++ b/src/function.rs @@ -404,9 +404,7 @@ pub fn definition_lineno(&self) -> i32 { /// If this function is a copy, return the original 1-based line number. Otherwise, return 0. pub fn copy_definition_lineno(&self) -> u32 { - self.copy_definition_lineno - .map(|val| val.get()) - .unwrap_or(0) + self.copy_definition_lineno.map_or(0, |val| val.get()) } /// Return a definition of the function, annotated with properties like event handlers and wrap diff --git a/src/highlight/highlight.rs b/src/highlight/highlight.rs index b8431b726..9330da535 100644 --- a/src/highlight/highlight.rs +++ b/src/highlight/highlight.rs @@ -344,9 +344,7 @@ pub fn autosuggest_validate_from_history( // Check the directory target, respecting CDPATH. // Permit the autosuggestion if the path is valid and not our directory. let path = path_get_cdpath(&cd_dir, working_directory, ctx.vars()); - return path - .map(|p| !paths_are_same_file(working_directory, &p)) - .unwrap_or(false); + return path.is_some_and(|p| !paths_are_same_file(working_directory, &p)); } } diff --git a/src/job_group.rs b/src/job_group.rs index a2b940949..8727a1ef3 100644 --- a/src/job_group.rs +++ b/src/job_group.rs @@ -25,7 +25,7 @@ fn deref(&self) -> &Self::Target { impl MaybeJobId { pub fn as_num(&self) -> i64 { - self.0.map(|j| i64::from(u32::from(j.0))).unwrap_or(-1) + self.0.map_or(-1, |j| i64::from(u32::from(j.0))) } } @@ -180,8 +180,7 @@ fn acquire() -> JobId { // in CONSUMED_JOB_IDS are sorted in ascending order, so we just have to check the last. let job_id = consumed_job_ids .last() - .map(JobId::next) - .unwrap_or(JobId(1.try_into().unwrap())); + .map_or(JobId(1.try_into().unwrap()), JobId::next); consumed_job_ids.push(job_id); job_id } diff --git a/src/parse_constants.rs b/src/parse_constants.rs index 9e38e3cc0..1c4d32bbf 100644 --- a/src/parse_constants.rs +++ b/src/parse_constants.rs @@ -338,8 +338,7 @@ pub fn describe_with_prefix( let line_end = src.as_char_slice()[last_char_in_range..] .iter() .position(|c| *c == '\n') - .map(|pos| pos + last_char_in_range) - .unwrap_or(src.len()); + .map_or(src.len(), |pos| pos + last_char_in_range); // We can only report squiggles on one line if start + len > line_end { len = line_end - start; diff --git a/src/parser.rs b/src/parser.rs index 378563603..7310dd21a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -815,7 +815,7 @@ pub fn get_lineno(&self) -> Option { /// Returns the current line number, indexed from 1, or zero if not sourced. pub fn get_lineno_for_display(&self) -> u32 { - self.get_lineno().map(|val| val.get()).unwrap_or(0) + self.get_lineno().map_or(0, |val| val.get()) } /// Return whether we are currently evaluating a "block" such as an if statement. @@ -1383,7 +1383,7 @@ fn append_block_description_to_stack_trace(parser: &Parser, b: &Block, trace: &m if let Some(file) = b.src_filename.as_ref() { trace.push_utfstr(&sprintf!( "\tcalled on line %d of file %s\n", - b.src_lineno.map(|n| n.get()).unwrap_or(0), + b.src_lineno.map_or(0, |n| n.get()), user_presentable_path(file, parser.vars()) )); } else if parser.libdata().within_fish_init { diff --git a/src/proc.rs b/src/proc.rs index d7dadcd7a..7a717741f 100644 --- a/src/proc.rs +++ b/src/proc.rs @@ -1452,7 +1452,7 @@ fn summary_command(j: &Job, p: Option<&Process>) -> WString { if j.external_procs().count() > 1 { // I don't think it's safe to blindly unwrap here because even though we exited with // a signal, the job could have contained a fish function? - let pid = p.pid().map(|p| p.to_string()).unwrap_or("-".to_string()); + let pid = p.pid().map_or("-".to_string(), |p| p.to_string()); buffer += &sprintf!(" %s", pid)[..]; buffer.push(' '); diff --git a/src/reader/reader.rs b/src/reader/reader.rs index 84e4c0fe4..c70746e3b 100644 --- a/src/reader/reader.rs +++ b/src/reader/reader.rs @@ -1087,8 +1087,7 @@ pub fn reader_change_cursor_end_mode(end_mode: CursorEndMode) { fn check_bool_var(vars: &dyn Environment, name: &wstr, default: bool) -> bool { vars.get(name) .map(|v| v.as_string()) - .map(|v| v != L!("0")) - .unwrap_or(default) + .map_or(default, |v| v != L!("0")) } /// Enable or disable autosuggestions based on the associated variable. @@ -4986,7 +4985,7 @@ fn get_autosuggestion_performer( parse_util_process_extent(&command_line, cursor_pos, Some(&mut tokens)); range_of_line_at_cursor( &command_line, - tokens.first().map(|tok| tok.offset()).unwrap_or(cursor_pos), + tokens.first().map_or(cursor_pos, |tok| tok.offset()), ) == range }; if !cursor_line_has_process_start { @@ -6282,14 +6281,12 @@ fn replace_line_at_cursor( .as_char_slice() .iter() .rposition(|&c| c == '\n') - .map(|newline| newline + 1) - .unwrap_or(0); + .map_or(0, |newline| newline + 1); let end = text[cursor..] .as_char_slice() .iter() .position(|&c| c == '\n') - .map(|pos| cursor + pos) - .unwrap_or(text.len()); + .map_or(text.len(), |pos| cursor + pos); *inout_cursor_pos = start + replacement.len(); text[..start].to_owned() + replacement + &text[end..] } diff --git a/src/screen.rs b/src/screen.rs index dfcb725f4..4faf1da69 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -433,15 +433,13 @@ struct ScrolledCursor { if is_final_rendering { usize::MAX } else { - scrolled_cursor - .map(|sc| { - if sc.scroll_amount != 0 { - sc.cursor.y - } else { - screen_height - 1 - } - }) - .unwrap_or(usize::MAX) + scrolled_cursor.map_or(usize::MAX, |sc| { + if sc.scroll_amount != 0 { + sc.cursor.y + } else { + screen_height - 1 + } + }) }, effective_commandline.as_char_slice()[i], colors[i], @@ -655,8 +653,7 @@ pub fn offset_in_cmdline_given_cursor( } else { None }) - .map(|char| char.offset_in_cmdline) - .unwrap_or(CharOffset::Pointer(0)); + .map_or(CharOffset::Pointer(0), |char| char.offset_in_cmdline); match offset { CharOffset::Cmd(value) if x >= line.len() => CharOffset::Cmd(value + 1), CharOffset::Pager(_) if x >= line.len() => CharOffset::None, diff --git a/src/terminal.rs b/src/terminal.rs index cfc2bcca8..002796ae8 100644 --- a/src/terminal.rs +++ b/src/terminal.rs @@ -929,8 +929,7 @@ fn get_num_cap(db: &terminfo::Database, code: &str) -> Option { /// Panics if the given code string does not contain exactly two bytes. fn get_flag_cap(db: &terminfo::Database, code: &str) -> bool { db.raw(code) - .map(|cap| matches!(cap, terminfo::Value::True)) - .unwrap_or(false) + .is_some_and(|cap| matches!(cap, terminfo::Value::True)) } /// Covers over tparm() with one parameter. diff --git a/src/tinyexpr.rs b/src/tinyexpr.rs index befb02240..e8f76f764 100644 --- a/src/tinyexpr.rs +++ b/src/tinyexpr.rs @@ -562,7 +562,7 @@ fn base(&mut self) -> f64 { // a closing parenthesis should be more obvious. // // Vararg functions need at least one argument. - let err = if f.arity().map(|arity| i < arity).unwrap_or(i == 0) { + let err = if f.arity().map_or(i == 0, |arity| i < arity) { ErrorKind::TooFewArgs } else { ErrorKind::TooManyArgs diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 45c97af7a..4f28c7f44 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -900,8 +900,7 @@ fn tok_is_string_character(c: char, next: Option) -> bool { '&' => { if feature_test(FeatureFlag::AmpersandNoBgInToken) { // Unlike in other shells, '&' is not special if followed by a string character. - next.map(|nc| tok_is_string_character(nc, None)) - .unwrap_or(false) + next.is_some_and(|nc| tok_is_string_character(nc, None)) } else { false } diff --git a/src/wcstringutil.rs b/src/wcstringutil.rs index 2cb2cb209..0dcb6a65b 100644 --- a/src/wcstringutil.rs +++ b/src/wcstringutil.rs @@ -366,8 +366,7 @@ pub fn split_string_tok<'val>( let next_sep = val[pos..] .iter() .position(|c| seps.contains(*c)) - .map(|p| pos + p) - .unwrap_or(end); + .map_or(end, |p| pos + p); out.push(wstr::from_char_slice(&val[pos..next_sep])); // Note we skip exactly one sep here. This is because on the last iteration we retain all // but the first leading separators. This is historical. @@ -529,8 +528,7 @@ fn next(&mut self) -> Option { let newline_or_end = self.coll[self.current..] .iter() .position(|b| *b == b'\n') - .map(|pos| self.current + pos) - .unwrap_or(self.coll.len()); + .map_or(self.coll.len(), |pos| self.current + pos); let result = &self.coll[self.current..newline_or_end]; self.current = newline_or_end; diff --git a/src/wildcard.rs b/src/wildcard.rs index e207661ac..0e1b90ebf 100644 --- a/src/wildcard.rs +++ b/src/wildcard.rs @@ -381,10 +381,7 @@ fn wildcard_test_flags_then_complete( } // regular file *excludes* broken links - we have no use for them as commands. - let is_regular_file = entry - .check_type() - .map(|x| x == DirEntryType::Reg) - .unwrap_or(false); + let is_regular_file = entry.check_type().is_some_and(|x| x == DirEntryType::Reg); let is_executable = Lazy::new(|| is_regular_file && waccess(filepath, X_OK) == 0); if executables_only && !*is_executable { return false; diff --git a/src/wutil/wcstod.rs b/src/wutil/wcstod.rs index e8be49254..beb0c6a86 100644 --- a/src/wutil/wcstod.rs +++ b/src/wutil/wcstod.rs @@ -16,11 +16,7 @@ fn parse_dec_float(chars: I, decimal_sep: char, consumed: &mut usize) -> Opti if let Some(sign) = chars.next_if(|c| ['-', '+'].contains(c)) { s.push(sign); } - if chars - .peek() - .map(|c| c.is_ascii_alphabetic()) - .unwrap_or(false) - { + if chars.peek().is_some_and(|c| c.is_ascii_alphabetic()) { return parse_inf_nan(chars, s.as_bytes().first().copied(), consumed); }