diff options
| author | Alex Macleod <alex@macleod.io> | 2025-01-08 13:36:11 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-01-08 13:36:11 +0000 |
| commit | 894e87cd5160a2198940a35dc105ce6e46d9763e (patch) | |
| tree | 426b727ff3d2bab4e23cfe67d05d11603d8f7659 | |
| parent | f5ca68f9db105f52aa1bf2e4eeb798fc86a10f6b (diff) | |
| parent | 8461d3febdcfc0808ef0620c3d8f4636fc138a69 (diff) | |
| download | rust-894e87cd5160a2198940a35dc105ce6e46d9763e.tar.gz rust-894e87cd5160a2198940a35dc105ce6e46d9763e.zip | |
Fix type suggestion for manual_is_ascii_check (#13916)
Fixes #13913 . changelog: [`manual_is_ascii_check`]: fix type suggestions for references Previously it only derived `char` and `u8` types, now it should always annotate the lambda parameter with the correct type (e.g. `&char`). I'm quite new to Rust and this is my first contact with clippy, so I'm open for suggetions :)
| -rw-r--r-- | clippy_lints/src/manual_is_ascii_check.rs | 25 | ||||
| -rw-r--r-- | tests/ui/manual_is_ascii_check.fixed | 5 | ||||
| -rw-r--r-- | tests/ui/manual_is_ascii_check.rs | 5 | ||||
| -rw-r--r-- | tests/ui/manual_is_ascii_check.stderr | 24 |
4 files changed, 43 insertions, 16 deletions
diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index 3f01f3cf30a..ba69b1aaf5b 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty; +use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -114,7 +114,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_)) { let arg = peel_ref_operators(cx, arg); - let ty_sugg = get_ty_sugg(cx, arg, start); + let ty_sugg = get_ty_sugg(cx, arg); let range = check_range(start, end); check_is_ascii(cx, expr.span, arg, &range, ty_sugg); } @@ -123,19 +123,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { extract_msrv_attr!(LateContext); } -fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> { - if let ExprKind::Lit(lit) = bound_expr.kind - && let local_hid = path_to_local(arg)? - && let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) +fn get_ty_sugg<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'_>) -> Option<(Span, Ty<'tcx>)> { + let local_hid = path_to_local(arg)?; + if let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid) // `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given && ty_span == span { - let ty_str = match lit.node { - Char(_) => "char", - Byte(_) => "u8", - _ => return None, - }; - return Some((*ty_span, ty_str)); + let arg_type = cx.typeck_results().expr_ty(arg); + return Some((*ty_span, arg_type)); } None } @@ -145,7 +140,7 @@ fn check_is_ascii( span: Span, recv: &Expr<'_>, range: &CharRange, - ty_sugg: Option<(Span, &'_ str)>, + ty_sugg: Option<(Span, Ty<'_>)>, ) { let sugg = match range { CharRange::UpperChar => "is_ascii_uppercase", @@ -159,8 +154,8 @@ fn check_is_ascii( let mut app = Applicability::MachineApplicable; let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par(); let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))]; - if let Some((ty_span, ty_str)) = ty_sugg { - suggestion.push((ty_span, format!("{recv}: {ty_str}"))); + if let Some((ty_span, ty)) = ty_sugg { + suggestion.push((ty_span, format!("{recv}: {ty}"))); } span_lint_and_then( diff --git a/tests/ui/manual_is_ascii_check.fixed b/tests/ui/manual_is_ascii_check.fixed index a72caa3a37e..179149f697d 100644 --- a/tests/ui/manual_is_ascii_check.fixed +++ b/tests/ui/manual_is_ascii_check.fixed @@ -82,3 +82,8 @@ fn generics() { take_while(|c: u8| c.is_ascii_uppercase()); take_while(|c: char| c.is_ascii_uppercase()); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); +} diff --git a/tests/ui/manual_is_ascii_check.rs b/tests/ui/manual_is_ascii_check.rs index bb6e2a317da..74f35ce94e8 100644 --- a/tests/ui/manual_is_ascii_check.rs +++ b/tests/ui/manual_is_ascii_check.rs @@ -82,3 +82,8 @@ fn generics() { take_while(|c| (b'A'..=b'Z').contains(&c)); take_while(|c: char| ('A'..='Z').contains(&c)); } + +fn adds_type_reference() { + let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); +} diff --git a/tests/ui/manual_is_ascii_check.stderr b/tests/ui/manual_is_ascii_check.stderr index a93ccace28a..92d93208006 100644 --- a/tests/ui/manual_is_ascii_check.stderr +++ b/tests/ui/manual_is_ascii_check.stderr @@ -173,5 +173,27 @@ error: manual check for common ascii range LL | take_while(|c: char| ('A'..='Z').contains(&c)); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()` -error: aborting due to 27 previous errors +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:87:63 + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: manual check for common ascii range + --> tests/ui/manual_is_ascii_check.rs:88:71 + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +help: try + | +LL | let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect(); + | ~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~ + +error: aborting due to 29 previous errors |
