diff options
| author | flip1995 <philipp.krones@embecosm.com> | 2021-11-04 12:52:36 +0000 |
|---|---|---|
| committer | flip1995 <philipp.krones@embecosm.com> | 2021-11-04 12:52:36 +0000 |
| commit | 4d6c2cce0adbb5deeeffe7eac6a27aedcfe03437 (patch) | |
| tree | 2a4aff84542ee92b4f55100c39a20f4e0a0867b4 /src/tools/clippy/clippy_utils | |
| parent | 4061c0407978a00c5c2518d898ad8406da28c106 (diff) | |
| parent | e18101137866b79045fee0ef996e696e68c920b4 (diff) | |
| download | rust-4d6c2cce0adbb5deeeffe7eac6a27aedcfe03437.tar.gz rust-4d6c2cce0adbb5deeeffe7eac6a27aedcfe03437.zip | |
Merge commit 'e18101137866b79045fee0ef996e696e68c920b4' into clippyup
Diffstat (limited to 'src/tools/clippy/clippy_utils')
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/camel_case.rs | 117 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/consts.rs | 59 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/diagnostics.rs | 2 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/higher.rs | 28 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/lib.rs | 31 | ||||
| -rw-r--r-- | src/tools/clippy/clippy_utils/src/str_utils.rs | 230 |
6 files changed, 325 insertions, 142 deletions
diff --git a/src/tools/clippy/clippy_utils/src/camel_case.rs b/src/tools/clippy/clippy_utils/src/camel_case.rs deleted file mode 100644 index a6636e39137..00000000000 --- a/src/tools/clippy/clippy_utils/src/camel_case.rs +++ /dev/null @@ -1,117 +0,0 @@ -/// Returns the index of the character after the first camel-case component of `s`. -#[must_use] -pub fn until(s: &str) -> usize { - let mut iter = s.char_indices(); - if let Some((_, first)) = iter.next() { - if !first.is_uppercase() { - return 0; - } - } else { - return 0; - } - let mut up = true; - let mut last_i = 0; - for (i, c) in iter { - if up { - if c.is_lowercase() { - up = false; - } else { - return last_i; - } - } else if c.is_uppercase() { - up = true; - last_i = i; - } else if !c.is_lowercase() { - return i; - } - } - if up { last_i } else { s.len() } -} - -/// Returns index of the last camel-case component of `s`. -#[must_use] -pub fn from(s: &str) -> usize { - let mut iter = s.char_indices().rev(); - if let Some((_, first)) = iter.next() { - if !first.is_lowercase() { - return s.len(); - } - } else { - return s.len(); - } - let mut down = true; - let mut last_i = s.len(); - for (i, c) in iter { - if down { - if c.is_uppercase() { - down = false; - last_i = i; - } else if !c.is_lowercase() { - return last_i; - } - } else if c.is_lowercase() { - down = true; - } else if c.is_uppercase() { - last_i = i; - } else { - return last_i; - } - } - last_i -} - -#[cfg(test)] -mod test { - use super::{from, until}; - - #[test] - fn from_full() { - assert_eq!(from("AbcDef"), 0); - assert_eq!(from("Abc"), 0); - assert_eq!(from("ABcd"), 0); - assert_eq!(from("ABcdEf"), 0); - assert_eq!(from("AabABcd"), 0); - } - - #[test] - fn from_partial() { - assert_eq!(from("abcDef"), 3); - assert_eq!(from("aDbc"), 1); - assert_eq!(from("aabABcd"), 3); - } - - #[test] - fn from_not() { - assert_eq!(from("AbcDef_"), 7); - assert_eq!(from("AbcDD"), 5); - } - - #[test] - fn from_caps() { - assert_eq!(from("ABCD"), 4); - } - - #[test] - fn until_full() { - assert_eq!(until("AbcDef"), 6); - assert_eq!(until("Abc"), 3); - } - - #[test] - fn until_not() { - assert_eq!(until("abcDef"), 0); - assert_eq!(until("aDbc"), 0); - } - - #[test] - fn until_partial() { - assert_eq!(until("AbcDef_"), 6); - assert_eq!(until("CallTypeC"), 8); - assert_eq!(until("AbcDD"), 3); - } - - #[test] - fn until_caps() { - assert_eq!(until("ABCD"), 0); - } -} diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 8bf31807d55..04347672e0f 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -155,6 +155,19 @@ impl Constant { _ => None, } } + + /// Returns the integer value or `None` if `self` or `val_type` is not integer type. + pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> { + if let Constant::Int(const_int) = *self { + match *val_type.kind() { + ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))), + ty::Uint(_) => Some(FullInt::U(const_int)), + _ => None, + } + } else { + None + } + } } /// Parses a `LitKind` to a `Constant`. @@ -202,6 +215,52 @@ pub fn constant_simple<'tcx>( constant(lcx, typeck_results, e).and_then(|(cst, res)| if res { None } else { Some(cst) }) } +pub fn constant_full_int( + lcx: &LateContext<'tcx>, + typeck_results: &ty::TypeckResults<'tcx>, + e: &Expr<'_>, +) -> Option<FullInt> { + constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e)) +} + +#[derive(Copy, Clone, Debug, Eq)] +pub enum FullInt { + S(i128), + U(u128), +} + +impl PartialEq for FullInt { + #[must_use] + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl PartialOrd for FullInt { + #[must_use] + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + Some(self.cmp(other)) + } +} + +impl Ord for FullInt { + #[must_use] + fn cmp(&self, other: &Self) -> Ordering { + use FullInt::{S, U}; + + fn cmp_s_u(s: i128, u: u128) -> Ordering { + u128::try_from(s).map_or(Ordering::Less, |x| x.cmp(&u)) + } + + match (*self, *other) { + (S(s), S(o)) => s.cmp(&o), + (U(s), U(o)) => s.cmp(&o), + (S(s), U(o)) => cmp_s_u(s, o), + (U(s), S(o)) => cmp_s_u(o, s).reverse(), + } + } +} + /// Creates a `ConstEvalLateContext` from the given `LateContext` and `TypeckResults`. pub fn constant_context<'a, 'tcx>( lcx: &'a LateContext<'tcx>, diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs index 9302e5c21fa..d47b002ad7a 100644 --- a/src/tools/clippy/clippy_utils/src/diagnostics.rs +++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs @@ -72,7 +72,7 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult /// 6 | let other_f64_nan = 0.0f64 / 0.0; /// | ^^^^^^^^^^^^ /// | -/// = help: Consider using `f64::NAN` if you would like a constant representing NaN +/// = help: consider using `f64::NAN` if you would like a constant representing NaN /// ``` pub fn span_lint_and_help<'a, T: LintContext>( cx: &'a T, diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 60c4cb361aa..b3a9a1de2ec 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -1,14 +1,14 @@ -//! This module contains functions that retrieves specifiec elements. +//! This module contains functions that retrieve specific elements. #![deny(clippy::missing_docs_in_private_items)] use crate::ty::is_type_diagnostic_item; -use crate::{is_expn_of, last_path_segment, match_def_path, paths}; +use crate::{is_expn_of, last_path_segment, match_def_path, path_to_local_id, paths}; use if_chain::if_chain; use rustc_ast::ast::{self, LitKind}; use rustc_hir as hir; use rustc_hir::{ - Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath, StmtKind, UnOp, + Arm, Block, BorrowKind, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, PatKind, QPath, StmtKind, UnOp, }; use rustc_lint::LateContext; use rustc_span::{sym, symbol, ExpnKind, Span, Symbol}; @@ -513,6 +513,8 @@ pub struct FormatArgsExpn<'tcx> { pub format_string_parts: &'tcx [Expr<'tcx>], /// Symbols corresponding to [`Self::format_string_parts`] pub format_string_symbols: Vec<Symbol>, + /// Match arm patterns, the `arg0`, etc. from the next field `args` + pub arg_names: &'tcx [Pat<'tcx>], /// Expressions like `ArgumentV1::new(arg0, Debug::fmt)` pub args: &'tcx [Expr<'tcx>], /// The final argument passed to `Arguments::new_v1_formatted`, if applicable @@ -557,6 +559,7 @@ impl FormatArgsExpn<'tcx> { _ => None, }) .collect(); + if let PatKind::Tuple(arg_names, None) = arm.pat.kind; if let ExprKind::Array(args) = arm.body.kind; then { Some(FormatArgsExpn { @@ -564,6 +567,7 @@ impl FormatArgsExpn<'tcx> { value_args, format_string_parts, format_string_symbols, + arg_names, args, fmt_expr, }) @@ -587,9 +591,15 @@ impl FormatArgsExpn<'tcx> { if let Some(position_field) = fields.iter().find(|f| f.ident.name == sym::position); if let ExprKind::Lit(lit) = &position_field.expr.kind; if let LitKind::Int(position, _) = lit.node; + if let Ok(i) = usize::try_from(position); + let arg = &self.args[i]; + if let ExprKind::Call(_, [arg_name, _]) = arg.kind; + if let Some(j) = self + .arg_names + .iter() + .position(|pat| path_to_local_id(arg_name, pat.hir_id)); then { - let i = usize::try_from(position).unwrap(); - Some(FormatArgsArg { value: self.value_args[i], arg: &self.args[i], fmt: Some(fmt) }) + Some(FormatArgsArg { value: self.value_args[j], arg, fmt: Some(fmt) }) } else { None } @@ -718,9 +728,7 @@ impl PanicExpn<'tcx> { /// Parses an expanded `panic!` invocation pub fn parse(expr: &'tcx Expr<'tcx>) -> Option<Self> { if_chain! { - if let ExprKind::Block(block, _) = expr.kind; - if let Some(init) = block.expr; - if let ExprKind::Call(_, [format_args]) = init.kind; + if let ExprKind::Call(_, [format_args]) = expr.kind; let expn_data = expr.span.ctxt().outer_expn_data(); if let Some(format_args) = FormatArgsExpn::parse(format_args); then { @@ -770,13 +778,13 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - } return Some(VecInitKind::WithExprCapacity(arg.hir_id)); } - } + }, ExprKind::Path(QPath::Resolved(_, path)) if match_def_path(cx, path.res.opt_def_id()?, &paths::DEFAULT_TRAIT_METHOD) && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Vec) => { return Some(VecInitKind::Default); - } + }, _ => (), } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 9bc380ca6ca..086fbc9d3dd 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -37,7 +37,6 @@ pub mod sym_helper; #[allow(clippy::module_name_repetitions)] pub mod ast_utils; pub mod attrs; -pub mod camel_case; pub mod comparisons; pub mod consts; pub mod diagnostics; @@ -50,6 +49,7 @@ pub mod paths; pub mod ptr; pub mod qualify_min_const_fn; pub mod source; +pub mod str_utils; pub mod sugg; pub mod ty; pub mod usage; @@ -712,7 +712,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { /// Checks if the top level expression can be moved into a closure as is. /// Currently checks for: /// * Break/Continue outside the given loop HIR ids. -/// * Yield/Return statments. +/// * Yield/Return statements. /// * Inline assembly. /// * Usages of a field of a local where the type of the local can be partially moved. /// @@ -844,10 +844,13 @@ pub fn capture_local_usage(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind let mut capture_expr_ty = e; for (parent_id, parent) in cx.tcx.hir().parent_iter(e.hir_id) { - if let [Adjustment { - kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)), - target, - }, ref adjust @ ..] = *cx + if let [ + Adjustment { + kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)), + target, + }, + ref adjust @ .., + ] = *cx .typeck_results() .adjustments() .get(child_id) @@ -1232,9 +1235,7 @@ pub fn get_enclosing_loop_or_closure(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Opti for (_, node) in tcx.hir().parent_iter(expr.hir_id) { match node { Node::Expr( - e - @ - Expr { + e @ Expr { kind: ExprKind::Loop(..) | ExprKind::Closure(..), .. }, @@ -1692,10 +1693,12 @@ pub fn is_async_fn(kind: FnKind<'_>) -> bool { pub fn get_async_fn_body(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'tcx Expr<'tcx>> { if let ExprKind::Call( _, - &[Expr { - kind: ExprKind::Closure(_, _, body, _, _), - .. - }], + &[ + Expr { + kind: ExprKind::Closure(_, _, body, _, _), + .. + }, + ], ) = body.value.kind { if let ExprKind::Block( @@ -2123,7 +2126,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool { vis.found } -/// Checks whether item either has `test` attribute appelied, or +/// Checks whether item either has `test` attribute applied, or /// is a module with `test` in its name. /// /// Note: If you use this function, please add a `#[test]` case in `tests/ui_test`. diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs new file mode 100644 index 00000000000..cba96e05a24 --- /dev/null +++ b/src/tools/clippy/clippy_utils/src/str_utils.rs @@ -0,0 +1,230 @@ +/// Dealing with sting indices can be hard, this struct ensures that both the +/// character and byte index are provided for correct indexing. +#[derive(Debug, Default, PartialEq, Eq)] +pub struct StrIndex { + pub char_index: usize, + pub byte_index: usize, +} + +impl StrIndex { + pub fn new(char_index: usize, byte_index: usize) -> Self { + Self { char_index, byte_index } + } +} + +/// Returns the index of the character after the first camel-case component of `s`. +/// +/// ``` +/// assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6)); +/// assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0)); +/// assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3)); +/// assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7)); +/// ``` +#[must_use] +pub fn camel_case_until(s: &str) -> StrIndex { + let mut iter = s.char_indices().enumerate(); + if let Some((_char_index, (_, first))) = iter.next() { + if !first.is_uppercase() { + return StrIndex::new(0, 0); + } + } else { + return StrIndex::new(0, 0); + } + let mut up = true; + let mut last_index = StrIndex::new(0, 0); + for (char_index, (byte_index, c)) in iter { + if up { + if c.is_lowercase() { + up = false; + } else { + return last_index; + } + } else if c.is_uppercase() { + up = true; + last_index.byte_index = byte_index; + last_index.char_index = char_index; + } else if !c.is_lowercase() { + return StrIndex::new(char_index, byte_index); + } + } + + if up { + last_index + } else { + StrIndex::new(s.chars().count(), s.len()) + } +} + +/// Returns index of the last camel-case component of `s`. +/// +/// ``` +/// assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0)); +/// assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3)); +/// assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4)); +/// assert_eq!(camel_case_start("abcd"), StrIndex::new(4, 4)); +/// assert_eq!(camel_case_start("\u{f6}\u{f6}cd"), StrIndex::new(4, 6)); +/// ``` +#[must_use] +pub fn camel_case_start(s: &str) -> StrIndex { + let char_count = s.chars().count(); + let range = 0..char_count; + let mut iter = range.rev().zip(s.char_indices().rev()); + if let Some((char_index, (_, first))) = iter.next() { + if !first.is_lowercase() { + return StrIndex::new(char_index, s.len()); + } + } else { + return StrIndex::new(char_count, s.len()); + } + let mut down = true; + let mut last_index = StrIndex::new(char_count, s.len()); + for (char_index, (byte_index, c)) in iter { + if down { + if c.is_uppercase() { + down = false; + last_index.byte_index = byte_index; + last_index.char_index = char_index; + } else if !c.is_lowercase() { + return last_index; + } + } else if c.is_lowercase() { + down = true; + } else if c.is_uppercase() { + last_index.byte_index = byte_index; + last_index.char_index = char_index; + } else { + return last_index; + } + } + last_index +} + +/// Dealing with sting comparison can be complicated, this struct ensures that both the +/// character and byte count are provided for correct indexing. +#[derive(Debug, Default, PartialEq, Eq)] +pub struct StrCount { + pub char_count: usize, + pub byte_count: usize, +} + +impl StrCount { + pub fn new(char_count: usize, byte_count: usize) -> Self { + Self { char_count, byte_count } + } +} + +/// Returns the number of chars that match from the start +/// +/// ``` +/// assert_eq!(count_match_start("hello_mouse", "hello_penguin"), StrCount::new(6, 6)); +/// assert_eq!(count_match_start("hello_clippy", "bye_bugs"), StrCount::new(0, 0)); +/// assert_eq!(count_match_start("hello_world", "hello_world"), StrCount::new(11, 11)); +/// assert_eq!(count_match_start("T\u{f6}ffT\u{f6}ff", "T\u{f6}ff"), StrCount::new(4, 5)); +/// ``` +#[must_use] +pub fn count_match_start(str1: &str, str2: &str) -> StrCount { + // (char_index, char1) + let char_count = str1.chars().count(); + let iter1 = (0..=char_count).zip(str1.chars()); + // (byte_index, char2) + let iter2 = str2.char_indices(); + + iter1 + .zip(iter2) + .take_while(|((_, c1), (_, c2))| c1 == c2) + .last() + .map_or_else(StrCount::default, |((char_index, _), (byte_index, character))| { + StrCount::new(char_index + 1, byte_index + character.len_utf8()) + }) +} + +/// Returns the number of chars and bytes that match from the end +/// +/// ``` +/// assert_eq!(count_match_end("hello_cat", "bye_cat"), StrCount::new(4, 4)); +/// assert_eq!(count_match_end("if_item_thing", "enum_value"), StrCount::new(0, 0)); +/// assert_eq!(count_match_end("Clippy", "Clippy"), StrCount::new(6, 6)); +/// assert_eq!(count_match_end("MyT\u{f6}ff", "YourT\u{f6}ff"), StrCount::new(4, 5)); +/// ``` +#[must_use] +pub fn count_match_end(str1: &str, str2: &str) -> StrCount { + let char_count = str1.chars().count(); + if char_count == 0 { + return StrCount::default(); + } + + // (char_index, char1) + let iter1 = (0..char_count).rev().zip(str1.chars().rev()); + // (byte_index, char2) + let byte_count = str2.len(); + let iter2 = str2.char_indices().rev(); + + iter1 + .zip(iter2) + .take_while(|((_, c1), (_, c2))| c1 == c2) + .last() + .map_or_else(StrCount::default, |((char_index, _), (byte_index, _))| { + StrCount::new(char_count - char_index, byte_count - byte_index) + }) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn camel_case_start_full() { + assert_eq!(camel_case_start("AbcDef"), StrIndex::new(0, 0)); + assert_eq!(camel_case_start("Abc"), StrIndex::new(0, 0)); + assert_eq!(camel_case_start("ABcd"), StrIndex::new(0, 0)); + assert_eq!(camel_case_start("ABcdEf"), StrIndex::new(0, 0)); + assert_eq!(camel_case_start("AabABcd"), StrIndex::new(0, 0)); + } + + #[test] + fn camel_case_start_partial() { + assert_eq!(camel_case_start("abcDef"), StrIndex::new(3, 3)); + assert_eq!(camel_case_start("aDbc"), StrIndex::new(1, 1)); + assert_eq!(camel_case_start("aabABcd"), StrIndex::new(3, 3)); + assert_eq!(camel_case_start("\u{f6}\u{f6}AabABcd"), StrIndex::new(2, 4)); + } + + #[test] + fn camel_case_start_not() { + assert_eq!(camel_case_start("AbcDef_"), StrIndex::new(7, 7)); + assert_eq!(camel_case_start("AbcDD"), StrIndex::new(5, 5)); + assert_eq!(camel_case_start("all_small"), StrIndex::new(9, 9)); + assert_eq!(camel_case_start("\u{f6}_all_small"), StrIndex::new(11, 12)); + } + + #[test] + fn camel_case_start_caps() { + assert_eq!(camel_case_start("ABCD"), StrIndex::new(4, 4)); + } + + #[test] + fn camel_case_until_full() { + assert_eq!(camel_case_until("AbcDef"), StrIndex::new(6, 6)); + assert_eq!(camel_case_until("Abc"), StrIndex::new(3, 3)); + assert_eq!(camel_case_until("Abc\u{f6}\u{f6}\u{f6}"), StrIndex::new(6, 9)); + } + + #[test] + fn camel_case_until_not() { + assert_eq!(camel_case_until("abcDef"), StrIndex::new(0, 0)); + assert_eq!(camel_case_until("aDbc"), StrIndex::new(0, 0)); + } + + #[test] + fn camel_case_until_partial() { + assert_eq!(camel_case_until("AbcDef_"), StrIndex::new(6, 6)); + assert_eq!(camel_case_until("CallTypeC"), StrIndex::new(8, 8)); + assert_eq!(camel_case_until("AbcDD"), StrIndex::new(3, 3)); + assert_eq!(camel_case_until("Abc\u{f6}\u{f6}DD"), StrIndex::new(5, 7)); + } + + #[test] + fn until_caps() { + assert_eq!(camel_case_until("ABCD"), StrIndex::new(0, 0)); + } +} |
