Merge remote-tracking branch 'upstream/auto' into rustup

This commit is contained in:
Philipp Krones 2022-09-08 21:27:09 +02:00
commit 4ee55c5528
No known key found for this signature in database
GPG Key ID: 1CA0DF2AF59D68A5
699 changed files with 15484 additions and 714 deletions

View File

@ -3583,7 +3583,7 @@ Released 2018-09-13
[`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
[`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
[`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
[`arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic
[`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
[`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
[`as_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_underscore
[`assertions_on_constants`]: https://rust-lang.github.io/rust-clippy/master/index.html#assertions_on_constants
@ -3603,6 +3603,7 @@ Released 2018-09-13
[`blocks_in_if_conditions`]: https://rust-lang.github.io/rust-clippy/master/index.html#blocks_in_if_conditions
[`bool_assert_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_assert_comparison
[`bool_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_comparison
[`bool_to_int_with_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#bool_to_int_with_if
[`borrow_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_as_ptr
[`borrow_deref_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
[`borrow_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const

View File

@ -42,6 +42,7 @@ filetime = "0.2"
rustc-workspace-hack = "1.0"
# UI test dependencies
clap = { version = "3.1", features = ["derive"] }
clippy_utils = { path = "clippy_utils" }
derive-new = "0.5"
if_chain = "1.0"

View File

@ -3,7 +3,7 @@ use aho_corasick::AhoCorasickBuilder;
use indoc::writedoc;
use itertools::Itertools;
use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
use std::collections::{HashMap, HashSet};
use std::collections::{BTreeSet, HashMap, HashSet};
use std::ffi::OsStr;
use std::fmt::Write;
use std::fs::{self, OpenOptions};
@ -124,6 +124,8 @@ fn generate_lint_files(
let content = gen_lint_group_list("all", all_group_lints);
process_file("clippy_lints/src/lib.register_all.rs", update_mode, &content);
update_docs(update_mode, &usable_lints);
for (lint_group, lints) in Lint::by_lint_group(usable_lints.into_iter().chain(internal_lints)) {
let content = gen_lint_group_list(&lint_group, lints.iter());
process_file(
@ -140,6 +142,62 @@ fn generate_lint_files(
process_file("tests/ui/rename.rs", update_mode, &content);
}
fn update_docs(update_mode: UpdateMode, usable_lints: &[Lint]) {
replace_region_in_file(update_mode, Path::new("src/docs.rs"), "docs! {\n", "\n}\n", |res| {
for name in usable_lints.iter().map(|lint| lint.name.clone()).sorted() {
writeln!(res, r#" "{name}","#).unwrap();
}
});
if update_mode == UpdateMode::Check {
let mut extra = BTreeSet::new();
let mut lint_names = usable_lints
.iter()
.map(|lint| lint.name.clone())
.collect::<BTreeSet<_>>();
for file in std::fs::read_dir("src/docs").unwrap() {
let filename = file.unwrap().file_name().into_string().unwrap();
if let Some(name) = filename.strip_suffix(".txt") {
if !lint_names.remove(name) {
extra.insert(name.to_string());
}
}
}
let failed = print_lint_names("extra lint docs:", &extra) | print_lint_names("missing lint docs:", &lint_names);
if failed {
exit_with_failure();
}
} else {
if std::fs::remove_dir_all("src/docs").is_err() {
eprintln!("could not remove src/docs directory");
}
if std::fs::create_dir("src/docs").is_err() {
eprintln!("could not recreate src/docs directory");
}
}
for lint in usable_lints {
process_file(
Path::new("src/docs").join(lint.name.clone() + ".txt"),
update_mode,
&lint.documentation,
);
}
}
fn print_lint_names(header: &str, lints: &BTreeSet<String>) -> bool {
if lints.is_empty() {
return false;
}
println!("{}", header);
for lint in lints.iter().sorted() {
println!(" {}", lint);
}
println!();
true
}
pub fn print_lints() {
let (lint_list, _, _) = gather_all();
let usable_lints = Lint::usable_lints(&lint_list);
@ -589,17 +647,26 @@ struct Lint {
desc: String,
module: String,
declaration_range: Range<usize>,
documentation: String,
}
impl Lint {
#[must_use]
fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
fn new(
name: &str,
group: &str,
desc: &str,
module: &str,
declaration_range: Range<usize>,
documentation: String,
) -> Self {
Self {
name: name.to_lowercase(),
group: group.into(),
desc: remove_line_splices(desc),
module: module.into(),
declaration_range,
documentation,
}
}
@ -852,27 +919,35 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
}| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
) {
let start = range.start;
let mut iter = iter
.by_ref()
.filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
let mut docs = String::with_capacity(128);
let mut iter = iter.by_ref().filter(|t| !matches!(t.token_kind, TokenKind::Whitespace));
// matches `!{`
match_tokens!(iter, Bang OpenBrace);
match iter.next() {
// #[clippy::version = "version"] pub
Some(LintDeclSearchResult {
token_kind: TokenKind::Pound,
..
}) => {
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
},
// pub
Some(LintDeclSearchResult {
token_kind: TokenKind::Ident,
..
}) => (),
_ => continue,
let mut in_code = false;
while let Some(t) = iter.next() {
match t.token_kind {
TokenKind::LineComment { .. } => {
if let Some(line) = t.content.strip_prefix("/// ").or_else(|| t.content.strip_prefix("///")) {
if line.starts_with("```") {
docs += "```\n";
in_code = !in_code;
} else if !(in_code && line.starts_with("# ")) {
docs += line;
docs.push('\n');
}
}
},
TokenKind::Pound => {
match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
break;
},
TokenKind::Ident => {
break;
},
_ => {},
}
}
docs.pop(); // remove final newline
let (name, group, desc) = match_tokens!(
iter,
@ -890,7 +965,7 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
..
}) = iter.next()
{
lints.push(Lint::new(name, group, desc, module, start..range.end));
lints.push(Lint::new(name, group, desc, module, start..range.end, docs));
}
}
}
@ -977,7 +1052,11 @@ fn remove_line_splices(s: &str) -> String {
.and_then(|s| s.strip_suffix('"'))
.unwrap_or_else(|| panic!("expected quoted string, found `{}`", s));
let mut res = String::with_capacity(s.len());
unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range]));
unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, ch| {
if ch.is_ok() {
res.push_str(&s[range]);
}
});
res
}
@ -1116,6 +1195,7 @@ mod tests {
"\"really long text\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"doc_markdown",
@ -1123,6 +1203,7 @@ mod tests {
"\"single line\"",
"module_name",
Range::default(),
String::new(),
),
];
assert_eq!(expected, result);
@ -1162,6 +1243,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq2",
@ -1169,6 +1251,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq2",
@ -1176,6 +1259,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
];
let expected = vec![Lint::new(
@ -1184,6 +1268,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
)];
assert_eq!(expected, Lint::usable_lints(&lints));
}
@ -1191,22 +1276,51 @@ mod tests {
#[test]
fn test_by_lint_group() {
let lints = vec![
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new(
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq2",
"group2",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"incorrect_match",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
];
let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
expected.insert(
"group1".to_string(),
vec![
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new(
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"incorrect_match",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
],
);
expected.insert(
@ -1217,6 +1331,7 @@ mod tests {
"\"abc\"",
"module_name",
Range::default(),
String::new(),
)],
);
assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
@ -1255,9 +1370,30 @@ mod tests {
#[test]
fn test_gen_lint_group_list() {
let lints = vec![
Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
Lint::new(
"abc",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"should_assert_eq",
"group1",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
Lint::new(
"internal",
"internal_style",
"\"abc\"",
"module_name",
Range::default(),
String::new(),
),
];
let expected = GENERATED_FILE_COMMENT.to_string()
+ &[

View File

@ -18,7 +18,7 @@ quine-mc_cluskey = "0.2"
regex-syntax = "0.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true }
tempfile = { version = "3.2", optional = true }
tempfile = { version = "3.3.0", optional = true }
toml = "0.5"
unicode-normalization = "0.1"
unicode-script = { version = "0.5", default-features = false }

View File

@ -54,7 +54,7 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
hir_id: body.value.hir_id,
};
let typeck_results = cx.tcx.typeck_body(body_id);
let expr_ty = typeck_results.expr_ty(&body.value);
let expr_ty = typeck_results.expr_ty(body.value);
if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
let return_expr_span = match &body.value.kind {

View File

@ -475,7 +475,7 @@ fn check_lint_reason(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem
fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
if let ItemKind::Fn(_, _, eid) = item.kind {
is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
} else {
true
}
@ -483,7 +483,7 @@ fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
fn is_relevant_impl(cx: &LateContext<'_>, item: &ImplItem<'_>) -> bool {
match item.kind {
ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value),
ImplItemKind::Fn(_, eid) => is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value),
_ => false,
}
}
@ -492,7 +492,7 @@ fn is_relevant_trait(cx: &LateContext<'_>, item: &TraitItem<'_>) -> bool {
match item.kind {
TraitItemKind::Fn(_, TraitFn::Required(_)) => true,
TraitItemKind::Fn(_, TraitFn::Provided(eid)) => {
is_relevant_expr(cx, cx.tcx.typeck_body(eid), &cx.tcx.hir().body(eid).value)
is_relevant_expr(cx, cx.tcx.typeck_body(eid), cx.tcx.hir().body(eid).value)
},
_ => false,
}

View File

@ -117,8 +117,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
);
}
} else {
let span =
block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
if span.from_expansion() || expr.span.from_expansion() {
return;
}

View File

@ -0,0 +1,125 @@
use rustc_ast::{ExprPrecedence, LitKind};
use rustc_hir::{Block, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use clippy_utils::{diagnostics::span_lint_and_then, is_else_clause, source::snippet_block_with_applicability};
use rustc_errors::Applicability;
declare_clippy_lint! {
/// ### What it does
/// Instead of using an if statement to convert a bool to an int,
/// this lint suggests using a `from()` function or an `as` coercion.
///
/// ### Why is this bad?
/// Coercion or `from()` is idiomatic way to convert bool to a number.
/// Both methods are guaranteed to return 1 for true, and 0 for false.
///
/// See https://doc.rust-lang.org/std/primitive.bool.html#impl-From%3Cbool%3E
///
/// ### Example
/// ```rust
/// # let condition = false;
/// if condition {
/// 1_i64
/// } else {
/// 0
/// };
/// ```
/// Use instead:
/// ```rust
/// # let condition = false;
/// i64::from(condition);
/// ```
/// or
/// ```rust
/// # let condition = false;
/// condition as i64;
/// ```
#[clippy::version = "1.65.0"]
pub BOOL_TO_INT_WITH_IF,
style,
"using if to convert bool to int"
}
declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
if !expr.span.from_expansion() {
check_if_else(ctx, expr);
}
}
}
fn check_if_else<'tcx>(ctx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
if let ExprKind::If(check, then, Some(else_)) = expr.kind
&& let Some(then_lit) = int_literal(then)
&& let Some(else_lit) = int_literal(else_)
&& check_int_literal_equals_val(then_lit, 1)
&& check_int_literal_equals_val(else_lit, 0)
{
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_block_with_applicability(ctx, check.span, "..", None, &mut applicability);
let snippet_with_braces = {
let need_parens = should_have_parentheses(check);
let (left_paren, right_paren) = if need_parens {("(", ")")} else {("", "")};
format!("{left_paren}{snippet}{right_paren}")
};
let ty = ctx.typeck_results().expr_ty(then_lit); // then and else must be of same type
let suggestion = {
let wrap_in_curly = is_else_clause(ctx.tcx, expr);
let (left_curly, right_curly) = if wrap_in_curly {("{", "}")} else {("", "")};
format!(
"{left_curly}{ty}::from({snippet}){right_curly}"
)
}; // when used in else clause if statement should be wrapped in curly braces
span_lint_and_then(ctx,
BOOL_TO_INT_WITH_IF,
expr.span,
"boolean to int conversion using if",
|diag| {
diag.span_suggestion(
expr.span,
"replace with from",
suggestion,
applicability,
);
diag.note(format!("`{snippet_with_braces} as {ty}` or `{snippet_with_braces}.into()` can also be valid options"));
});
};
}
// If block contains only a int literal expression, return literal expression
fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
if let ExprKind::Block(block, _) = expr.kind
&& let Block {
stmts: [], // Shouldn't lint if statements with side effects
expr: Some(expr),
..
} = block
&& let ExprKind::Lit(lit) = &expr.kind
&& let LitKind::Int(_, _) = lit.node
{
Some(expr)
} else {
None
}
}
fn check_int_literal_equals_val<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>, expected_value: u128) -> bool {
if let ExprKind::Lit(lit) = &expr.kind
&& let LitKind::Int(val, _) = lit.node
&& val == expected_value
{
true
} else {
false
}
}
fn should_have_parentheses<'tcx>(check: &'tcx rustc_hir::Expr<'tcx>) -> bool {
check.precedence().order() < ExprPrecedence::Cast.order()
}

View File

@ -69,7 +69,10 @@ struct NumericFallbackVisitor<'a, 'tcx> {
impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
fn new(cx: &'a LateContext<'tcx>) -> Self {
Self { ty_bounds: vec![TyBound::Nothing], cx }
Self {
ty_bounds: vec![TyBound::Nothing],
cx,
}
}
/// Check whether a passed literal has potential to cause fallback or not.
@ -126,21 +129,19 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
}
return;
}
}
},
ExprKind::MethodCall(_, receiver, args, _) => {
if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(expr.hir_id) {
let fn_sig = self.cx.tcx.fn_sig(def_id).skip_binder();
for (expr, bound) in
iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs())
{
for (expr, bound) in iter::zip(std::iter::once(*receiver).chain(args.iter()), fn_sig.inputs()) {
self.ty_bounds.push(TyBound::Ty(*bound));
self.visit_expr(expr);
self.ty_bounds.pop();
}
return;
}
}
},
ExprKind::Struct(_, fields, base) => {
let ty = self.cx.typeck_results().expr_ty(expr);
@ -175,15 +176,15 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
return;
}
}
}
},
ExprKind::Lit(lit) => {
let ty = self.cx.typeck_results().expr_ty(expr);
self.check_lit(lit, ty, expr.hir_id);
return;
}
},
_ => {}
_ => {},
}
walk_expr(self, expr);
@ -197,7 +198,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
} else {
self.ty_bounds.push(TyBound::Nothing);
}
}
},
_ => self.ty_bounds.push(TyBound::Nothing),
}

View File

@ -830,25 +830,22 @@ fn walk_parents<'tcx>(
)
{
return Some(Position::MethodReceiverRefImpl)
} else {
return Some(Position::MethodReceiver)
}
return Some(Position::MethodReceiver);
}
args.iter()
.position(|arg| arg.hir_id == child_id)
.map(|i| {
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
if let ty::Param(param_ty) = ty.kind() {
needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
} else {
ty_auto_deref_stability(
cx,
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
precedence,
)
.position_for_arg()
}
})
args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i + 1];
if let ty::Param(param_ty) = ty.kind() {
needless_borrow_impl_arg_position(cx, parent, i + 1, *param_ty, e, precedence, msrv)
} else {
ty_auto_deref_stability(
cx,
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i + 1)),
precedence,
)
.position_for_arg()
}
})
},
ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),

View File

@ -40,7 +40,7 @@ declare_clippy_lint! {
///
/// ### Known problems
/// Derive macros [sometimes use incorrect bounds](https://github.com/rust-lang/rust/issues/26925)
/// in generic types and the user defined `impl` maybe is more generalized or
/// in generic types and the user defined `impl` may be more generalized or
/// specialized than what derive will produce. This lint can't detect the manual `impl`
/// has exactly equal bounds, and therefore this lint is disabled for types with
/// generic parameters.

View File

@ -236,7 +236,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
typeck_results: cx.tcx.typeck(item.def_id),
panic_span: None,
};
fpu.visit_expr(&body.value);
fpu.visit_expr(body.value);
lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
}
},
@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
typeck_results: cx.tcx.typeck(item.def_id),
panic_span: None,
};
fpu.visit_expr(&body.value);
fpu.visit_expr(body.value);
lint_for_missing_headers(cx, item.def_id, item.span, sig, headers, Some(body_id), fpu.panic_span);
}
}
@ -348,7 +348,7 @@ fn lint_for_missing_headers<'tcx>(
if let Some(future) = cx.tcx.lang_items().future_trait();
let typeck = cx.tcx.typeck_body(body_id);
let body = cx.tcx.hir().body(body_id);
let ret_ty = typeck.expr_ty(&body.value);
let ret_ty = typeck.expr_ty(body.value);
if implements_trait(cx, ret_ty, future, &[]);
if let ty::Opaque(_, subs) = ret_ty.kind();
if let Some(gen) = subs.types().next();
@ -828,7 +828,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.cx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.cx, receiver_ty, sym::Result)
{

View File

@ -83,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
};
if body.value.span.from_expansion() {
if body.params.is_empty() {
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, &body.value) {
if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, body.value) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
let closure_ty = cx.typeck_results().expr_ty(expr);
if_chain!(
if !is_adjusted(cx, &body.value);
if !is_adjusted(cx, body.value);
if let ExprKind::Call(callee, args) = body.value.kind;
if let ExprKind::Path(_) = callee.kind;
if check_inputs(cx, body.params, None, args);
@ -145,7 +145,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
);
if_chain!(
if !is_adjusted(cx, &body.value);
if !is_adjusted(cx, body.value);
if let ExprKind::MethodCall(path, receiver, args, _) = body.value.kind;
if check_inputs(cx, body.params, Some(receiver), args);
let method_def_id = cx.typeck_results().type_dependent_def_id(body.value.hir_id).unwrap();
@ -206,8 +206,7 @@ fn check_inputs(
_ => false,
}
};
std::iter::zip(params, receiver.into_iter().chain(call_args.iter()))
.all(|(param, arg)| check_inputs(param, arg))
std::iter::zip(params, receiver.into_iter().chain(call_args.iter())).all(|(param, arg)| check_inputs(param, arg))
}
fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tcx>) -> bool {

View File

@ -84,7 +84,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
{
@ -110,7 +110,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h
typeck_results: cx.tcx.typeck(impl_item.id.def_id),
result: Vec::new(),
};
fpu.visit_expr(&body.value);
fpu.visit_expr(body.value);
// if we've found one, lint
if !fpu.result.is_empty() {

View File

@ -238,23 +238,23 @@ fn get_integer_from_float_constant(value: &Constant) -> Option<i32> {
fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
// Check receiver
if let Some((value, _)) = constant(cx, cx.typeck_results(), receiver) {
let method = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
"exp"
if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
Some("exp")
} else if F32(2.0) == value || F64(2.0) == value {
"exp2"
Some("exp2")
} else {
return;
};
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"exponent for bases 2 and e can be computed more accurately",
"consider using",
format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
Applicability::MachineApplicable,
);
None
} {
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"exponent for bases 2 and e can be computed more accurately",
"consider using",
format!("{}.{}()", prepare_receiver_sugg(cx, &args[0]), method),
Applicability::MachineApplicable,
);
}
}
// Check argument

View File

@ -99,12 +99,7 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData {
}
}
fn check_format_in_format_args(
cx: &LateContext<'_>,
call_site: Span,
name: Symbol,
arg: &Expr<'_>,
) {
fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) {
let expn_data = arg.span.ctxt().outer_expn_data();
if expn_data.call_site.from_expansion() {
return;
@ -182,7 +177,10 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
// Returns true if `hir_id` is referred to by multiple format params
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
args.params().filter(|param| param.value.hir_id == hir_id).at_most_one().is_err()
args.params()
.filter(|param| param.value.hir_id == hir_id)
.at_most_one()
.is_err()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
@ -192,7 +190,11 @@ where
let mut n_total = 0;
let mut n_needed = 0;
loop {
if let Some(Adjustment { kind: Adjust::Deref(overloaded_deref), target }) = iter.next() {
if let Some(Adjustment {
kind: Adjust::Deref(overloaded_deref),
target,
}) = iter.next()
{
n_total += 1;
if overloaded_deref.is_some() {
n_needed = n_total;

View File

@ -272,6 +272,6 @@ fn mutates_static<'tcx>(cx: &LateContext<'tcx>, body: &'tcx hir::Body<'_>) -> bo
cx,
mutates_static: false,
};
intravisit::walk_expr(&mut v, &body.value);
intravisit::walk_expr(&mut v, body.value);
v.mutates_static
}

View File

@ -4,7 +4,6 @@ use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_span::{sym, Span};
use rustc_typeck::hir_ty_to_ty;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::trait_ref_of_method;
@ -17,11 +16,12 @@ use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
fn result_err_ty<'tcx>(
cx: &LateContext<'tcx>,
decl: &hir::FnDecl<'tcx>,
id: hir::def_id::LocalDefId,
item_span: Span,
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
if !in_external_macro(cx.sess(), item_span)
&& let hir::FnRetTy::Return(hir_ty) = decl.output
&& let ty = hir_ty_to_ty(cx.tcx, hir_ty)
&& let ty = cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).output())
&& is_type_diagnostic_item(cx, ty, sym::Result)
&& let ty::Adt(_, substs) = ty.kind()
{
@ -34,7 +34,7 @@ fn result_err_ty<'tcx>(
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span)
{
if cx.access_levels.is_exported(item.def_id) {
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
@ -47,7 +47,7 @@ pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, l
pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
// Don't lint if method is a trait's implementation, we can't do anything about those
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span)
&& trait_ref_of_method(cx, item.def_id).is_none()
{
if cx.access_levels.is_exported(item.def_id) {
@ -61,7 +61,7 @@ pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.def_id, item.span) {
if cx.access_levels.is_exported(item.def_id) {
check_result_unit_err(cx, err_ty, fn_header_span);
}

View File

@ -232,7 +232,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
return;
}
let res_ty = cx.typeck_results().expr_ty(&body.value);
let res_ty = cx.typeck_results().expr_ty(body.value);
if res_ty.is_unit() || res_ty.is_never() {
return;
}
@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitReturn {
None => return,
}
} else {
&body.value
body.value
};
lint_implicit_returns(cx, expr, expr.span.ctxt(), None);
}

View File

@ -59,7 +59,7 @@ impl<'tcx> LateLintPass<'tcx> for InfiniteIter {
MaybeInfinite => (MAYBE_INFINITE_ITER, "possible infinite iteration detected"),
Finite => {
return;
}
},
};
span_lint(cx, lint, expr.span, msg);
}
@ -161,7 +161,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
if method.ident.name == sym!(flat_map) && args.len() == 1 {
if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
let body = cx.tcx.hir().body(body);
return is_infinite(cx, &body.value);
return is_infinite(cx, body.value);
}
}
Finite
@ -230,8 +230,10 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
}
}
if method.ident.name == sym!(last) && args.is_empty() {
let not_double_ended =
cx.tcx.get_diagnostic_item(sym::DoubleEndedIterator).map_or(false, |id| {
let not_double_ended = cx
.tcx
.get_diagnostic_item(sym::DoubleEndedIterator)
.map_or(false, |id| {
!implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
});
if not_double_ended {

View File

@ -1,13 +1,12 @@
//! lint when there is a large size difference between variants on an enum
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{diagnostics::span_lint_and_then, ty::is_copy};
use clippy_utils::{diagnostics::span_lint_and_then, ty::approx_ty_size, ty::is_copy};
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{Adt, Ty};
use rustc_middle::ty::{Adt, AdtDef, GenericArg, List, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
@ -17,7 +16,7 @@ declare_clippy_lint! {
/// `enum`s.
///
/// ### Why is this bad?
/// Enum size is bounded by the largest variant. Having a
/// Enum size is bounded by the largest variant. Having one
/// large variant can penalize the memory layout of that enum.
///
/// ### Known problems
@ -33,8 +32,9 @@ declare_clippy_lint! {
/// use case it may be possible to store the large data in an auxiliary
/// structure (e.g. Arena or ECS).
///
/// The lint will ignore generic types if the layout depends on the
/// generics, even if the size difference will be large anyway.
/// The lint will ignore the impact of generic types to the type layout by
/// assuming every type parameter is zero-sized. Depending on your use case,
/// this may lead to a false positive.
///
/// ### Example
/// ```rust
@ -83,6 +83,38 @@ struct VariantInfo {
fields_size: Vec<FieldInfo>,
}
fn variants_size<'tcx>(
cx: &LateContext<'tcx>,
adt: AdtDef<'tcx>,
subst: &'tcx List<GenericArg<'tcx>>,
) -> Vec<VariantInfo> {
let mut variants_size = adt
.variants()
.iter()
.enumerate()
.map(|(i, variant)| {
let mut fields_size = variant
.fields
.iter()
.enumerate()
.map(|(i, f)| FieldInfo {
ind: i,
size: approx_ty_size(cx, f.ty(cx.tcx, subst)),
})
.collect::<Vec<_>>();
fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
VariantInfo {
ind: i,
size: fields_size.iter().map(|info| info.size).sum(),
fields_size,
}
})
.collect::<Vec<_>>();
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
variants_size
}
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
@ -92,36 +124,14 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
}
if let ItemKind::Enum(ref def, _) = item.kind {
let ty = cx.tcx.type_of(item.def_id);
let adt = ty.ty_adt_def().expect("already checked whether this is an enum");
let (adt, subst) = match ty.kind() {
Adt(adt, subst) => (adt, subst),
_ => panic!("already checked whether this is an enum"),
};
if adt.variants().len() <= 1 {
return;
}
let mut variants_size: Vec<VariantInfo> = Vec::new();
for (i, variant) in adt.variants().iter().enumerate() {
let mut fields_size = Vec::new();
for (i, f) in variant.fields.iter().enumerate() {
let ty = cx.tcx.type_of(f.did);
// don't lint variants which have a field of generic type.
match cx.layout_of(ty) {
Ok(l) => {
let fsize = l.size.bytes();
fields_size.push(FieldInfo { ind: i, size: fsize });
},
Err(_) => {
return;
},
}
}
let size: u64 = fields_size.iter().map(|info| info.size).sum();
variants_size.push(VariantInfo {
ind: i,
size,
fields_size,
});
}
variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
let variants_size = variants_size(cx, *adt, subst);
let mut difference = variants_size[0].size - variants_size[1].size;
if difference > self.maximum_size_difference_allowed {
@ -129,20 +139,30 @@ impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
span_lint_and_then(
cx,
LARGE_ENUM_VARIANT,
def.variants[variants_size[0].ind].span,
item.span,
"large size difference between variants",
|diag| {
diag.span_label(
def.variants[variants_size[0].ind].span,
&format!("this variant is {} bytes", variants_size[0].size),
item.span,
format!("the entire enum is at least {} bytes", approx_ty_size(cx, ty)),
);
diag.span_note(
diag.span_label(
def.variants[variants_size[0].ind].span,
format!("the largest variant contains at least {} bytes", variants_size[0].size),
);
diag.span_label(
def.variants[variants_size[1].ind].span,
&format!("and the second-largest variant is {} bytes:", variants_size[1].size),
&if variants_size[1].fields_size.is_empty() {
"the second-largest variant carries no data at all".to_owned()
} else {
format!(
"the second-largest variant contains at least {} bytes",
variants_size[1].size
)
},
);
let fields = def.variants[variants_size[0].ind].data.fields();
variants_size[0].fields_size.sort_by(|a, b| (a.size.cmp(&b.size)));
let mut applicability = Applicability::MaybeIncorrect;
if is_copy(cx, ty) || maybe_copy(cx, ty) {
diag.span_note(

View File

@ -370,7 +370,8 @@ fn check_for_is_empty<'tcx>(
}
fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>, op: &str, compare_to: u32) {
if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind) {
if let (&ExprKind::MethodCall(method_path, receiver, args, _), &ExprKind::Lit(ref lit)) = (&method.kind, &lit.kind)
{
// check if we are in an is_empty() method
if let Some(name) = get_item_name(cx, method) {
if name.as_str() == "is_empty" {
@ -378,12 +379,23 @@ fn check_cmp(cx: &LateContext<'_>, span: Span, method: &Expr<'_>, lit: &Expr<'_>
}
}
check_len(cx, span, method_path.ident.name, receiver, args, &lit.node, op, compare_to);
check_len(
cx,
span,
method_path.ident.name,
receiver,
args,
&lit.node,
op,
compare_to,
);
} else {
check_empty_expr(cx, span, method, lit, op);
}
}
// FIXME(flip1995): Figure out how to reduce the number of arguments
#[allow(clippy::too_many_arguments)]
fn check_len(
cx: &LateContext<'_>,
span: Span,

View File

@ -17,6 +17,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),

View File

@ -56,6 +56,7 @@ store.register_lints(&[
await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
bool_to_int_with_if::BOOL_TO_INT_WITH_IF,
booleans::NONMINIMAL_BOOL,
booleans::OVERLY_COMPLEX_BOOL_EXPR,
borrow_deref_ref::BORROW_DEREF_REF,
@ -436,7 +437,7 @@ store.register_lints(&[
octal_escapes::OCTAL_ESCAPES,
only_used_in_recursion::ONLY_USED_IN_RECURSION,
operators::ABSURD_EXTREME_COMPARISONS,
operators::ARITHMETIC,
operators::ARITHMETIC_SIDE_EFFECTS,
operators::ASSIGN_OP_PATTERN,
operators::BAD_BIT_MASK,
operators::CMP_NAN,

View File

@ -30,6 +30,8 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(strings::STRING_LIT_AS_BYTES),
LintId::of(suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS),
LintId::of(trailing_empty_array::TRAILING_EMPTY_ARRAY),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(transmute::TRANSMUTE_UNDEFINED_REPR),
LintId::of(unused_rounding::UNUSED_ROUNDING),
LintId::of(use_self::USE_SELF),

View File

@ -88,8 +88,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
LintId::of(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
LintId::of(transmute::TRANSMUTE_PTR_TO_PTR),
LintId::of(types::LINKEDLIST),
LintId::of(types::OPTION_OPTION),

View File

@ -50,7 +50,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
LintId::of(module_style::MOD_MODULE_FILES),
LintId::of(module_style::SELF_NAMED_MODULE_FILES),
LintId::of(operators::ARITHMETIC),
LintId::of(operators::ARITHMETIC_SIDE_EFFECTS),
LintId::of(operators::FLOAT_ARITHMETIC),
LintId::of(operators::FLOAT_CMP_CONST),
LintId::of(operators::INTEGER_ARITHMETIC),

View File

@ -6,6 +6,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
LintId::of(bool_to_int_with_if::BOOL_TO_INT_WITH_IF),
LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
LintId::of(collapsible_if::COLLAPSIBLE_ELSE_IF),

View File

@ -179,6 +179,7 @@ mod attrs;
mod await_holding_invalid;
mod blocks_in_if_conditions;
mod bool_assert_comparison;
mod bool_to_int_with_if;
mod booleans;
mod borrow_deref_ref;
mod cargo;
@ -544,8 +545,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(utils::internal_lints::MsrvAttrImpl));
}
let arithmetic_allowed = conf.arithmetic_allowed.clone();
store.register_late_pass(move || Box::new(operators::arithmetic::Arithmetic::new(arithmetic_allowed.clone())));
let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
store.register_late_pass(move || {
Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(
arithmetic_side_effects_allowed.clone(),
))
});
store.register_late_pass(|| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|| Box::new(utils::author::Author));
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
@ -901,6 +906,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
store.register_late_pass(|| Box::new(bool_to_int_with_if::BoolToIntWithIf));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View File

@ -276,7 +276,7 @@ fn could_use_elision<'tcx>(
let mut checker = BodyLifetimeChecker {
lifetimes_used_in_body: false,
};
checker.visit_expr(&body.value);
checker.visit_expr(body.value);
if checker.lifetimes_used_in_body {
return false;
}

View File

@ -373,7 +373,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
},
ExprKind::Closure(&Closure { body, .. }) => {
let body = self.cx.tcx.hir().body(body);
self.visit_expr(&body.value);
self.visit_expr(body.value);
},
_ => walk_expr(self, expr),
}

View File

@ -356,7 +356,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
after_loop: false,
used_iter: false,
};
v.visit_expr(&cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
v.used_iter
}
}

View File

@ -35,7 +35,8 @@ struct PathAndSpan {
span: Span,
}
/// `MacroRefData` includes the name of the macro.
/// `MacroRefData` includes the name of the macro
/// and the path from `SourceMap::span_to_filename`.
#[derive(Debug, Clone)]
pub struct MacroRefData {
name: String,

View File

@ -40,7 +40,7 @@ pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'
arm.pat.span,
&format!("`Err({})` matches all errors", ident_bind_name),
None,
"match each error separately or use the error output, or use `.except(msg)` if the error case is unreachable",
"match each error separately or use the error output, or use `.expect(msg)` if the error case is unreachable",
);
}
}

View File

@ -201,12 +201,8 @@ fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>,
// in the arms, so we need to evaluate the correct offsets here in order to iterate in
// both arms at the same time.
let len = max(
left_in.len() + {
if left_pos.is_some() { 1 } else { 0 }
},
right_in.len() + {
if right_pos.is_some() { 1 } else { 0 }
},
left_in.len() + usize::from(left_pos.is_some()),
right_in.len() + usize::from(right_pos.is_some()),
);
let mut left_pos = left_pos.unwrap_or(usize::MAX);
let mut right_pos = right_pos.unwrap_or(usize::MAX);

View File

@ -152,7 +152,7 @@ pub(crate) trait BindInsteadOfMap {
match arg.kind {
hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) => {
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(&closure_body.value);
let closure_expr = peel_blocks(closure_body.value);
if Self::lint_closure_autofixable(cx, expr, recv, closure_expr, fn_decl_span) {
true

View File

@ -23,7 +23,7 @@ pub(super) fn check(
if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
let mut applicability = Applicability::MachineApplicable;
let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0].0).peel_refs();
let self_ty = cx.typeck_results().expr_ty_adjusted(args[0].0).peel_refs();
if *self_ty.kind() != ty::Str {
return false;

View File

@ -21,7 +21,11 @@ pub(super) fn check(
receiver: &Expr<'_>,
args: &[Expr<'_>],
) {
let arg = if method_name == sym::clone && args.is_empty() { receiver } else { return };
let arg = if method_name == sym::clone && args.is_empty() {
receiver
} else {
return;
};
if cx
.typeck_results()
.type_dependent_def_id(expr.hir_id)

View File

@ -25,7 +25,7 @@ fn is_method<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Sy
},
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
let body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(&body.value);
let closure_expr = peel_blocks(body.value);
let arg_id = body.params[0].pat.hir_id;
match closure_expr.kind {
hir::ExprKind::MethodCall(hir::PathSegment { ident, .. }, receiver, ..) => {

View File

@ -30,7 +30,7 @@ pub(super) fn check<'tcx>(
if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
then {
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(&closure_body.value);
let closure_expr = peel_blocks(closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
hir::BindingAnnotation::NONE, .., name, None

View File

@ -825,8 +825,9 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `.or(foo(..))`, `.unwrap_or(foo(..))`,
/// etc., and suggests to use `or_else`, `unwrap_or_else`, etc., or
/// `unwrap_or_default` instead.
/// `.or_insert(foo(..))` etc., and suggests to use `.or_else(|| foo(..))`,
/// `.unwrap_or_else(|| foo(..))`, `.unwrap_or_default()` or `.or_default()`
/// etc. instead.
///
/// ### Why is this bad?
/// The function will always be called and potentially

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{expr_custom_deref_adjustment, ty::is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
@ -11,6 +11,7 @@ use super::MUT_MUTEX_LOCK;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
if_chain! {
if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut));
if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);

View File

@ -40,7 +40,7 @@ fn get_open_options(cx: &LateContext<'_>, argument: &Expr<'_>, options: &mut Vec
let obj_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
// Only proceed if this is a call on some object of type std::fs::OpenOptions
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && arguments.len() >= 1 {
if match_type(cx, obj_ty, &paths::OPEN_OPTIONS) && !arguments.is_empty() {
let argument_option = match arguments[0].kind {
ExprKind::Lit(ref span) => {
if let Spanned {

View File

@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(
}),
hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(&closure_body.value);
let closure_expr = peel_blocks(closure_body.value);
match &closure_expr.kind {
hir::ExprKind::MethodCall(_, receiver, [], _) => {

View File

@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
if let hir::ExprKind::Closure(&hir::Closure { body, fn_decl_span, .. }) = map_arg.kind;
let arg_snippet = snippet(cx, fn_decl_span, "..");
let body = cx.tcx.hir().body(body);
if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
if let Some((func, [arg_char])) = reduce_unit_expression(body.value);
if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
if Some(id) == cx.tcx.lang_items().option_some_variant();
then {

View File

@ -23,7 +23,8 @@ pub(super) fn check<'tcx>(
receiver: &'tcx hir::Expr<'_>,
args: &'tcx [hir::Expr<'_>],
) {
/// Checks for `unwrap_or(T::new())` or `unwrap_or(T::default())`.
/// Checks for `unwrap_or(T::new())`, `unwrap_or(T::default())`,
/// `or_insert(T::new())` or `or_insert(T::default())`.
#[allow(clippy::too_many_arguments)]
fn check_unwrap_or_default(
cx: &LateContext<'_>,
@ -43,7 +44,11 @@ pub(super) fn check<'tcx>(
if_chain! {
if !or_has_args;
if name == "unwrap_or";
if let Some(sugg) = match name {
"unwrap_or" => Some("unwrap_or_default"),
"or_insert" => Some("or_default"),
_ => None,
};
if let hir::ExprKind::Path(ref qpath) = fun.kind;
if let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default);
let path = last_path_segment(qpath).ident.name;
@ -59,7 +64,7 @@ pub(super) fn check<'tcx>(
method_span.with_hi(span.hi()),
&format!("use of `{}` followed by a call to `{}`", name, path),
"try this",
"unwrap_or_default()".to_string(),
format!("{}()", sugg),
Applicability::MachineApplicable,
);
@ -83,7 +88,7 @@ pub(super) fn check<'tcx>(
fun_span: Option<Span>,
) {
// (path, fn_has_argument, methods, suffix)
static KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
const KNOW_TYPES: [(&[&str], bool, &[&str], &str); 4] = [
(&paths::BTREEMAP_ENTRY, false, &["or_insert"], "with"),
(&paths::HASHMAP_ENTRY, false, &["or_insert"], "with"),
(&paths::OPTION, false, &["map_or", "ok_or", "or", "unwrap_or"], "else"),

View File

@ -15,9 +15,9 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, count_recv: &hi
if let Some(def_id) = cx.tcx.hir().opt_local_def_id(closure.hir_id);
if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(def_id);
let closure_body = cx.tcx.hir().body(body_id);
if !cx.typeck_results().expr_ty(&closure_body.value).is_unit();
if !cx.typeck_results().expr_ty(closure_body.value).is_unit();
then {
if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
if let Some(map_mutated_vars) = mutated_variables(closure_body.value, cx) {
// A variable is used mutably inside of the closure. Suppress the lint.
if !map_mutated_vars.is_empty() {
return;

View File

@ -21,14 +21,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
let body = cx.tcx.hir().body(body);
let arg_id = body.params[0].pat.hir_id;
let mutates_arg =
mutated_variables(&body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, &body.value);
let mutates_arg = mutated_variables(body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, body.value);
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, &body.value);
let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
let mut return_visitor = ReturnVisitor::new(cx, arg_id);
return_visitor.visit_expr(&body.value);
return_visitor.visit_expr(body.value);
found_mapping |= return_visitor.found_mapping;
found_filtering |= return_visitor.found_filtering;
@ -36,7 +35,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<
let sugg = if !found_filtering {
if name == "filter_map" { "map" } else { "map(..).next()" }
} else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
match cx.typeck_results().expr_ty(&body.value).kind() {
match cx.typeck_results().expr_ty(body.value).kind() {
ty::Adt(adt, subst)
if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && in_ty == subst.type_at(0) =>
{

View File

@ -31,7 +31,7 @@ pub(super) fn check(
// Extract the body of the closure passed to fold
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = acc.kind;
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(&closure_body.value);
let closure_expr = peel_blocks(closure_body.value);
// Check if the closure body is of the form `acc <op> some_expr(x)`
if let hir::ExprKind::Binary(ref bin_op, left_expr, right_expr) = closure_expr.kind;

View File

@ -131,7 +131,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
] = &closure_body.params;
if let ExprKind::MethodCall(method_path, left_expr, [right_expr], _) = closure_body.value.kind;
if method_path.ident.name == sym::cmp;
if is_trait_method(cx, &closure_body.value, sym::Ord);
if is_trait_method(cx, closure_body.value, sym::Ord);
then {
let (closure_body, closure_arg, reverse) = if mirrored_exprs(
left_expr,

View File

@ -1,21 +1,25 @@
use super::implicit_clone::is_clone_like;
use super::unnecessary_iter_cloned::{self, is_into_iter};
use crate::rustc_middle::ty::Subst;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{
get_associated_type, get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, peel_mid_ty_refs,
};
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item};
use clippy_utils::ty::{get_associated_type, get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs};
use clippy_utils::visitors::find_all_ret_expressions;
use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
use clippy_utils::{meets_msrv, msrvs};
use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind};
use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref};
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef};
use rustc_middle::ty::{self, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
use rustc_middle::ty::EarlyBinder;
use rustc_middle::ty::{self, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty};
use rustc_semver::RustcVersion;
use rustc_span::{sym, Symbol};
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
use rustc_typeck::check::{FnCtxt, Inherited};
use std::cmp::max;
use super::UNNECESSARY_TO_OWNED;
@ -34,7 +38,7 @@ pub fn check<'tcx>(
then {
if is_cloned_or_copied(cx, method_name, method_def_id) {
unnecessary_iter_cloned::check(cx, expr, method_name, receiver);
} else if is_to_owned_like(cx, method_name, method_def_id) {
} else if is_to_owned_like(cx, expr, method_name, method_def_id) {
// At this point, we know the call is of a `to_owned`-like function. The functions
// `check_addr_of_expr` and `check_call_arg` determine whether the call is unnecessary
// based on its context, that is, whether it is a referent in an `AddrOf` expression, an
@ -246,17 +250,12 @@ fn check_other_call_arg<'tcx>(
) -> bool {
if_chain! {
if let Some((maybe_call, maybe_arg)) = skip_addr_of_ancestors(cx, expr);
if let Some((callee_def_id, call_substs, call_receiver, call_args)) = get_callee_substs_and_args(cx, maybe_call);
if let Some((callee_def_id, _, recv, call_args)) = get_callee_substs_and_args(cx, maybe_call);
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
let index = if let Some(call_receiver) = call_receiver {
std::iter::once(call_receiver).chain(call_args.iter()).position(|arg| arg.hir_id == maybe_arg.hir_id)
} else {
call_args.iter().position(|arg| arg.hir_id == maybe_arg.hir_id)
};
if let Some(i) = index;
if let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id);
if let Some(input) = fn_sig.inputs().get(i);
let (input, n_refs) = peel_mid_ty_refs(*input);
if let (trait_predicates, projection_predicates) = get_input_traits_and_projections(cx, callee_def_id, input);
if let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input);
if let Some(sized_def_id) = cx.tcx.lang_items().sized_trait();
if let [trait_predicate] = trait_predicates
.iter()
@ -264,52 +263,13 @@ fn check_other_call_arg<'tcx>(
.collect::<Vec<_>>()[..];
if let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref);
if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef);
if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id;
let receiver_ty = cx.typeck_results().expr_ty(receiver);
// If the callee has type parameters, they could appear in `projection_predicate.ty` or the
// types of `trait_predicate.trait_ref.substs`.
if if trait_predicate.def_id() == deref_trait_id {
if let [projection_predicate] = projection_predicates[..] {
let normalized_ty =
cx.tcx
.subst_and_normalize_erasing_regions(call_substs, cx.param_env, projection_predicate.term);
implements_trait(cx, receiver_ty, deref_trait_id, &[])
&& get_associated_type(cx, receiver_ty, deref_trait_id, "Target")
.map_or(false, |ty| ty::TermKind::Ty(ty) == normalized_ty.unpack())
} else {
false
}
} else if trait_predicate.def_id() == as_ref_trait_id {
let composed_substs = compose_substs(
cx,
&trait_predicate.trait_ref.substs.iter().skip(1).collect::<Vec<_>>()[..],
call_substs,
);
// if `expr` is a `String` and generic target is [u8], skip
// (https://github.com/rust-lang/rust-clippy/issues/9317).
if let [subst] = composed_substs[..]
&& let GenericArgKind::Type(arg_ty) = subst.unpack()
&& arg_ty.is_slice()
&& let inner_ty = arg_ty.builtin_index().unwrap()
&& let ty::Uint(ty::UintTy::U8) = inner_ty.kind()
&& let self_ty = cx.typeck_results().expr_ty(expr).peel_refs()
&& is_type_diagnostic_item(cx, self_ty, sym::String) {
false
} else {
implements_trait(cx, receiver_ty, as_ref_trait_id, &composed_substs)
}
} else {
false
};
if can_change_type(cx, maybe_arg, receiver_ty);
// We can't add an `&` when the trait is `Deref` because `Target = &T` won't match
// `Target = T`.
if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id;
let n_refs = max(n_refs, if is_copy(cx, receiver_ty) { 0 } else { 1 });
// If the trait is `AsRef` and the input type variable `T` occurs in the output type, then
// `T` must not be instantiated with a reference
// (https://github.com/rust-lang/rust-clippy/issues/8507).
if (n_refs == 0 && !receiver_ty.is_ref())
|| trait_predicate.def_id() != as_ref_trait_id
|| !fn_sig.output().contains(input);
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
span_lint_and_sugg(
@ -359,11 +319,11 @@ fn get_callee_substs_and_args<'tcx>(
}
}
if_chain! {
if let ExprKind::MethodCall(_, receiver, args, _) = expr.kind;
if let ExprKind::MethodCall(_, recv, args, _) = expr.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
then {
let substs = cx.typeck_results().node_substs(expr.hir_id);
return Some((method_def_id, substs, Some(receiver), args));
return Some((method_def_id, substs, Some(recv), args));
}
}
None
@ -395,22 +355,103 @@ fn get_input_traits_and_projections<'tcx>(
(trait_predicates, projection_predicates)
}
/// Composes two substitutions by applying the latter to the types of the former.
fn compose_substs<'tcx>(
cx: &LateContext<'tcx>,
left: &[GenericArg<'tcx>],
right: SubstsRef<'tcx>,
) -> Vec<GenericArg<'tcx>> {
left.iter()
.map(|arg| {
if let GenericArgKind::Type(arg_ty) = arg.unpack() {
let normalized_ty = cx.tcx.subst_and_normalize_erasing_regions(right, cx.param_env, arg_ty);
GenericArg::from(normalized_ty)
} else {
*arg
fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<'a>) -> bool {
for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
match node {
Node::Stmt(_) => return true,
Node::Block(..) => continue,
Node::Item(item) => {
if let ItemKind::Fn(_, _, body_id) = &item.kind
&& let output_ty = return_ty(cx, item.hir_id())
&& let local_def_id = cx.tcx.hir().local_def_id(item.hir_id())
&& Inherited::build(cx.tcx, local_def_id).enter(|inherited| {
let fn_ctxt = FnCtxt::new(&inherited, cx.param_env, item.hir_id());
fn_ctxt.can_coerce(ty, output_ty)
}) {
if has_lifetime(output_ty) && has_lifetime(ty) {
return false;
}
let body = cx.tcx.hir().body(*body_id);
let body_expr = &body.value;
let mut count = 0;
return find_all_ret_expressions(cx, body_expr, |_| { count += 1; count <= 1 });
}
}
})
.collect()
Node::Expr(parent_expr) => {
if let Some((callee_def_id, call_substs, recv, call_args)) = get_callee_substs_and_args(cx, parent_expr)
{
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
if let Some(arg_index) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == expr.hir_id)
&& let Some(param_ty) = fn_sig.inputs().get(arg_index)
&& let ty::Param(ParamTy { index: param_index , ..}) = param_ty.kind()
{
if fn_sig
.inputs()
.iter()
.enumerate()
.filter(|(i, _)| *i != arg_index)
.any(|(_, ty)| ty.contains(*param_ty))
{
return false;
}
let mut trait_predicates = cx.tcx.param_env(callee_def_id)
.caller_bounds().iter().filter(|predicate| {
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& trait_predicate.trait_ref.self_ty() == *param_ty {
true
} else {
false
}
});
let new_subst = cx.tcx.mk_substs(
call_substs.iter()
.enumerate()
.map(|(i, t)|
if i == (*param_index as usize) {
GenericArg::from(ty)
} else {
t
}));
if trait_predicates.any(|predicate| {
let predicate = EarlyBinder(predicate).subst(cx.tcx, new_subst);
let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
!cx.tcx
.infer_ctxt()
.enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
}) {
return false;
}
let output_ty = fn_sig.output();
if output_ty.contains(*param_ty) {
if let Ok(new_ty) = cx.tcx.try_subst_and_normalize_erasing_regions(
new_subst, cx.param_env, output_ty) {
expr = parent_expr;
ty = new_ty;
continue;
}
return false;
}
return true;
}
} else if let ExprKind::Block(..) = parent_expr.kind {
continue;
}
return false;
},
_ => return false,
}
}
false
}
fn has_lifetime(ty: Ty<'_>) -> bool {
ty.walk().any(|t| matches!(t.unpack(), GenericArgKind::Lifetime(_)))
}
/// Returns true if the named method is `Iterator::cloned` or `Iterator::copied`.
@ -421,10 +462,10 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id:
/// Returns true if the named method can be used to convert the receiver to its "owned"
/// representation.
fn is_to_owned_like(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool {
is_clone_like(cx, method_name.as_str(), method_def_id)
|| is_cow_into_owned(cx, method_name, method_def_id)
|| is_to_string(cx, method_name, method_def_id)
|| is_to_string_on_string_like(cx, call_expr, method_name, method_def_id)
}
/// Returns true if the named method is `Cow::into_owned`.
@ -432,7 +473,27 @@ fn is_cow_into_owned(cx: &LateContext<'_>, method_name: Symbol, method_def_id: D
method_name.as_str() == "into_owned" && is_diag_item_method(cx, method_def_id, sym::Cow)
}
/// Returns true if the named method is `ToString::to_string`.
fn is_to_string(cx: &LateContext<'_>, method_name: Symbol, method_def_id: DefId) -> bool {
method_name == sym::to_string && is_diag_trait_item(cx, method_def_id, sym::ToString)
/// Returns true if the named method is `ToString::to_string` and it's called on a type that
/// is string-like i.e. implements `AsRef<str>` or `Deref<str>`.
fn is_to_string_on_string_like<'a>(
cx: &LateContext<'_>,
call_expr: &'a Expr<'a>,
method_name: Symbol,
method_def_id: DefId,
) -> bool {
if method_name != sym::to_string || !is_diag_trait_item(cx, method_def_id, sym::ToString) {
return false;
}
if let Some(substs) = cx.typeck_results().node_substs_opt(call_expr.hir_id)
&& let [generic_arg] = substs.as_slice()
&& let GenericArgKind::Type(ty) = generic_arg.unpack()
&& let Some(deref_trait_id) = cx.tcx.get_diagnostic_item(sym::Deref)
&& let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef)
&& (implements_trait(cx, ty, deref_trait_id, &[cx.tcx.types.str_.into()]) ||
implements_trait(cx, ty, as_ref_trait_id, &[cx.tcx.types.str_.into()])) {
true
} else {
false
}
}

View File

@ -5,10 +5,11 @@ use clippy_utils::{
diagnostics::span_lint_and_sugg, is_default_equivalent_call, source::snippet_with_applicability,
ty::is_type_diagnostic_item,
};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
use rustc_span::{sym, symbol};
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
@ -25,7 +26,7 @@ pub(super) fn check<'tcx>(
if_chain! {
if is_option || is_result;
if is_default_equivalent_call(cx, u_arg);
if closure_body_returns_empty_to_string(cx, u_arg) || is_default_equivalent_call(cx, u_arg);
then {
let mut applicability = Applicability::MachineApplicable;
@ -44,3 +45,22 @@ pub(super) fn check<'tcx>(
}
}
}
fn closure_body_returns_empty_to_string(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> bool {
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = e.kind {
let body = cx.tcx.hir().body(body);
if body.params.is_empty()
&& let hir::Expr{ kind, .. } = &body.value
&& let hir::ExprKind::MethodCall(hir::PathSegment {ident, ..}, self_arg, _, _) = kind
&& ident == &symbol::Ident::from_str("to_string")
&& let hir::Expr{ kind, .. } = self_arg
&& let hir::ExprKind::Lit(lit) = kind
&& let LitKind::Str(symbol::kw::Empty, _) = lit.node
{
return true;
}
}
false
}

View File

@ -1,7 +1,6 @@
use clippy_utils::consts::{constant_simple, Constant};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{match_trait_method, paths};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -84,19 +83,16 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons
}
},
ExprKind::MethodCall(path, receiver, args @ [_], _) => {
if_chain! {
if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD);
then {
if path.ident.name == sym!(max) {
fetch_const(cx, Some(receiver), args, MinMax::Max)
} else if path.ident.name == sym!(min) {
fetch_const(cx, Some(receiver), args, MinMax::Min)
} else {
None
}
if cx.typeck_results().expr_ty(receiver).is_floating_point() || match_trait_method(cx, expr, &paths::ORD) {
if path.ident.name == sym!(max) {
fetch_const(cx, Some(receiver), args, MinMax::Max)
} else if path.ident.name == sym!(min) {
fetch_const(cx, Some(receiver), args, MinMax::Min)
} else {
None
}
} else {
None
}
},
_ => None,
@ -109,18 +105,18 @@ fn fetch_const<'a>(
args: &'a [Expr<'a>],
m: MinMax,
) -> Option<(MinMax, Constant, &'a Expr<'a>)> {
let mut args = receiver.into_iter().chain(args.into_iter());
let arg0 = args.next()?;
let arg1 = args.next()?;
let mut args = receiver.into_iter().chain(args);
let first_arg = args.next()?;
let second_arg = args.next()?;
if args.next().is_some() {
return None;
}
constant_simple(cx, cx.typeck_results(), arg0).map_or_else(
|| constant_simple(cx, cx.typeck_results(), arg1).map(|c| (m, c, arg0)),
constant_simple(cx, cx.typeck_results(), first_arg).map_or_else(
|| constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)),
|c| {
if constant_simple(cx, cx.typeck_results(), arg1).is_none() {
if constant_simple(cx, cx.typeck_results(), second_arg).is_none() {
// otherwise ignore
Some((m, c, arg1))
Some((m, c, second_arg))
} else {
None
}

View File

@ -8,6 +8,7 @@
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_from_proc_macro;
use if_chain::if_chain;
use rustc_ast::ast::{self, MetaItem, MetaItemKind};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};

View File

@ -95,10 +95,6 @@ impl<'a, 'tcx> Visitor<'tcx> for MutArgVisitor<'a, 'tcx> {
self.found = true;
return;
},
ExprKind::If(..) => {
self.found = true;
return;
},
ExprKind::Path(_) => {
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id) {
if adj

View File

@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
if let ExprKind::Block(..) = body.value.kind;
then {
let mut ret_collector = RetCollector::default();
ret_collector.visit_expr(&body.value);
ret_collector.visit_expr(body.value);
// Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
if ret_collector.ret_in_loop {

View File

@ -12,6 +12,7 @@ use rustc_middle::ty::{self, ConstKind};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
use std::iter;
declare_clippy_lint! {
/// ### What it does
@ -234,11 +235,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
})) => (
def_id.to_def_id(),
FnKind::TraitFn,
if sig.decl.implicit_self.has_implicit_self() {
1
} else {
0
},
usize::from(sig.decl.implicit_self.has_implicit_self()),
),
Some(Node::ImplItem(&ImplItem {
kind: ImplItemKind::Fn(ref sig, _),
@ -253,11 +250,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
(
trait_item_id,
FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.substs) as *const _ as usize),
if sig.decl.implicit_self.has_implicit_self() {
1
} else {
0
},
usize::from(sig.decl.implicit_self.has_implicit_self()),
)
} else {
(def_id.to_def_id(), FnKind::Fn, 0)
@ -310,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
&& has_matching_substs(param.fn_kind, typeck.node_substs(parent.hir_id))
}) =>
{
if let Some(idx) = std::iter::once(receiver).chain(args.iter()).position(|arg| arg.hir_id == child_id) {
if let Some(idx) = iter::once(receiver).chain(args).position(|arg| arg.hir_id == child_id) {
param.uses.push(Usage::new(span, idx));
}
return;

View File

@ -1,119 +0,0 @@
#![allow(
// False positive
clippy::match_same_arms
)]
use super::ARITHMETIC;
use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::source_map::Span;
const HARD_CODED_ALLOWED: &[&str] = &["std::num::Saturating", "std::string::String", "std::num::Wrapping"];
#[derive(Debug)]
pub struct Arithmetic {
allowed: FxHashSet<String>,
// Used to check whether expressions are constants, such as in enum discriminants and consts
const_span: Option<Span>,
expr_span: Option<Span>,
}
impl_lint_pass!(Arithmetic => [ARITHMETIC]);
impl Arithmetic {
#[must_use]
pub fn new(mut allowed: FxHashSet<String>) -> Self {
allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
Self {
allowed,
const_span: None,
expr_span: None,
}
}
/// Checks if the given `expr` has any of the inner `allowed` elements.
fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
self.allowed.contains(
cx.typeck_results()
.expr_ty(expr)
.to_string()
.split('<')
.next()
.unwrap_or_default(),
)
}
fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
span_lint(cx, ARITHMETIC, expr.span, "arithmetic detected");
self.expr_span = Some(expr.span);
}
}
impl<'tcx> LateLintPass<'tcx> for Arithmetic {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if self.expr_span.is_some() {
return;
}
if let Some(span) = self.const_span && span.contains(expr.span) {
return;
}
match &expr.kind {
hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
let (
hir::BinOpKind::Add
| hir::BinOpKind::Sub
| hir::BinOpKind::Mul
| hir::BinOpKind::Div
| hir::BinOpKind::Rem
| hir::BinOpKind::Shl
| hir::BinOpKind::Shr
) = op.node else {
return;
};
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
return;
}
self.issue_lint(cx, expr);
},
hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
// CTFE already takes care of things like `-1` that do not overflow.
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
self.issue_lint(cx, expr);
}
},
_ => {},
}
}
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
match cx.tcx.hir().body_owner_kind(body_owner) {
hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => {
let body_span = cx.tcx.def_span(body_owner);
if let Some(span) = self.const_span && span.contains(body_span) {
return;
}
self.const_span = Some(body_span);
},
hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => {},
}
}
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
let body_owner = cx.tcx.hir().body_owner(body.id());
let body_span = cx.tcx.hir().span(body_owner);
if let Some(span) = self.const_span && span.contains(body_span) {
return;
}
self.const_span = None;
}
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if Some(expr.span) == self.expr_span {
self.expr_span = None;
}
}
}

View File

@ -0,0 +1,173 @@
#![allow(
// False positive
clippy::match_same_arms
)]
use super::ARITHMETIC_SIDE_EFFECTS;
use clippy_utils::{consts::constant_simple, diagnostics::span_lint};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::Ty;
use rustc_session::impl_lint_pass;
use rustc_span::source_map::{Span, Spanned};
const HARD_CODED_ALLOWED: &[&str] = &[
"f32",
"f64",
"std::num::Saturating",
"std::string::String",
"std::num::Wrapping",
];
#[derive(Debug)]
pub struct ArithmeticSideEffects {
allowed: FxHashSet<String>,
// Used to check whether expressions are constants, such as in enum discriminants and consts
const_span: Option<Span>,
expr_span: Option<Span>,
}
impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]);
impl ArithmeticSideEffects {
#[must_use]
pub fn new(mut allowed: FxHashSet<String>) -> Self {
allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from));
Self {
allowed,
const_span: None,
expr_span: None,
}
}
/// Checks assign operators (+=, -=, *=, /=) of integers in a non-constant environment that
/// won't overflow.
fn has_valid_assign_op(op: &Spanned<hir::BinOpKind>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
if !Self::is_literal_integer(rhs, rhs_refs) {
return false;
}
if let hir::BinOpKind::Div | hir::BinOpKind::Mul = op.node
&& let hir::ExprKind::Lit(ref lit) = rhs.kind
&& let ast::LitKind::Int(1, _) = lit.node
{
return true;
}
false
}
/// Checks "raw" binary operators (+, -, *, /) of integers in a non-constant environment
/// already handled by the CTFE.
fn has_valid_bin_op(lhs: &hir::Expr<'_>, lhs_refs: Ty<'_>, rhs: &hir::Expr<'_>, rhs_refs: Ty<'_>) -> bool {
Self::is_literal_integer(lhs, lhs_refs) && Self::is_literal_integer(rhs, rhs_refs)
}
/// Checks if the given `expr` has any of the inner `allowed` elements.
fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
self.allowed.contains(
cx.typeck_results()
.expr_ty(expr)
.to_string()
.split('<')
.next()
.unwrap_or_default(),
)
}
/// Explicit integers like `1` or `i32::MAX`. Does not take into consideration references.
fn is_literal_integer(expr: &hir::Expr<'_>, expr_refs: Ty<'_>) -> bool {
let is_integral = expr_refs.is_integral();
let is_literal = matches!(expr.kind, hir::ExprKind::Lit(_));
is_integral && is_literal
}
fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, "arithmetic detected");
self.expr_span = Some(expr.span);
}
/// Manages when the lint should be triggered. Operations in constant environments, hard coded
/// types, custom allowed types and non-constant operations that won't overflow are ignored.
fn manage_bin_ops(
&mut self,
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
op: &Spanned<hir::BinOpKind>,
lhs: &hir::Expr<'_>,
rhs: &hir::Expr<'_>,
) {
if constant_simple(cx, cx.typeck_results(), expr).is_some() {
return;
}
if !matches!(
op.node,
hir::BinOpKind::Add
| hir::BinOpKind::Sub
| hir::BinOpKind::Mul
| hir::BinOpKind::Div
| hir::BinOpKind::Rem
| hir::BinOpKind::Shl
| hir::BinOpKind::Shr
) {
return;
};
if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) {
return;
}
let lhs_refs = cx.typeck_results().expr_ty(lhs).peel_refs();
let rhs_refs = cx.typeck_results().expr_ty(rhs).peel_refs();
let has_valid_assign_op = Self::has_valid_assign_op(op, rhs, rhs_refs);
if has_valid_assign_op || Self::has_valid_bin_op(lhs, lhs_refs, rhs, rhs_refs) {
return;
}
self.issue_lint(cx, expr);
}
}
impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) {
return;
}
match &expr.kind {
hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => {
self.manage_bin_ops(cx, expr, op, lhs, rhs);
},
hir::ExprKind::Unary(hir::UnOp::Neg, _) => {
if constant_simple(cx, cx.typeck_results(), expr).is_none() {
self.issue_lint(cx, expr);
}
},
_ => {},
}
}
fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
let body_owner = cx.tcx.hir().body_owner(body.id());
let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner);
let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id);
if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind {
let body_span = cx.tcx.hir().span_with_body(body_owner);
if let Some(span) = self.const_span && span.contains(body_span) {
return;
}
self.const_span = Some(body_span);
}
}
fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
let body_owner = cx.tcx.hir().body_owner(body.id());
let body_span = cx.tcx.hir().span(body_owner);
if let Some(span) = self.const_span && span.contains(body_span) {
return;
}
self.const_span = None;
}
fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if Some(expr.span) == self.expr_span {
self.expr_span = None;
}
}
}

View File

@ -21,7 +21,7 @@ mod ptr_eq;
mod self_assignment;
mod verbose_bit_mask;
pub(crate) mod arithmetic;
pub(crate) mod arithmetic_side_effects;
use rustc_hir::{Body, Expr, ExprKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
@ -61,25 +61,29 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Checks for any kind of arithmetic operation of any type.
/// Checks any kind of arithmetic operation of any type.
///
/// Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust
/// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
/// or can panic (`/`, `%`). Known safe built-in types like `Wrapping` or `Saturing` are filtered
/// away.
/// or can panic (`/`, `%`).
///
/// Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant
/// environments, allowed types and non-constant operations that won't overflow are ignored.
///
/// ### Why is this bad?
/// Integer overflow will trigger a panic in debug builds or will wrap in
/// release mode. Division by zero will cause a panic in either mode. In some applications one
/// wants explicitly checked, wrapping or saturating arithmetic.
/// For integers, overflow will trigger a panic in debug builds or wrap the result in
/// release mode; division by zero will cause a panic in either mode. As a result, it is
/// desirable to explicitly call checked, wrapping or saturating arithmetic methods.
///
/// #### Example
/// ```rust
/// # let a = 0;
/// a + 1;
/// // `n` can be any number, including `i32::MAX`.
/// fn foo(n: i32) -> i32 {
/// n + 1
/// }
/// ```
///
/// Third-party types also tend to overflow.
/// Third-party types can also overflow or present unwanted side-effects.
///
/// #### Example
/// ```ignore,rust
@ -88,11 +92,11 @@ declare_clippy_lint! {
/// ```
///
/// ### Allowed types
/// Custom allowed types can be specified through the "arithmetic-allowed" filter.
/// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter.
#[clippy::version = "1.64.0"]
pub ARITHMETIC,
pub ARITHMETIC_SIDE_EFFECTS,
restriction,
"any arithmetic expression that could overflow or panic"
"any arithmetic expression that can cause side effects like overflows or panics"
}
declare_clippy_lint! {
@ -785,7 +789,7 @@ pub struct Operators {
}
impl_lint_pass!(Operators => [
ABSURD_EXTREME_COMPARISONS,
ARITHMETIC,
ARITHMETIC_SIDE_EFFECTS,
INTEGER_ARITHMETIC,
FLOAT_ARITHMETIC,
ASSIGN_OP_PATTERN,

View File

@ -69,7 +69,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
}
true
})
.visit_expr(&body.value);
.visit_expr(body.value);
if !panics.is_empty() {
span_lint_and_then(
cx,

View File

@ -507,7 +507,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
if let Some(args) = args
&& !args.is_empty()
&& body.map_or(true, |body| {
sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, &body.value)
sig.header.unsafety == Unsafety::Unsafe || contains_unsafe_block(cx, body.value)
})
{
span_lint_and_then(
@ -664,7 +664,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
results,
skip_count,
};
v.visit_expr(&body.value);
v.visit_expr(body.value);
v.results
}

View File

@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
} else {
RetReplacement::Empty
};
check_final_expr(cx, &body.value, Some(body.value.span), replacement);
check_final_expr(cx, body.value, Some(body.value.span), replacement);
},
FnKind::ItemFn(..) | FnKind::Method(..) => {
if let ExprKind::Block(block, _) = body.value.kind {

View File

@ -55,11 +55,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
if matches!(cx.tcx.def_kind(id.def_id), DefKind::Impl)
&& let item = cx.tcx.hir().item(id)
&& let ItemKind::Impl(Impl {
items,
of_trait,
self_ty,
..
}) = &item.kind
items,
of_trait,
self_ty,
..
}) = &item.kind
&& let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind
{
if !map.contains_key(res) {

View File

@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
]
.iter()
.find(|&(ts, _)| ts.iter().any(|&t| Ok(trait_id) == cx.tcx.lang_items().require(t)));
if count_binops(&body.value) == 1;
if count_binops(body.value) == 1;
then {
span_lint(
cx,

View File

@ -149,7 +149,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd {
let args = std::iter::once(receiver).chain(args.iter()).collect::<Vec<_>>();
for (i, trait_name) in arg_indices {
if i < args.len() {
match check_arg(cx, &args[i]) {
match check_arg(cx, args[i]) {
Some((span, None)) => {
span_lint(
cx,

View File

@ -50,7 +50,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
})
.collect::<Vec<_>>();
if !args_to_recover.is_empty() && !is_from_proc_macro(cx, expr) {
lint_unit_args(cx, expr, &args_to_recover.as_slice());
lint_unit_args(cx, expr, args_to_recover.as_slice());
}
}

View File

@ -115,7 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
// Check if all return expression respect the following condition and collect them.
let mut suggs = Vec::new();
let can_sugg = find_all_ret_expressions(cx, &body.value, |ret_expr| {
let can_sugg = find_all_ret_expressions(cx, body.value, |ret_expr| {
if_chain! {
if !ret_expr.span.from_expansion();
// Check if a function call.

View File

@ -160,7 +160,7 @@ fn collect_unwrap_info<'tcx>(
let name = method_name.ident.as_str();
if is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name);
then {
assert!(args.len() == 0);
assert!(args.is_empty());
let unwrappable = match name {
"is_some" | "is_ok" => true,
"is_err" | "is_none" => false,

View File

@ -83,7 +83,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
// check for `expect`
if let Some(arglists) = method_chain_args(expr, &["expect"]) {
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
{
@ -93,7 +93,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindExpectUnwrap<'a, 'tcx> {
// check for `unwrap`
if let Some(arglists) = method_chain_args(expr, &["unwrap"]) {
let receiver_ty = self.typeck_results.expr_ty(&arglists[0].0).peel_refs();
let receiver_ty = self.typeck_results.expr_ty(arglists[0].0).peel_refs();
if is_type_diagnostic_item(self.lcx, receiver_ty, sym::Option)
|| is_type_diagnostic_item(self.lcx, receiver_ty, sym::Result)
{
@ -114,7 +114,7 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tc
typeck_results: cx.tcx.typeck(impl_item.def_id),
result: Vec::new(),
};
fpu.visit_expr(&body.value);
fpu.visit_expr(body.value);
// if we've found one, lint
if !fpu.result.is_empty() {

View File

@ -142,7 +142,7 @@ fn check_item(cx: &LateContext<'_>, hir_id: HirId) {
let hir = cx.tcx.hir();
if let Some(body_id) = hir.maybe_body_owned_by(hir_id.expect_owner()) {
check_node(cx, hir_id, |v| {
v.expr(&v.bind("expr", &hir.body(body_id).value));
v.expr(&v.bind("expr", hir.body(body_id).value));
});
}
}

View File

@ -208,7 +208,7 @@ define_Conf! {
/// Lint: Arithmetic.
///
/// Suppress checking of the passed type names.
(arithmetic_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
(arithmetic_side_effects_allowed: rustc_data_structures::fx::FxHashSet<String> = <_>::default()),
/// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX.
///
/// Suppress lints whenever the suggested change would cause breakage for other crates.

View File

@ -505,7 +505,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
.hir_id(),
),
);
collector.visit_expr(&cx.tcx.hir().body(body_id).value);
collector.visit_expr(cx.tcx.hir().body(body_id).value);
}
}
}
@ -653,7 +653,7 @@ impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
}
if_chain! {
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind;
let fn_name = path.ident;
if let Some(sugg) = self.map.get(fn_name.as_str());
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
@ -685,9 +685,8 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
let method_names: Vec<&str> = method_names.iter().map(Symbol::as_str).collect();
if_chain! {
if let ["expn_data", "outer_expn"] = method_names.as_slice();
let args = arg_lists[1];
if args.len() == 1;
let self_arg = &args.0;
let (self_arg, args)= arg_lists[1];
if args.is_empty();
let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
if match_type(cx, self_ty, &paths::SYNTAX_CONTEXT);
then {
@ -734,30 +733,30 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
if and_then_args.len() == 5;
if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
let body = cx.tcx.hir().body(body);
let only_expr = peel_blocks_with_stmt(&body.value);
if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
if let ExprKind::Path(..) = span_call_args[0].kind;
let only_expr = peel_blocks_with_stmt(body.value);
if let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind;
if let ExprKind::Path(..) = recv.kind;
then {
let and_then_snippets = get_and_then_snippets(cx, and_then_args);
let mut sle = SpanlessEq::new(cx).deny_side_effects();
match ps.ident.as_str() {
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
"span_suggestion" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
suggest_suggestion(cx, expr, &and_then_snippets, &span_suggestion_snippets(cx, span_call_args));
},
"span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
"span_help" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), true);
},
"span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[1]) => {
let note_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
"span_note" if sle.eq_expr(&and_then_args[2], &span_call_args[0]) => {
let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), true);
},
"help" => {
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
let help_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
suggest_help(cx, expr, &and_then_snippets, help_snippet.borrow(), false);
}
"note" => {
let note_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
let note_snippet = snippet(cx, span_call_args[0].span, r#""...""#);
suggest_note(cx, expr, &and_then_snippets, note_snippet.borrow(), false);
}
_ => (),
@ -798,9 +797,9 @@ fn span_suggestion_snippets<'a, 'hir>(
cx: &LateContext<'_>,
span_call_args: &'hir [Expr<'hir>],
) -> SpanSuggestionSnippets<'a> {
let help_snippet = snippet(cx, span_call_args[2].span, r#""...""#);
let sugg_snippet = snippet(cx, span_call_args[3].span, "..");
let applicability_snippet = snippet(cx, span_call_args[4].span, "Applicability::MachineApplicable");
let help_snippet = snippet(cx, span_call_args[1].span, r#""...""#);
let sugg_snippet = snippet(cx, span_call_args[2].span, "..");
let applicability_snippet = snippet(cx, span_call_args[3].span, "Applicability::MachineApplicable");
SpanSuggestionSnippets {
help: help_snippet,
@ -954,7 +953,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
let body = cx.tcx.hir().body(body_id);
return path_to_matched_type(cx, &body.value);
return path_to_matched_type(cx, body.value);
}
}
},
@ -1046,7 +1045,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
if el_ty.is_str();
let body = cx.tcx.hir().body(body_id);
let typeck_results = cx.tcx.typeck_body(body_id);
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value);
let path: Vec<&str> = path.iter().map(|x| {
if let Constant::Str(s) = x {
s.as_str()
@ -1177,7 +1176,7 @@ impl InterningDefinedSymbol {
};
if_chain! {
// is a method call
if let ExprKind::MethodCall(_, [item], _) = call.kind;
if let ExprKind::MethodCall(_, item, [], _) = call.kind;
if let Some(did) = cx.typeck_results().type_dependent_def_id(call.hir_id);
let ty = cx.typeck_results().expr_ty(item);
// ...on either an Ident or a Symbol

View File

@ -1145,8 +1145,8 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
self.add_single_span_suggestion();
}
},
ExprKind::MethodCall(path, arg, _arg_span) => {
let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(&arg[0]));
ExprKind::MethodCall(path, recv, _, _arg_span) => {
let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
if match_type(self.cx, self_ty, &paths::DIAGNOSTIC_BUILDER) {
let called_method = path.ident.name.as_str().to_string();
for (method_name, is_multi_part) in &SUGGESTION_DIAGNOSTIC_BUILDER_METHODS {

View File

@ -86,7 +86,7 @@ impl VecPushSearcher {
},
ExprKind::Unary(UnOp::Deref, _) | ExprKind::Index(..) if !needs_mut => {
let mut last_place = parent;
while let Some(parent) = get_parent_expr(cx, parent) {
while let Some(parent) = get_parent_expr(cx, last_place) {
if matches!(parent.kind, ExprKind::Unary(UnOp::Deref, _) | ExprKind::Field(..))
|| matches!(parent.kind, ExprKind::Index(e, _) if e.hir_id == last_place.hir_id)
{

View File

@ -805,7 +805,11 @@ fn check_newlines(fmtstr: &StrLit) -> bool {
let contents = fmtstr.symbol.as_str();
let mut cb = |r: Range<usize>, c: Result<char, EscapeError>| {
let c = c.unwrap();
let c = match c {
Ok(c) => c,
Err(e) if !e.is_fatal() => return,
Err(e) => panic!("{:?}", e),
};
if r.end == contents.len() && c == '\n' && !last_was_cr && !has_internal_newline {
should_lint = true;

View File

@ -140,7 +140,7 @@ fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
(expr_search_pat(tcx, e).0, Pat::Str("await"))
},
ExprKind::Closure(&Closure { body, .. }) => (Pat::Str(""), expr_search_pat(tcx, &tcx.hir().body(body).value).1),
ExprKind::Closure(&Closure { body, .. }) => (Pat::Str(""), expr_search_pat(tcx, tcx.hir().body(body).value).1),
ExprKind::Block(
Block {
rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
@ -254,7 +254,7 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
let (start_pat, end_pat) = match kind {
FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")),
FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")),
FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, &body.value).1),
FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1),
};
let start_pat = match tcx.hir().get(hir_id) {
Node::Item(Item { vis_span, .. }) | Node::ImplItem(ImplItem { vis_span, .. }) => {

View File

@ -45,7 +45,7 @@ impl ops::BitOrAssign for EagernessSuggestion {
}
/// Determine the eagerness of the given function call.
fn fn_eagerness<'tcx>(cx: &LateContext<'tcx>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion {
fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion {
use EagernessSuggestion::{Eager, Lazy, NoChange};
let name = name.as_str();

View File

@ -201,8 +201,8 @@ impl HirEqInterExpr<'_, '_, '_> {
self.inner.cx.tcx.typeck_body(right),
));
let res = self.eq_expr(
&self.inner.cx.tcx.hir().body(left).value,
&self.inner.cx.tcx.hir().body(right).value,
self.inner.cx.tcx.hir().body(left).value,
self.inner.cx.tcx.hir().body(right).value,
);
self.inner.maybe_typeck_results = old_maybe_typeck_results;
res
@ -649,7 +649,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}) => {
std::mem::discriminant(&capture_clause).hash(&mut self.s);
// closures inherit TypeckResults
self.hash_expr(&self.cx.tcx.hir().body(body).value);
self.hash_expr(self.cx.tcx.hir().body(body).value);
},
ExprKind::Field(e, ref f) => {
self.hash_expr(e);
@ -1010,7 +1010,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
pub fn hash_body(&mut self, body_id: BodyId) {
// swap out TypeckResults when hashing a body
let old_maybe_typeck_results = self.maybe_typeck_results.replace(self.cx.tcx.typeck_body(body_id));
self.hash_expr(&self.cx.tcx.hir().body(body_id).value);
self.hash_expr(self.cx.tcx.hir().body(body_id).value);
self.maybe_typeck_results = old_maybe_typeck_results;
}
@ -1018,7 +1018,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
for arg in arg_list {
match *arg {
GenericArg::Lifetime(l) => self.hash_lifetime(l),
GenericArg::Type(ref ty) => self.hash_ty(ty),
GenericArg::Type(ty) => self.hash_ty(ty),
GenericArg::Const(ref ca) => self.hash_body(ca.value.body),
GenericArg::Infer(ref inf) => self.hash_ty(&inf.to_ty()),
}

View File

@ -1031,12 +1031,12 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'
v.allow_closure.then_some(v.captures)
}
/// Arguments of a method: the receiver and all the additional arguments.
pub type MethodArguments<'tcx> = Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>;
/// Returns the method names and argument list of nested method call expressions that make up
/// `expr`. method/span lists are sorted with the most recent call first.
pub fn method_calls<'tcx>(
expr: &'tcx Expr<'tcx>,
max_depth: usize,
) -> (Vec<Symbol>, Vec<(&'tcx Expr<'tcx>, &'tcx [Expr<'tcx>])>, Vec<Span>) {
pub fn method_calls<'tcx>(expr: &'tcx Expr<'tcx>, max_depth: usize) -> (Vec<Symbol>, MethodArguments<'tcx>, Vec<Span>) {
let mut method_names = Vec::with_capacity(max_depth);
let mut arg_lists = Vec::with_capacity(max_depth);
let mut spans = Vec::with_capacity(max_depth);

View File

@ -389,8 +389,10 @@ impl FormatString {
};
let mut unescaped = String::with_capacity(inner.len());
unescape_literal(inner, mode, &mut |_, ch| {
unescaped.push(ch.unwrap());
unescape_literal(inner, mode, &mut |_, ch| match ch {
Ok(ch) => unescaped.push(ch),
Err(e) if !e.is_fatal() => (),
Err(e) => panic!("{:?}", e),
});
let mut parts = Vec::new();

View File

@ -6,8 +6,8 @@
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
Body, CastKind, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator,
TerminatorKind, NonDivergingIntrinsic
Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
Terminator, TerminatorKind,
};
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
@ -212,9 +212,7 @@ fn check_statement<'tcx>(
check_place(tcx, **place, span, body)
},
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
check_operand(tcx, op, span, body)
},
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body),
StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
rustc_middle::mir::CopyNonOverlapping { dst, src, count },

View File

@ -274,7 +274,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
}
!found
})
.visit_expr(&cx.tcx.hir().body(body).value);
.visit_expr(cx.tcx.hir().body(body).value);
found
}
@ -568,6 +568,7 @@ pub fn for_each_local_use_after_expr<'tcx, B>(
// Calls the given function for every unconsumed temporary created by the expression. Note the
// function is only guaranteed to be called for types which need to be dropped, but it may be called
// for other types.
#[allow(clippy::too_many_lines)]
pub fn for_each_unconsumed_temporary<'tcx, B>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'tcx>,

View File

@ -1,6 +1,6 @@
[crates]
# some of these are from cargotest
cargo = {name = "cargo", versions = ['0.49.0']}
cargo = {name = "cargo", versions = ['0.64.0']}
iron = {name = "iron", versions = ['0.6.1']}
ripgrep = {name = "ripgrep", versions = ['12.1.1']}
xsv = {name = "xsv", versions = ['0.13.0']}

596
src/docs.rs Normal file
View File

@ -0,0 +1,596 @@
// autogenerated. Please look at /clippy_dev/src/update_lints.rs
macro_rules! include_lint {
($file_name: expr) => {
include_str!($file_name)
};
}
macro_rules! docs {
($($lint_name: expr,)*) => {
pub fn explain(lint: &str) {
println!("{}", match lint {
$(
$lint_name => include_lint!(concat!("docs/", concat!($lint_name, ".txt"))),
)*
_ => "unknown lint",
})
}
}
}
docs! {
"absurd_extreme_comparisons",
"alloc_instead_of_core",
"allow_attributes_without_reason",
"almost_complete_letter_range",
"almost_swapped",
"approx_constant",
"arithmetic_side_effects",
"as_conversions",
"as_underscore",
"assertions_on_constants",
"assertions_on_result_states",
"assign_op_pattern",
"async_yields_async",
"await_holding_invalid_type",
"await_holding_lock",
"await_holding_refcell_ref",
"bad_bit_mask",
"bind_instead_of_map",
"blanket_clippy_restriction_lints",
"blocks_in_if_conditions",
"bool_assert_comparison",
"bool_comparison",
"bool_to_int_with_if",
"borrow_as_ptr",
"borrow_deref_ref",
"borrow_interior_mutable_const",
"borrowed_box",
"box_collection",
"boxed_local",
"branches_sharing_code",
"builtin_type_shadow",
"bytes_count_to_len",
"bytes_nth",
"cargo_common_metadata",
"case_sensitive_file_extension_comparisons",
"cast_abs_to_unsigned",
"cast_enum_constructor",
"cast_enum_truncation",
"cast_lossless",
"cast_possible_truncation",
"cast_possible_wrap",
"cast_precision_loss",
"cast_ptr_alignment",
"cast_ref_to_mut",
"cast_sign_loss",
"cast_slice_different_sizes",
"cast_slice_from_raw_parts",
"char_lit_as_u8",
"chars_last_cmp",
"chars_next_cmp",
"checked_conversions",
"clone_double_ref",
"clone_on_copy",
"clone_on_ref_ptr",
"cloned_instead_of_copied",
"cmp_nan",
"cmp_null",
"cmp_owned",
"cognitive_complexity",
"collapsible_else_if",
"collapsible_if",
"collapsible_match",
"collapsible_str_replace",
"comparison_chain",
"comparison_to_empty",
"copy_iterator",
"crate_in_macro_def",
"create_dir",
"crosspointer_transmute",
"dbg_macro",
"debug_assert_with_mut_call",
"decimal_literal_representation",
"declare_interior_mutable_const",
"default_instead_of_iter_empty",
"default_numeric_fallback",
"default_trait_access",
"default_union_representation",
"deprecated_cfg_attr",
"deprecated_semver",
"deref_addrof",
"deref_by_slicing",
"derivable_impls",
"derive_hash_xor_eq",
"derive_ord_xor_partial_ord",
"derive_partial_eq_without_eq",
"disallowed_methods",
"disallowed_names",
"disallowed_script_idents",
"disallowed_types",
"diverging_sub_expression",
"doc_link_with_quotes",
"doc_markdown",
"double_comparisons",
"double_must_use",
"double_neg",
"double_parens",
"drop_copy",
"drop_non_drop",
"drop_ref",
"duplicate_mod",
"duplicate_underscore_argument",
"duration_subsec",
"else_if_without_else",
"empty_drop",
"empty_enum",
"empty_line_after_outer_attr",
"empty_loop",
"empty_structs_with_brackets",
"enum_clike_unportable_variant",
"enum_glob_use",
"enum_variant_names",
"eq_op",
"equatable_if_let",
"erasing_op",
"err_expect",
"excessive_precision",
"exhaustive_enums",
"exhaustive_structs",
"exit",
"expect_fun_call",
"expect_used",
"expl_impl_clone_on_copy",
"explicit_auto_deref",
"explicit_counter_loop",
"explicit_deref_methods",
"explicit_into_iter_loop",
"explicit_iter_loop",
"explicit_write",
"extend_with_drain",
"extra_unused_lifetimes",
"fallible_impl_from",
"field_reassign_with_default",
"filetype_is_file",
"filter_map_identity",
"filter_map_next",
"filter_next",
"flat_map_identity",
"flat_map_option",
"float_arithmetic",
"float_cmp",
"float_cmp_const",
"float_equality_without_abs",
"fn_address_comparisons",
"fn_params_excessive_bools",
"fn_to_numeric_cast",
"fn_to_numeric_cast_any",
"fn_to_numeric_cast_with_truncation",
"for_kv_map",
"for_loops_over_fallibles",
"forget_copy",
"forget_non_drop",
"forget_ref",
"format_in_format_args",
"format_push_string",
"from_iter_instead_of_collect",
"from_over_into",
"from_str_radix_10",
"future_not_send",
"get_first",
"get_last_with_len",
"get_unwrap",
"identity_op",
"if_let_mutex",
"if_not_else",
"if_same_then_else",
"if_then_some_else_none",
"ifs_same_cond",
"implicit_clone",
"implicit_hasher",
"implicit_return",
"implicit_saturating_sub",
"imprecise_flops",
"inconsistent_digit_grouping",
"inconsistent_struct_constructor",
"index_refutable_slice",
"indexing_slicing",
"ineffective_bit_mask",
"inefficient_to_string",
"infallible_destructuring_match",
"infinite_iter",
"inherent_to_string",
"inherent_to_string_shadow_display",
"init_numbered_fields",
"inline_always",
"inline_asm_x86_att_syntax",
"inline_asm_x86_intel_syntax",
"inline_fn_without_body",
"inspect_for_each",
"int_plus_one",
"integer_arithmetic",
"integer_division",
"into_iter_on_ref",
"invalid_null_ptr_usage",
"invalid_regex",
"invalid_upcast_comparisons",
"invalid_utf8_in_unchecked",
"invisible_characters",
"is_digit_ascii_radix",
"items_after_statements",
"iter_cloned_collect",
"iter_count",
"iter_next_loop",
"iter_next_slice",
"iter_not_returning_iterator",
"iter_nth",
"iter_nth_zero",
"iter_on_empty_collections",
"iter_on_single_items",
"iter_overeager_cloned",
"iter_skip_next",
"iter_with_drain",
"iterator_step_by_zero",
"just_underscores_and_digits",
"large_const_arrays",
"large_digit_groups",
"large_enum_variant",
"large_include_file",
"large_stack_arrays",
"large_types_passed_by_value",
"len_without_is_empty",
"len_zero",
"let_and_return",
"let_underscore_drop",
"let_underscore_lock",
"let_underscore_must_use",
"let_unit_value",
"linkedlist",
"lossy_float_literal",
"macro_use_imports",
"main_recursion",
"manual_assert",
"manual_async_fn",
"manual_bits",
"manual_filter_map",
"manual_find",
"manual_find_map",
"manual_flatten",
"manual_instant_elapsed",
"manual_map",
"manual_memcpy",
"manual_non_exhaustive",
"manual_ok_or",
"manual_range_contains",
"manual_rem_euclid",
"manual_retain",
"manual_saturating_arithmetic",
"manual_split_once",
"manual_str_repeat",
"manual_string_new",
"manual_strip",
"manual_swap",
"manual_unwrap_or",
"many_single_char_names",
"map_clone",
"map_collect_result_unit",
"map_entry",
"map_err_ignore",
"map_flatten",
"map_identity",
"map_unwrap_or",
"match_as_ref",
"match_bool",
"match_like_matches_macro",
"match_on_vec_items",
"match_overlapping_arm",
"match_ref_pats",
"match_result_ok",
"match_same_arms",
"match_single_binding",
"match_str_case_mismatch",
"match_wild_err_arm",
"match_wildcard_for_single_variants",
"maybe_infinite_iter",
"mem_forget",
"mem_replace_option_with_none",
"mem_replace_with_default",
"mem_replace_with_uninit",
"min_max",
"mismatched_target_os",
"mismatching_type_param_order",
"misrefactored_assign_op",
"missing_const_for_fn",
"missing_docs_in_private_items",
"missing_enforced_import_renames",
"missing_errors_doc",
"missing_inline_in_public_items",
"missing_panics_doc",
"missing_safety_doc",
"missing_spin_loop",
"mistyped_literal_suffixes",
"mixed_case_hex_literals",
"mixed_read_write_in_expression",
"mod_module_files",
"module_inception",
"module_name_repetitions",
"modulo_arithmetic",
"modulo_one",
"multi_assignments",
"multiple_crate_versions",
"multiple_inherent_impl",
"must_use_candidate",
"must_use_unit",
"mut_from_ref",
"mut_mut",
"mut_mutex_lock",
"mut_range_bound",
"mutable_key_type",
"mutex_atomic",
"mutex_integer",
"naive_bytecount",
"needless_arbitrary_self_type",
"needless_bitwise_bool",
"needless_bool",
"needless_borrow",
"needless_borrowed_reference",
"needless_collect",
"needless_continue",
"needless_doctest_main",
"needless_for_each",
"needless_late_init",
"needless_lifetimes",
"needless_match",
"needless_option_as_deref",
"needless_option_take",
"needless_parens_on_range_literals",
"needless_pass_by_value",
"needless_question_mark",
"needless_range_loop",
"needless_return",
"needless_splitn",
"needless_update",
"neg_cmp_op_on_partial_ord",
"neg_multiply",
"negative_feature_names",
"never_loop",
"new_ret_no_self",
"new_without_default",
"no_effect",
"no_effect_replace",
"no_effect_underscore_binding",
"non_ascii_literal",
"non_octal_unix_permissions",
"non_send_fields_in_send_ty",
"nonminimal_bool",
"nonsensical_open_options",
"nonstandard_macro_braces",
"not_unsafe_ptr_arg_deref",
"obfuscated_if_else",
"octal_escapes",
"ok_expect",
"only_used_in_recursion",
"op_ref",
"option_as_ref_deref",
"option_env_unwrap",
"option_filter_map",
"option_if_let_else",
"option_map_or_none",
"option_map_unit_fn",
"option_option",
"or_fun_call",
"or_then_unwrap",
"out_of_bounds_indexing",
"overflow_check_conditional",
"overly_complex_bool_expr",
"panic",
"panic_in_result_fn",
"panicking_unwrap",
"partialeq_ne_impl",
"partialeq_to_none",
"path_buf_push_overwrite",
"pattern_type_mismatch",
"positional_named_format_parameters",
"possible_missing_comma",
"precedence",
"print_in_format_impl",
"print_literal",
"print_stderr",
"print_stdout",
"print_with_newline",
"println_empty_string",
"ptr_arg",
"ptr_as_ptr",
"ptr_eq",
"ptr_offset_with_cast",
"pub_use",
"question_mark",
"range_minus_one",
"range_plus_one",
"range_zip_with_len",
"rc_buffer",
"rc_clone_in_vec_init",
"rc_mutex",
"read_zero_byte_vec",
"recursive_format_impl",
"redundant_allocation",
"redundant_clone",
"redundant_closure",
"redundant_closure_call",
"redundant_closure_for_method_calls",
"redundant_else",
"redundant_feature_names",
"redundant_field_names",
"redundant_pattern",
"redundant_pattern_matching",
"redundant_pub_crate",
"redundant_slicing",
"redundant_static_lifetimes",
"ref_binding_to_reference",
"ref_option_ref",
"repeat_once",
"rest_pat_in_fully_bound_structs",
"result_large_err",
"result_map_or_into_option",
"result_map_unit_fn",
"result_unit_err",
"return_self_not_must_use",
"reversed_empty_ranges",
"same_functions_in_if_condition",
"same_item_push",
"same_name_method",
"search_is_some",
"self_assignment",
"self_named_constructors",
"self_named_module_files",
"semicolon_if_nothing_returned",
"separated_literal_suffix",
"serde_api_misuse",
"shadow_reuse",
"shadow_same",
"shadow_unrelated",
"short_circuit_statement",
"should_implement_trait",
"significant_drop_in_scrutinee",
"similar_names",
"single_char_add_str",
"single_char_lifetime_names",
"single_char_pattern",
"single_component_path_imports",
"single_element_loop",
"single_match",
"single_match_else",
"size_of_in_element_count",
"skip_while_next",
"slow_vector_initialization",
"stable_sort_primitive",
"std_instead_of_alloc",
"std_instead_of_core",
"str_to_string",
"string_add",
"string_add_assign",
"string_extend_chars",
"string_from_utf8_as_bytes",
"string_lit_as_bytes",
"string_slice",
"string_to_string",
"strlen_on_c_strings",
"struct_excessive_bools",
"suboptimal_flops",
"suspicious_arithmetic_impl",
"suspicious_assignment_formatting",
"suspicious_else_formatting",
"suspicious_map",
"suspicious_op_assign_impl",
"suspicious_operation_groupings",
"suspicious_splitn",
"suspicious_to_owned",
"suspicious_unary_op_formatting",
"swap_ptr_to_ref",
"tabs_in_doc_comments",
"temporary_assignment",
"to_digit_is_some",
"to_string_in_format_args",
"todo",
"too_many_arguments",
"too_many_lines",
"toplevel_ref_arg",
"trailing_empty_array",
"trait_duplication_in_bounds",
"transmute_bytes_to_str",
"transmute_float_to_int",
"transmute_int_to_bool",
"transmute_int_to_char",
"transmute_int_to_float",
"transmute_num_to_bytes",
"transmute_ptr_to_ptr",
"transmute_ptr_to_ref",
"transmute_undefined_repr",
"transmutes_expressible_as_ptr_casts",
"transmuting_null",
"trim_split_whitespace",
"trivial_regex",
"trivially_copy_pass_by_ref",
"try_err",
"type_complexity",
"type_repetition_in_bounds",
"undocumented_unsafe_blocks",
"undropped_manually_drops",
"unicode_not_nfc",
"unimplemented",
"uninit_assumed_init",
"uninit_vec",
"unit_arg",
"unit_cmp",
"unit_hash",
"unit_return_expecting_ord",
"unnecessary_cast",
"unnecessary_filter_map",
"unnecessary_find_map",
"unnecessary_fold",
"unnecessary_join",
"unnecessary_lazy_evaluations",
"unnecessary_mut_passed",
"unnecessary_operation",
"unnecessary_owned_empty_strings",
"unnecessary_self_imports",
"unnecessary_sort_by",
"unnecessary_to_owned",
"unnecessary_unwrap",
"unnecessary_wraps",
"unneeded_field_pattern",
"unneeded_wildcard_pattern",
"unnested_or_patterns",
"unreachable",
"unreadable_literal",
"unsafe_derive_deserialize",
"unsafe_removed_from_name",
"unseparated_literal_suffix",
"unsound_collection_transmute",
"unused_async",
"unused_io_amount",
"unused_peekable",
"unused_rounding",
"unused_self",
"unused_unit",
"unusual_byte_groupings",
"unwrap_in_result",
"unwrap_or_else_default",
"unwrap_used",
"upper_case_acronyms",
"use_debug",
"use_self",
"used_underscore_binding",
"useless_asref",
"useless_attribute",
"useless_conversion",
"useless_format",
"useless_let_if_seq",
"useless_transmute",
"useless_vec",
"vec_box",
"vec_init_then_push",
"vec_resize_to_zero",
"verbose_bit_mask",
"verbose_file_reads",
"vtable_address_comparisons",
"while_immutable_condition",
"while_let_loop",
"while_let_on_iterator",
"wildcard_dependencies",
"wildcard_enum_match_arm",
"wildcard_imports",
"wildcard_in_or_patterns",
"write_literal",
"write_with_newline",
"writeln_empty_string",
"wrong_self_convention",
"wrong_transmute",
"zero_divided_by_zero",
"zero_prefixed_literal",
"zero_ptr",
"zero_sized_map_values",
"zst_offset",
}

View File

@ -0,0 +1,25 @@
### What it does
Checks for comparisons where one side of the relation is
either the minimum or maximum value for its type and warns if it involves a
case that is always true or always false. Only integer and boolean types are
checked.
### Why is this bad?
An expression like `min <= x` may misleadingly imply
that it is possible for `x` to be less than the minimum. Expressions like
`max < x` are probably mistakes.
### Known problems
For `usize` the size of the current compile target will
be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
a comparison to detect target pointer width will trigger this lint. One can
use `mem::sizeof` and compare its value or conditional compilation
attributes
like `#[cfg(target_pointer_width = "64")] ..` instead.
### Example
```
let vec: Vec<isize> = Vec::new();
if vec.len() <= 0 {}
if 100 > i32::MAX {}
```

View File

@ -0,0 +1,18 @@
### What it does
Finds items imported through `alloc` when available through `core`.
### Why is this bad?
Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
is also useful for crates migrating to become `no_std` compatible.
### Example
```
use alloc::slice::from_ref;
```
Use instead:
```
use core::slice::from_ref;
```

View File

@ -0,0 +1,22 @@
### What it does
Checks for attributes that allow lints without a reason.
(This requires the `lint_reasons` feature)
### Why is this bad?
Allowing a lint should always have a reason. This reason should be documented to
ensure that others understand the reasoning
### Example
```
#![feature(lint_reasons)]
#![allow(clippy::some_lint)]
```
Use instead:
```
#![feature(lint_reasons)]
#![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
```

View File

@ -0,0 +1,15 @@
### What it does
Checks for ranges which almost include the entire range of letters from 'a' to 'z', but
don't because they're a half open range.
### Why is this bad?
This (`'a'..'z'`) is almost certainly a typo meant to include all letters.
### Example
```
let _ = 'a'..'z';
```
Use instead:
```
let _ = 'a'..='z';
```

View File

@ -0,0 +1,15 @@
### What it does
Checks for `foo = bar; bar = foo` sequences.
### Why is this bad?
This looks like a failed attempt to swap.
### Example
```
a = b;
b = a;
```
If swapping is intended, use `swap()` instead:
```
std::mem::swap(&mut a, &mut b);
```

View File

@ -0,0 +1,24 @@
### What it does
Checks for floating point literals that approximate
constants which are defined in
[`std::f32::consts`](https://doc.rust-lang.org/stable/std/f32/consts/#constants)
or
[`std::f64::consts`](https://doc.rust-lang.org/stable/std/f64/consts/#constants),
respectively, suggesting to use the predefined constant.
### Why is this bad?
Usually, the definition in the standard library is more
precise than what people come up with. If you find that your definition is
actually more precise, please [file a Rust
issue](https://github.com/rust-lang/rust/issues).
### Example
```
let x = 3.14;
let y = 1_f64 / x;
```
Use instead:
```
let x = std::f32::consts::PI;
let y = std::f64::consts::FRAC_1_PI;
```

View File

@ -0,0 +1,33 @@
### What it does
Checks any kind of arithmetic operation of any type.
Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust
Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
or can panic (`/`, `%`).
Known safe built-in types like `Wrapping` or `Saturing`, floats, operations in constant
environments, allowed types and non-constant operations that won't overflow are ignored.
### Why is this bad?
For integers, overflow will trigger a panic in debug builds or wrap the result in
release mode; division by zero will cause a panic in either mode. As a result, it is
desirable to explicitly call checked, wrapping or saturating arithmetic methods.
#### Example
```
// `n` can be any number, including `i32::MAX`.
fn foo(n: i32) -> i32 {
n + 1
}
```
Third-party types can also overflow or present unwanted side-effects.
#### Example
```
use rust_decimal::Decimal;
let _n = Decimal::MAX + Decimal::MAX;
```
### Allowed types
Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter.

View File

@ -0,0 +1,32 @@
### What it does
Checks for usage of `as` conversions.
Note that this lint is specialized in linting *every single* use of `as`
regardless of whether good alternatives exist or not.
If you want more precise lints for `as`, please consider using these separate lints:
`unnecessary_cast`, `cast_lossless/cast_possible_truncation/cast_possible_wrap/cast_precision_loss/cast_sign_loss`,
`fn_to_numeric_cast(_with_truncation)`, `char_lit_as_u8`, `ref_to_mut` and `ptr_as_ptr`.
There is a good explanation the reason why this lint should work in this way and how it is useful
[in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
### Why is this bad?
`as` conversions will perform many kinds of
conversions, including silently lossy conversions and dangerous coercions.
There are cases when it makes sense to use `as`, so the lint is
Allow by default.
### Example
```
let a: u32;
...
f(a as u16);
```
Use instead:
```
f(a.try_into()?);
// or
f(a.try_into().expect("Unexpected u16 overflow in f"));
```

View File

@ -0,0 +1,21 @@
### What it does
Check for the usage of `as _` conversion using inferred type.
### Why is this bad?
The conversion might include lossy conversion and dangerous cast that might go
undetected due to the type being inferred.
The lint is allowed by default as using `_` is less wordy than always specifying the type.
### Example
```
fn foo(n: usize) {}
let n: u16 = 256;
foo(n as _);
```
Use instead:
```
fn foo(n: usize) {}
let n: u16 = 256;
foo(n as usize);
```

View File

@ -0,0 +1,14 @@
### What it does
Checks for `assert!(true)` and `assert!(false)` calls.
### Why is this bad?
Will be optimized out by the compiler or should probably be replaced by a
`panic!()` or `unreachable!()`
### Example
```
assert!(false)
assert!(true)
const B: bool = false;
assert!(B)
```

View File

@ -0,0 +1,14 @@
### What it does
Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls.
### Why is this bad?
An assertion failure cannot output an useful message of the error.
### Known problems
The suggested replacement decreases the readability of code and log output.
### Example
```
assert!(r.is_ok());
assert!(r.is_err());
```

View File

@ -0,0 +1,28 @@
### What it does
Checks for `a = a op b` or `a = b commutative_op a`
patterns.
### Why is this bad?
These can be written as the shorter `a op= b`.
### Known problems
While forbidden by the spec, `OpAssign` traits may have
implementations that differ from the regular `Op` impl.
### Example
```
let mut a = 5;
let b = 0;
// ...
a = a + b;
```
Use instead:
```
let mut a = 5;
let b = 0;
// ...
a += b;
```

View File

@ -0,0 +1,28 @@
### What it does
Checks for async blocks that yield values of types
that can themselves be awaited.
### Why is this bad?
An await is likely missing.
### Example
```
async fn foo() {}
fn bar() {
let x = async {
foo()
};
}
```
Use instead:
```
async fn foo() {}
fn bar() {
let x = async {
foo().await
};
}
```

View File

@ -0,0 +1,29 @@
### What it does
Allows users to configure types which should not be held across `await`
suspension points.
### Why is this bad?
There are some types which are perfectly "safe" to be used concurrently
from a memory access perspective but will cause bugs at runtime if they
are held in such a way.
### Example
```
await-holding-invalid-types = [
# You can specify a type name
"CustomLockType",
# You can (optionally) specify a reason
{ path = "OtherCustomLockType", reason = "Relies on a thread local" }
]
```
```
struct CustomLockType;
struct OtherCustomLockType;
async fn foo() {
let _x = CustomLockType;
let _y = OtherCustomLockType;
baz().await; // Lint violation
}
```

View File

@ -0,0 +1,51 @@
### What it does
Checks for calls to await while holding a non-async-aware MutexGuard.
### Why is this bad?
The Mutex types found in std::sync and parking_lot
are not designed to operate in an async context across await points.
There are two potential solutions. One is to use an async-aware Mutex
type. Many asynchronous foundation crates provide such a Mutex type. The
other solution is to ensure the mutex is unlocked before calling await,
either by introducing a scope or an explicit call to Drop::drop.
### Known problems
Will report false positive for explicitly dropped guards
([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
### Example
```
async fn foo(x: &Mutex<u32>) {
let mut guard = x.lock().unwrap();
*guard += 1;
baz().await;
}
async fn bar(x: &Mutex<u32>) {
let mut guard = x.lock().unwrap();
*guard += 1;
drop(guard); // explicit drop
baz().await;
}
```
Use instead:
```
async fn foo(x: &Mutex<u32>) {
{
let mut guard = x.lock().unwrap();
*guard += 1;
}
baz().await;
}
async fn bar(x: &Mutex<u32>) {
{
let mut guard = x.lock().unwrap();
*guard += 1;
} // guard dropped here at end of scope
baz().await;
}
```

Some files were not shown because too many files have changed in this diff Show More