mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-30 16:39:26 +00:00
skip warning when generic evolved;
suggest explicit type when its inferred in closure
This commit is contained in:
parent
c6bf9548d5
commit
cbdc36aa09
4 changed files with 152 additions and 32 deletions
|
@ -1,13 +1,14 @@
|
||||||
use clippy_config::msrvs::{self, Msrv};
|
use clippy_config::msrvs::{self, Msrv};
|
||||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::macros::matching_root_macro_call;
|
use clippy_utils::macros::matching_root_macro_call;
|
||||||
use clippy_utils::sugg::Sugg;
|
use clippy_utils::sugg::Sugg;
|
||||||
use clippy_utils::{higher, in_constant};
|
use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators};
|
||||||
use rustc_ast::ast::RangeLimits;
|
use rustc_ast::ast::RangeLimits;
|
||||||
use rustc_ast::LitKind::{Byte, Char};
|
use rustc_ast::LitKind::{Byte, Char};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{BorrowKind, Expr, ExprKind, PatKind, RangeEnd};
|
use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
|
use rustc_middle::ty;
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
||||||
if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
|
if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
|
||||||
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
|
if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
|
||||||
let range = check_pat(&arm.pat.kind);
|
let range = check_pat(&arm.pat.kind);
|
||||||
check_is_ascii(cx, macro_call.span, recv, &range);
|
check_is_ascii(cx, macro_call.span, recv, &range, None);
|
||||||
}
|
}
|
||||||
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
|
} else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
|
||||||
&& path.ident.name == sym!(contains)
|
&& path.ident.name == sym!(contains)
|
||||||
|
@ -108,42 +109,67 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
|
||||||
end: Some(end),
|
end: Some(end),
|
||||||
limits: RangeLimits::Closed,
|
limits: RangeLimits::Closed,
|
||||||
}) = higher::Range::hir(receiver)
|
}) = higher::Range::hir(receiver)
|
||||||
|
&& !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 range = check_range(start, end);
|
let range = check_range(start, end);
|
||||||
if let ExprKind::AddrOf(BorrowKind::Ref, _, e) = arg.kind {
|
check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
|
||||||
check_is_ascii(cx, expr.span, e, &range);
|
|
||||||
} else {
|
|
||||||
check_is_ascii(cx, expr.span, arg, &range);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_msrv_attr!(LateContext);
|
extract_msrv_attr!(LateContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_is_ascii(cx: &LateContext<'_>, span: Span, recv: &Expr<'_>, range: &CharRange) {
|
fn get_ty_sugg(cx: &LateContext<'_>, arg: &Expr<'_>, bound_expr: &Expr<'_>) -> Option<(Span, &'static str)> {
|
||||||
if let Some(sugg) = match range {
|
if let ExprKind::Lit(lit) = bound_expr.kind
|
||||||
CharRange::UpperChar => Some("is_ascii_uppercase"),
|
&& let local_hid = path_to_local(arg)?
|
||||||
CharRange::LowerChar => Some("is_ascii_lowercase"),
|
&& let Node::Param(Param { ty_span, span, .. }) = cx.tcx.parent_hir_node(local_hid)
|
||||||
CharRange::FullChar => Some("is_ascii_alphabetic"),
|
// `ty_span` and `span` are the same for inferred type, thus a type suggestion must be given
|
||||||
CharRange::Digit => Some("is_ascii_digit"),
|
&& ty_span == span
|
||||||
CharRange::HexDigit => Some("is_ascii_hexdigit"),
|
{
|
||||||
CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => None,
|
let ty_str = match lit.node {
|
||||||
} {
|
Char(_) => "char",
|
||||||
let default_snip = "..";
|
Byte(_) => "u8",
|
||||||
let mut app = Applicability::MachineApplicable;
|
_ => return None,
|
||||||
let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_par();
|
};
|
||||||
|
return Some((*ty_span, ty_str));
|
||||||
span_lint_and_sugg(
|
|
||||||
cx,
|
|
||||||
MANUAL_IS_ASCII_CHECK,
|
|
||||||
span,
|
|
||||||
"manual check for common ascii range",
|
|
||||||
"try",
|
|
||||||
format!("{recv}.{sugg}()"),
|
|
||||||
app,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_is_ascii(
|
||||||
|
cx: &LateContext<'_>,
|
||||||
|
span: Span,
|
||||||
|
recv: &Expr<'_>,
|
||||||
|
range: &CharRange,
|
||||||
|
ty_sugg: Option<(Span, &'_ str)>,
|
||||||
|
) {
|
||||||
|
let sugg = match range {
|
||||||
|
CharRange::UpperChar => "is_ascii_uppercase",
|
||||||
|
CharRange::LowerChar => "is_ascii_lowercase",
|
||||||
|
CharRange::FullChar => "is_ascii_alphabetic",
|
||||||
|
CharRange::Digit => "is_ascii_digit",
|
||||||
|
CharRange::HexDigit => "is_ascii_hexdigit",
|
||||||
|
CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return,
|
||||||
|
};
|
||||||
|
let default_snip = "..";
|
||||||
|
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}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
span_lint_and_then(
|
||||||
|
cx,
|
||||||
|
MANUAL_IS_ASCII_CHECK,
|
||||||
|
span,
|
||||||
|
"manual check for common ascii range",
|
||||||
|
|diag| {
|
||||||
|
diag.multipart_suggestion("try", suggestion, app);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
|
fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
|
||||||
|
|
|
@ -55,3 +55,30 @@ fn msrv_1_47() {
|
||||||
const FOO: bool = 'x'.is_ascii_digit();
|
const FOO: bool = 'x'.is_ascii_digit();
|
||||||
const BAR: bool = 'x'.is_ascii_hexdigit();
|
const BAR: bool = 'x'.is_ascii_hexdigit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::deref_addrof, clippy::needless_borrow)]
|
||||||
|
fn with_refs() {
|
||||||
|
let cool_letter = &&'g';
|
||||||
|
cool_letter.is_ascii_digit();
|
||||||
|
cool_letter.is_ascii_lowercase();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generics() {
|
||||||
|
fn a<U>(u: &U) -> bool
|
||||||
|
where
|
||||||
|
char: PartialOrd<U>,
|
||||||
|
U: PartialOrd<char> + ?Sized,
|
||||||
|
{
|
||||||
|
('A'..='Z').contains(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_while<Item, F>(cond: F)
|
||||||
|
where
|
||||||
|
Item: Sized,
|
||||||
|
F: Fn(Item) -> bool,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
take_while(|c: char| c.is_ascii_uppercase());
|
||||||
|
take_while(|c: u8| c.is_ascii_uppercase());
|
||||||
|
take_while(|c: char| c.is_ascii_uppercase());
|
||||||
|
}
|
||||||
|
|
|
@ -55,3 +55,30 @@ fn msrv_1_47() {
|
||||||
const FOO: bool = matches!('x', '0'..='9');
|
const FOO: bool = matches!('x', '0'..='9');
|
||||||
const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
|
const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::deref_addrof, clippy::needless_borrow)]
|
||||||
|
fn with_refs() {
|
||||||
|
let cool_letter = &&'g';
|
||||||
|
('0'..='9').contains(&&cool_letter);
|
||||||
|
('a'..='z').contains(*cool_letter);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generics() {
|
||||||
|
fn a<U>(u: &U) -> bool
|
||||||
|
where
|
||||||
|
char: PartialOrd<U>,
|
||||||
|
U: PartialOrd<char> + ?Sized,
|
||||||
|
{
|
||||||
|
('A'..='Z').contains(u)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn take_while<Item, F>(cond: F)
|
||||||
|
where
|
||||||
|
Item: Sized,
|
||||||
|
F: Fn(Item) -> bool,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
take_while(|c| ('A'..='Z').contains(&c));
|
||||||
|
take_while(|c| (b'A'..=b'Z').contains(&c));
|
||||||
|
take_while(|c: char| ('A'..='Z').contains(&c));
|
||||||
|
}
|
||||||
|
|
|
@ -133,5 +133,45 @@ error: manual check for common ascii range
|
||||||
LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
|
LL | const BAR: bool = matches!('x', '0'..='9' | 'a'..='f' | 'A'..='F');
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()`
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'x'.is_ascii_hexdigit()`
|
||||||
|
|
||||||
error: aborting due to 22 previous errors
|
error: manual check for common ascii range
|
||||||
|
--> tests/ui/manual_is_ascii_check.rs:62:5
|
||||||
|
|
|
||||||
|
LL | ('0'..='9').contains(&&cool_letter);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_digit()`
|
||||||
|
|
||||||
|
error: manual check for common ascii range
|
||||||
|
--> tests/ui/manual_is_ascii_check.rs:63:5
|
||||||
|
|
|
||||||
|
LL | ('a'..='z').contains(*cool_letter);
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cool_letter.is_ascii_lowercase()`
|
||||||
|
|
||||||
|
error: manual check for common ascii range
|
||||||
|
--> tests/ui/manual_is_ascii_check.rs:81:20
|
||||||
|
|
|
||||||
|
LL | take_while(|c| ('A'..='Z').contains(&c));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try
|
||||||
|
|
|
||||||
|
LL | take_while(|c: char| c.is_ascii_uppercase());
|
||||||
|
| ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: manual check for common ascii range
|
||||||
|
--> tests/ui/manual_is_ascii_check.rs:82:20
|
||||||
|
|
|
||||||
|
LL | take_while(|c| (b'A'..=b'Z').contains(&c));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: try
|
||||||
|
|
|
||||||
|
LL | take_while(|c: u8| c.is_ascii_uppercase());
|
||||||
|
| ~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: manual check for common ascii range
|
||||||
|
--> tests/ui/manual_is_ascii_check.rs:83:26
|
||||||
|
|
|
||||||
|
LL | take_while(|c: char| ('A'..='Z').contains(&c));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()`
|
||||||
|
|
||||||
|
error: aborting due to 27 previous errors
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue