about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Macleod <alex@macleod.io>2025-01-08 13:36:11 +0000
committerGitHub <noreply@github.com>2025-01-08 13:36:11 +0000
commit894e87cd5160a2198940a35dc105ce6e46d9763e (patch)
tree426b727ff3d2bab4e23cfe67d05d11603d8f7659
parentf5ca68f9db105f52aa1bf2e4eeb798fc86a10f6b (diff)
parent8461d3febdcfc0808ef0620c3d8f4636fc138a69 (diff)
downloadrust-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.rs25
-rw-r--r--tests/ui/manual_is_ascii_check.fixed5
-rw-r--r--tests/ui/manual_is_ascii_check.rs5
-rw-r--r--tests/ui/manual_is_ascii_check.stderr24
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