mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 15:54:15 +00:00
Merge remote-tracking branch 'upstream/auto' into rustup
This commit is contained in:
commit
4ee55c5528
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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()
|
||||
+ &[
|
||||
|
@ -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 }
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
125
clippy_lints/src/bool_to_int_with_if.rs
Normal file
125
clippy_lints/src/bool_to_int_with_if.rs
Normal 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()
|
||||
}
|
@ -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),
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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),
|
||||
|
@ -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`
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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, ..) => {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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, [], _) => {
|
||||
|
@ -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 {
|
||||
|
@ -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"),
|
||||
|
@ -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;
|
||||
|
@ -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) =>
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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};
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
173
clippy_lints/src/operators/arithmetic_side_effects.rs
Normal file
173
clippy_lints/src/operators/arithmetic_side_effects.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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() {
|
||||
|
@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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, .. }) => {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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()),
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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 },
|
||||
|
@ -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>,
|
||||
|
@ -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
596
src/docs.rs
Normal 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",
|
||||
|
||||
}
|
25
src/docs/absurd_extreme_comparisons.txt
Normal file
25
src/docs/absurd_extreme_comparisons.txt
Normal 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 {}
|
||||
```
|
18
src/docs/alloc_instead_of_core.txt
Normal file
18
src/docs/alloc_instead_of_core.txt
Normal 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;
|
||||
```
|
22
src/docs/allow_attributes_without_reason.txt
Normal file
22
src/docs/allow_attributes_without_reason.txt
Normal 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")]
|
||||
```
|
15
src/docs/almost_complete_letter_range.txt
Normal file
15
src/docs/almost_complete_letter_range.txt
Normal 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';
|
||||
```
|
15
src/docs/almost_swapped.txt
Normal file
15
src/docs/almost_swapped.txt
Normal 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);
|
||||
```
|
24
src/docs/approx_constant.txt
Normal file
24
src/docs/approx_constant.txt
Normal 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;
|
||||
```
|
33
src/docs/arithmetic_side_effects.txt
Normal file
33
src/docs/arithmetic_side_effects.txt
Normal 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.
|
32
src/docs/as_conversions.txt
Normal file
32
src/docs/as_conversions.txt
Normal 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"));
|
||||
```
|
21
src/docs/as_underscore.txt
Normal file
21
src/docs/as_underscore.txt
Normal 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);
|
||||
```
|
14
src/docs/assertions_on_constants.txt
Normal file
14
src/docs/assertions_on_constants.txt
Normal 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)
|
||||
```
|
14
src/docs/assertions_on_result_states.txt
Normal file
14
src/docs/assertions_on_result_states.txt
Normal 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());
|
||||
```
|
28
src/docs/assign_op_pattern.txt
Normal file
28
src/docs/assign_op_pattern.txt
Normal 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;
|
||||
```
|
28
src/docs/async_yields_async.txt
Normal file
28
src/docs/async_yields_async.txt
Normal 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
|
||||
};
|
||||
}
|
||||
```
|
29
src/docs/await_holding_invalid_type.txt
Normal file
29
src/docs/await_holding_invalid_type.txt
Normal 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
|
||||
}
|
||||
```
|
51
src/docs/await_holding_lock.txt
Normal file
51
src/docs/await_holding_lock.txt
Normal 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
Loading…
Reference in New Issue
Block a user