mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
clippy_dev: Sort lint and lint pass declarations (parsing revamp part 5/N) (#15979)
Based on rust-lang/rust-clippy#15978 This will prevent some pointless merge conflict when multiple people add lints to the same lint pass (e.g. methods). changelog: None
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
use crate::parse::cursor::{self, Capture, Cursor};
|
||||
use crate::parse::{ActiveLint, DeprecatedLint, Lint, LintData, LintName, ParseCx, RenamedLint};
|
||||
use crate::update_lints::generate_lint_files;
|
||||
use crate::utils::{
|
||||
ErrAction, FileUpdater, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists,
|
||||
expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target,
|
||||
@@ -40,7 +39,7 @@ pub fn deprecate<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, name
|
||||
};
|
||||
|
||||
remove_lint_declaration(name, &prev_lint, &data, &mut FileUpdater::default());
|
||||
generate_lint_files(UpdateMode::Change, &data);
|
||||
data.gen_decls(UpdateMode::Change);
|
||||
println!("info: `{name}` has successfully been deprecated");
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
}
|
||||
@@ -74,7 +73,7 @@ pub fn uplift<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam
|
||||
updater.update_file(e.path(), &mut update_fn);
|
||||
}
|
||||
}
|
||||
generate_lint_files(UpdateMode::Change, &data);
|
||||
data.gen_decls(UpdateMode::Change);
|
||||
println!("info: `{old_name}` has successfully been uplifted as `{new_name}`");
|
||||
println!("note: you must run `cargo uitest` to update the test results");
|
||||
}
|
||||
@@ -151,7 +150,7 @@ pub fn rename<'cx, 'env: 'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_nam
|
||||
updater.update_file(e.path(), &mut update_fn);
|
||||
}
|
||||
}
|
||||
generate_lint_files(UpdateMode::Change, &data);
|
||||
data.gen_decls(UpdateMode::Change);
|
||||
|
||||
println!("Renamed `{old_name}` to `{new_name}`");
|
||||
println!("All code referencing the old name has been updated");
|
||||
@@ -172,11 +171,11 @@ fn remove_lint_declaration(name: &str, lint: &ActiveLint<'_>, data: &LintData<'_
|
||||
delete_file_if_exists(lint.path.as_ref())
|
||||
} else {
|
||||
updater.update_file(&lint.path, &mut |_, src, dst| -> UpdateStatus {
|
||||
let mut start = &src[..lint.declaration_range.start];
|
||||
let mut start = &src[..lint.declaration_range.start as usize];
|
||||
if start.ends_with("\n\n") {
|
||||
start = &start[..start.len() - 1];
|
||||
}
|
||||
let mut end = &src[lint.declaration_range.end..];
|
||||
let mut end = &src[lint.declaration_range.end as usize..];
|
||||
if end.starts_with("\n\n") {
|
||||
end = &end[1..];
|
||||
}
|
||||
|
||||
+29
-1
@@ -1,3 +1,6 @@
|
||||
use crate::generate::gen_sorted_lints_file;
|
||||
use crate::new_parse_cx;
|
||||
use crate::parse::VecBuf;
|
||||
use crate::utils::{
|
||||
ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads,
|
||||
walk_dir_no_dot_or_target,
|
||||
@@ -326,10 +329,35 @@ fn run_rustfmt(update_mode: UpdateMode) {
|
||||
|
||||
// the "main" function of cargo dev fmt
|
||||
pub fn run(update_mode: UpdateMode) {
|
||||
run_rustfmt(update_mode);
|
||||
fmt_syms(update_mode);
|
||||
if let Err(e) = fmt_conf(update_mode.is_check()) {
|
||||
e.display();
|
||||
process::exit(1);
|
||||
}
|
||||
|
||||
new_parse_cx(|cx| {
|
||||
let mut data = cx.parse_lint_decls();
|
||||
let (mut lints, passes) = data.split_by_lint_file();
|
||||
let mut updater = FileUpdater::default();
|
||||
let mut ranges = VecBuf::with_capacity(256);
|
||||
|
||||
for passes in passes {
|
||||
let path = passes[0].path.clone();
|
||||
let mut lints = lints.remove(&*path);
|
||||
let lints = lints.as_deref_mut().unwrap_or_default();
|
||||
updater.update_file_checked("cargo dev fmt", update_mode, &path, &mut |_, src, dst| {
|
||||
gen_sorted_lints_file(src, dst, lints, passes, &mut ranges);
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
});
|
||||
}
|
||||
|
||||
for (&path, lints) in &mut lints {
|
||||
updater.update_file_checked("cargo dev fmt", update_mode, path, &mut |_, src, dst| {
|
||||
gen_sorted_lints_file(src, dst, lints, &mut [], &mut ranges);
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
run_rustfmt(update_mode);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
use crate::parse::cursor::Cursor;
|
||||
use crate::parse::{Lint, LintData, LintPass, VecBuf};
|
||||
use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn};
|
||||
use core::range::Range;
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
use std::path::{self, Path};
|
||||
|
||||
const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
|
||||
// Use that command to update this file and do not edit by hand.\n\
|
||||
// Manual edits will be overwritten.\n\n";
|
||||
|
||||
const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
|
||||
|
||||
impl LintData<'_> {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn gen_decls(&self, update_mode: UpdateMode) {
|
||||
let mut updater = FileUpdater::default();
|
||||
|
||||
let mut lints: Vec<_> = self.lints.iter().map(|(&x, y)| (x, y)).collect();
|
||||
lints.sort_by_key(|&(x, _)| x);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"CHANGELOG.md",
|
||||
&mut update_text_region_fn(
|
||||
"<!-- begin autogenerated links to lint list -->\n",
|
||||
"<!-- end autogenerated links to lint list -->",
|
||||
|dst| {
|
||||
for &(lint, _) in &lints {
|
||||
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let mut active = Vec::with_capacity(lints.len());
|
||||
let mut deprecated = Vec::with_capacity(lints.len() / 8);
|
||||
let mut renamed = Vec::with_capacity(lints.len() / 8);
|
||||
for &(name, lint) in &lints {
|
||||
match lint {
|
||||
Lint::Active(lint) => active.push((name, lint)),
|
||||
Lint::Deprecated(lint) => deprecated.push((name, lint)),
|
||||
Lint::Renamed(lint) => renamed.push((name, lint)),
|
||||
}
|
||||
}
|
||||
active.sort_by_key(|&(_, lint)| lint.module);
|
||||
|
||||
// Round to avoid updating the readme every time a lint is added/deprecated.
|
||||
let lint_count = active.len() / 50 * 50;
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"book/src/README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"clippy_lints/src/deprecated_lints.rs",
|
||||
&mut |_, src, dst| {
|
||||
let mut cursor = Cursor::new(src);
|
||||
assert!(
|
||||
cursor.find_ident("declare_with_version").is_some()
|
||||
&& cursor.find_ident("declare_with_version").is_some(),
|
||||
"error reading deprecated lints"
|
||||
);
|
||||
dst.push_str(&src[..cursor.pos() as usize]);
|
||||
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
|
||||
for &(name, data) in &deprecated {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.reason,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str(
|
||||
"]}\n\n\
|
||||
#[rustfmt::skip]\n\
|
||||
declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
|
||||
",
|
||||
);
|
||||
for &(name, data) in &renamed {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.new_name,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str("]}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/deprecated.rs",
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
for &(lint, _) in &deprecated {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/rename.rs",
|
||||
&mut move |_, src, dst| {
|
||||
let mut seen_lints = HashSet::new();
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
|
||||
for &(_, lint) in &renamed {
|
||||
if seen_lints.insert(lint.new_name) {
|
||||
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
|
||||
}
|
||||
}
|
||||
for &(lint, _) in &renamed {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| {
|
||||
let Some(path::Component::Normal(name)) = lint.path.components().next() else {
|
||||
// All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
|
||||
panic!(
|
||||
"internal error: can't read crate name from path `{}`",
|
||||
lint.path.display()
|
||||
);
|
||||
};
|
||||
name
|
||||
}) {
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/lib.rs"),
|
||||
&mut update_text_region_fn(
|
||||
"// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
|
||||
"// end lints modules, do not remove this comment, it's used in `update_lints`",
|
||||
|dst| {
|
||||
let mut prev = "";
|
||||
for &(_, lint) in &lints {
|
||||
if lint.module != prev {
|
||||
writeln!(dst, "mod {};", lint.module).unwrap();
|
||||
prev = lint.module;
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/declared_lints.rs"),
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
|
||||
let mut buf = String::new();
|
||||
for &(name, lint) in &lints {
|
||||
buf.clear();
|
||||
buf.push_str(name);
|
||||
buf.make_ascii_uppercase();
|
||||
if lint.module.is_empty() {
|
||||
writeln!(dst, " crate::{buf}_INFO,").unwrap();
|
||||
} else {
|
||||
writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap();
|
||||
}
|
||||
}
|
||||
dst.push_str("];\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LintPass<'_> {
|
||||
pub fn gen_mac(&self, dst: &mut String) {
|
||||
let mut line_start = dst.len();
|
||||
dst.extend([self.mac.name(), "!("]);
|
||||
let has_docs = write_comment_lines(self.docs, "\n ", dst);
|
||||
let (list_indent, list_multi_end, end) = if has_docs {
|
||||
dst.push_str("\n ");
|
||||
line_start = dst.len() - 4;
|
||||
(" ", "\n ", "]\n);")
|
||||
} else {
|
||||
(" ", "\n", "]);")
|
||||
};
|
||||
dst.push_str(self.name);
|
||||
if let Some(lt) = self.lt {
|
||||
dst.extend(["<", lt, ">"]);
|
||||
}
|
||||
dst.push_str(" => [");
|
||||
let fmt = write_list(
|
||||
self.lints.iter().copied(),
|
||||
80usize.saturating_sub(dst.len() - line_start),
|
||||
list_indent,
|
||||
dst,
|
||||
);
|
||||
if matches!(fmt, ListFmt::MultiLine) {
|
||||
dst.push_str(list_multi_end);
|
||||
}
|
||||
dst.push_str(end);
|
||||
}
|
||||
}
|
||||
|
||||
fn write_comment_lines(s: &str, prefix: &str, dst: &mut String) -> bool {
|
||||
let mut has_doc = false;
|
||||
for line in s.split('\n') {
|
||||
let line = line.trim_start();
|
||||
if !line.is_empty() {
|
||||
has_doc = true;
|
||||
dst.extend([prefix, line]);
|
||||
}
|
||||
}
|
||||
has_doc
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum ListFmt {
|
||||
SingleLine,
|
||||
MultiLine,
|
||||
}
|
||||
|
||||
fn write_list<'a>(
|
||||
items: impl Iterator<Item = &'a str> + Clone,
|
||||
single_line_limit: usize,
|
||||
indent: &str,
|
||||
dst: &mut String,
|
||||
) -> ListFmt {
|
||||
let len = items.clone().map(str::len).sum::<usize>();
|
||||
if len > single_line_limit {
|
||||
for item in items {
|
||||
dst.extend(["\n", indent, item, ","]);
|
||||
}
|
||||
ListFmt::MultiLine
|
||||
} else {
|
||||
let _ = write!(dst, "{}", items.format(", "));
|
||||
ListFmt::SingleLine
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the contents of a lint's source file with all the lint and lint pass
|
||||
/// declarations sorted.
|
||||
pub fn gen_sorted_lints_file(
|
||||
src: &str,
|
||||
dst: &mut String,
|
||||
lints: &mut [(&str, Range<u32>)],
|
||||
passes: &mut [LintPass<'_>],
|
||||
ranges: &mut VecBuf<Range<u32>>,
|
||||
) {
|
||||
ranges.with(|ranges| {
|
||||
ranges.extend(lints.iter().map(|&(_, x)| x));
|
||||
ranges.extend(passes.iter().map(|x| x.decl_range));
|
||||
ranges.sort_unstable_by_key(|x| x.start);
|
||||
|
||||
lints.sort_unstable_by_key(|&(x, _)| x);
|
||||
passes.sort_by_key(|x| x.name);
|
||||
|
||||
let mut ranges = ranges.iter();
|
||||
let pos = if let Some(range) = ranges.next() {
|
||||
dst.push_str(&src[..range.start as usize]);
|
||||
for &(_, range) in &*lints {
|
||||
dst.push_str(&src[range.start as usize..range.end as usize]);
|
||||
dst.push_str("\n\n");
|
||||
}
|
||||
for pass in passes {
|
||||
pass.gen_mac(dst);
|
||||
dst.push_str("\n\n");
|
||||
}
|
||||
range.end
|
||||
} else {
|
||||
dst.push_str(src);
|
||||
return;
|
||||
};
|
||||
|
||||
let pos = ranges.fold(pos, |start, range| {
|
||||
let s = &src[start as usize..range.start as usize];
|
||||
dst.push_str(if s.trim_start().is_empty() {
|
||||
// Only whitespace between this and the previous item. No need to keep that.
|
||||
""
|
||||
} else if src[..pos as usize].ends_with("\n\n")
|
||||
&& let Some(s) = s.strip_prefix("\n\n")
|
||||
{
|
||||
// Empty line before and after. Remove one of them.
|
||||
s
|
||||
} else {
|
||||
// Remove only full lines unless something is in the way.
|
||||
s.strip_prefix('\n').unwrap_or(s)
|
||||
});
|
||||
range.end
|
||||
});
|
||||
|
||||
// Since we always generate an empty line at the end, make sure to always skip it.
|
||||
let s = &src[pos as usize..];
|
||||
dst.push_str(s.strip_prefix('\n').map_or(s, |s| s.strip_prefix('\n').unwrap_or(s)));
|
||||
});
|
||||
}
|
||||
@@ -33,8 +33,8 @@
|
||||
pub mod serve;
|
||||
pub mod setup;
|
||||
pub mod sync;
|
||||
pub mod update_lints;
|
||||
|
||||
mod generate;
|
||||
mod parse;
|
||||
mod utils;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use clippy_dev::{
|
||||
ClippyInfo, UpdateMode, dogfood, edit_lints, fmt, lint, new_lint, new_parse_cx, release, serve, setup, sync,
|
||||
update_lints,
|
||||
};
|
||||
use std::env;
|
||||
|
||||
@@ -27,7 +26,9 @@ fn main() {
|
||||
allow_no_vcs,
|
||||
} => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
|
||||
DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)),
|
||||
DevCommand::UpdateLints { check } => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::from_check(check))),
|
||||
DevCommand::UpdateLints { check } => {
|
||||
new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::from_check(check)));
|
||||
},
|
||||
DevCommand::NewLint {
|
||||
pass,
|
||||
name,
|
||||
@@ -35,7 +36,7 @@ fn main() {
|
||||
r#type,
|
||||
msrv,
|
||||
} => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) {
|
||||
Ok(()) => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::Change)),
|
||||
Ok(()) => new_parse_cx(|cx| cx.parse_lint_decls().gen_decls(UpdateMode::Change)),
|
||||
Err(e) => eprintln!("Unable to create lint: {e}"),
|
||||
},
|
||||
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
|
||||
|
||||
+166
-21
@@ -1,7 +1,7 @@
|
||||
pub mod cursor;
|
||||
|
||||
use self::cursor::{Capture, Cursor};
|
||||
use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target};
|
||||
use crate::utils::{ErrAction, File, Scoped, expect_action, slice_groups_mut, walk_dir_no_dot_or_target};
|
||||
use core::fmt::{self, Display, Write as _};
|
||||
use core::range::Range;
|
||||
use rustc_arena::DroplessArena;
|
||||
@@ -13,6 +13,7 @@
|
||||
pub struct ParseCxImpl<'cx> {
|
||||
pub arena: &'cx DroplessArena,
|
||||
pub str_buf: StrBuf,
|
||||
pub str_list_buf: VecBuf<&'cx str>,
|
||||
}
|
||||
pub type ParseCx<'cx> = &'cx mut ParseCxImpl<'cx>;
|
||||
|
||||
@@ -22,6 +23,7 @@ pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env,
|
||||
f(&mut Scoped::new(ParseCxImpl {
|
||||
arena: &arena,
|
||||
str_buf: StrBuf::with_capacity(128),
|
||||
str_list_buf: VecBuf::with_capacity(128),
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -82,6 +84,20 @@ pub fn with<T>(&mut self, f: impl FnOnce(&mut String) -> T) -> T {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct VecBuf<T>(Vec<T>);
|
||||
impl<T> VecBuf<T> {
|
||||
/// Creates a new buffer with the specified initial capacity.
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self(Vec::with_capacity(cap))
|
||||
}
|
||||
|
||||
/// Performs an operation with the freshly cleared buffer.
|
||||
pub fn with<R>(&mut self, f: impl FnOnce(&mut Vec<T>) -> R) -> R {
|
||||
self.0.clear();
|
||||
f(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum LintTool {
|
||||
Rustc,
|
||||
@@ -128,7 +144,7 @@ pub struct ActiveLint<'cx> {
|
||||
pub group: &'cx str,
|
||||
pub module: &'cx str,
|
||||
pub path: PathBuf,
|
||||
pub declaration_range: Range<usize>,
|
||||
pub declaration_range: Range<u32>,
|
||||
}
|
||||
|
||||
pub struct DeprecatedLint<'cx> {
|
||||
@@ -147,8 +163,59 @@ pub enum Lint<'cx> {
|
||||
Renamed(RenamedLint<'cx>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum LintPassMac {
|
||||
Declare,
|
||||
Impl,
|
||||
}
|
||||
impl LintPassMac {
|
||||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
Self::Declare => "declare_lint_pass",
|
||||
Self::Impl => "impl_lint_pass",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LintPass<'cx> {
|
||||
/// The raw text of the documentation comments. May include leading/trailing
|
||||
/// whitespace and empty lines.
|
||||
pub docs: &'cx str,
|
||||
pub name: &'cx str,
|
||||
pub lt: Option<&'cx str>,
|
||||
pub mac: LintPassMac,
|
||||
pub decl_range: Range<u32>,
|
||||
pub lints: &'cx [&'cx str],
|
||||
pub path: PathBuf,
|
||||
}
|
||||
|
||||
pub struct LintData<'cx> {
|
||||
pub lints: FxHashMap<&'cx str, Lint<'cx>>,
|
||||
pub lint_passes: Vec<LintPass<'cx>>,
|
||||
}
|
||||
impl<'cx> LintData<'cx> {
|
||||
#[expect(clippy::type_complexity)]
|
||||
pub fn split_by_lint_file<'s>(
|
||||
&'s mut self,
|
||||
) -> (
|
||||
FxHashMap<&'s Path, Vec<(&'s str, Range<u32>)>>,
|
||||
impl Iterator<Item = &'s mut [LintPass<'cx>]>,
|
||||
) {
|
||||
#[expect(clippy::default_trait_access)]
|
||||
let mut lints = FxHashMap::with_capacity_and_hasher(500, Default::default());
|
||||
for (&name, lint) in &self.lints {
|
||||
if let Lint::Active(lint) = lint {
|
||||
lints
|
||||
.entry(&*lint.path)
|
||||
.or_insert_with(|| Vec::with_capacity(8))
|
||||
.push((name, lint.declaration_range));
|
||||
}
|
||||
}
|
||||
let passes = slice_groups_mut(&mut self.lint_passes, |head, tail| {
|
||||
tail.iter().take_while(|&x| x.path == head.path).count()
|
||||
});
|
||||
(lints, passes)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'cx> ParseCxImpl<'cx> {
|
||||
@@ -158,6 +225,7 @@ pub fn parse_lint_decls(&mut self) -> LintData<'cx> {
|
||||
let mut data = LintData {
|
||||
#[expect(clippy::default_trait_access)]
|
||||
lints: FxHashMap::with_capacity_and_hasher(1000, Default::default()),
|
||||
lint_passes: Vec::with_capacity(400),
|
||||
};
|
||||
|
||||
let mut contents = String::new();
|
||||
@@ -193,7 +261,7 @@ pub fn parse_lint_decls(&mut self) -> LintData<'cx> {
|
||||
self.str_buf
|
||||
.alloc_replaced(self.arena, path, path::MAIN_SEPARATOR, "::")
|
||||
};
|
||||
self.parse_clippy_lint_decls(
|
||||
self.parse_lint_src_file(
|
||||
e.path(),
|
||||
File::open_read_to_cleared_string(e.path(), &mut contents),
|
||||
module,
|
||||
@@ -208,11 +276,11 @@ pub fn parse_lint_decls(&mut self) -> LintData<'cx> {
|
||||
}
|
||||
|
||||
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
|
||||
fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) {
|
||||
fn parse_lint_src_file(&mut self, path: &Path, contents: &str, module: &'cx str, data: &mut LintData<'cx>) {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use cursor::Pat::*;
|
||||
#[rustfmt::skip]
|
||||
static DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
static LINT_DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// !{ /// docs
|
||||
Bang, OpenBrace, AnyComment,
|
||||
// #[clippy::version = "version"]
|
||||
@@ -220,24 +288,101 @@ fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx
|
||||
// pub NAME, GROUP,
|
||||
Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
static PASS_DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// !( NAME <'lt> => [
|
||||
Bang, OpenParen, CaptureDocLines, CaptureIdent, CaptureOptLifetimeArg, FatArrow, OpenBracket,
|
||||
];
|
||||
|
||||
let mut cursor = Cursor::new(contents);
|
||||
let mut captures = [Capture::EMPTY; 2];
|
||||
while let Some(start) = cursor.find_ident("declare_clippy_lint") {
|
||||
if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) {
|
||||
assert!(
|
||||
data.lints
|
||||
.insert(
|
||||
self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])),
|
||||
Lint::Active(ActiveLint {
|
||||
group: self.arena.alloc_str(cursor.get_text(captures[1])),
|
||||
module,
|
||||
path: path.into(),
|
||||
declaration_range: start as usize..cursor.pos() as usize,
|
||||
}),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
let mut captures = [Capture::EMPTY; 3];
|
||||
while let Some(mac_name) = cursor.find_any_ident() {
|
||||
match cursor.get_text(mac_name) {
|
||||
"declare_clippy_lint"
|
||||
if cursor.match_all(LINT_DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) =>
|
||||
{
|
||||
assert!(
|
||||
data.lints
|
||||
.insert(
|
||||
self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])),
|
||||
Lint::Active(ActiveLint {
|
||||
group: self.arena.alloc_str(cursor.get_text(captures[1])),
|
||||
module,
|
||||
path: path.into(),
|
||||
declaration_range: mac_name.pos..cursor.pos(),
|
||||
}),
|
||||
)
|
||||
.is_none()
|
||||
);
|
||||
},
|
||||
mac @ ("declare_lint_pass" | "impl_lint_pass") if cursor.match_all(PASS_DECL_TOKENS, &mut captures) => {
|
||||
let mac = if matches!(mac, "declare_lint_pass") {
|
||||
LintPassMac::Declare
|
||||
} else {
|
||||
LintPassMac::Impl
|
||||
};
|
||||
let docs = match cursor.get_text(captures[0]) {
|
||||
"" => "",
|
||||
x => self.arena.alloc_str(x),
|
||||
};
|
||||
let name = self.arena.alloc_str(cursor.get_text(captures[1]));
|
||||
let lt = cursor.get_text(captures[2]);
|
||||
let lt = if lt.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(self.arena.alloc_str(lt))
|
||||
};
|
||||
|
||||
let lints = self.str_list_buf.with(|buf| {
|
||||
// Parses a comma separated list of paths and converts each path
|
||||
// to a string with whitespace removed.
|
||||
while !cursor.match_pat(CloseBracket) {
|
||||
buf.push(self.str_buf.with(|buf| {
|
||||
if cursor.match_pat(DoubleColon) {
|
||||
buf.push_str("::");
|
||||
}
|
||||
let capture = cursor.capture_ident()?;
|
||||
buf.push_str(cursor.get_text(capture));
|
||||
while cursor.match_pat(DoubleColon) {
|
||||
buf.push_str("::");
|
||||
let capture = cursor.capture_ident()?;
|
||||
buf.push_str(cursor.get_text(capture));
|
||||
}
|
||||
Some(self.arena.alloc_str(buf))
|
||||
})?);
|
||||
|
||||
if !cursor.match_pat(Comma) {
|
||||
if !cursor.match_pat(CloseBracket) {
|
||||
return None;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The arena panics when allocating a size of zero.
|
||||
Some(if buf.is_empty() {
|
||||
&[]
|
||||
} else {
|
||||
buf.sort_unstable();
|
||||
&*self.arena.alloc_slice(buf)
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(lints) = lints
|
||||
&& cursor.match_all(&[CloseParen, Semi], &mut [])
|
||||
{
|
||||
data.lint_passes.push(LintPass {
|
||||
docs,
|
||||
name,
|
||||
lt,
|
||||
mac,
|
||||
decl_range: mac_name.pos..cursor.pos(),
|
||||
lints,
|
||||
path: path.into(),
|
||||
});
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ pub enum Pat<'a> {
|
||||
/// Matches any number of comments and doc comments.
|
||||
AnyComment,
|
||||
Ident(&'a str),
|
||||
CaptureDocLines,
|
||||
CaptureIdent,
|
||||
LitStr,
|
||||
CaptureLitStr,
|
||||
@@ -22,12 +23,14 @@ pub enum Pat<'a> {
|
||||
Comma,
|
||||
DoubleColon,
|
||||
Eq,
|
||||
FatArrow,
|
||||
Lifetime,
|
||||
Lt,
|
||||
Gt,
|
||||
OpenBrace,
|
||||
OpenBracket,
|
||||
OpenParen,
|
||||
CaptureOptLifetimeArg,
|
||||
Pound,
|
||||
Semi,
|
||||
}
|
||||
@@ -112,6 +115,7 @@ pub fn step(&mut self) {
|
||||
///
|
||||
/// For each capture made by the pattern one item will be taken from the capture
|
||||
/// sequence with the result placed inside.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture>) -> bool {
|
||||
loop {
|
||||
match (pat, self.next_token.kind) {
|
||||
@@ -121,7 +125,6 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture
|
||||
Pat::AnyComment,
|
||||
TokenKind::BlockComment { terminated: true, .. } | TokenKind::LineComment { .. },
|
||||
) => self.step(),
|
||||
(Pat::AnyComment, _) => return true,
|
||||
(Pat::Bang, TokenKind::Bang)
|
||||
| (Pat::CloseBrace, TokenKind::CloseBrace)
|
||||
| (Pat::CloseBracket, TokenKind::CloseBracket)
|
||||
@@ -152,12 +155,48 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture
|
||||
},
|
||||
(Pat::DoubleColon, TokenKind::Colon) => {
|
||||
self.step();
|
||||
if !self.at_end() && matches!(self.next_token.kind, TokenKind::Colon) {
|
||||
if matches!(self.next_token.kind, TokenKind::Colon) {
|
||||
self.step();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(Pat::FatArrow, TokenKind::Eq) => {
|
||||
self.step();
|
||||
if matches!(self.next_token.kind, TokenKind::Gt) {
|
||||
self.step();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(Pat::CaptureOptLifetimeArg, TokenKind::Lt) => {
|
||||
self.step();
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Lifetime { .. } => break,
|
||||
TokenKind::Whitespace => self.step(),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
*captures.next().unwrap() = Capture {
|
||||
pos: self.pos,
|
||||
len: self.next_token.len,
|
||||
};
|
||||
self.step();
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Gt => break,
|
||||
TokenKind::Whitespace => self.step(),
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Pat::CaptureOptLifetimeArg, _) => {
|
||||
*captures.next().unwrap() = Capture { pos: 0, len: 0 };
|
||||
return true;
|
||||
},
|
||||
#[rustfmt::skip]
|
||||
(
|
||||
Pat::CaptureLitStr,
|
||||
@@ -173,6 +212,28 @@ fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Pat::CaptureDocLines, TokenKind::LineComment { doc_style: Some(_) }) => {
|
||||
let pos = self.pos;
|
||||
loop {
|
||||
self.step();
|
||||
if !matches!(
|
||||
self.next_token.kind,
|
||||
TokenKind::Whitespace | TokenKind::LineComment { doc_style: Some(_) }
|
||||
) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*captures.next().unwrap() = Capture {
|
||||
pos,
|
||||
len: self.pos - pos,
|
||||
};
|
||||
return true;
|
||||
},
|
||||
(Pat::CaptureDocLines, _) => {
|
||||
*captures.next().unwrap() = Capture::EMPTY;
|
||||
return true;
|
||||
},
|
||||
(Pat::AnyComment, _) => return true,
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
@@ -219,8 +280,8 @@ pub fn find_any_ident(&mut self) -> Option<Capture> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consume the returns the position of the next non-whitespace token if it's an
|
||||
/// identifier. Returns `None` otherwise.
|
||||
/// Consume the returns the position of the next non-whitespace token if it's the
|
||||
/// specified identifier. Returns `None` otherwise.
|
||||
pub fn match_ident(&mut self, s: &str) -> Option<u32> {
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
@@ -235,6 +296,23 @@ pub fn match_ident(&mut self, s: &str) -> Option<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes and captures the next non-whitespace token if it's an identifier. Returns
|
||||
/// `None` otherwise.
|
||||
pub fn capture_ident(&mut self) -> Option<Capture> {
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Ident => {
|
||||
let pos = self.pos;
|
||||
let len = self.next_token.len;
|
||||
self.step();
|
||||
return Some(Capture { pos, len });
|
||||
},
|
||||
TokenKind::Whitespace => self.step(),
|
||||
_ => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Continually attempt to match the pattern on subsequent tokens until a match is
|
||||
/// found. Returns whether the pattern was successfully matched.
|
||||
///
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
use crate::parse::cursor::Cursor;
|
||||
use crate::parse::{Lint, LintData, ParseCx};
|
||||
use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn};
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
use std::path::{self, Path};
|
||||
|
||||
const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
|
||||
// Use that command to update this file and do not edit by hand.\n\
|
||||
// Manual edits will be overwritten.\n\n";
|
||||
|
||||
const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
|
||||
|
||||
/// Runs the `update_lints` command.
|
||||
///
|
||||
/// This updates various generated values from the lint source code.
|
||||
///
|
||||
/// `update_mode` indicates if the files should be updated or if updates should be checked for.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if a file path could not read from or then written to
|
||||
pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) {
|
||||
let data = cx.parse_lint_decls();
|
||||
generate_lint_files(update_mode, &data);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn generate_lint_files(update_mode: UpdateMode, data: &LintData<'_>) {
|
||||
let mut updater = FileUpdater::default();
|
||||
|
||||
let mut lints: Vec<_> = data.lints.iter().map(|(&x, y)| (x, y)).collect();
|
||||
lints.sort_by_key(|&(x, _)| x);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"CHANGELOG.md",
|
||||
&mut update_text_region_fn(
|
||||
"<!-- begin autogenerated links to lint list -->\n",
|
||||
"<!-- end autogenerated links to lint list -->",
|
||||
|dst| {
|
||||
for &(lint, _) in &lints {
|
||||
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
let mut active = Vec::with_capacity(lints.len());
|
||||
let mut deprecated = Vec::with_capacity(lints.len() / 8);
|
||||
let mut renamed = Vec::with_capacity(lints.len() / 8);
|
||||
for &(name, lint) in &lints {
|
||||
match lint {
|
||||
Lint::Active(lint) => active.push((name, lint)),
|
||||
Lint::Deprecated(lint) => deprecated.push((name, lint)),
|
||||
Lint::Renamed(lint) => renamed.push((name, lint)),
|
||||
}
|
||||
}
|
||||
active.sort_by_key(|&(_, lint)| lint.module);
|
||||
|
||||
// Round to avoid updating the readme every time a lint is added/deprecated.
|
||||
let lint_count = active.len() / 50 * 50;
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"book/src/README.md",
|
||||
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
|
||||
write!(dst, "{lint_count}").unwrap();
|
||||
}),
|
||||
);
|
||||
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"clippy_lints/src/deprecated_lints.rs",
|
||||
&mut |_, src, dst| {
|
||||
let mut cursor = Cursor::new(src);
|
||||
assert!(
|
||||
cursor.find_ident("declare_with_version").is_some()
|
||||
&& cursor.find_ident("declare_with_version").is_some(),
|
||||
"error reading deprecated lints"
|
||||
);
|
||||
dst.push_str(&src[..cursor.pos() as usize]);
|
||||
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
|
||||
for &(name, data) in &deprecated {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.reason,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str(
|
||||
"]}\n\n\
|
||||
#[rustfmt::skip]\n\
|
||||
declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
|
||||
",
|
||||
);
|
||||
for &(name, data) in &renamed {
|
||||
write!(
|
||||
dst,
|
||||
" #[clippy::version = \"{}\"]\n (\"clippy::{name}\", \"{}\"),\n",
|
||||
data.version, data.new_name,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
dst.push_str("]}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/deprecated.rs",
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
for &(lint, _) in &deprecated {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
"tests/ui/rename.rs",
|
||||
&mut move |_, src, dst| {
|
||||
let mut seen_lints = HashSet::new();
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
|
||||
for &(_, lint) in &renamed {
|
||||
if seen_lints.insert(lint.new_name) {
|
||||
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
|
||||
}
|
||||
}
|
||||
for &(lint, _) in &renamed {
|
||||
writeln!(dst, "#![warn(clippy::{lint})] //~ ERROR: lint `clippy::{lint}`").unwrap();
|
||||
}
|
||||
dst.push_str("\nfn main() {}\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
for (crate_name, lints) in active.iter().copied().into_group_map_by(|&(_, lint)| {
|
||||
let Some(path::Component::Normal(name)) = lint.path.components().next() else {
|
||||
// All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
|
||||
panic!(
|
||||
"internal error: can't read crate name from path `{}`",
|
||||
lint.path.display()
|
||||
);
|
||||
};
|
||||
name
|
||||
}) {
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/lib.rs"),
|
||||
&mut update_text_region_fn(
|
||||
"// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
|
||||
"// end lints modules, do not remove this comment, it's used in `update_lints`",
|
||||
|dst| {
|
||||
let mut prev = "";
|
||||
for &(_, lint) in &lints {
|
||||
if lint.module != prev {
|
||||
writeln!(dst, "mod {};", lint.module).unwrap();
|
||||
prev = lint.module;
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
updater.update_file_checked(
|
||||
"cargo dev update_lints",
|
||||
update_mode,
|
||||
Path::new(crate_name).join("src/declared_lints.rs"),
|
||||
&mut |_, src, dst| {
|
||||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
|
||||
let mut buf = String::new();
|
||||
for &(name, lint) in &lints {
|
||||
buf.clear();
|
||||
buf.push_str(name);
|
||||
buf.make_ascii_uppercase();
|
||||
if lint.module.is_empty() {
|
||||
writeln!(dst, " crate::{buf}_INFO,").unwrap();
|
||||
} else {
|
||||
writeln!(dst, " crate::{}::{buf}_INFO,", lint.module).unwrap();
|
||||
}
|
||||
}
|
||||
dst.push_str("];\n");
|
||||
UpdateStatus::from_changed(src != dst)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
use core::fmt::{self, Display};
|
||||
use core::marker::PhantomData;
|
||||
use core::mem;
|
||||
use core::num::NonZero;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::range::Range;
|
||||
@@ -600,3 +601,29 @@ pub fn walk_dir_no_dot_or_target(p: impl AsRef<Path>) -> impl Iterator<Item = ::
|
||||
.is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.'))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn slice_groups_mut<T>(
|
||||
slice: &mut [T],
|
||||
split_idx: impl FnMut(&T, &[T]) -> usize,
|
||||
) -> impl Iterator<Item = &mut [T]> {
|
||||
struct I<'a, T, F> {
|
||||
slice: &'a mut [T],
|
||||
split_idx: F,
|
||||
}
|
||||
impl<'a, T, F: FnMut(&T, &[T]) -> usize> Iterator for I<'a, T, F> {
|
||||
type Item = &'a mut [T];
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (head, tail) = self.slice.split_first()?;
|
||||
let idx = (self.split_idx)(head, tail) + 1;
|
||||
// `mem::take` makes it so `self.slice` isn't reborrowed.
|
||||
if let Some((head, tail)) = mem::take(&mut self.slice).split_at_mut_checked(idx) {
|
||||
self.slice = tail;
|
||||
Some(head)
|
||||
} else {
|
||||
self.slice = &mut [];
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
I { slice, split_idx }
|
||||
}
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
restriction,
|
||||
"checks for usage of an item without a `use` statement"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
|
||||
|
||||
pub struct AbsolutePaths {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
suspicious,
|
||||
"almost complete range"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AlmostCompleteRange => [ALMOST_COMPLETE_RANGE]);
|
||||
|
||||
pub struct AlmostCompleteRange {
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"the approximate of a known float constant (in `std::fXX::consts`)"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
|
||||
|
||||
// Tuples are of the form (constant, name, min_digits, msrv)
|
||||
const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [
|
||||
(f64::E, "E", 4, None),
|
||||
@@ -111,8 +113,6 @@ fn check_known_consts(&self, cx: &LateContext<'_>, span: Span, s: symbol::Symbol
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ApproxConstant => [APPROX_CONSTANT]);
|
||||
|
||||
fn count_digits_after_dot(input: &str) -> usize {
|
||||
input
|
||||
.char_indices()
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
suspicious,
|
||||
"using `Arc` with a type that does not implement `Send` and `Sync`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
|
||||
|
||||
@@ -57,54 +57,6 @@ fn check_asm_syntax(
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of Intel x86 assembly syntax.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To enforce consistent use of AT&T x86 assembly syntax.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
restriction,
|
||||
"prefer AT&T x86 assembly syntax"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
|
||||
|
||||
impl EarlyLintPass for InlineAsmX86IntelSyntax {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of AT&T x86 assembly syntax.
|
||||
@@ -137,8 +89,56 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
"prefer Intel x86 assembly syntax"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of Intel x86 assembly syntax.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To enforce consistent use of AT&T x86 assembly syntax.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea {}, [{}]", lateout(reg) _, in(reg) ptr);
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// # #![feature(asm)]
|
||||
/// # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
/// # unsafe { let ptr = "".as_ptr();
|
||||
/// # use std::arch::asm;
|
||||
/// asm!("lea ({}), {}", in(reg) ptr, lateout(reg) _, options(att_syntax));
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
restriction,
|
||||
"prefer AT&T x86 assembly syntax"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]);
|
||||
|
||||
declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
|
||||
|
||||
impl EarlyLintPass for InlineAsmX86IntelSyntax {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
|
||||
check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EarlyLintPass for InlineAsmX86AttSyntax {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
}
|
||||
|
||||
impl_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]);
|
||||
|
||||
pub struct AssertionsOnConstants {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
"assigning the result of cloning may be inefficient"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||
|
||||
pub struct AssigningClones {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -63,8 +65,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for AssigningClones {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Assign(lhs, rhs, _) = e.kind
|
||||
|
||||
+310
-313
@@ -25,106 +25,60 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items annotated with `#[inline(always)]`,
|
||||
/// unless the annotated function is empty or simply panics.
|
||||
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
|
||||
/// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
|
||||
///
|
||||
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
|
||||
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// While there are valid uses of this annotation (and once
|
||||
/// you know when to use it, by all means `allow` this lint), it's a common
|
||||
/// newbie-mistake to pepper one's code with it.
|
||||
///
|
||||
/// As a rule of thumb, before slapping `#[inline(always)]` on a function,
|
||||
/// measure if that additional function call really affects your runtime profile
|
||||
/// sufficiently to make up for the increase in compile time.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False positives, big time. This lint is meant to be
|
||||
/// deactivated by everyone doing serious performance work. This means having
|
||||
/// done the measurement.
|
||||
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
|
||||
/// the expectation is unfulfilled. This can be useful to be notified when the
|
||||
/// lint is no longer triggered.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[inline(always)]
|
||||
/// fn not_quite_hot_code(..) { ... }
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INLINE_ALWAYS,
|
||||
pedantic,
|
||||
"use of `#[inline(always)]`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `extern crate` and `use` items annotated with
|
||||
/// lint attributes.
|
||||
///
|
||||
/// This lint permits lint attributes for lints emitted on the items themself.
|
||||
/// For `use` items these lints are:
|
||||
/// * ambiguous_glob_reexports
|
||||
/// * dead_code
|
||||
/// * deprecated
|
||||
/// * hidden_glob_reexports
|
||||
/// * unreachable_pub
|
||||
/// * unused
|
||||
/// * unused_braces
|
||||
/// * unused_import_braces
|
||||
/// * clippy::disallowed_types
|
||||
/// * clippy::enum_glob_use
|
||||
/// * clippy::macro_use_imports
|
||||
/// * clippy::module_name_repetitions
|
||||
/// * clippy::redundant_pub_crate
|
||||
/// * clippy::single_component_path_imports
|
||||
/// * clippy::unsafe_removed_from_name
|
||||
/// * clippy::wildcard_imports
|
||||
///
|
||||
/// For `extern crate` items these lints are:
|
||||
/// * `unused_imports` on items with `#[macro_use]`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Lint attributes have no effect on crate imports. Most
|
||||
/// likely a `!` was forgotten.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[deny(dead_code)]
|
||||
/// extern crate foo;
|
||||
/// #[forbid(dead_code)]
|
||||
/// use foo::bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_imports)]
|
||||
/// use foo::baz;
|
||||
/// #[allow(unused_imports)]
|
||||
/// #[macro_use]
|
||||
/// extern crate baz;
|
||||
/// #[expect(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub USELESS_ATTRIBUTE,
|
||||
correctness,
|
||||
"use of lint attributes on `extern crate` items"
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub ALLOW_ATTRIBUTES,
|
||||
restriction,
|
||||
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[deprecated]` annotations with a `since`
|
||||
/// field that is not a valid semantic version. Also allows "TBD" to signal
|
||||
/// future deprecation.
|
||||
/// Checks for attributes that allow lints without a reason.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// For checking the version of the deprecation, it must be
|
||||
/// a valid semver. Failing that, the contained information is useless.
|
||||
/// ### Why restrict this?
|
||||
/// Justifying each `allow` helps readers understand the reasoning,
|
||||
/// and may allow removing `allow` attributes if their purpose is obsolete.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[deprecated(since = "forever")]
|
||||
/// fn something_else() { /* ... */ }
|
||||
/// #![allow(clippy::some_lint)]
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DEPRECATED_SEMVER,
|
||||
correctness,
|
||||
"use of `#[deprecated(since = \"x\")]` where x is not semver"
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
restriction,
|
||||
"ensures that all `allow` and `expect` attributes have a reason"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -181,161 +135,6 @@
|
||||
"usage of `cfg_attr(rustfmt)` instead of tool attributes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for attributes that allow lints without a reason.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Justifying each `allow` helps readers understand the reasoning,
|
||||
/// and may allow removing `allow` attributes if their purpose is obsolete.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::some_lint)]
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
restriction,
|
||||
"ensures that all `allow` and `expect` attributes have a reason"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `#[allow]` attribute and suggests replacing it with
|
||||
/// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
|
||||
///
|
||||
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
|
||||
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
|
||||
/// the expectation is unfulfilled. This can be useful to be notified when the
|
||||
/// lint is no longer triggered.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[expect(unused_mut)]
|
||||
/// fn foo() -> usize {
|
||||
/// let mut a = Vec::new();
|
||||
/// a.len()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.70.0"]
|
||||
pub ALLOW_ATTRIBUTES,
|
||||
restriction,
|
||||
"`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[should_panic]` attributes without specifying the expected panic message.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The expected panic message should be specified to ensure that the test is actually
|
||||
/// panicking with the expected message, and not another unrelated panic.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic = "attempt to divide by zero"]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.74.0"]
|
||||
pub SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
pedantic,
|
||||
"ensures that all `should_panic` attributes specify its expected panic message"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items with `#[repr(packed)]`-attribute without ABI qualification
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable.
|
||||
/// While this is fine as long as the type is accessed correctly within Rust-code, most uses
|
||||
/// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or
|
||||
/// other external specifications. In such situations, the unstable Rust-ABI implied in
|
||||
/// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change.
|
||||
///
|
||||
/// In case you are relying on a well defined and stable memory layout, qualify the type's
|
||||
/// representation using the `C`-ABI. Otherwise, if the type in question is only ever
|
||||
/// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[repr(packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[repr(C, packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub REPR_PACKED_WITHOUT_ABI,
|
||||
suspicious,
|
||||
"ensures that `repr(packed)` always comes with a qualified ABI"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `any` and `all` combinators in `cfg` with only one condition.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If there is only one condition, no need to wrap it into `any` or `all` combinators.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(any(unix))]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(unix)]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub NON_MINIMAL_CFG,
|
||||
style,
|
||||
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for
|
||||
@@ -364,63 +163,23 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]`
|
||||
/// and suggests to replace it with `#[allow(clippy::lint)]`.
|
||||
/// Checks for `#[deprecated]` annotations with a `since`
|
||||
/// field that is not a valid semantic version. Also allows "TBD" to signal
|
||||
/// future deprecation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There is no reason to put clippy attributes behind a clippy `cfg` as they are not
|
||||
/// run by anything else than clippy.
|
||||
/// For checking the version of the deprecation, it must be
|
||||
/// a valid semver. Failing that, the contained information is useless.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))]
|
||||
/// #[deprecated(since = "forever")]
|
||||
/// fn something_else() { /* ... */ }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::deprecated_cfg_attr)]
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub UNNECESSARY_CLIPPY_CFG,
|
||||
suspicious,
|
||||
"usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items that have the same kind of attributes with mixed styles (inner/outer).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Having both style of said attributes makes it more complicated to read code.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint currently has false-negatives when mixing same attributes
|
||||
/// but they have different path symbols, for example:
|
||||
/// ```ignore
|
||||
/// #[custom_attribute]
|
||||
/// pub fn foo() {
|
||||
/// #![my_crate::custom_attribute]
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// pub fn foo() {
|
||||
/// #![cfg(windows)]
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// #[cfg(windows)]
|
||||
/// pub fn foo() {
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MIXED_ATTRIBUTES_STYLE,
|
||||
style,
|
||||
"item has both inner and outer attributes"
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DEPRECATED_SEMVER,
|
||||
correctness,
|
||||
"use of `#[deprecated(since = \"x\")]` where x is not semver"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -478,15 +237,272 @@
|
||||
"ignored tests without messages"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items annotated with `#[inline(always)]`,
|
||||
/// unless the annotated function is empty or simply panics.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// While there are valid uses of this annotation (and once
|
||||
/// you know when to use it, by all means `allow` this lint), it's a common
|
||||
/// newbie-mistake to pepper one's code with it.
|
||||
///
|
||||
/// As a rule of thumb, before slapping `#[inline(always)]` on a function,
|
||||
/// measure if that additional function call really affects your runtime profile
|
||||
/// sufficiently to make up for the increase in compile time.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False positives, big time. This lint is meant to be
|
||||
/// deactivated by everyone doing serious performance work. This means having
|
||||
/// done the measurement.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[inline(always)]
|
||||
/// fn not_quite_hot_code(..) { ... }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub INLINE_ALWAYS,
|
||||
pedantic,
|
||||
"use of `#[inline(always)]`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items that have the same kind of attributes with mixed styles (inner/outer).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Having both style of said attributes makes it more complicated to read code.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint currently has false-negatives when mixing same attributes
|
||||
/// but they have different path symbols, for example:
|
||||
/// ```ignore
|
||||
/// #[custom_attribute]
|
||||
/// pub fn foo() {
|
||||
/// #![my_crate::custom_attribute]
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// pub fn foo() {
|
||||
/// #![cfg(windows)]
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(linux)]
|
||||
/// #[cfg(windows)]
|
||||
/// pub fn foo() {
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub MIXED_ATTRIBUTES_STYLE,
|
||||
style,
|
||||
"item has both inner and outer attributes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `any` and `all` combinators in `cfg` with only one condition.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If there is only one condition, no need to wrap it into `any` or `all` combinators.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[cfg(any(unix))]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[cfg(unix)]
|
||||
/// pub struct Bar;
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub NON_MINIMAL_CFG,
|
||||
style,
|
||||
"ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for items with `#[repr(packed)]`-attribute without ABI qualification
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable.
|
||||
/// While this is fine as long as the type is accessed correctly within Rust-code, most uses
|
||||
/// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or
|
||||
/// other external specifications. In such situations, the unstable Rust-ABI implied in
|
||||
/// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change.
|
||||
///
|
||||
/// In case you are relying on a well defined and stable memory layout, qualify the type's
|
||||
/// representation using the `C`-ABI. Otherwise, if the type in question is only ever
|
||||
/// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[repr(packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[repr(C, packed)]
|
||||
/// struct NetworkPacketHeader {
|
||||
/// header_length: u8,
|
||||
/// header_version: u16
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub REPR_PACKED_WITHOUT_ABI,
|
||||
suspicious,
|
||||
"ensures that `repr(packed)` always comes with a qualified ABI"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[should_panic]` attributes without specifying the expected panic message.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The expected panic message should be specified to ensure that the test is actually
|
||||
/// panicking with the expected message, and not another unrelated panic.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn random() -> i32 { 0 }
|
||||
///
|
||||
/// #[should_panic = "attempt to divide by zero"]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// let _ = 1 / random();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.74.0"]
|
||||
pub SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
pedantic,
|
||||
"ensures that all `should_panic` attributes specify its expected panic message"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]`
|
||||
/// and suggests to replace it with `#[allow(clippy::lint)]`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// There is no reason to put clippy attributes behind a clippy `cfg` as they are not
|
||||
/// run by anything else than clippy.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))]
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![allow(clippy::deprecated_cfg_attr)]
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub UNNECESSARY_CLIPPY_CFG,
|
||||
suspicious,
|
||||
"usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `extern crate` and `use` items annotated with
|
||||
/// lint attributes.
|
||||
///
|
||||
/// This lint permits lint attributes for lints emitted on the items themself.
|
||||
/// For `use` items these lints are:
|
||||
/// * ambiguous_glob_reexports
|
||||
/// * dead_code
|
||||
/// * deprecated
|
||||
/// * hidden_glob_reexports
|
||||
/// * unreachable_pub
|
||||
/// * unused
|
||||
/// * unused_braces
|
||||
/// * unused_import_braces
|
||||
/// * clippy::disallowed_types
|
||||
/// * clippy::enum_glob_use
|
||||
/// * clippy::macro_use_imports
|
||||
/// * clippy::module_name_repetitions
|
||||
/// * clippy::redundant_pub_crate
|
||||
/// * clippy::single_component_path_imports
|
||||
/// * clippy::unsafe_removed_from_name
|
||||
/// * clippy::wildcard_imports
|
||||
///
|
||||
/// For `extern crate` items these lints are:
|
||||
/// * `unused_imports` on items with `#[macro_use]`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Lint attributes have no effect on crate imports. Most
|
||||
/// likely a `!` was forgotten.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[deny(dead_code)]
|
||||
/// extern crate foo;
|
||||
/// #[forbid(dead_code)]
|
||||
/// use foo::bar;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// #[allow(unused_imports)]
|
||||
/// use foo::baz;
|
||||
/// #[allow(unused_imports)]
|
||||
/// #[macro_use]
|
||||
/// extern crate baz;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub USELESS_ATTRIBUTE,
|
||||
correctness,
|
||||
"use of lint attributes on `extern crate` items"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Attributes => [INLINE_ALWAYS, REPR_PACKED_WITHOUT_ABI]);
|
||||
|
||||
impl_lint_pass!(EarlyAttributes => [
|
||||
DEPRECATED_CFG_ATTR,
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
NON_MINIMAL_CFG,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
]);
|
||||
|
||||
impl_lint_pass!(PostExpansionEarlyAttributes => [
|
||||
ALLOW_ATTRIBUTES,
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
DEPRECATED_SEMVER,
|
||||
DUPLICATED_ATTRIBUTES,
|
||||
IGNORE_WITHOUT_REASON,
|
||||
MIXED_ATTRIBUTES_STYLE,
|
||||
SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
USELESS_ATTRIBUTE,
|
||||
]);
|
||||
|
||||
pub struct Attributes {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Attributes => [
|
||||
INLINE_ALWAYS,
|
||||
REPR_PACKED_WITHOUT_ABI,
|
||||
]);
|
||||
|
||||
impl Attributes {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
@@ -529,13 +545,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(EarlyAttributes => [
|
||||
DEPRECATED_CFG_ATTR,
|
||||
NON_MINIMAL_CFG,
|
||||
DEPRECATED_CLIPPY_CFG_ATTR,
|
||||
UNNECESSARY_CLIPPY_CFG,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyAttributes {
|
||||
fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
|
||||
deprecated_cfg_attr::check(cx, attr, &self.msrv);
|
||||
@@ -558,18 +567,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(PostExpansionEarlyAttributes => [
|
||||
ALLOW_ATTRIBUTES,
|
||||
ALLOW_ATTRIBUTES_WITHOUT_REASON,
|
||||
DEPRECATED_SEMVER,
|
||||
IGNORE_WITHOUT_REASON,
|
||||
USELESS_ATTRIBUTE,
|
||||
BLANKET_CLIPPY_RESTRICTION_LINTS,
|
||||
SHOULD_PANIC_WITHOUT_EXPECT,
|
||||
MIXED_ATTRIBUTES_STYLE,
|
||||
DUPLICATED_ATTRIBUTES,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for PostExpansionEarlyAttributes {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
|
||||
blanket_clippy_restriction_lints::check_command_line(cx);
|
||||
|
||||
@@ -10,6 +10,43 @@
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Span, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### 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 use concurrently from
|
||||
/// a memory access perspective, but that will cause bugs at runtime if
|
||||
/// they are held in such a way.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```toml
|
||||
/// 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" }
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// # async fn baz() {}
|
||||
/// struct CustomLockType;
|
||||
/// struct OtherCustomLockType;
|
||||
/// async fn foo() {
|
||||
/// let _x = CustomLockType;
|
||||
/// let _y = OtherCustomLockType;
|
||||
/// baz().await; // Lint violation
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub AWAIT_HOLDING_INVALID_TYPE,
|
||||
suspicious,
|
||||
"holding a type across an await point which is not allowed to be held as per the configuration"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for calls to `await` while holding a non-async-aware
|
||||
@@ -134,44 +171,11 @@
|
||||
"inside an async function, holding a `RefCell` ref while calling `await`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### 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 use concurrently from
|
||||
/// a memory access perspective, but that will cause bugs at runtime if
|
||||
/// they are held in such a way.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```toml
|
||||
/// 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" }
|
||||
/// ]
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// # async fn baz() {}
|
||||
/// struct CustomLockType;
|
||||
/// struct OtherCustomLockType;
|
||||
/// async fn foo() {
|
||||
/// let _x = CustomLockType;
|
||||
/// let _y = OtherCustomLockType;
|
||||
/// baz().await; // Lint violation
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub AWAIT_HOLDING_INVALID_TYPE,
|
||||
suspicious,
|
||||
"holding a type across an await point which is not allowed to be held as per the configuration"
|
||||
}
|
||||
|
||||
impl_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF, AWAIT_HOLDING_INVALID_TYPE]);
|
||||
impl_lint_pass!(AwaitHolding => [
|
||||
AWAIT_HOLDING_INVALID_TYPE,
|
||||
AWAIT_HOLDING_LOCK,
|
||||
AWAIT_HOLDING_REFCELL_REF,
|
||||
]);
|
||||
|
||||
pub struct AwaitHolding {
|
||||
def_ids: DefIdMap<(&'static str, &'static DisallowedPathWithoutReplacement)>,
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
pedantic,
|
||||
"using if to convert bool to int"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
|
||||
|
||||
@@ -74,6 +74,8 @@
|
||||
"boolean expressions that contain terminals which can be eliminated"
|
||||
}
|
||||
|
||||
impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
|
||||
|
||||
// For each pairs, both orders are considered.
|
||||
const METHODS_WITH_NEGATION: [(Option<RustcVersion>, Symbol, Symbol); 3] = [
|
||||
(None, sym::is_some, sym::is_none),
|
||||
@@ -91,8 +93,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(NonminimalBool => [NONMINIMAL_BOOL, OVERLY_COMPLEX_BOOL_EXPR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NonminimalBool {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
style,
|
||||
"hard to read byte char slice"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]);
|
||||
|
||||
impl EarlyLintPass for ByteCharSlice {
|
||||
|
||||
+127
-127
@@ -56,126 +56,6 @@
|
||||
"common metadata is defined in `Cargo.toml`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// These prefixes and suffixes have no significant meaning.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with feature name redundancy
|
||||
/// [features]
|
||||
/// default = ["use-abc", "with-def", "ghi-support"]
|
||||
/// use-abc = [] // redundant
|
||||
/// with-def = [] // redundant
|
||||
/// ghi-support = [] // redundant
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def", "ghi"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
/// ghi = []
|
||||
/// ```
|
||||
///
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub REDUNDANT_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a redundant feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for negative feature names with prefix `no-` or `not-`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Features are supposed to be additive, and negatively-named features violate it.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with negative feature names
|
||||
/// [features]
|
||||
/// default = []
|
||||
/// no-abc = []
|
||||
/// not-def = []
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
///
|
||||
/// ```
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub NEGATIVE_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a negative feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks to see if multiple versions of a crate are being
|
||||
/// used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This bloats the size of targets, and can lead to
|
||||
/// confusing error messages when structs or traits are used interchangeably
|
||||
/// between different versions of a crate.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Because this can be caused purely by the dependencies
|
||||
/// themselves, it's not always possible to fix this issue.
|
||||
/// In those cases, you can allow that specific crate using
|
||||
/// the `allowed-duplicate-crates` configuration option.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
|
||||
/// [dependencies]
|
||||
/// ctrlc = "=3.1.0"
|
||||
/// ansi_term = "=0.11.0"
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MULTIPLE_CRATE_VERSIONS,
|
||||
cargo,
|
||||
"multiple versions of the same crate being used"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for wildcard dependencies in the `Cargo.toml`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
|
||||
/// it is highly unlikely that you work with any possible version of your dependency,
|
||||
/// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// regex = "*"
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// # allow patch updates, but not minor or major version changes
|
||||
/// some_crate_1 = "~1.2.3"
|
||||
///
|
||||
/// # pin the version to a specific version
|
||||
/// some_crate_2 = "=1.2.3"
|
||||
/// ```
|
||||
#[clippy::version = "1.32.0"]
|
||||
pub WILDCARD_DEPENDENCIES,
|
||||
cargo,
|
||||
"wildcard dependencies being used"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for lint groups with the same priority as lints in the `Cargo.toml`
|
||||
@@ -213,20 +93,140 @@
|
||||
"a lint group in `Cargo.toml` at the same priority as a lint"
|
||||
}
|
||||
|
||||
pub struct Cargo {
|
||||
allowed_duplicate_crates: FxHashSet<String>,
|
||||
ignore_publish: bool,
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks to see if multiple versions of a crate are being
|
||||
/// used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This bloats the size of targets, and can lead to
|
||||
/// confusing error messages when structs or traits are used interchangeably
|
||||
/// between different versions of a crate.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// Because this can be caused purely by the dependencies
|
||||
/// themselves, it's not always possible to fix this issue.
|
||||
/// In those cases, you can allow that specific crate using
|
||||
/// the `allowed-duplicate-crates` configuration option.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # This will pull in both winapi v0.3.x and v0.2.x, triggering a warning.
|
||||
/// [dependencies]
|
||||
/// ctrlc = "=3.1.0"
|
||||
/// ansi_term = "=0.11.0"
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MULTIPLE_CRATE_VERSIONS,
|
||||
cargo,
|
||||
"multiple versions of the same crate being used"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for negative feature names with prefix `no-` or `not-`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Features are supposed to be additive, and negatively-named features violate it.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with negative feature names
|
||||
/// [features]
|
||||
/// default = []
|
||||
/// no-abc = []
|
||||
/// not-def = []
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
///
|
||||
/// ```
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub NEGATIVE_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a negative feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for feature names with prefix `use-`, `with-` or suffix `-support`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// These prefixes and suffixes have no significant meaning.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// # The `Cargo.toml` with feature name redundancy
|
||||
/// [features]
|
||||
/// default = ["use-abc", "with-def", "ghi-support"]
|
||||
/// use-abc = [] // redundant
|
||||
/// with-def = [] // redundant
|
||||
/// ghi-support = [] // redundant
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [features]
|
||||
/// default = ["abc", "def", "ghi"]
|
||||
/// abc = []
|
||||
/// def = []
|
||||
/// ghi = []
|
||||
/// ```
|
||||
///
|
||||
#[clippy::version = "1.57.0"]
|
||||
pub REDUNDANT_FEATURE_NAMES,
|
||||
cargo,
|
||||
"usage of a redundant feature name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for wildcard dependencies in the `Cargo.toml`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// [As the edition guide says](https://rust-lang-nursery.github.io/edition-guide/rust-2018/cargo-and-crates-io/crates-io-disallows-wildcard-dependencies.html),
|
||||
/// it is highly unlikely that you work with any possible version of your dependency,
|
||||
/// and wildcard dependencies would cause unnecessary breakage in the ecosystem.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// regex = "*"
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```toml
|
||||
/// [dependencies]
|
||||
/// # allow patch updates, but not minor or major version changes
|
||||
/// some_crate_1 = "~1.2.3"
|
||||
///
|
||||
/// # pin the version to a specific version
|
||||
/// some_crate_2 = "=1.2.3"
|
||||
/// ```
|
||||
#[clippy::version = "1.32.0"]
|
||||
pub WILDCARD_DEPENDENCIES,
|
||||
cargo,
|
||||
"wildcard dependencies being used"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Cargo => [
|
||||
CARGO_COMMON_METADATA,
|
||||
REDUNDANT_FEATURE_NAMES,
|
||||
NEGATIVE_FEATURE_NAMES,
|
||||
MULTIPLE_CRATE_VERSIONS,
|
||||
WILDCARD_DEPENDENCIES,
|
||||
LINT_GROUPS_PRIORITY,
|
||||
MULTIPLE_CRATE_VERSIONS,
|
||||
NEGATIVE_FEATURE_NAMES,
|
||||
REDUNDANT_FEATURE_NAMES,
|
||||
WILDCARD_DEPENDENCIES,
|
||||
]);
|
||||
|
||||
pub struct Cargo {
|
||||
allowed_duplicate_crates: FxHashSet<String>,
|
||||
ignore_publish: bool,
|
||||
}
|
||||
|
||||
impl Cargo {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
|
||||
+505
-505
@@ -36,50 +36,231 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from any numeric type to a float type where
|
||||
/// the receiving type cannot store all values from the original type without
|
||||
/// rounding errors. This possible rounding is to be expected, so this lint is
|
||||
/// `Allow` by default.
|
||||
/// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type.
|
||||
///
|
||||
/// Basically, this warns on casting any integer with 32 or more bits to `f32`
|
||||
/// or any 64-bit integer to `f64`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not bad at all. But in some applications it can be
|
||||
/// helpful to know where precision loss can take place. This lint can help find
|
||||
/// those places in the code.
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include a dangerous cast that might go undetected due to the type being inferred.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = u64::MAX;
|
||||
/// x as f64;
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// // BUG: `t` is already a reference, so we will here
|
||||
/// // return a dangling pointer to a temporary value instead
|
||||
/// &t as *const _ as usize
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_PRECISION_LOSS,
|
||||
pedantic,
|
||||
"casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// t as *const T as usize
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub AS_POINTER_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as *mut _` and `as *const _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from a signed to an unsigned numeric
|
||||
/// type. In this case, negative values wrap around to large positive values,
|
||||
/// which can be quite surprising in practice. However, since the cast works as
|
||||
/// defined, this lint is `Allow` by default.
|
||||
/// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Possibly surprising results. You can activate this lint
|
||||
/// as a one-time check to see where numeric wrapping can arise.
|
||||
/// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
|
||||
/// mutability is used, making it unlikely that having it as a mutable pointer is correct.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let y: i8 = -1;
|
||||
/// y as u64; // will return 18446744073709551615
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_ptr() as *mut u8;
|
||||
/// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_mut_ptr();
|
||||
/// unsafe { ptr.write(4) };
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub AS_PTR_CAST_MUT,
|
||||
nursery,
|
||||
"casting the result of the `&self`-taking `as_ptr` to a mutable pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include lossy conversion or a 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
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `&raw const` or
|
||||
/// `&raw mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `&raw` explanation in the Reference for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &raw const val;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &raw mut val_mut;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `abs()` method that cast the result to unsigned.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `unsigned_abs()` method avoids panic when called on the MIN value.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.abs() as u32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.unsigned_abs();
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub CAST_ABS_TO_UNSIGNED,
|
||||
suspicious,
|
||||
"casting the result of `abs()` to an unsigned integer can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum tuple constructor to an integer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The cast is easily confused with casting a c-like enum value to an integer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X(i32) };
|
||||
/// let _ = E::X as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_CONSTRUCTOR,
|
||||
suspicious,
|
||||
"casts from an enum tuple constructor to an integer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum type to an integral type that will definitely truncate the
|
||||
/// value.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The resulting integral value will not match the value of the variant it came from.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X = 256 };
|
||||
/// let _ = E::X as u8;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_TRUNCATION,
|
||||
suspicious,
|
||||
"casts from an enum type to an integral type that will truncate the value"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts between numeric types that can be replaced by safe
|
||||
/// conversion functions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Rust's `as` keyword will perform many kinds of conversions, including
|
||||
/// silently lossy conversions. Conversion functions such as `i32::from`
|
||||
/// will only perform lossless conversions. Using the conversion functions
|
||||
/// prevents conversions from becoming silently lossy if the input types
|
||||
/// ever change, and makes it clear for people reading the code that the
|
||||
/// conversion is lossless.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// x as u64
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `::from` would look like this:
|
||||
///
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// u64::from(x)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_SIGN_LOSS,
|
||||
pub CAST_LOSSLESS,
|
||||
pedantic,
|
||||
"casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
|
||||
"casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a known NaN float being cast to an integer
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// NaNs are cast into zero, so one could simply use this and make the
|
||||
/// code more readable. The lint could also hint at a programmer error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _ = (0.0_f32 / 0.0) as u64;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _ = 0_u64;
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub CAST_NAN_TO_INT,
|
||||
suspicious,
|
||||
"casting a known floating-point NaN into an integer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -159,71 +340,28 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts between numeric types that can be replaced by safe
|
||||
/// conversion functions.
|
||||
/// Checks for casts from any numeric type to a float type where
|
||||
/// the receiving type cannot store all values from the original type without
|
||||
/// rounding errors. This possible rounding is to be expected, so this lint is
|
||||
/// `Allow` by default.
|
||||
///
|
||||
/// Basically, this warns on casting any integer with 32 or more bits to `f32`
|
||||
/// or any 64-bit integer to `f64`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Rust's `as` keyword will perform many kinds of conversions, including
|
||||
/// silently lossy conversions. Conversion functions such as `i32::from`
|
||||
/// will only perform lossless conversions. Using the conversion functions
|
||||
/// prevents conversions from becoming silently lossy if the input types
|
||||
/// ever change, and makes it clear for people reading the code that the
|
||||
/// conversion is lossless.
|
||||
/// It's not bad at all. But in some applications it can be
|
||||
/// helpful to know where precision loss can take place. This lint can help find
|
||||
/// those places in the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// x as u64
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `::from` would look like this:
|
||||
///
|
||||
/// ```no_run
|
||||
/// fn as_u64(x: u8) -> u64 {
|
||||
/// u64::from(x)
|
||||
/// }
|
||||
/// let x = u64::MAX;
|
||||
/// x as f64;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_LOSSLESS,
|
||||
pub CAST_PRECISION_LOSS,
|
||||
pedantic,
|
||||
"casts using `as` that are known to be lossless, e.g., `x as u64` where `x: u8`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts to the same type, casts of int literals to integer
|
||||
/// types, casts of float literals to float types, and casts between raw
|
||||
/// pointers that don't change type or constness.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's just unnecessary.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// When the expression on the left is a function call, the lint considers
|
||||
/// the return type to be a type alias if it's aliased through a `use`
|
||||
/// statement (like `use std::io::Result as IoResult`). It will not lint
|
||||
/// such cases.
|
||||
///
|
||||
/// This check will only work on primitive types without any intermediate
|
||||
/// references: raw pointers and trait objects may or may not work.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = 2i32 as i32;
|
||||
/// let _ = 0.5 as f32;
|
||||
/// ```
|
||||
///
|
||||
/// Better:
|
||||
///
|
||||
/// ```no_run
|
||||
/// let _ = 2_i32;
|
||||
/// let _ = 0.5_f32;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNNECESSARY_CAST,
|
||||
complexity,
|
||||
"cast to the same type, e.g., `x as i32` where `x: i32`"
|
||||
"casts that cause loss of precision, e.g., `x as f32` where `x: u64`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -254,6 +392,154 @@
|
||||
"cast from a pointer to a more strictly aligned pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from a signed to an unsigned numeric
|
||||
/// type. In this case, negative values wrap around to large positive values,
|
||||
/// which can be quite surprising in practice. However, since the cast works as
|
||||
/// defined, this lint is `Allow` by default.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Possibly surprising results. You can activate this lint
|
||||
/// as a one-time check to see where numeric wrapping can arise.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let y: i8 = -1;
|
||||
/// y as u64; // will return 18446744073709551615
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CAST_SIGN_LOSS,
|
||||
pedantic,
|
||||
"casts from signed types to unsigned types, e.g., `x as u32` where `x: i32`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `as` casts between raw pointers to slices with differently sized elements.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The produced raw pointer to a slice does not update its length metadata. The produced
|
||||
/// pointer will point to a different number of bytes than the original pointer because the
|
||||
/// length metadata of a raw slice pointer is in elements rather than bytes.
|
||||
/// Producing a slice reference from the raw pointer will either create a slice with
|
||||
/// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// // Missing data
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let p = &a as *const [i32] as *const [u8];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// // Undefined Behavior (note: also potential alignment issues)
|
||||
/// ```no_run
|
||||
/// let a = [1_u8, 2, 3, 4];
|
||||
/// let p = &a as *const [u8] as *const [u32];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let old_ptr = &a as *const [i32];
|
||||
/// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
|
||||
/// // The length comes from the known length of 4 i32s times the 4 bytes per i32
|
||||
/// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*new_ptr);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_SLICE_DIFFERENT_SIZES,
|
||||
correctness,
|
||||
"casting using `as` between raw pointers to slices of types with different sizes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a raw slice being cast to a slice pointer
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can result in multiple `&mut` references to the same location when only a pointer is
|
||||
/// required.
|
||||
/// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
|
||||
/// the same [safety requirements] to be upheld.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
|
||||
/// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
|
||||
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
|
||||
/// ```
|
||||
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub CAST_SLICE_FROM_RAW_PARTS,
|
||||
suspicious,
|
||||
"casting a slice created from a pointer and length to a slice pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions where a character literal is cast
|
||||
/// to `u8` and suggests using a byte literal instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In general, casting values to smaller types is
|
||||
/// error-prone and should be avoided where possible. In the particular case of
|
||||
/// converting a character literal to `u8`, it is easy to avoid by just using a
|
||||
/// byte literal instead. As an added bonus, `b'a'` is also slightly shorter
|
||||
/// than `'a' as u8`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// 'x' as u8
|
||||
/// ```
|
||||
///
|
||||
/// A better version, using the byte literal:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// b'x'
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CHAR_LIT_AS_U8,
|
||||
complexity,
|
||||
"casting a character literal to `u8` truncates"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a primitive method pointer like `max`/`min` to any integer type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Casting a function pointer to an integer can have surprising results and can occur
|
||||
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
|
||||
/// low-level with function pointers then you can opt out of casting functions to integers in
|
||||
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
|
||||
/// pointer casts in your code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = u16::max as usize;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = u16::MAX as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
suspicious,
|
||||
"casting a primitive method pointer to any integer type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of function pointers to something other than `usize`.
|
||||
@@ -284,39 +570,6 @@
|
||||
"casting a function pointer to a numeric type other than `usize`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a function pointer to a numeric type not wide enough to
|
||||
/// store an address.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Such a cast discards some bits of the function's address. If this is intended, it would be more
|
||||
/// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with
|
||||
/// a comment) to perform the truncation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let _ = fn1 as i32;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Cast to usize first, then comment with the reason for the truncation
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let fn_ptr = fn1 as usize;
|
||||
/// let fn_ptr_truncated = fn_ptr as i32;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
style,
|
||||
"casting a function pointer to a numeric type not wide enough to store the address"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a function pointer to any integer type.
|
||||
@@ -361,30 +614,87 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for expressions where a character literal is cast
|
||||
/// to `u8` and suggests using a byte literal instead.
|
||||
/// Checks for casts of a function pointer to a numeric type not wide enough to
|
||||
/// store an address.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// In general, casting values to smaller types is
|
||||
/// error-prone and should be avoided where possible. In the particular case of
|
||||
/// converting a character literal to `u8`, it is easy to avoid by just using a
|
||||
/// byte literal instead. As an added bonus, `b'a'` is also slightly shorter
|
||||
/// than `'a' as u8`.
|
||||
/// Such a cast discards some bits of the function's address. If this is intended, it would be more
|
||||
/// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with
|
||||
/// a comment) to perform the truncation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// 'x' as u8
|
||||
/// ```no_run
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let _ = fn1 as i32;
|
||||
/// ```
|
||||
///
|
||||
/// A better version, using the byte literal:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// b'x'
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Cast to usize first, then comment with the reason for the truncation
|
||||
/// fn fn1() -> i16 {
|
||||
/// 1
|
||||
/// };
|
||||
/// let fn_ptr = fn1 as usize;
|
||||
/// let fn_ptr_truncated = fn_ptr as i32;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub CHAR_LIT_AS_U8,
|
||||
complexity,
|
||||
"casting a character literal to `u8` truncates"
|
||||
pub FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
style,
|
||||
"casting a function pointer to a numeric type not wide enough to store the address"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This creates a dangling pointer and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let ptr = 4 as *const u32;
|
||||
/// let aligned = std::mem::align_of::<u32>() as *const u32;
|
||||
/// let mut_ptr: *mut i64 = 8 as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let ptr = std::ptr::dangling::<u32>();
|
||||
/// let aligned = std::ptr::dangling::<u32>();
|
||||
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
|
||||
/// ```
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub MANUAL_DANGLING_PTR,
|
||||
style,
|
||||
"casting small constant literals to pointers to create dangling pointers"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bindings (constants, statics, or let bindings) that are defined
|
||||
/// with one numeric type but are consistently cast to a different type in all usages.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a binding is always cast to a different type when used, it would be clearer
|
||||
/// and more efficient to define it with the target type from the start.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// const SIZE: u16 = 15;
|
||||
/// let arr: [u8; SIZE as usize] = [0; SIZE as usize];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// const SIZE: usize = 15;
|
||||
/// let arr: [u8; SIZE] = [0; SIZE];
|
||||
/// ```
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub NEEDLESS_TYPE_CAST,
|
||||
nursery,
|
||||
"binding defined with one type but always cast to another"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -455,243 +765,62 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum type to an integral type that will definitely truncate the
|
||||
/// value.
|
||||
/// Checks for casts of references to pointer using `as`
|
||||
/// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The resulting integral value will not match the value of the variant it came from.
|
||||
/// Using `as` casts may result in silently changing mutability or type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X = 256 };
|
||||
/// let _ = E::X as u8;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_TRUNCATION,
|
||||
suspicious,
|
||||
"casts from an enum type to an integral type that will truncate the value"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `as` casts between raw pointers to slices with differently sized elements.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The produced raw pointer to a slice does not update its length metadata. The produced
|
||||
/// pointer will point to a different number of bytes than the original pointer because the
|
||||
/// length metadata of a raw slice pointer is in elements rather than bytes.
|
||||
/// Producing a slice reference from the raw pointer will either create a slice with
|
||||
/// less data (which can be surprising) or create a slice with more data and cause Undefined Behavior.
|
||||
///
|
||||
/// ### Example
|
||||
/// // Missing data
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let p = &a as *const [i32] as *const [u8];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// // Undefined Behavior (note: also potential alignment issues)
|
||||
/// ```no_run
|
||||
/// let a = [1_u8, 2, 3, 4];
|
||||
/// let p = &a as *const [u8] as *const [u32];
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*p);
|
||||
/// }
|
||||
/// ```
|
||||
/// Instead use `ptr::slice_from_raw_parts` to construct a slice from a data pointer and the correct length
|
||||
/// ```no_run
|
||||
/// let a = [1_i32, 2, 3, 4];
|
||||
/// let old_ptr = &a as *const [i32];
|
||||
/// // The data pointer is cast to a pointer to the target `u8` not `[u8]`
|
||||
/// // The length comes from the known length of 4 i32s times the 4 bytes per i32
|
||||
/// let new_ptr = core::ptr::slice_from_raw_parts(old_ptr as *const u8, 16);
|
||||
/// unsafe {
|
||||
/// println!("{:?}", &*new_ptr);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_SLICE_DIFFERENT_SIZES,
|
||||
correctness,
|
||||
"casting using `as` between raw pointers to slices of types with different sizes"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts from an enum tuple constructor to an integer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The cast is easily confused with casting a c-like enum value to an integer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// enum E { X(i32) };
|
||||
/// let _ = E::X as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub CAST_ENUM_CONSTRUCTOR,
|
||||
suspicious,
|
||||
"casts from an enum tuple constructor to an integer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the `abs()` method that cast the result to unsigned.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `unsigned_abs()` method avoids panic when called on the MIN value.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.abs() as u32;
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = a_ref as *const _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x: i32 = -42;
|
||||
/// let y: u32 = x.unsigned_abs();
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = std::ptr::from_ref(a_ref);
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub CAST_ABS_TO_UNSIGNED,
|
||||
suspicious,
|
||||
"casting the result of `abs()` to an unsigned integer can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include lossy conversion or a 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
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `&raw const` or
|
||||
/// `&raw mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `&raw` explanation in the Reference for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let val = 1;
|
||||
/// let p = &raw const val;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &raw mut val_mut;
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub REF_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
"using `as` to cast a reference to pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a raw slice being cast to a slice pointer
|
||||
/// Checks for casts to the same type, casts of int literals to integer
|
||||
/// types, casts of float literals to float types, and casts between raw
|
||||
/// pointers that don't change type or constness.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can result in multiple `&mut` references to the same location when only a pointer is
|
||||
/// required.
|
||||
/// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
|
||||
/// the same [safety requirements] to be upheld.
|
||||
/// It's just unnecessary.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
|
||||
/// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
|
||||
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
|
||||
/// ```
|
||||
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub CAST_SLICE_FROM_RAW_PARTS,
|
||||
suspicious,
|
||||
"casting a slice created from a pointer and length to a slice pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer.
|
||||
/// ### Known problems
|
||||
/// When the expression on the left is a function call, the lint considers
|
||||
/// the return type to be a type alias if it's aliased through a `use`
|
||||
/// statement (like `use std::io::Result as IoResult`). It will not lint
|
||||
/// such cases.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
|
||||
/// mutability is used, making it unlikely that having it as a mutable pointer is correct.
|
||||
/// This check will only work on primitive types without any intermediate
|
||||
/// references: raw pointers and trait objects may or may not work.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_ptr() as *mut u8;
|
||||
/// unsafe { ptr.write(4) }; // UNDEFINED BEHAVIOUR
|
||||
/// let _ = 2i32 as i32;
|
||||
/// let _ = 0.5 as f32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
///
|
||||
/// Better:
|
||||
///
|
||||
/// ```no_run
|
||||
/// let mut vec = Vec::<u8>::with_capacity(1);
|
||||
/// let ptr = vec.as_mut_ptr();
|
||||
/// unsafe { ptr.write(4) };
|
||||
/// let _ = 2_i32;
|
||||
/// let _ = 0.5_f32;
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub AS_PTR_CAST_MUT,
|
||||
nursery,
|
||||
"casting the result of the `&self`-taking `as_ptr` to a mutable pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a known NaN float being cast to an integer
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// NaNs are cast into zero, so one could simply use this and make the
|
||||
/// code more readable. The lint could also hint at a programmer error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _ = (0.0_f32 / 0.0) as u64;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _ = 0_u64;
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub CAST_NAN_TO_INT,
|
||||
suspicious,
|
||||
"casting a known floating-point NaN into an integer"
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNNECESSARY_CAST,
|
||||
complexity,
|
||||
"cast to the same type, e.g., `x as i32` where `x: i32`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -717,134 +846,36 @@
|
||||
"using `0 as *{const, mut} T`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of references to pointer using `as`
|
||||
/// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `as` casts may result in silently changing mutability or type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = a_ref as *const _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let a_ref = &1;
|
||||
/// let a_ptr = std::ptr::from_ref(a_ref);
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub REF_AS_PTR,
|
||||
pedantic,
|
||||
"using `as` to cast a reference to pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `as *const _` or `as *mut _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The conversion might include a dangerous cast that might go undetected due to the type being inferred.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// // BUG: `t` is already a reference, so we will here
|
||||
/// // return a dangling pointer to a temporary value instead
|
||||
/// &t as *const _ as usize
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn as_usize<T>(t: &T) -> usize {
|
||||
/// t as *const T as usize
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub AS_POINTER_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as *mut _` and `as *const _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of small constant literals or `mem::align_of` results to raw pointers.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This creates a dangling pointer and is better expressed as
|
||||
/// {`std`, `core`}`::ptr::`{`dangling`, `dangling_mut`}.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let ptr = 4 as *const u32;
|
||||
/// let aligned = std::mem::align_of::<u32>() as *const u32;
|
||||
/// let mut_ptr: *mut i64 = 8 as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let ptr = std::ptr::dangling::<u32>();
|
||||
/// let aligned = std::ptr::dangling::<u32>();
|
||||
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
|
||||
/// ```
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub MANUAL_DANGLING_PTR,
|
||||
style,
|
||||
"casting small constant literals to pointers to create dangling pointers"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a primitive method pointer like `max`/`min` to any integer type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Casting a function pointer to an integer can have surprising results and can occur
|
||||
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
|
||||
/// low-level with function pointers then you can opt out of casting functions to integers in
|
||||
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
|
||||
/// pointer casts in your code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = u16::max as usize;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = u16::MAX as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
suspicious,
|
||||
"casting a primitive method pointer to any integer type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bindings (constants, statics, or let bindings) that are defined
|
||||
/// with one numeric type but are consistently cast to a different type in all usages.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a binding is always cast to a different type when used, it would be clearer
|
||||
/// and more efficient to define it with the target type from the start.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// const SIZE: u16 = 15;
|
||||
/// let arr: [u8; SIZE as usize] = [0; SIZE as usize];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// const SIZE: usize = 15;
|
||||
/// let arr: [u8; SIZE] = [0; SIZE];
|
||||
/// ```
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub NEEDLESS_TYPE_CAST,
|
||||
nursery,
|
||||
"binding defined with one type but always cast to another"
|
||||
}
|
||||
impl_lint_pass!(Casts => [
|
||||
AS_POINTER_UNDERSCORE,
|
||||
AS_PTR_CAST_MUT,
|
||||
AS_UNDERSCORE,
|
||||
BORROW_AS_PTR,
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_LOSSLESS,
|
||||
CAST_NAN_TO_INT,
|
||||
CAST_POSSIBLE_TRUNCATION,
|
||||
CAST_POSSIBLE_WRAP,
|
||||
CAST_PRECISION_LOSS,
|
||||
CAST_PTR_ALIGNMENT,
|
||||
CAST_SIGN_LOSS,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
CAST_SLICE_FROM_RAW_PARTS,
|
||||
CHAR_LIT_AS_U8,
|
||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
FN_TO_NUMERIC_CAST,
|
||||
FN_TO_NUMERIC_CAST_ANY,
|
||||
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
MANUAL_DANGLING_PTR,
|
||||
NEEDLESS_TYPE_CAST,
|
||||
PTR_AS_PTR,
|
||||
PTR_CAST_CONSTNESS,
|
||||
REF_AS_PTR,
|
||||
UNNECESSARY_CAST,
|
||||
ZERO_PTR,
|
||||
]);
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Msrv,
|
||||
@@ -856,37 +887,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Casts => [
|
||||
CAST_PRECISION_LOSS,
|
||||
CAST_SIGN_LOSS,
|
||||
CAST_POSSIBLE_TRUNCATION,
|
||||
CAST_POSSIBLE_WRAP,
|
||||
CAST_LOSSLESS,
|
||||
CAST_PTR_ALIGNMENT,
|
||||
CAST_SLICE_DIFFERENT_SIZES,
|
||||
UNNECESSARY_CAST,
|
||||
FN_TO_NUMERIC_CAST_ANY,
|
||||
FN_TO_NUMERIC_CAST,
|
||||
FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
|
||||
CHAR_LIT_AS_U8,
|
||||
PTR_AS_PTR,
|
||||
PTR_CAST_CONSTNESS,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
AS_UNDERSCORE,
|
||||
BORROW_AS_PTR,
|
||||
CAST_SLICE_FROM_RAW_PARTS,
|
||||
AS_PTR_CAST_MUT,
|
||||
CAST_NAN_TO_INT,
|
||||
ZERO_PTR,
|
||||
REF_AS_PTR,
|
||||
AS_POINTER_UNDERSCORE,
|
||||
MANUAL_DANGLING_PTR,
|
||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
NEEDLESS_TYPE_CAST,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.in_external_macro(cx.sess().source_map()) {
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
"`try_from` could replace manual bounds checking when casting"
|
||||
}
|
||||
|
||||
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
|
||||
pub struct CheckedConversions {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -44,8 +46,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]);
|
||||
|
||||
impl LateLintPass<'_> for CheckedConversions {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) {
|
||||
if let ExprKind::Binary(op, lhs, rhs) = item.kind
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
"cloning a reference for slice references"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]);
|
||||
|
||||
pub struct ClonedRefToSliceRefs<'a> {
|
||||
msrv: &'a Msrv,
|
||||
}
|
||||
@@ -53,8 +55,6 @@ pub fn new(conf: &'a Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if self.msrv.meets(cx, {
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
nursery,
|
||||
"coercing to `&dyn Any` when dereferencing could produce a `dyn Any` without coercion is usually not intended"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny {
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
@eval_always = true
|
||||
}
|
||||
|
||||
impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
|
||||
|
||||
pub struct CognitiveComplexity {
|
||||
limit: LimitStack,
|
||||
}
|
||||
@@ -52,8 +54,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
|
||||
|
||||
impl CognitiveComplexity {
|
||||
fn check<'tcx>(
|
||||
&self,
|
||||
|
||||
@@ -12,38 +12,6 @@
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{BytePos, Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for nested `if` statements which can be collapsed
|
||||
/// by `&&`-combining their conditions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// // …
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x && y {
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub COLLAPSIBLE_IF,
|
||||
style,
|
||||
"nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for collapsible `else { if ... }` expressions
|
||||
@@ -80,6 +48,40 @@
|
||||
"nested `else`-`if` expressions that can be collapsed (e.g., `else { if x { ... } }`)"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for nested `if` statements which can be collapsed
|
||||
/// by `&&`-combining their conditions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Each `if`-statement adds one level of nesting, which
|
||||
/// makes code look more complex than it really is.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x {
|
||||
/// if y {
|
||||
/// // …
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let (x, y) = (true, true);
|
||||
/// if x && y {
|
||||
/// // …
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub COLLAPSIBLE_IF,
|
||||
style,
|
||||
"nested `if`s that can be collapsed (e.g., `if x { if y { ... } }`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_ELSE_IF, COLLAPSIBLE_IF]);
|
||||
|
||||
pub struct CollapsibleIf {
|
||||
msrv: Msrv,
|
||||
lint_commented_code: bool,
|
||||
@@ -259,8 +261,6 @@ fn check_significant_tokens_and_expect_attrs(
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CollapsibleIf => [COLLAPSIBLE_IF, COLLAPSIBLE_ELSE_IF]);
|
||||
|
||||
impl LateLintPass<'_> for CollapsibleIf {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if let ExprKind::If(cond, then, else_) = &expr.kind
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
nursery,
|
||||
"a collection is never queried"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
suspicious,
|
||||
"using `crate` in a macro definition"
|
||||
}
|
||||
|
||||
declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
|
||||
|
||||
impl EarlyLintPass for CrateInMacroDef {
|
||||
|
||||
@@ -33,6 +33,8 @@
|
||||
"`dbg!` macro is intended as a debugging tool"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
|
||||
pub struct DbgMacro {
|
||||
allow_dbg_in_tests: bool,
|
||||
/// Tracks the `dbg!` macro callsites that are already checked.
|
||||
@@ -41,8 +43,6 @@ pub struct DbgMacro {
|
||||
prev_ctxt: SyntaxContext,
|
||||
}
|
||||
|
||||
impl_lint_pass!(DbgMacro => [DBG_MACRO]);
|
||||
|
||||
impl DbgMacro {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
DbgMacro {
|
||||
|
||||
@@ -69,14 +69,14 @@
|
||||
"binding initialized with Default should have its fields set in the initializer"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Default {
|
||||
// Spans linted by `field_reassign_with_default`.
|
||||
reassigned_linted: FxHashSet<Span>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Default {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion()
|
||||
|
||||
@@ -45,7 +45,10 @@
|
||||
complexity,
|
||||
"unit structs can be constructed without calling `default`"
|
||||
}
|
||||
declare_lint_pass!(DefaultConstructedUnitStructs => [DEFAULT_CONSTRUCTED_UNIT_STRUCTS]);
|
||||
|
||||
declare_lint_pass!(DefaultConstructedUnitStructs => [
|
||||
DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
|
||||
]);
|
||||
|
||||
fn is_alias(ty: hir::Ty<'_>) -> bool {
|
||||
if let hir::TyKind::Path(ref qpath) = ty.kind {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
style,
|
||||
"check `std::iter::Empty::default()` and replace with `std::iter::empty()`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DefaultIterEmpty => [DEFAULT_INSTEAD_OF_ITER_EMPTY]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
restriction,
|
||||
"unions without a `#[repr(C)]` attribute"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DefaultUnionRepresentation => [DEFAULT_UNION_REPRESENTATION]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
|
||||
|
||||
@@ -23,6 +23,29 @@
|
||||
use rustc_span::{Span, Symbol};
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for dereferencing expressions which would be covered by auto-deref.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This unnecessarily complicates the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &*x;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &x;
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub EXPLICIT_AUTO_DEREF,
|
||||
complexity,
|
||||
"dereferencing when the compiler would automatically dereference"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for explicit `deref()` or `deref_mut()` method calls.
|
||||
@@ -120,34 +143,11 @@
|
||||
"`ref` binding to a reference"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for dereferencing expressions which would be covered by auto-deref.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This unnecessarily complicates the code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &*x;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x = String::new();
|
||||
/// let y: &str = &x;
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub EXPLICIT_AUTO_DEREF,
|
||||
complexity,
|
||||
"dereferencing when the compiler would automatically dereference"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Dereferencing<'_> => [
|
||||
EXPLICIT_AUTO_DEREF,
|
||||
EXPLICIT_DEREF_METHODS,
|
||||
NEEDLESS_BORROW,
|
||||
REF_BINDING_TO_REFERENCE,
|
||||
EXPLICIT_AUTO_DEREF,
|
||||
]);
|
||||
|
||||
#[derive(Default)]
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
"manual implementation of the `Default` trait which is equal to a derive"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
|
||||
|
||||
pub struct DerivableImpls {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -66,8 +68,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DerivableImpls => [DERIVABLE_IMPLS]);
|
||||
|
||||
fn is_path_self(e: &Expr<'_>) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(_, p)) = e.kind {
|
||||
matches!(p.res, Res::SelfCtor(..) | Res::Def(DefKind::Ctor(..), _))
|
||||
|
||||
@@ -10,36 +10,6 @@
|
||||
mod expl_impl_clone_on_copy;
|
||||
mod unsafe_derive_deserialize;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints against manual `PartialEq` implementations for types with a derived `Hash`
|
||||
/// implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The implementation of these traits must agree (for
|
||||
/// example for use with `HashMap`) so it’s probably a bad idea to use a
|
||||
/// default-generated `Hash` implementation with an explicitly defined
|
||||
/// `PartialEq`. In particular, the following must hold for any type:
|
||||
///
|
||||
/// ```text
|
||||
/// k1 == k2 ⇒ hash(k1) == hash(k2)
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[derive(Hash)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialEq for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DERIVED_HASH_WITH_MANUAL_EQ,
|
||||
correctness,
|
||||
"deriving `Hash` but implementing `PartialEq` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord`
|
||||
@@ -91,6 +61,68 @@
|
||||
"deriving `Ord` but implementing `PartialOrd` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for types that derive `PartialEq` and could implement `Eq`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
|
||||
/// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
|
||||
/// in APIs that require `Eq` types. It also allows structs containing `T` to derive
|
||||
/// `Eq` themselves.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq, Eq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
nursery,
|
||||
"deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints against manual `PartialEq` implementations for types with a derived `Hash`
|
||||
/// implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The implementation of these traits must agree (for
|
||||
/// example for use with `HashMap`) so it’s probably a bad idea to use a
|
||||
/// default-generated `Hash` implementation with an explicitly defined
|
||||
/// `PartialEq`. In particular, the following must hold for any type:
|
||||
///
|
||||
/// ```text
|
||||
/// k1 == k2 ⇒ hash(k1) == hash(k2)
|
||||
/// ```
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #[derive(Hash)]
|
||||
/// struct Foo;
|
||||
///
|
||||
/// impl PartialEq for Foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DERIVED_HASH_WITH_MANUAL_EQ,
|
||||
correctness,
|
||||
"deriving `Hash` but implementing `PartialEq` explicitly"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for explicit `Clone` implementations for `Copy`
|
||||
@@ -152,44 +184,12 @@
|
||||
"deriving `serde::Deserialize` on a type that has methods using `unsafe`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for types that derive `PartialEq` and could implement `Eq`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
|
||||
/// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
|
||||
/// in APIs that require `Eq` types. It also allows structs containing `T` to derive
|
||||
/// `Eq` themselves.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #[derive(PartialEq, Eq)]
|
||||
/// struct Foo {
|
||||
/// i_am_eq: i32,
|
||||
/// i_am_eq_too: Vec<String>,
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
nursery,
|
||||
"deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Derive => [
|
||||
EXPL_IMPL_CLONE_ON_COPY,
|
||||
DERIVED_HASH_WITH_MANUAL_EQ,
|
||||
DERIVE_ORD_XOR_PARTIAL_ORD,
|
||||
DERIVE_PARTIAL_EQ_WITHOUT_EQ,
|
||||
EXPL_IMPL_CLONE_ON_COPY,
|
||||
UNSAFE_DERIVE_DESERIALIZE,
|
||||
DERIVE_PARTIAL_EQ_WITHOUT_EQ
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Derive {
|
||||
|
||||
@@ -56,6 +56,8 @@
|
||||
"declaration of a disallowed field use"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedFields => [DISALLOWED_FIELDS]);
|
||||
|
||||
pub struct DisallowedFields {
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
}
|
||||
@@ -74,8 +76,6 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedFields => [DISALLOWED_FIELDS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedFields {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let (id, span) = match &expr.kind {
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
"use of a disallowed macro"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
|
||||
|
||||
pub struct DisallowedMacros {
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
seen: FxHashSet<ExpnId>,
|
||||
@@ -125,8 +127,6 @@ fn check(&mut self, cx: &LateContext<'_>, span: Span, derive_src: Option<OwnerId
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
|
||||
|
||||
impl LateLintPass<'_> for DisallowedMacros {
|
||||
fn check_crate(&mut self, cx: &LateContext<'_>) {
|
||||
// once we check a crate in the late pass we can emit the early pass lints
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
"use of a disallowed method call"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
|
||||
|
||||
pub struct DisallowedMethods {
|
||||
disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
}
|
||||
@@ -84,8 +86,6 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if expr.span.desugaring_kind().is_some() {
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
"usage of a disallowed/placeholder name"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
|
||||
|
||||
pub struct DisallowedNames {
|
||||
disallow: FxHashSet<Symbol>,
|
||||
}
|
||||
@@ -38,8 +40,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedNames {
|
||||
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
|
||||
if let PatKind::Binding(.., ident, _) = pat.kind
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
"usage of non-allowed Unicode scripts"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]);
|
||||
|
||||
pub struct DisallowedScriptIdents {
|
||||
whitelist: FxHashSet<Script>,
|
||||
}
|
||||
@@ -62,8 +64,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedScriptIdents => [DISALLOWED_SCRIPT_IDENTS]);
|
||||
|
||||
impl EarlyLintPass for DisallowedScriptIdents {
|
||||
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
|
||||
// Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint:
|
||||
|
||||
@@ -57,6 +57,8 @@
|
||||
"use of disallowed types"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
|
||||
|
||||
pub struct DisallowedTypes {
|
||||
def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>,
|
||||
prim_tys: FxHashMap<PrimTy, (&'static str, &'static DisallowedPath)>,
|
||||
@@ -104,8 +106,6 @@ pub fn def_kind_predicate(def_kind: DefKind) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind
|
||||
|
||||
+416
-416
@@ -39,6 +39,198 @@
|
||||
mod test_attr_in_doctest;
|
||||
mod too_long_first_doc_paragraph;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks the doc comments have unbroken links, mostly caused
|
||||
/// by bad formatted links such as broken across multiple lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Because documentation generated by rustdoc will be broken
|
||||
/// since expected links won't be links and just text.
|
||||
///
|
||||
/// ### Examples
|
||||
/// This link is broken:
|
||||
/// ```no_run
|
||||
/// /// [example of a bad link](https://
|
||||
/// /// github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
///
|
||||
/// It shouldn't be broken across multiple lines to work:
|
||||
/// ```no_run
|
||||
/// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.90.0"]
|
||||
pub DOC_BROKEN_LINK,
|
||||
pedantic,
|
||||
"broken document link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may
|
||||
/// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`)
|
||||
/// is clearer in this regard.
|
||||
///
|
||||
/// ### Example
|
||||
/// The two replacement dots in this example represent a double space.
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and··
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and\
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
pedantic,
|
||||
"double-space used for doc comment linebreak instead of `\\`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if included files in doc comments are included only for `cfg(doc)`.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// These files are not useful for compilation but will still be included.
|
||||
/// Also, if any of these non-source code file is updated, it will trigger a
|
||||
/// recompilation.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// Excluding this will currently result in the file being left out if
|
||||
/// the item's docs are inlined from another crate. This may be fixed in a
|
||||
/// future version of rustdoc.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #![doc = include_str!("some_file.md")]
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_INCLUDE_WITHOUT_CFG,
|
||||
restriction,
|
||||
"check if files included in documentation are behind `cfg(doc)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// In CommonMark Markdown, the language used to write doc comments, a
|
||||
/// paragraph nested within a list or block quote does not need any line
|
||||
/// after the first one to be indented or marked. The specification calls
|
||||
/// this a "lazy paragraph continuation."
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// This is easy to write but hard to read. Lazy continuations makes
|
||||
/// unintended markers hard to see, and make it harder to deduce the
|
||||
/// document's intended structure.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// This table is probably intended to have two rows,
|
||||
/// but it does not. It has zero rows, and is followed by
|
||||
/// a block quote.
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// >= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by escaping the marker:
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// \>= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// This example is actually intended to be a list:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by indenting the list contents:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub DOC_LAZY_CONTINUATION,
|
||||
style,
|
||||
"require every line of a paragraph to be indented and marked"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for links with code directly adjacent to code text:
|
||||
/// `` [`MyItem`]`<`[`u32`]`>` ``.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can be written more simply using HTML-style `<code>` tags.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// //! [`first`](x)`second`
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! <code>[first](x)second</code>
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_LINK_CODE,
|
||||
nursery,
|
||||
"link with code back-to-back with other code"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
/// outside of code blocks
|
||||
/// ### Why is this bad?
|
||||
/// It is likely a typo when defining an intra-doc link
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// See also: ['foo']
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the presence of `_`, `::` or camel-case words
|
||||
@@ -83,59 +275,149 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for links with code directly adjacent to code text:
|
||||
/// `` [`MyItem`]`<`[`u32`]`>` ``.
|
||||
/// Warns if a link reference definition appears at the start of a
|
||||
/// list item or quote.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It can be written more simply using HTML-style `<code>` tags.
|
||||
/// This is probably intended as an intra-doc link. If it is really
|
||||
/// supposed to be a reference definition, it can be written outside
|
||||
/// of the list item or quote.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// //! [`first`](x)`second`
|
||||
/// //! - [link]: description
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! <code>[first](x)second</code>
|
||||
/// //! - [link][]: description (for intra-doc link)
|
||||
/// //!
|
||||
/// //! [link]: destination (for link reference definition)
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_LINK_CODE,
|
||||
nursery,
|
||||
"link with code back-to-back with other code"
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_NESTED_REFDEFS,
|
||||
suspicious,
|
||||
"link reference defined in list item or quote"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// unsafe functions and warns if there is no `# Safety` section.
|
||||
///
|
||||
/// Detects overindented list items in doc comments where the continuation
|
||||
/// lines are indented more than necessary.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unsafe functions should document their safety
|
||||
/// preconditions, so that users can be sure they are using them safely.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
/// Overindented list items in doc comments can lead to inconsistent and
|
||||
/// poorly formatted documentation when rendered. Excessive indentation may
|
||||
/// cause the text to be misinterpreted as a nested list item or code block,
|
||||
/// affecting readability and the overall structure of the documentation.
|
||||
///
|
||||
/// At least write a line about safety:
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.39.0"]
|
||||
pub MISSING_SAFETY_DOC,
|
||||
///
|
||||
/// Fixes this into:
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub DOC_OVERINDENTED_LIST_ITEMS,
|
||||
style,
|
||||
"`pub unsafe fn` without `# Safety` docs"
|
||||
"ensure list items are not overindented"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for doc comments whose paragraphs do not end with a period or another punctuation mark.
|
||||
/// Various Markdowns constructs are taken into account to avoid false positives.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A project may wish to enforce consistent doc comments by making sure paragraphs end with a
|
||||
/// punctuation mark.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Returns a random number
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// Returns a random number.
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll.
|
||||
/// ```
|
||||
///
|
||||
/// ### Terminal punctuation marks
|
||||
/// This lint treats these characters as end markers: '.', '?', '!', '…' and ':'.
|
||||
///
|
||||
/// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that
|
||||
/// introduce a table or a list for example.
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
restriction,
|
||||
"missing terminal punctuation in doc comments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects syntax that looks like a footnote reference.
|
||||
///
|
||||
/// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).
|
||||
/// GFM does not parse a footnote reference unless its definition also
|
||||
/// exists. This lint checks for footnote references with missing
|
||||
/// definitions, unless it thinks you're writing a regex.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This probably means that a footnote was meant to exist,
|
||||
/// but was not written.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// This is not a footnote[^1], because no definition exists.
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// This is a footnote[^1].
|
||||
/// ///
|
||||
/// /// [^1]: defined here
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub DOC_SUSPICIOUS_FOOTNOTES,
|
||||
suspicious,
|
||||
"looks like a link or footnote ref, but with no definition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects documentation that is empty.
|
||||
/// ### Why is this bad?
|
||||
/// Empty docs clutter code without adding value, reducing readability and maintainability.
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// ///
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub EMPTY_DOCS,
|
||||
suspicious,
|
||||
"docstrings exist but documentation is empty"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -211,6 +493,41 @@
|
||||
"`pub fn` may panic without `# Panics` in doc comment"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// unsafe functions and warns if there is no `# Safety` section.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unsafe functions should document their safety
|
||||
/// preconditions, so that users can be sure they are using them safely.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// At least write a line about safety:
|
||||
///
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub unsafe fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.39.0"]
|
||||
pub MISSING_SAFETY_DOC,
|
||||
style,
|
||||
"`pub unsafe fn` without `# Safety` docs"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `fn main() { .. }` in doctests
|
||||
@@ -240,126 +557,6 @@
|
||||
"presence of `fn main() {` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `#[test]` in doctests unless they are marked with
|
||||
/// either `ignore`, `no_run` or `compile_fail`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Code in examples marked as `#[test]` will somewhat
|
||||
/// surprisingly not be run by `cargo test`. If you really want
|
||||
/// to show how to test stuff in an example, mark it `no_run` to
|
||||
/// make the intent clear.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// /// An example of a doctest with a `main()` function
|
||||
/// ///
|
||||
/// /// # Examples
|
||||
/// ///
|
||||
/// /// ```
|
||||
/// /// #[test]
|
||||
/// /// fn equality_works() {
|
||||
/// /// assert_eq!(1_u8, 1);
|
||||
/// /// }
|
||||
/// /// ```
|
||||
/// fn test_attr_in_doctest() {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub TEST_ATTR_IN_DOCTEST,
|
||||
suspicious,
|
||||
"presence of `#[test]` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the syntax `['foo']` in documentation comments (notice quotes instead of backticks)
|
||||
/// outside of code blocks
|
||||
/// ### Why is this bad?
|
||||
/// It is likely a typo when defining an intra-doc link
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// See also: ['foo']
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks the doc comments have unbroken links, mostly caused
|
||||
/// by bad formatted links such as broken across multiple lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Because documentation generated by rustdoc will be broken
|
||||
/// since expected links won't be links and just text.
|
||||
///
|
||||
/// ### Examples
|
||||
/// This link is broken:
|
||||
/// ```no_run
|
||||
/// /// [example of a bad link](https://
|
||||
/// /// github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
///
|
||||
/// It shouldn't be broken across multiple lines to work:
|
||||
/// ```no_run
|
||||
/// /// [example of a good link](https://github.com/rust-lang/rust-clippy/)
|
||||
/// pub fn do_something() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.90.0"]
|
||||
pub DOC_BROKEN_LINK,
|
||||
pedantic,
|
||||
"broken document link"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// safe functions and traits and warns if there is a `# Safety` section.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Safe functions and traits are safe to implement and therefore do not
|
||||
/// need to describe safety preconditions that users are required to uphold.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub fn start_apocalypse_but_safely(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The function is safe, so there shouldn't be any preconditions
|
||||
/// that have to be explained for safety reasons.
|
||||
///
|
||||
/// ```no_run
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub UNNECESSARY_SAFETY_DOC,
|
||||
restriction,
|
||||
"`pub fn` or `pub trait` with `# Safety` docs"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`
|
||||
@@ -409,116 +606,35 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects documentation that is empty.
|
||||
/// Checks for `#[test]` in doctests unless they are marked with
|
||||
/// either `ignore`, `no_run` or `compile_fail`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Empty docs clutter code without adding value, reducing readability and maintainability.
|
||||
/// ### Example
|
||||
/// Code in examples marked as `#[test]` will somewhat
|
||||
/// surprisingly not be run by `cargo test`. If you really want
|
||||
/// to show how to test stuff in an example, mark it `no_run` to
|
||||
/// make the intent clear.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// /// An example of a doctest with a `main()` function
|
||||
/// ///
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// /// # Examples
|
||||
/// ///
|
||||
/// /// ```
|
||||
/// /// #[test]
|
||||
/// /// fn equality_works() {
|
||||
/// /// assert_eq!(1_u8, 1);
|
||||
/// /// }
|
||||
/// /// ```
|
||||
/// fn test_attr_in_doctest() {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn returns_true() -> bool {
|
||||
/// true
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.78.0"]
|
||||
pub EMPTY_DOCS,
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub TEST_ATTR_IN_DOCTEST,
|
||||
suspicious,
|
||||
"docstrings exist but documentation is empty"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// In CommonMark Markdown, the language used to write doc comments, a
|
||||
/// paragraph nested within a list or block quote does not need any line
|
||||
/// after the first one to be indented or marked. The specification calls
|
||||
/// this a "lazy paragraph continuation."
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// This is easy to write but hard to read. Lazy continuations makes
|
||||
/// unintended markers hard to see, and make it harder to deduce the
|
||||
/// document's intended structure.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// This table is probably intended to have two rows,
|
||||
/// but it does not. It has zero rows, and is followed by
|
||||
/// a block quote.
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// >= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by escaping the marker:
|
||||
/// ```no_run
|
||||
/// /// Range | Description
|
||||
/// /// ----- | -----------
|
||||
/// /// \>= 1 | fully opaque
|
||||
/// /// < 1 | partially see-through
|
||||
/// fn set_opacity(opacity: f32) {}
|
||||
/// ```
|
||||
///
|
||||
/// This example is actually intended to be a list:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
///
|
||||
/// Fix it by indenting the list contents:
|
||||
/// ```no_run
|
||||
/// /// * Do nothing.
|
||||
/// /// * Then do something. Whatever it is needs done,
|
||||
/// /// it should be done right now.
|
||||
/// # fn do_stuff() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub DOC_LAZY_CONTINUATION,
|
||||
style,
|
||||
"require every line of a paragraph to be indented and marked"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Detects overindented list items in doc comments where the continuation
|
||||
/// lines are indented more than necessary.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Overindented list items in doc comments can lead to inconsistent and
|
||||
/// poorly formatted documentation when rendered. Excessive indentation may
|
||||
/// cause the text to be misinterpreted as a nested list item or code block,
|
||||
/// affecting readability and the overall structure of the documentation.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
///
|
||||
/// Fixes this into:
|
||||
/// ```no_run
|
||||
/// /// - This is the first item in a list
|
||||
/// /// and this line is overindented.
|
||||
/// # fn foo() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub DOC_OVERINDENTED_LIST_ITEMS,
|
||||
style,
|
||||
"ensure list items are not overindented"
|
||||
"presence of `#[test]` in code examples"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -554,156 +670,63 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if included files in doc comments are included only for `cfg(doc)`.
|
||||
/// Checks for the doc comments of publicly visible
|
||||
/// safe functions and traits and warns if there is a `# Safety` section.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// These files are not useful for compilation but will still be included.
|
||||
/// Also, if any of these non-source code file is updated, it will trigger a
|
||||
/// recompilation.
|
||||
/// Safe functions and traits are safe to implement and therefore do not
|
||||
/// need to describe safety preconditions that users are required to uphold.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// Excluding this will currently result in the file being left out if
|
||||
/// the item's docs are inlined from another crate. This may be fixed in a
|
||||
/// future version of rustdoc.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// #![doc = include_str!("some_file.md")]
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_INCLUDE_WITHOUT_CFG,
|
||||
restriction,
|
||||
"check if files included in documentation are behind `cfg(doc)`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if a link reference definition appears at the start of a
|
||||
/// list item or quote.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably intended as an intra-doc link. If it is really
|
||||
/// supposed to be a reference definition, it can be written outside
|
||||
/// of the list item or quote.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// //! - [link]: description
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// //! - [link][]: description (for intra-doc link)
|
||||
/// //!
|
||||
/// //! [link]: destination (for link reference definition)
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
pub DOC_NESTED_REFDEFS,
|
||||
suspicious,
|
||||
"link reference defined in list item or quote"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects doc comment linebreaks that use double spaces to separate lines, instead of back-slash (`\`).
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Double spaces, when used as doc comment linebreaks, can be difficult to see, and may
|
||||
/// accidentally be removed during automatic formatting or manual refactoring. The use of a back-slash (`\`)
|
||||
/// is clearer in this regard.
|
||||
///
|
||||
/// ### Example
|
||||
/// The two replacement dots in this example represent a double space.
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and··
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
///# type Universe = ();
|
||||
/// /// # Safety
|
||||
/// ///
|
||||
/// /// This function should not be called before the horsemen are ready.
|
||||
/// pub fn start_apocalypse_but_safely(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// The function is safe, so there shouldn't be any preconditions
|
||||
/// that have to be explained for safety reasons.
|
||||
///
|
||||
/// ```no_run
|
||||
/// /// This command takes two numbers as inputs and\
|
||||
/// /// adds them together, and then returns the result.
|
||||
/// fn add(l: i32, r: i32) -> i32 {
|
||||
/// l + r
|
||||
///# type Universe = ();
|
||||
/// /// This function should really be documented
|
||||
/// pub fn start_apocalypse(u: &mut Universe) {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
pedantic,
|
||||
"double-space used for doc comment linebreak instead of `\\`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects syntax that looks like a footnote reference.
|
||||
///
|
||||
/// Rustdoc footnotes are compatible with GitHub-Flavored Markdown (GFM).
|
||||
/// GFM does not parse a footnote reference unless its definition also
|
||||
/// exists. This lint checks for footnote references with missing
|
||||
/// definitions, unless it thinks you're writing a regex.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This probably means that a footnote was meant to exist,
|
||||
/// but was not written.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// This is not a footnote[^1], because no definition exists.
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// This is a footnote[^1].
|
||||
/// ///
|
||||
/// /// [^1]: defined here
|
||||
/// fn my_fn() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub DOC_SUSPICIOUS_FOOTNOTES,
|
||||
suspicious,
|
||||
"looks like a link or footnote ref, but with no definition"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for doc comments whose paragraphs do not end with a period or another punctuation mark.
|
||||
/// Various Markdowns constructs are taken into account to avoid false positives.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A project may wish to enforce consistent doc comments by making sure paragraphs end with a
|
||||
/// punctuation mark.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// /// Returns a random number
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// /// Returns a random number.
|
||||
/// ///
|
||||
/// /// It was chosen by a fair dice roll.
|
||||
/// ```
|
||||
///
|
||||
/// ### Terminal punctuation marks
|
||||
/// This lint treats these characters as end markers: '.', '?', '!', '…' and ':'.
|
||||
///
|
||||
/// The colon is not exactly a terminal punctuation mark, but this is required for paragraphs that
|
||||
/// introduce a table or a list for example.
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub UNNECESSARY_SAFETY_DOC,
|
||||
restriction,
|
||||
"missing terminal punctuation in doc comments"
|
||||
"`pub fn` or `pub trait` with `# Safety` docs"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Documentation => [
|
||||
DOC_BROKEN_LINK,
|
||||
DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
DOC_INCLUDE_WITHOUT_CFG,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
DOC_LINK_CODE,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_MARKDOWN,
|
||||
DOC_NESTED_REFDEFS,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
DOC_SUSPICIOUS_FOOTNOTES,
|
||||
EMPTY_DOCS,
|
||||
MISSING_ERRORS_DOC,
|
||||
MISSING_PANICS_DOC,
|
||||
MISSING_SAFETY_DOC,
|
||||
NEEDLESS_DOCTEST_MAIN,
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
TEST_ATTR_IN_DOCTEST,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
]);
|
||||
|
||||
pub struct Documentation {
|
||||
valid_idents: FxHashSet<String>,
|
||||
check_private_items: bool,
|
||||
@@ -718,29 +741,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Documentation => [
|
||||
DOC_LINK_CODE,
|
||||
DOC_LINK_WITH_QUOTES,
|
||||
DOC_BROKEN_LINK,
|
||||
DOC_MARKDOWN,
|
||||
DOC_NESTED_REFDEFS,
|
||||
MISSING_SAFETY_DOC,
|
||||
MISSING_ERRORS_DOC,
|
||||
MISSING_PANICS_DOC,
|
||||
NEEDLESS_DOCTEST_MAIN,
|
||||
TEST_ATTR_IN_DOCTEST,
|
||||
UNNECESSARY_SAFETY_DOC,
|
||||
SUSPICIOUS_DOC_COMMENTS,
|
||||
EMPTY_DOCS,
|
||||
DOC_LAZY_CONTINUATION,
|
||||
DOC_OVERINDENTED_LIST_ITEMS,
|
||||
TOO_LONG_FIRST_DOC_PARAGRAPH,
|
||||
DOC_INCLUDE_WITHOUT_CFG,
|
||||
DOC_COMMENT_DOUBLE_SPACE_LINEBREAKS,
|
||||
DOC_SUSPICIOUS_FOOTNOTES,
|
||||
DOC_PARAGRAPHS_MISSING_PUNCTUATION,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for Documentation {
|
||||
fn check_attributes(&mut self, cx: &EarlyContext<'_>, attrs: &[rustc_ast::Attribute]) {
|
||||
include_in_doc_without_cfg::check(cx, attrs);
|
||||
|
||||
@@ -70,17 +70,13 @@
|
||||
"`mem::forget` usage on `Drop` types, likely to cause memory leaks"
|
||||
}
|
||||
|
||||
declare_lint_pass!(DropForgetRef => [DROP_NON_DROP, FORGET_NON_DROP, MEM_FORGET]);
|
||||
|
||||
const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
|
||||
Dropping such a type only extends its contained lifetimes";
|
||||
const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
|
||||
Forgetting such a type is the same as dropping it";
|
||||
|
||||
declare_lint_pass!(DropForgetRef => [
|
||||
DROP_NON_DROP,
|
||||
FORGET_NON_DROP,
|
||||
MEM_FORGET,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let ExprKind::Call(path, [arg]) = expr.kind
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
"file loaded as module multiple times"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
|
||||
|
||||
struct Modules {
|
||||
local_path: PathBuf,
|
||||
spans: Vec<Span>,
|
||||
@@ -59,8 +61,6 @@ pub struct DuplicateMod {
|
||||
modules: BTreeMap<PathBuf, Modules>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
|
||||
|
||||
impl EarlyLintPass for DuplicateMod {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
if let ItemKind::Mod(_, _, ModKind::Loaded(_, Inline::No { .. }, mod_spans)) = &item.kind
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
restriction,
|
||||
"empty `Drop` implementations"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
|
||||
|
||||
impl LateLintPass<'_> for EmptyDrop {
|
||||
|
||||
@@ -12,41 +12,6 @@
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, ExpnKind, Ident, InnerSpan, Span, SpanData, Symbol, kw, sym};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after outer attributes
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute may have meant to be an inner attribute (`#![attr]`). If
|
||||
/// it was meant to be an outer attribute (`#[attr]`) then the empty line
|
||||
/// should be removed
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Good (as inner attribute)
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
/// fn this_is_fine() {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[allow(dead_code)]
|
||||
/// fn this_is_fine_too() {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
suspicious,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after doc comments.
|
||||
@@ -88,6 +53,46 @@
|
||||
"empty line after doc comments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty lines after outer attributes
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute may have meant to be an inner attribute (`#![attr]`). If
|
||||
/// it was meant to be an outer attribute (`#[attr]`) then the empty line
|
||||
/// should be removed
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// #[allow(dead_code)]
|
||||
///
|
||||
/// fn not_quite_good_code() {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // Good (as inner attribute)
|
||||
/// #![allow(dead_code)]
|
||||
///
|
||||
/// fn this_is_fine() {}
|
||||
///
|
||||
/// // or
|
||||
///
|
||||
/// // Good (as outer attribute)
|
||||
/// #[allow(dead_code)]
|
||||
/// fn this_is_fine_too() {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
suspicious,
|
||||
"empty line after outer attribute"
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyLineAfter => [
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
]);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ItemInfo {
|
||||
kind: &'static str,
|
||||
@@ -100,11 +105,6 @@ pub struct EmptyLineAfter {
|
||||
items: Vec<ItemInfo>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyLineAfter => [
|
||||
EMPTY_LINE_AFTER_OUTER_ATTR,
|
||||
EMPTY_LINE_AFTER_DOC_COMMENTS,
|
||||
]);
|
||||
|
||||
impl EmptyLineAfter {
|
||||
pub fn new() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
|
||||
@@ -14,34 +14,6 @@
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Empty brackets after a struct declaration can be omitted,
|
||||
/// and it may be desirable to do so consistently for style.
|
||||
///
|
||||
/// However, removing the brackets also introduces a public constant named after the struct,
|
||||
/// so this is not just a syntactic simplification but an API change, and adding them back
|
||||
/// is a *breaking* API change.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct Cookie {}
|
||||
/// struct Biscuit();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct Cookie;
|
||||
/// struct Biscuit;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub EMPTY_STRUCTS_WITH_BRACKETS,
|
||||
restriction,
|
||||
"finds struct declarations with empty brackets"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds enum variants without fields that are declared with empty brackets.
|
||||
@@ -77,6 +49,39 @@
|
||||
"finds enum variants with empty brackets"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Empty brackets after a struct declaration can be omitted,
|
||||
/// and it may be desirable to do so consistently for style.
|
||||
///
|
||||
/// However, removing the brackets also introduces a public constant named after the struct,
|
||||
/// so this is not just a syntactic simplification but an API change, and adding them back
|
||||
/// is a *breaking* API change.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct Cookie {}
|
||||
/// struct Biscuit();
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct Cookie;
|
||||
/// struct Biscuit;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
pub EMPTY_STRUCTS_WITH_BRACKETS,
|
||||
restriction,
|
||||
"finds struct declarations with empty brackets"
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyWithBrackets => [
|
||||
EMPTY_ENUM_VARIANTS_WITH_BRACKETS,
|
||||
EMPTY_STRUCTS_WITH_BRACKETS,
|
||||
]);
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Usage {
|
||||
Unused { redundant_use_sites: Vec<Span> },
|
||||
@@ -90,8 +95,6 @@ pub struct EmptyWithBrackets {
|
||||
empty_tuple_enum_variants: FxIndexMap<LocalDefId, Usage>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(EmptyWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS, EMPTY_ENUM_VARIANTS_WITH_BRACKETS]);
|
||||
|
||||
impl LateLintPass<'_> for EmptyWithBrackets {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
// FIXME: handle `struct $name {}`
|
||||
|
||||
@@ -8,6 +8,24 @@
|
||||
use rustc_span::Symbol;
|
||||
use std::fmt::Write;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To ensure use of little-endian or the target’s endianness rather than big-endian.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _x = 2i32.to_be_bytes();
|
||||
/// let _y = 2i64.to_be_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub BIG_ENDIAN_BYTES,
|
||||
restriction,
|
||||
"disallows usage of the `to_be_bytes` method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
|
||||
@@ -45,25 +63,11 @@
|
||||
"disallows usage of the `to_le_bytes` method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To ensure use of little-endian or the target’s endianness rather than big-endian.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _x = 2i32.to_be_bytes();
|
||||
/// let _y = 2i64.to_be_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.72.0"]
|
||||
pub BIG_ENDIAN_BYTES,
|
||||
restriction,
|
||||
"disallows usage of the `to_be_bytes` method"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EndianBytes => [HOST_ENDIAN_BYTES, LITTLE_ENDIAN_BYTES, BIG_ENDIAN_BYTES]);
|
||||
declare_lint_pass!(EndianBytes => [
|
||||
BIG_ENDIAN_BYTES,
|
||||
HOST_ENDIAN_BYTES,
|
||||
LITTLE_ENDIAN_BYTES,
|
||||
]);
|
||||
|
||||
const HOST_NAMES: [Symbol; 2] = [sym::from_ne_bytes, sym::to_ne_bytes];
|
||||
const LITTLE_NAMES: [Symbol; 2] = [sym::from_le_bytes, sym::to_le_bytes];
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
restriction,
|
||||
"exported types named `Error` that implement `Error`"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ErrorImplError => [ERROR_IMPL_ERROR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
|
||||
|
||||
@@ -50,6 +50,8 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
"using `Box<T>` where unnecessary"
|
||||
}
|
||||
|
||||
impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
|
||||
|
||||
fn is_non_trait_box(ty: Ty<'_>) -> bool {
|
||||
ty.boxed_ty().is_some_and(|boxed| !boxed.is_trait())
|
||||
}
|
||||
@@ -61,8 +63,6 @@ struct EscapeDelegate<'a, 'tcx> {
|
||||
too_large_for_stack: u64,
|
||||
}
|
||||
|
||||
impl_lint_pass!(BoxedLocal => [BOXED_LOCAL]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
|
||||
@@ -66,7 +66,10 @@
|
||||
"redundant closures for method calls"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
|
||||
declare_lint_pass!(EtaReduction => [
|
||||
REDUNDANT_CLOSURE,
|
||||
REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
|
||||
@@ -9,6 +9,44 @@
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for excessive use of
|
||||
/// bools in function definitions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calls to such functions
|
||||
/// are confusing and error prone, because it's
|
||||
/// hard to remember argument order and you have
|
||||
/// no type system support to back you up. Using
|
||||
/// two-variant enums instead of bools often makes
|
||||
/// API easier to use.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// fn f(is_round: bool, is_hot: bool) { ... }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// enum Shape {
|
||||
/// Round,
|
||||
/// Spiky,
|
||||
/// }
|
||||
///
|
||||
/// enum Temperature {
|
||||
/// Hot,
|
||||
/// IceCold,
|
||||
/// }
|
||||
///
|
||||
/// fn f(shape: Shape, temperature: Temperature) { ... }
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
pedantic,
|
||||
"using too many bools in function parameters"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for excessive
|
||||
@@ -50,43 +88,10 @@
|
||||
"using too many bools in a struct"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for excessive use of
|
||||
/// bools in function definitions.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Calls to such functions
|
||||
/// are confusing and error prone, because it's
|
||||
/// hard to remember argument order and you have
|
||||
/// no type system support to back you up. Using
|
||||
/// two-variant enums instead of bools often makes
|
||||
/// API easier to use.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// fn f(is_round: bool, is_hot: bool) { ... }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// enum Shape {
|
||||
/// Round,
|
||||
/// Spiky,
|
||||
/// }
|
||||
///
|
||||
/// enum Temperature {
|
||||
/// Hot,
|
||||
/// IceCold,
|
||||
/// }
|
||||
///
|
||||
/// fn f(shape: Shape, temperature: Temperature) { ... }
|
||||
/// ```
|
||||
#[clippy::version = "1.43.0"]
|
||||
pub FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
pedantic,
|
||||
"using too many bools in function parameters"
|
||||
}
|
||||
impl_lint_pass!(ExcessiveBools => [
|
||||
FN_PARAMS_EXCESSIVE_BOOLS,
|
||||
STRUCT_EXCESSIVE_BOOLS,
|
||||
]);
|
||||
|
||||
pub struct ExcessiveBools {
|
||||
max_struct_bools: u64,
|
||||
@@ -102,8 +107,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExcessiveBools => [STRUCT_EXCESSIVE_BOOLS, FN_PARAMS_EXCESSIVE_BOOLS]);
|
||||
|
||||
fn has_n_bools<'tcx>(iter: impl Iterator<Item = &'tcx Ty<'tcx>>, mut count: u64) -> bool {
|
||||
iter.filter(|ty| is_bool(ty)).any(|_| {
|
||||
let (x, overflow) = count.overflowing_sub(1);
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
complexity,
|
||||
"checks for blocks nested beyond a certain threshold"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]);
|
||||
|
||||
pub struct ExcessiveNesting {
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
|
||||
|
||||
pub struct ExplicitWrite {
|
||||
format_args: FormatArgsStorage,
|
||||
}
|
||||
@@ -49,8 +51,6 @@ pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// match call to unwrap
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
"unused type parameters in function definitions"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
|
||||
|
||||
pub struct ExtraUnusedTypeParameters {
|
||||
avoid_breaking_exported_api: bool,
|
||||
}
|
||||
@@ -52,8 +54,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExtraUnusedTypeParameters => [EXTRA_UNUSED_TYPE_PARAMETERS]);
|
||||
|
||||
/// A visitor struct that walks a given function and gathers generic type parameters, plus any
|
||||
/// trait bounds those parameters have.
|
||||
struct TypeWalker<'cx, 'tcx> {
|
||||
|
||||
@@ -47,7 +47,9 @@
|
||||
"checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FieldScopedVisibilityModifiers => [FIELD_SCOPED_VISIBILITY_MODIFIERS]);
|
||||
declare_lint_pass!(FieldScopedVisibilityModifiers => [
|
||||
FIELD_SCOPED_VISIBILITY_MODIFIERS,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for FieldScopedVisibilityModifiers {
|
||||
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
||||
|
||||
@@ -61,14 +61,12 @@
|
||||
"lossy whole number float literals"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
|
||||
|
||||
pub struct FloatLiteral {
|
||||
const_literal_digits_threshold: usize,
|
||||
}
|
||||
|
||||
impl_lint_pass!(FloatLiteral => [
|
||||
EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL
|
||||
]);
|
||||
|
||||
impl FloatLiteral {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -103,10 +103,7 @@
|
||||
"usage of sub-optimal floating point operations"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FloatingPointArithmetic => [
|
||||
IMPRECISE_FLOPS,
|
||||
SUBOPTIMAL_FLOPS
|
||||
]);
|
||||
declare_lint_pass!(FloatingPointArithmetic => [IMPRECISE_FLOPS, SUBOPTIMAL_FLOPS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
"useless use of `format!`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
||||
|
||||
pub struct UselessFormat {
|
||||
format_args: FormatArgsStorage,
|
||||
}
|
||||
@@ -49,8 +51,6 @@ pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(UselessFormat => [USELESS_FORMAT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
|
||||
@@ -59,32 +59,30 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `Debug` formatting (`{:?}`) applied to an `OsStr` or `Path`.
|
||||
/// Detects [pointer format] as well as `Debug` formatting of raw pointers or function pointers
|
||||
/// or any types that have a derived `Debug` impl that recursively contains them.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Rust doesn't guarantee what `Debug` formatting looks like, and it could
|
||||
/// change in the future. `OsStr`s and `Path`s can be `Display` formatted
|
||||
/// using their `display` methods.
|
||||
/// ### Why restrict this?
|
||||
/// The addresses are only useful in very specific contexts, and certain projects may want to keep addresses of
|
||||
/// certain data structures or functions from prying hacker eyes as an additional line of security.
|
||||
///
|
||||
/// Furthermore, with `Debug` formatting, certain characters are escaped.
|
||||
/// Thus, a `Debug` formatted `Path` is less likely to be clickable.
|
||||
/// ### Known problems
|
||||
/// The lint currently only looks through derived `Debug` implementations. Checking whether a manual
|
||||
/// implementation prints an address is left as an exercise to the next lint implementer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("...");
|
||||
/// println!("The path is {:?}", path);
|
||||
/// let foo = &0_u32;
|
||||
/// fn bar() {}
|
||||
/// println!("{:p}", foo);
|
||||
/// let _ = format!("{:?}", &(bar as fn()));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("…");
|
||||
/// println!("The path is {}", path.display());
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub UNNECESSARY_DEBUG_FORMATTING,
|
||||
pedantic,
|
||||
"`Debug` formatting applied to an `OsStr` or `Path` when `.display()` is available"
|
||||
///
|
||||
/// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub POINTER_FORMAT,
|
||||
restriction,
|
||||
"formatting a pointer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -172,61 +170,32 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects [formatting parameters] that have no effect on the output of
|
||||
/// `format!()`, `println!()` or similar macros.
|
||||
/// Checks for `Debug` formatting (`{:?}`) applied to an `OsStr` or `Path`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Shorter format specifiers are easier to read, it may also indicate that
|
||||
/// an expected formatting operation such as adding padding isn't happening.
|
||||
/// Rust doesn't guarantee what `Debug` formatting looks like, and it could
|
||||
/// change in the future. `OsStr`s and `Path`s can be `Display` formatted
|
||||
/// using their `display` methods.
|
||||
///
|
||||
/// Furthermore, with `Debug` formatting, certain characters are escaped.
|
||||
/// Thus, a `Debug` formatted `Path` is less likely to be clickable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("{:.}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {:5}", format_args!("..."));
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("...");
|
||||
/// println!("The path is {:?}", path);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// println!("{}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {}", format_args!("..."));
|
||||
/// // OR
|
||||
/// println!("padded: {:5}", format!("..."));
|
||||
/// # use std::path::Path;
|
||||
/// let path = Path::new("…");
|
||||
/// println!("The path is {}", path.display());
|
||||
/// ```
|
||||
///
|
||||
/// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub UNUSED_FORMAT_SPECS,
|
||||
complexity,
|
||||
"use of a format specifier that has no effect"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects [pointer format] as well as `Debug` formatting of raw pointers or function pointers
|
||||
/// or any types that have a derived `Debug` impl that recursively contains them.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// The addresses are only useful in very specific contexts, and certain projects may want to keep addresses of
|
||||
/// certain data structures or functions from prying hacker eyes as an additional line of security.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint currently only looks through derived `Debug` implementations. Checking whether a manual
|
||||
/// implementation prints an address is left as an exercise to the next lint implementer.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let foo = &0_u32;
|
||||
/// fn bar() {}
|
||||
/// println!("{:p}", foo);
|
||||
/// let _ = format!("{:?}", &(bar as fn()));
|
||||
/// ```
|
||||
///
|
||||
/// [pointer format]: https://doc.rust-lang.org/std/fmt/index.html#formatting-traits
|
||||
#[clippy::version = "1.89.0"]
|
||||
pub POINTER_FORMAT,
|
||||
restriction,
|
||||
"formatting a pointer"
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub UNNECESSARY_DEBUG_FORMATTING,
|
||||
pedantic,
|
||||
"`Debug` formatting applied to an `OsStr` or `Path` when `.display()` is available"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -258,14 +227,45 @@
|
||||
"unnecessary trailing comma before closing parenthesis"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects [formatting parameters] that have no effect on the output of
|
||||
/// `format!()`, `println!()` or similar macros.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Shorter format specifiers are easier to read, it may also indicate that
|
||||
/// an expected formatting operation such as adding padding isn't happening.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// println!("{:.}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {:5}", format_args!("..."));
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// println!("{}", 1.0);
|
||||
///
|
||||
/// println!("not padded: {}", format_args!("..."));
|
||||
/// // OR
|
||||
/// println!("padded: {:5}", format!("..."));
|
||||
/// ```
|
||||
///
|
||||
/// [formatting parameters]: https://doc.rust-lang.org/std/fmt/index.html#formatting-parameters
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub UNUSED_FORMAT_SPECS,
|
||||
complexity,
|
||||
"use of a format specifier that has no effect"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatArgs<'_> => [
|
||||
FORMAT_IN_FORMAT_ARGS,
|
||||
POINTER_FORMAT,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
UNINLINED_FORMAT_ARGS,
|
||||
UNNECESSARY_DEBUG_FORMATTING,
|
||||
UNUSED_FORMAT_SPECS,
|
||||
POINTER_FORMAT,
|
||||
UNNECESSARY_TRAILING_COMMA,
|
||||
UNUSED_FORMAT_SPECS,
|
||||
]);
|
||||
|
||||
#[expect(clippy::struct_field_names)]
|
||||
|
||||
@@ -10,45 +10,6 @@
|
||||
use rustc_span::Symbol;
|
||||
use rustc_span::symbol::kw;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for format trait implementations (e.g. `Display`) with a recursive call to itself
|
||||
/// which uses `self` as a parameter.
|
||||
/// This is typically done indirectly with the `write!` macro or with `to_string()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will lead to infinite recursion and a stack overflow.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.to_string())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.0)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub RECURSIVE_FORMAT_IMPL,
|
||||
correctness,
|
||||
"Format trait method called while implementing the same Format trait"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of `println`, `print`, `eprintln` or `eprint` in an
|
||||
@@ -90,6 +51,47 @@
|
||||
"use of a print macro in a formatting trait impl"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for format trait implementations (e.g. `Display`) with a recursive call to itself
|
||||
/// which uses `self` as a parameter.
|
||||
/// This is typically done indirectly with the `write!` macro or with `to_string()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will lead to infinite recursion and a stack overflow.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.to_string())
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// use std::fmt;
|
||||
///
|
||||
/// struct Structure(i32);
|
||||
/// impl fmt::Display for Structure {
|
||||
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
/// write!(f, "{}", self.0)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.48.0"]
|
||||
pub RECURSIVE_FORMAT_IMPL,
|
||||
correctness,
|
||||
"Format trait method called while implementing the same Format trait"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatImpl => [PRINT_IN_FORMAT_IMPL, RECURSIVE_FORMAT_IMPL]);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct FormatTraitNames {
|
||||
/// e.g. `sym::Display`
|
||||
@@ -113,8 +115,6 @@ pub fn new(format_args: FormatArgsStorage) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatImpl => [RECURSIVE_FORMAT_IMPL, PRINT_IN_FORMAT_IMPL]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FormatImpl {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
|
||||
self.format_trait_impl = is_format_trait_impl(cx, impl_item);
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
pedantic,
|
||||
"`format!(..)` appended to existing `String`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
|
||||
|
||||
pub(crate) struct FormatPushString {
|
||||
|
||||
@@ -6,6 +6,52 @@
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for possible missing comma in an array. It lints if
|
||||
/// an array element is a binary operator expression and it lies on two lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This could lead to unexpected results.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let a = &[
|
||||
/// -1, -2, -3 // <= no comma here
|
||||
/// -4, -5, -6
|
||||
/// ];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub POSSIBLE_MISSING_COMMA,
|
||||
correctness,
|
||||
"possible missing comma in array"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for an `if` expression followed by either a block or another `if` that
|
||||
/// looks like it should have an `else` between them.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably some refactoring remnant, even if the code is correct, it
|
||||
/// might look confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if foo {
|
||||
/// } { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } if bar { // looks like an `else` is missing here
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.91.0"]
|
||||
pub POSSIBLE_MISSING_ELSE,
|
||||
suspicious,
|
||||
"possibly missing `else`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of the non-existent `=*`, `=!` and `=-`
|
||||
@@ -25,35 +71,6 @@
|
||||
"suspicious formatting of `*=`, `-=` or `!=`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks the formatting of a unary operator on the right hand side
|
||||
/// of a binary operator. It lints if there is no space between the binary and unary operators,
|
||||
/// but there is a space between the unary and its operand.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is either a typo in the binary operator or confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// // &&! looks like a different operator
|
||||
/// if foo &&! bar {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// if foo && !bar {}
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
suspicious,
|
||||
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for formatting of `else`. It lints if the `else`
|
||||
@@ -93,56 +110,39 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for an `if` expression followed by either a block or another `if` that
|
||||
/// looks like it should have an `else` between them.
|
||||
/// Checks the formatting of a unary operator on the right hand side
|
||||
/// of a binary operator. It lints if there is no space between the binary and unary operators,
|
||||
/// but there is a space between the unary and its operand.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably some refactoring remnant, even if the code is correct, it
|
||||
/// might look confusing.
|
||||
/// This is either a typo in the binary operator or confusing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// if foo {
|
||||
/// } { // looks like an `else` is missing here
|
||||
/// }
|
||||
///
|
||||
/// if foo {
|
||||
/// } if bar { // looks like an `else` is missing here
|
||||
/// }
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// // &&! looks like a different operator
|
||||
/// if foo &&! bar {}
|
||||
/// ```
|
||||
#[clippy::version = "1.91.0"]
|
||||
pub POSSIBLE_MISSING_ELSE,
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let foo = true;
|
||||
/// # let bar = false;
|
||||
/// if foo && !bar {}
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
suspicious,
|
||||
"possibly missing `else`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for possible missing comma in an array. It lints if
|
||||
/// an array element is a binary operator expression and it lies on two lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This could lead to unexpected results.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let a = &[
|
||||
/// -1, -2, -3 // <= no comma here
|
||||
/// -4, -5, -6
|
||||
/// ];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub POSSIBLE_MISSING_COMMA,
|
||||
correctness,
|
||||
"possible missing comma in array"
|
||||
"suspicious formatting of unary `-` or `!` on the RHS of a BinOp"
|
||||
}
|
||||
|
||||
declare_lint_pass!(Formatting => [
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
SUSPICIOUS_ELSE_FORMATTING,
|
||||
POSSIBLE_MISSING_COMMA,
|
||||
POSSIBLE_MISSING_ELSE,
|
||||
POSSIBLE_MISSING_COMMA
|
||||
SUSPICIOUS_ASSIGNMENT_FORMATTING,
|
||||
SUSPICIOUS_ELSE_FORMATTING,
|
||||
SUSPICIOUS_UNARY_OP_FORMATTING,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for Formatting {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
suspicious,
|
||||
"comments with 4 forward slashes (`////`) likely intended to be doc comments (`///`)"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FourForwardSlashes => [FOUR_FORWARD_SLASHES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes {
|
||||
|
||||
@@ -52,6 +52,8 @@
|
||||
"Warns on implementations of `Into<..>` to use `From<..>`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
||||
|
||||
pub struct FromOverInto {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -62,8 +64,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for FromOverInto {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Impl(Impl {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
suspicious,
|
||||
"creating a `Box` from a void raw pointer"
|
||||
}
|
||||
|
||||
declare_lint_pass!(FromRawWithVoidPtr => [FROM_RAW_WITH_VOID_PTR]);
|
||||
|
||||
impl LateLintPass<'_> for FromRawWithVoidPtr {
|
||||
|
||||
+276
-276
@@ -21,6 +21,30 @@
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::{DefIdSet, LocalDefId};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute without
|
||||
/// further information on functions and methods that return a type already
|
||||
/// marked as `#[must_use]`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute isn't needed. Not using the result
|
||||
/// will already be reported. Alternatively, one can add some text to the
|
||||
/// attribute to improve the lint message.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn double_must_use() -> Result<(), ()> {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub DOUBLE_MUST_USE,
|
||||
style,
|
||||
"`#[must_use]` attribute on a `#[must_use]`-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for function arguments having the similar names
|
||||
@@ -46,48 +70,122 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions with too many parameters.
|
||||
/// Lints when `impl Trait` is being used in a function's parameters.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Functions with lots of parameters are considered bad
|
||||
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
|
||||
/// grouping some parameters into a new type.
|
||||
/// ### Why restrict this?
|
||||
/// Turbofish syntax (`::<>`) cannot be used to specify the type of an `impl Trait` parameter,
|
||||
/// making `impl Trait` less powerful. Readability may also be a factor.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # struct Color;
|
||||
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
|
||||
/// // ..
|
||||
/// trait MyTrait {}
|
||||
/// fn foo(a: impl MyTrait) {
|
||||
/// // [...]
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub TOO_MANY_ARGUMENTS,
|
||||
complexity,
|
||||
"functions with too many arguments"
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// trait MyTrait {}
|
||||
/// fn foo<T: MyTrait>(a: T) {
|
||||
/// // [...]
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub IMPL_TRAIT_IN_PARAMS,
|
||||
restriction,
|
||||
"`impl Trait` is used in the function's parameters"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions with a large amount of lines.
|
||||
/// Checks for getter methods that return a field that doesn't correspond
|
||||
/// to the name of the method, when there is a field's whose name matches that of the method.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Functions with a lot of lines are harder to understand
|
||||
/// due to having to look at a larger amount of code to understand what the
|
||||
/// function is doing. Consider splitting the body of the function into
|
||||
/// multiple functions.
|
||||
/// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
|
||||
///
|
||||
/// ### Example
|
||||
|
||||
/// ```no_run
|
||||
/// fn im_too_long() {
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.b
|
||||
/// }
|
||||
/// }
|
||||
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.a
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.34.0"]
|
||||
pub TOO_MANY_LINES,
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub MISNAMED_GETTERS,
|
||||
suspicious,
|
||||
"getter method returning the wrong field"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for public functions that have no
|
||||
/// `#[must_use]` attribute, but return something not already marked
|
||||
/// must-use, have no mutable arg and mutate no statics.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not bad at all, this lint just shows places where
|
||||
/// you could add the attribute.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint only checks the arguments for mutable
|
||||
/// types without looking if they are actually changed. On the other hand,
|
||||
/// it also ignores a broad range of potentially interesting side effects,
|
||||
/// because we cannot decide whether the programmer intends the function to
|
||||
/// be called for the side effect or the result. Expect many false
|
||||
/// positives. At least we don't lint if the result type is unit or already
|
||||
/// `#[must_use]`.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// // this could be annotated with `#[must_use]`.
|
||||
/// pub fn id<T>(t: T) -> T { t }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_CANDIDATE,
|
||||
pedantic,
|
||||
"functions with too many lines"
|
||||
"function or method that could take a `#[must_use]` attribute"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute on
|
||||
/// unit-returning functions and methods.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unit values are useless. The attribute is likely
|
||||
/// a remnant of a refactoring that removed the return type.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn useless() { }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_UNIT,
|
||||
style,
|
||||
"`#[must_use]` attribute on a unit-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -152,76 +250,131 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute on
|
||||
/// unit-returning functions and methods.
|
||||
/// Warns when a function signature uses `&Option<T>` instead of `Option<&T>`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Unit values are useless. The attribute is likely
|
||||
/// a remnant of a refactoring that removed the return type.
|
||||
/// More flexibility, better memory optimization, and more idiomatic Rust code.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn useless() { }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_UNIT,
|
||||
style,
|
||||
"`#[must_use]` attribute on a unit-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a `#[must_use]` attribute without
|
||||
/// further information on functions and methods that return a type already
|
||||
/// marked as `#[must_use]`.
|
||||
/// `&Option<T>` in a function signature breaks encapsulation because the caller must own T
|
||||
/// and move it into an Option to call with it. When returned, the owner must internally store
|
||||
/// it as `Option<T>` in order to return it.
|
||||
/// At a lower level, `&Option<T>` points to memory with the `presence` bit flag plus the `T` value,
|
||||
/// whereas `Option<&T>` is usually [optimized](https://doc.rust-lang.org/1.81.0/std/option/index.html#representation)
|
||||
/// to a single pointer, so it may be more optimal.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The attribute isn't needed. Not using the result
|
||||
/// will already be reported. Alternatively, one can add some text to the
|
||||
/// attribute to improve the lint message.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// #[must_use]
|
||||
/// fn double_must_use() -> Result<(), ()> {
|
||||
/// unimplemented!();
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub DOUBLE_MUST_USE,
|
||||
style,
|
||||
"`#[must_use]` attribute on a `#[must_use]`-returning function / method"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for public functions that have no
|
||||
/// `#[must_use]` attribute, but return something not already marked
|
||||
/// must-use, have no mutable arg and mutate no statics.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not bad at all, this lint just shows places where
|
||||
/// you could add the attribute.
|
||||
/// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by
|
||||
/// Logan Smith for an in-depth explanation of why this is important.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint only checks the arguments for mutable
|
||||
/// types without looking if they are actually changed. On the other hand,
|
||||
/// it also ignores a broad range of potentially interesting side effects,
|
||||
/// because we cannot decide whether the programmer intends the function to
|
||||
/// be called for the side effect or the result. Expect many false
|
||||
/// positives. At least we don't lint if the result type is unit or already
|
||||
/// `#[must_use]`.
|
||||
/// This lint recommends changing the function signatures, but it cannot
|
||||
/// automatically change the function calls or the function implementations.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // caller uses foo(&opt)
|
||||
/// fn foo(a: &Option<String>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> &Option<String> { &None }
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // caller should use `foo1(opt.as_ref())`
|
||||
/// fn foo1(a: Option<&String>) {}
|
||||
/// // better yet, use string slice `foo2(opt.as_deref())`
|
||||
/// fn foo2(a: Option<&str>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> Option<&String> { None }
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub REF_OPTION,
|
||||
pedantic,
|
||||
"function signature uses `&Option<T>` instead of `Option<&T>`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints when the name of function parameters from trait impl is
|
||||
/// different than its default implementation.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Using the default name for parameters of a trait method is more consistent.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, b: &Self) -> bool {
|
||||
/// self.0 == b.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, other: &Self) -> bool {
|
||||
/// self.0 == other.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub RENAMED_FUNCTION_PARAMS,
|
||||
restriction,
|
||||
"renamed function parameters in trait implementation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions that return `Result` with an unusually large
|
||||
/// `Err`-variant.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A `Result` is at least as large as the `Err`-variant. While we
|
||||
/// expect that variant to be seldom used, the compiler needs to reserve
|
||||
/// and move that much memory every single time.
|
||||
/// Furthermore, errors are often simply passed up the call-stack, making
|
||||
/// use of the `?`-operator and its type-conversion mechanics. If the
|
||||
/// `Err`-variant further up the call-stack stores the `Err`-variant in
|
||||
/// question (as library code often does), it itself needs to be at least
|
||||
/// as large, propagating the problem.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The size determined by Clippy is platform-dependent.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```no_run
|
||||
/// // this could be annotated with `#[must_use]`.
|
||||
/// pub fn id<T>(t: T) -> T { t }
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes([u8; 512]),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` has at least 512 bytes, even in the `Ok`-case
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.40.0"]
|
||||
pub MUST_USE_CANDIDATE,
|
||||
pedantic,
|
||||
"function or method that could take a `#[must_use]` attribute"
|
||||
/// should be
|
||||
/// ```no_run
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes(Box<[u8; 512]>),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` is slightly larger than a pointer
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub RESULT_LARGE_ERR,
|
||||
perf,
|
||||
"function returning `Result` with large `Err` type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -276,205 +429,67 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions that return `Result` with an unusually large
|
||||
/// `Err`-variant.
|
||||
/// Checks for functions with too many parameters.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A `Result` is at least as large as the `Err`-variant. While we
|
||||
/// expect that variant to be seldom used, the compiler needs to reserve
|
||||
/// and move that much memory every single time.
|
||||
/// Furthermore, errors are often simply passed up the call-stack, making
|
||||
/// use of the `?`-operator and its type-conversion mechanics. If the
|
||||
/// `Err`-variant further up the call-stack stores the `Err`-variant in
|
||||
/// question (as library code often does), it itself needs to be at least
|
||||
/// as large, propagating the problem.
|
||||
/// Functions with lots of parameters are considered bad
|
||||
/// style and reduce readability (“what does the 5th parameter mean?”). Consider
|
||||
/// grouping some parameters into a new type.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The size determined by Clippy is platform-dependent.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes([u8; 512]),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` has at least 512 bytes, even in the `Ok`-case
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// # struct Color;
|
||||
/// fn foo(x: u32, y: u32, name: &str, c: Color, w: f32, h: f32, a: f32, b: f32) {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// should be
|
||||
/// ```no_run
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes(Box<[u8; 512]>),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` is slightly larger than a pointer
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.65.0"]
|
||||
pub RESULT_LARGE_ERR,
|
||||
perf,
|
||||
"function returning `Result` with large `Err` type"
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub TOO_MANY_ARGUMENTS,
|
||||
complexity,
|
||||
"functions with too many arguments"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for getter methods that return a field that doesn't correspond
|
||||
/// to the name of the method, when there is a field's whose name matches that of the method.
|
||||
/// Checks for functions with a large amount of lines.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is most likely that such a method is a bug caused by a typo or by copy-pasting.
|
||||
///
|
||||
/// ### Example
|
||||
|
||||
/// ```no_run
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.b
|
||||
/// }
|
||||
/// }
|
||||
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct A {
|
||||
/// a: String,
|
||||
/// b: String,
|
||||
/// }
|
||||
///
|
||||
/// impl A {
|
||||
/// fn a(&self) -> &str{
|
||||
/// &self.a
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub MISNAMED_GETTERS,
|
||||
suspicious,
|
||||
"getter method returning the wrong field"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints when `impl Trait` is being used in a function's parameters.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Turbofish syntax (`::<>`) cannot be used to specify the type of an `impl Trait` parameter,
|
||||
/// making `impl Trait` less powerful. Readability may also be a factor.
|
||||
/// Functions with a lot of lines are harder to understand
|
||||
/// due to having to look at a larger amount of code to understand what the
|
||||
/// function is doing. Consider splitting the body of the function into
|
||||
/// multiple functions.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// trait MyTrait {}
|
||||
/// fn foo(a: impl MyTrait) {
|
||||
/// // [...]
|
||||
/// fn im_too_long() {
|
||||
/// println!("");
|
||||
/// // ... 100 more LoC
|
||||
/// println!("");
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// trait MyTrait {}
|
||||
/// fn foo<T: MyTrait>(a: T) {
|
||||
/// // [...]
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.69.0"]
|
||||
pub IMPL_TRAIT_IN_PARAMS,
|
||||
restriction,
|
||||
"`impl Trait` is used in the function's parameters"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Lints when the name of function parameters from trait impl is
|
||||
/// different than its default implementation.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Using the default name for parameters of a trait method is more consistent.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, b: &Self) -> bool {
|
||||
/// self.0 == b.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// struct A(u32);
|
||||
///
|
||||
/// impl PartialEq for A {
|
||||
/// fn eq(&self, other: &Self) -> bool {
|
||||
/// self.0 == other.0
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub RENAMED_FUNCTION_PARAMS,
|
||||
restriction,
|
||||
"renamed function parameters in trait implementation"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns when a function signature uses `&Option<T>` instead of `Option<&T>`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// More flexibility, better memory optimization, and more idiomatic Rust code.
|
||||
///
|
||||
/// `&Option<T>` in a function signature breaks encapsulation because the caller must own T
|
||||
/// and move it into an Option to call with it. When returned, the owner must internally store
|
||||
/// it as `Option<T>` in order to return it.
|
||||
/// At a lower level, `&Option<T>` points to memory with the `presence` bit flag plus the `T` value,
|
||||
/// whereas `Option<&T>` is usually [optimized](https://doc.rust-lang.org/1.81.0/std/option/index.html#representation)
|
||||
/// to a single pointer, so it may be more optimal.
|
||||
///
|
||||
/// See this [YouTube video](https://www.youtube.com/watch?v=6c7pZYP_iIE) by
|
||||
/// Logan Smith for an in-depth explanation of why this is important.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint recommends changing the function signatures, but it cannot
|
||||
/// automatically change the function calls or the function implementations.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // caller uses foo(&opt)
|
||||
/// fn foo(a: &Option<String>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> &Option<String> { &None }
|
||||
/// # }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// // caller should use `foo1(opt.as_ref())`
|
||||
/// fn foo1(a: Option<&String>) {}
|
||||
/// // better yet, use string slice `foo2(opt.as_deref())`
|
||||
/// fn foo2(a: Option<&str>) {}
|
||||
/// # struct Unit {}
|
||||
/// # impl Unit {
|
||||
/// fn bar(&self) -> Option<&String> { None }
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub REF_OPTION,
|
||||
#[clippy::version = "1.34.0"]
|
||||
pub TOO_MANY_LINES,
|
||||
pedantic,
|
||||
"function signature uses `&Option<T>` instead of `Option<&T>`"
|
||||
"functions with too many lines"
|
||||
}
|
||||
|
||||
declare_lint_pass!(EarlyFunctions => [DUPLICATE_UNDERSCORE_ARGUMENT]);
|
||||
|
||||
impl_lint_pass!(Functions => [
|
||||
DOUBLE_MUST_USE,
|
||||
IMPL_TRAIT_IN_PARAMS,
|
||||
MISNAMED_GETTERS,
|
||||
MUST_USE_CANDIDATE,
|
||||
MUST_USE_UNIT,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
REF_OPTION,
|
||||
RENAMED_FUNCTION_PARAMS,
|
||||
RESULT_LARGE_ERR,
|
||||
RESULT_UNIT_ERR,
|
||||
TOO_MANY_ARGUMENTS,
|
||||
TOO_MANY_LINES,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for EarlyFunctions {
|
||||
fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: visit::FnKind<'_>, _: Span, _: ast::NodeId) {
|
||||
duplicate_underscore_argument::check(cx, fn_kind);
|
||||
@@ -515,21 +530,6 @@ pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Functions => [
|
||||
TOO_MANY_ARGUMENTS,
|
||||
TOO_MANY_LINES,
|
||||
NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
MUST_USE_UNIT,
|
||||
DOUBLE_MUST_USE,
|
||||
MUST_USE_CANDIDATE,
|
||||
RESULT_UNIT_ERR,
|
||||
RESULT_LARGE_ERR,
|
||||
MISNAMED_GETTERS,
|
||||
IMPL_TRAIT_IN_PARAMS,
|
||||
RENAMED_FUNCTION_PARAMS,
|
||||
REF_OPTION,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
fn check_fn(
|
||||
&mut self,
|
||||
|
||||
@@ -50,6 +50,8 @@
|
||||
"Finds if-else that could be written using either `bool::then` or `bool::then_some`"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||
|
||||
pub struct IfThenSomeElseNone {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -60,8 +62,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(higher::If {
|
||||
|
||||
+62
-62
@@ -11,6 +11,62 @@
|
||||
mod ifs_same_cond;
|
||||
mod same_functions_in_if_cond;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if the `if` and `else` block contain shared code that can be
|
||||
/// moved out of the blocks.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Duplicate code is less maintainable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// println!("Hello World");
|
||||
/// 13
|
||||
/// } else {
|
||||
/// println!("Hello World");
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// println!("Hello World");
|
||||
/// let foo = if … {
|
||||
/// 13
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "1.53.0"]
|
||||
pub BRANCHES_SHARING_CODE,
|
||||
nursery,
|
||||
"`if` statement with shared code in all blocks"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `if/else` with the same body as the *then* part
|
||||
/// and the *else* part.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably a copy & paste error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IF_SAME_THEN_ELSE,
|
||||
style,
|
||||
"`if` with the same `then` and `else` blocks"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for consecutive `if`s with the same condition.
|
||||
@@ -91,61 +147,12 @@
|
||||
"consecutive `if`s with the same function call"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `if/else` with the same body as the *then* part
|
||||
/// and the *else* part.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is probably a copy & paste error.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// 42
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub IF_SAME_THEN_ELSE,
|
||||
style,
|
||||
"`if` with the same `then` and `else` blocks"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks if the `if` and `else` block contain shared code that can be
|
||||
/// moved out of the blocks.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Duplicate code is less maintainable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// let foo = if … {
|
||||
/// println!("Hello World");
|
||||
/// 13
|
||||
/// } else {
|
||||
/// println!("Hello World");
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// println!("Hello World");
|
||||
/// let foo = if … {
|
||||
/// 13
|
||||
/// } else {
|
||||
/// 42
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "1.53.0"]
|
||||
pub BRANCHES_SHARING_CODE,
|
||||
nursery,
|
||||
"`if` statement with shared code in all blocks"
|
||||
}
|
||||
impl_lint_pass!(CopyAndPaste<'_> => [
|
||||
BRANCHES_SHARING_CODE,
|
||||
IFS_SAME_COND,
|
||||
IF_SAME_THEN_ELSE,
|
||||
SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
]);
|
||||
|
||||
pub struct CopyAndPaste<'tcx> {
|
||||
interior_mut: InteriorMut<'tcx>,
|
||||
@@ -159,13 +166,6 @@ pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(CopyAndPaste<'_> => [
|
||||
IFS_SAME_COND,
|
||||
SAME_FUNCTIONS_IN_IF_CONDITION,
|
||||
IF_SAME_THEN_ELSE,
|
||||
BRANCHES_SHARING_CODE
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste<'tcx> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if !expr.span.from_expansion() && matches!(expr.kind, ExprKind::If(..)) && !is_else_clause(cx.tcx, expr) {
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
pedantic,
|
||||
"suggest replacing `_` by `()` in patterns where appropriate"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns {
|
||||
|
||||
@@ -69,7 +69,9 @@
|
||||
"ensures that the semantics of `Borrow` for `Hash` are satisfied when `Borrow<str>` and `Borrow<[u8]>` are implemented"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImplHashWithBorrowStrBytes => [IMPL_HASH_BORROW_WITH_STR_AND_BYTES]);
|
||||
declare_lint_pass!(ImplHashWithBorrowStrBytes => [
|
||||
IMPL_HASH_BORROW_WITH_STR_AND_BYTES,
|
||||
]);
|
||||
|
||||
impl LateLintPass<'_> for ImplHashWithBorrowStrBytes {
|
||||
/// We are emitting this lint at the Hash impl of a type that implements all
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
style,
|
||||
"Perform saturating addition instead of implicitly checking max bound of data type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
|
||||
|
||||
@@ -79,12 +79,15 @@
|
||||
"Check if a variable is smaller than another one and still subtract from it even if smaller"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ImplicitSaturatingSub => [
|
||||
IMPLICIT_SATURATING_SUB,
|
||||
INVERTED_SATURATING_SUB,
|
||||
]);
|
||||
|
||||
pub struct ImplicitSaturatingSub {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]);
|
||||
|
||||
impl ImplicitSaturatingSub {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
complexity,
|
||||
"specifying bounds that are implied by other bounds in `impl Trait` type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ImpliedBoundsInImpls => [IMPLIED_BOUNDS_IN_IMPLS]);
|
||||
|
||||
fn emit_lint(
|
||||
|
||||
@@ -72,6 +72,8 @@
|
||||
"ensures that all items used in the crate are available for the current MSRV"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum Availability {
|
||||
FeatureEnabled,
|
||||
@@ -114,8 +116,6 @@ pub struct IncompatibleMsrv {
|
||||
called_path: Option<HirId>,
|
||||
}
|
||||
|
||||
impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
|
||||
|
||||
impl IncompatibleMsrv {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
|
||||
@@ -64,6 +64,10 @@
|
||||
"the order of the field init is inconsistent with the order in the struct definition"
|
||||
}
|
||||
|
||||
impl_lint_pass!(InconsistentStructConstructor => [
|
||||
INCONSISTENT_STRUCT_CONSTRUCTOR,
|
||||
]);
|
||||
|
||||
pub struct InconsistentStructConstructor {
|
||||
check_inconsistent_struct_field_initializers: bool,
|
||||
}
|
||||
@@ -76,8 +80,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
let ExprKind::Struct(_, fields, _) = expr.kind else {
|
||||
|
||||
@@ -53,6 +53,8 @@
|
||||
"avoid indexing on slices which could be destructed"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
|
||||
|
||||
pub struct IndexRefutableSlice {
|
||||
max_suggested_slice: u64,
|
||||
msrv: Msrv,
|
||||
@@ -67,8 +69,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr)
|
||||
|
||||
@@ -9,36 +9,6 @@
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for out of bounds array indexing with a constant
|
||||
/// index.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will always panic at runtime.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// let x = [1, 2, 3, 4];
|
||||
///
|
||||
/// x[9];
|
||||
/// &x[2..9];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let x = [1, 2, 3, 4];
|
||||
/// // Index within bounds
|
||||
///
|
||||
/// x[0];
|
||||
/// x[3];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub OUT_OF_BOUNDS_INDEXING,
|
||||
correctness,
|
||||
"out of bounds constant indexing"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of indexing or slicing that may panic at runtime.
|
||||
@@ -92,6 +62,36 @@
|
||||
"indexing/slicing usage"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for out of bounds array indexing with a constant
|
||||
/// index.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This will always panic at runtime.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// let x = [1, 2, 3, 4];
|
||||
///
|
||||
/// x[9];
|
||||
/// &x[2..9];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let x = [1, 2, 3, 4];
|
||||
/// // Index within bounds
|
||||
///
|
||||
/// x[0];
|
||||
/// x[3];
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub OUT_OF_BOUNDS_INDEXING,
|
||||
correctness,
|
||||
"out of bounds constant indexing"
|
||||
}
|
||||
|
||||
impl_lint_pass!(IndexingSlicing => [INDEXING_SLICING, OUT_OF_BOUNDS_INDEXING]);
|
||||
|
||||
pub struct IndexingSlicing {
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
suspicious,
|
||||
"TryFrom with infallible Error type"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
|
||||
|
||||
@@ -88,7 +88,10 @@
|
||||
"type implements inherent method `to_string()`, which gets shadowed by the implementation of the `Display` trait"
|
||||
}
|
||||
|
||||
declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_SHADOW_DISPLAY]);
|
||||
declare_lint_pass!(InherentToString => [
|
||||
INHERENT_TO_STRING,
|
||||
INHERENT_TO_STRING_SHADOW_DISPLAY,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for InherentToString {
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
|
||||
|
||||
@@ -45,6 +45,37 @@
|
||||
"enums where all variants share a prefix/postfix"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for modules that have the same name as their
|
||||
/// parent module
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A typical beginner mistake is to have `mod foo;` and
|
||||
/// again `mod foo { ..
|
||||
/// }` in `foo.rs`.
|
||||
/// The expectation is that items inside the inner `mod foo { .. }` are then
|
||||
/// available
|
||||
/// through `foo::x`, but they are only available through
|
||||
/// `foo::foo::x`.
|
||||
/// If this is done on purpose, it would be better to choose a more
|
||||
/// representative module name.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // lib.rs
|
||||
/// mod foo;
|
||||
/// // foo.rs
|
||||
/// mod foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MODULE_INCEPTION,
|
||||
style,
|
||||
"modules that have the same name as their parent module"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects public item names that are prefixed or suffixed by the
|
||||
@@ -89,36 +120,6 @@
|
||||
"type names prefixed/postfixed with their containing module's name"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for modules that have the same name as their
|
||||
/// parent module
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A typical beginner mistake is to have `mod foo;` and
|
||||
/// again `mod foo { ..
|
||||
/// }` in `foo.rs`.
|
||||
/// The expectation is that items inside the inner `mod foo { .. }` are then
|
||||
/// available
|
||||
/// through `foo::x`, but they are only available through
|
||||
/// `foo::foo::x`.
|
||||
/// If this is done on purpose, it would be better to choose a more
|
||||
/// representative module name.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// // lib.rs
|
||||
/// mod foo;
|
||||
/// // foo.rs
|
||||
/// mod foo {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MODULE_INCEPTION,
|
||||
style,
|
||||
"modules that have the same name as their parent module"
|
||||
}
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects struct fields that are prefixed or suffixed
|
||||
@@ -155,6 +156,13 @@
|
||||
"structs where all fields share a prefix/postfix or contain the name of the struct"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ItemNameRepetitions => [
|
||||
ENUM_VARIANT_NAMES,
|
||||
MODULE_INCEPTION,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
STRUCT_FIELD_NAMES,
|
||||
]);
|
||||
|
||||
pub struct ItemNameRepetitions {
|
||||
/// The module path the lint pass is in.
|
||||
modules: Vec<ModInfo>,
|
||||
@@ -195,13 +203,6 @@ fn is_allowed_prefix(&self, prefix: &str) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ItemNameRepetitions => [
|
||||
ENUM_VARIANT_NAMES,
|
||||
STRUCT_FIELD_NAMES,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
MODULE_INCEPTION
|
||||
]);
|
||||
|
||||
#[must_use]
|
||||
fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
|
||||
prefixes.iter().all(|p| p == &"" || p == &"_")
|
||||
|
||||
@@ -9,50 +9,6 @@
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly
|
||||
/// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`.
|
||||
///
|
||||
/// ### Limitations
|
||||
/// This lint focuses on providing an idiomatic API. Therefore, it will only
|
||||
/// lint on types which are accessible outside of the crate. For internal types,
|
||||
/// the `IntoIterator` trait can be implemented on demand if it is actually needed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// impl<'a> IntoIterator for &MySlice<'a> {
|
||||
/// type Item = &'a u8;
|
||||
/// type IntoIter = std::slice::Iter<'a, u8>;
|
||||
/// fn into_iter(self) -> Self::IntoIter {
|
||||
/// self.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
pub ITER_WITHOUT_INTO_ITER,
|
||||
pedantic,
|
||||
"implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// This is the opposite of the `iter_without_into_iter` lint.
|
||||
@@ -105,7 +61,54 @@
|
||||
"implementing `IntoIterator for (&|&mut) Type` without an inherent `iter(_mut)` method"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IterWithoutIntoIter => [ITER_WITHOUT_INTO_ITER, INTO_ITER_WITHOUT_ITER]);
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for `iter` and `iter_mut` methods without an associated `IntoIterator for (&|&mut) Type` implementation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It's not bad, but having them is idiomatic and allows the type to be used in for loops directly
|
||||
/// (`for val in &iter {}`), without having to first call `iter()` or `iter_mut()`.
|
||||
///
|
||||
/// ### Limitations
|
||||
/// This lint focuses on providing an idiomatic API. Therefore, it will only
|
||||
/// lint on types which are accessible outside of the crate. For internal types,
|
||||
/// the `IntoIterator` trait can be implemented on demand if it is actually needed.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// struct MySlice<'a>(&'a [u8]);
|
||||
/// impl<'a> MySlice<'a> {
|
||||
/// pub fn iter(&self) -> std::slice::Iter<'a, u8> {
|
||||
/// self.0.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// impl<'a> IntoIterator for &MySlice<'a> {
|
||||
/// type Item = &'a u8;
|
||||
/// type IntoIter = std::slice::Iter<'a, u8>;
|
||||
/// fn into_iter(self) -> Self::IntoIter {
|
||||
/// self.iter()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.75.0"]
|
||||
pub ITER_WITHOUT_INTO_ITER,
|
||||
pedantic,
|
||||
"implementing `iter(_mut)` without an associated `IntoIterator for (&|&mut) Type` impl"
|
||||
}
|
||||
|
||||
declare_lint_pass!(IterWithoutIntoIter => [
|
||||
INTO_ITER_WITHOUT_ITER,
|
||||
ITER_WITHOUT_INTO_ITER,
|
||||
]);
|
||||
|
||||
/// Checks if a given type is nameable in a trait (impl).
|
||||
/// RPIT is stable, but impl Trait in traits is not (yet), so when we have
|
||||
|
||||
@@ -32,6 +32,8 @@
|
||||
"large non-scalar const array may cause performance overhead"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
|
||||
|
||||
pub struct LargeConstArrays {
|
||||
maximum_allowed_size: u64,
|
||||
}
|
||||
@@ -44,8 +46,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Const(ident, generics, _, _) = &item.kind
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
"large size difference between variants on an enum"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
|
||||
|
||||
pub struct LargeEnumVariant {
|
||||
maximum_size_difference_allowed: u64,
|
||||
}
|
||||
@@ -70,8 +72,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
|
||||
if let ItemKind::Enum(ident, _, ref def) = item.kind
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
"large future may lead to unexpected stack overflows"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
|
||||
|
||||
pub struct LargeFuture {
|
||||
future_size_threshold: u64,
|
||||
}
|
||||
@@ -52,8 +54,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeFuture {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
"including a large file"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
|
||||
|
||||
pub struct LargeIncludeFile {
|
||||
max_file_size: u64,
|
||||
}
|
||||
@@ -50,8 +52,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
|
||||
|
||||
impl LateLintPass<'_> for LargeIncludeFile {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
|
||||
if let ExprKind::Lit(lit) = &expr.kind
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
"allocating large arrays on stack may cause stack overflow"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
||||
|
||||
pub struct LargeStackArrays {
|
||||
maximum_allowed_size: u64,
|
||||
prev_vec_macro_callsite: Option<Span>,
|
||||
@@ -61,8 +63,6 @@ fn is_from_vec_macro(&mut self, cx: &LateContext<'_>, span: Span) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeStackArrays => [LARGE_STACK_ARRAYS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LargeStackArrays {
|
||||
fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
if matches!(item.kind, ItemKind::Static(..) | ItemKind::Const(..)) {
|
||||
|
||||
@@ -82,6 +82,8 @@
|
||||
"checks for functions that allocate a lot of stack space"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeStackFrames => [LARGE_STACK_FRAMES]);
|
||||
|
||||
pub struct LargeStackFrames {
|
||||
maximum_allowed_size: u64,
|
||||
allow_large_stack_frames_in_tests: bool,
|
||||
@@ -96,8 +98,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LargeStackFrames => [LARGE_STACK_FRAMES]);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Space {
|
||||
Used(u64),
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
style,
|
||||
"checks for usage of legacy std numeric constants and methods"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LegacyNumericConstants => [LEGACY_NUMERIC_CONSTANTS]);
|
||||
|
||||
pub struct LegacyNumericConstants {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -44,8 +47,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(LegacyNumericConstants => [LEGACY_NUMERIC_CONSTANTS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
|
||||
// Integer modules are "TBD" deprecated, and the contents are too,
|
||||
|
||||
@@ -16,41 +16,6 @@
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for getting the length of something via `.len()`
|
||||
/// just to compare to zero, and suggests using `.is_empty()` where applicable.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Some structures can answer `.is_empty()` much faster
|
||||
/// than calculating their length. So it is good to get into the habit of using
|
||||
/// `.is_empty()`, and having it is cheap.
|
||||
/// Besides, it makes the intent clearer than a manual comparison in some contexts.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// if x.len() == 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// if y.len() != 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
/// instead use
|
||||
/// ```ignore
|
||||
/// if x.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// if !y.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub LEN_ZERO,
|
||||
style,
|
||||
"checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for comparing to an empty slice such as `""` or `[]`,
|
||||
@@ -89,12 +54,47 @@
|
||||
"checking `x == \"\"` or `x == []` (or similar) when `.is_empty()` could be used instead"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for getting the length of something via `.len()`
|
||||
/// just to compare to zero, and suggests using `.is_empty()` where applicable.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Some structures can answer `.is_empty()` much faster
|
||||
/// than calculating their length. So it is good to get into the habit of using
|
||||
/// `.is_empty()`, and having it is cheap.
|
||||
/// Besides, it makes the intent clearer than a manual comparison in some contexts.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// if x.len() == 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// if y.len() != 0 {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
/// instead use
|
||||
/// ```ignore
|
||||
/// if x.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// if !y.is_empty() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub LEN_ZERO,
|
||||
style,
|
||||
"checking `.len() == 0` or `.len() > 0` (or similar) when `.is_empty()` could be used instead"
|
||||
}
|
||||
|
||||
impl_lint_pass!(LenZero => [COMPARISON_TO_EMPTY, LEN_ZERO]);
|
||||
|
||||
pub struct LenZero {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl_lint_pass!(LenZero => [LEN_ZERO, COMPARISON_TO_EMPTY]);
|
||||
|
||||
impl LenZero {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
|
||||
@@ -9,25 +9,33 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `let _ = <expr>` where expr is `#[must_use]`
|
||||
/// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// To ensure that all `#[must_use]` types are used rather than ignored.
|
||||
/// ### Why is this bad?
|
||||
/// Futures must be polled for work to be done. The original intention was most likely to await the future
|
||||
/// and ignore the resulting value.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// fn f() -> Result<u32, u32> {
|
||||
/// Ok(0)
|
||||
/// async fn foo() -> Result<(), ()> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// let _ = f();
|
||||
/// // is_ok() is marked #[must_use]
|
||||
/// let _ = f().is_ok();
|
||||
/// let _ = foo();
|
||||
/// ```
|
||||
#[clippy::version = "1.42.0"]
|
||||
pub LET_UNDERSCORE_MUST_USE,
|
||||
restriction,
|
||||
"non-binding `let` on a `#[must_use]` expression"
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # async fn context() {
|
||||
/// async fn foo() -> Result<(), ()> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// let _ = foo().await;
|
||||
/// # }
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub LET_UNDERSCORE_FUTURE,
|
||||
suspicious,
|
||||
"non-binding `let` on a future"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -60,33 +68,25 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
|
||||
/// Checks for `let _ = <expr>` where expr is `#[must_use]`
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Futures must be polled for work to be done. The original intention was most likely to await the future
|
||||
/// and ignore the resulting value.
|
||||
/// ### Why restrict this?
|
||||
/// To ensure that all `#[must_use]` types are used rather than ignored.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// async fn foo() -> Result<(), ()> {
|
||||
/// Ok(())
|
||||
/// fn f() -> Result<u32, u32> {
|
||||
/// Ok(0)
|
||||
/// }
|
||||
/// let _ = foo();
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # async fn context() {
|
||||
/// async fn foo() -> Result<(), ()> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// let _ = foo().await;
|
||||
/// # }
|
||||
/// let _ = f();
|
||||
/// // is_ok() is marked #[must_use]
|
||||
/// let _ = f().is_ok();
|
||||
/// ```
|
||||
#[clippy::version = "1.67.0"]
|
||||
pub LET_UNDERSCORE_FUTURE,
|
||||
suspicious,
|
||||
"non-binding `let` on a future"
|
||||
#[clippy::version = "1.42.0"]
|
||||
pub LET_UNDERSCORE_MUST_USE,
|
||||
restriction,
|
||||
"non-binding `let` on a `#[must_use]` expression"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -127,7 +127,12 @@
|
||||
"non-binding `let` without a type annotation"
|
||||
}
|
||||
|
||||
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);
|
||||
declare_lint_pass!(LetUnderscore => [
|
||||
LET_UNDERSCORE_FUTURE,
|
||||
LET_UNDERSCORE_LOCK,
|
||||
LET_UNDERSCORE_MUST_USE,
|
||||
LET_UNDERSCORE_UNTYPED,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
complexity,
|
||||
"unneeded underscore type (`_`) in a variable declaration"
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
|
||||
|
||||
impl EarlyLintPass for UnderscoreTyped {
|
||||
|
||||
@@ -27,41 +27,6 @@
|
||||
use rustc_span::symbol::{Ident, kw};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for lifetime annotations which can be removed by
|
||||
/// relying on lifetime elision.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The additional lifetimes make the code look more
|
||||
/// complicated, while there is nothing out of the ordinary going on. Removing
|
||||
/// them leads to more readable code.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint ignores functions with `where` clauses that reference
|
||||
/// lifetimes to prevent false positives.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // Unnecessary lifetime annotations
|
||||
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn elided(x: &u8, y: u8) -> &u8 {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_LIFETIMES,
|
||||
complexity,
|
||||
"using explicit lifetimes for references in function arguments when elision rules \
|
||||
would allow omitting them"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for lifetime annotations which can be replaced with anonymous lifetimes (`'_`).
|
||||
@@ -124,6 +89,47 @@
|
||||
"unused lifetimes in function definitions"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for lifetime annotations which can be removed by
|
||||
/// relying on lifetime elision.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The additional lifetimes make the code look more
|
||||
/// complicated, while there is nothing out of the ordinary going on. Removing
|
||||
/// them leads to more readable code.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint ignores functions with `where` clauses that reference
|
||||
/// lifetimes to prevent false positives.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // Unnecessary lifetime annotations
|
||||
/// fn in_and_out<'a>(x: &'a u8, y: u8) -> &'a u8 {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn elided(x: &u8, y: u8) -> &u8 {
|
||||
/// x
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_LIFETIMES,
|
||||
complexity,
|
||||
"using explicit lifetimes for references in function arguments when elision rules \
|
||||
would allow omitting them"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Lifetimes => [
|
||||
ELIDABLE_LIFETIME_NAMES,
|
||||
EXTRA_UNUSED_LIFETIMES,
|
||||
NEEDLESS_LIFETIMES,
|
||||
]);
|
||||
|
||||
pub struct Lifetimes {
|
||||
msrv: Msrv,
|
||||
}
|
||||
@@ -134,12 +140,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Lifetimes => [
|
||||
NEEDLESS_LIFETIMES,
|
||||
ELIDABLE_LIFETIME_NAMES,
|
||||
EXTRA_UNUSED_LIFETIMES,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Lifetimes {
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
|
||||
if let ItemKind::Fn {
|
||||
|
||||
@@ -12,51 +12,22 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if a long integral or floating-point constant does
|
||||
/// not contain underscores.
|
||||
/// Warns if there is a better representation for a numeric literal.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Reading long numbers is difficult without separators.
|
||||
/// ### Why restrict this?
|
||||
/// Especially for big powers of 2, a hexadecimal representation is usually more
|
||||
/// readable than a decimal representation.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let _: u64 =
|
||||
/// 61864918973511
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let _: u64 =
|
||||
/// 61_864_918_973_511
|
||||
/// # ;
|
||||
/// ```text
|
||||
/// `255` => `0xFF`
|
||||
/// `65_535` => `0xFFFF`
|
||||
/// `4_042_322_160` => `0xF0F0_F0F0`
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub UNREADABLE_LITERAL,
|
||||
pedantic,
|
||||
"long literal without underscores"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns for mistyped suffix in literals
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This is most probably a typo
|
||||
///
|
||||
/// ### Known problems
|
||||
/// - Does not match on integers too large to fit in the corresponding unsigned type
|
||||
/// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// `2_32` => `2_i32`
|
||||
/// `250_8 => `250_u8`
|
||||
/// ```
|
||||
#[clippy::version = "1.30.0"]
|
||||
pub MISTYPED_LITERAL_SUFFIXES,
|
||||
correctness,
|
||||
"mistyped literal suffix"
|
||||
pub DECIMAL_LITERAL_REPRESENTATION,
|
||||
restriction,
|
||||
"using decimal representation when hexadecimal would be better"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -87,25 +58,6 @@
|
||||
"integer literals with digits grouped inconsistently"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if hexadecimal or binary literals are not grouped
|
||||
/// by nibble or byte.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Negatively impacts readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: u32 = 0xFFF_FFF;
|
||||
/// let y: u8 = 0b01_011_101;
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub UNUSUAL_BYTE_GROUPINGS,
|
||||
style,
|
||||
"binary or hex literals that aren't grouped by four"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if the digits of an integral or floating-point
|
||||
@@ -127,24 +79,82 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if there is a better representation for a numeric literal.
|
||||
/// Warns for mistyped suffix in literals
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Especially for big powers of 2, a hexadecimal representation is usually more
|
||||
/// readable than a decimal representation.
|
||||
/// ### Why is this bad?
|
||||
/// This is most probably a typo
|
||||
///
|
||||
/// ### Known problems
|
||||
/// - Does not match on integers too large to fit in the corresponding unsigned type
|
||||
/// - Does not match on `_127` since that is a valid grouping for decimal and octal numbers
|
||||
///
|
||||
/// ### Example
|
||||
/// ```text
|
||||
/// `255` => `0xFF`
|
||||
/// `65_535` => `0xFFFF`
|
||||
/// `4_042_322_160` => `0xF0F0_F0F0`
|
||||
/// ```ignore
|
||||
/// `2_32` => `2_i32`
|
||||
/// `250_8 => `250_u8`
|
||||
/// ```
|
||||
#[clippy::version = "1.30.0"]
|
||||
pub MISTYPED_LITERAL_SUFFIXES,
|
||||
correctness,
|
||||
"mistyped literal suffix"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if a long integral or floating-point constant does
|
||||
/// not contain underscores.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Reading long numbers is difficult without separators.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let _: u64 =
|
||||
/// 61864918973511
|
||||
/// # ;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let _: u64 =
|
||||
/// 61_864_918_973_511
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub DECIMAL_LITERAL_REPRESENTATION,
|
||||
restriction,
|
||||
"using decimal representation when hexadecimal would be better"
|
||||
pub UNREADABLE_LITERAL,
|
||||
pedantic,
|
||||
"long literal without underscores"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Warns if hexadecimal or binary literals are not grouped
|
||||
/// by nibble or byte.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Negatively impacts readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x: u32 = 0xFFF_FFF;
|
||||
/// let y: u8 = 0b01_011_101;
|
||||
/// ```
|
||||
#[clippy::version = "1.49.0"]
|
||||
pub UNUSUAL_BYTE_GROUPINGS,
|
||||
style,
|
||||
"binary or hex literals that aren't grouped by four"
|
||||
}
|
||||
|
||||
impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]);
|
||||
|
||||
impl_lint_pass!(LiteralDigitGrouping => [
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
MISTYPED_LITERAL_SUFFIXES,
|
||||
UNREADABLE_LITERAL,
|
||||
UNUSUAL_BYTE_GROUPINGS,
|
||||
]);
|
||||
|
||||
enum WarningType {
|
||||
UnreadableLiteral,
|
||||
InconsistentDigitGrouping,
|
||||
@@ -195,14 +205,6 @@ pub struct LiteralDigitGrouping {
|
||||
lint_fraction_readability: bool,
|
||||
}
|
||||
|
||||
impl_lint_pass!(LiteralDigitGrouping => [
|
||||
UNREADABLE_LITERAL,
|
||||
INCONSISTENT_DIGIT_GROUPING,
|
||||
LARGE_DIGIT_GROUPS,
|
||||
MISTYPED_LITERAL_SUFFIXES,
|
||||
UNUSUAL_BYTE_GROUPINGS,
|
||||
]);
|
||||
|
||||
impl EarlyLintPass for LiteralDigitGrouping {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Lit(lit) = expr.kind
|
||||
@@ -414,8 +416,6 @@ pub struct DecimalLiteralRepresentation {
|
||||
threshold: u64,
|
||||
}
|
||||
|
||||
impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]);
|
||||
|
||||
impl EarlyLintPass for DecimalLiteralRepresentation {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
|
||||
if let ExprKind::Lit(lit) = expr.kind
|
||||
|
||||
@@ -36,7 +36,9 @@
|
||||
"Checks if string literals have formatting arguments"
|
||||
}
|
||||
|
||||
declare_lint_pass!(LiteralStringWithFormattingArg => [LITERAL_STRING_WITH_FORMATTING_ARGS]);
|
||||
declare_lint_pass!(LiteralStringWithFormattingArg => [
|
||||
LITERAL_STRING_WITH_FORMATTING_ARGS,
|
||||
]);
|
||||
|
||||
fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, spans: &[(Span, Option<String>)]) {
|
||||
if !spans.is_empty()
|
||||
|
||||
+502
-502
@@ -37,214 +37,45 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for for-loops that manually copy items between
|
||||
/// slices that could be optimized by having a memcpy.
|
||||
/// Checks for usage of a character position yielded by `.chars().enumerate()` in a context where a **byte index** is expected,
|
||||
/// such as an argument to a specific `str` method or indexing into a `str` or `String`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is not as fast as a memcpy.
|
||||
/// A character (more specifically, a Unicode scalar value) that is yielded by `str::chars` can take up multiple bytes,
|
||||
/// so a character position does not necessarily have the same byte index at which the character is stored.
|
||||
/// Thus, using the character position where a byte index is expected can unexpectedly return wrong values
|
||||
/// or panic when the string consists of multibyte characters.
|
||||
///
|
||||
/// For example, the character `a` in `äa` is stored at byte index 2 but has the character position 1.
|
||||
/// Using the character position 1 to index into the string will lead to a panic as it is in the middle of the first character.
|
||||
///
|
||||
/// Instead of `.chars().enumerate()`, the correct iterator to use is `.char_indices()`, which yields byte indices.
|
||||
///
|
||||
/// This pattern is technically fine if the strings are known to only use the ASCII subset,
|
||||
/// though in those cases it would be better to use `bytes()` directly to make the intent clearer,
|
||||
/// but there is also no downside to just using `.char_indices()` directly and supporting non-ASCII strings.
|
||||
///
|
||||
/// You may also want to read the [chapter on strings in the Rust Book](https://doc.rust-lang.org/book/ch08-02-strings.html)
|
||||
/// which goes into this in more detail.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let src = vec![1];
|
||||
/// # let mut dst = vec![0; 65];
|
||||
/// for i in 0..src.len() {
|
||||
/// dst[i + 64] = src[i];
|
||||
/// # let s = "...";
|
||||
/// for (idx, c) in s.chars().enumerate() {
|
||||
/// let _ = s[idx..]; // ⚠️ Panics for strings consisting of multibyte characters
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let src = vec![1];
|
||||
/// # let mut dst = vec![0; 65];
|
||||
/// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MANUAL_MEMCPY,
|
||||
perf,
|
||||
"manually copying items between slices"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for looping over the range of `0..len` of some
|
||||
/// collection just to get the values by index.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Just iterating the collection itself makes the intent
|
||||
/// more clear and is probably faster because it eliminates
|
||||
/// the bounds check that is done when indexing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let vec = vec!['a', 'b', 'c'];
|
||||
/// for i in 0..vec.len() {
|
||||
/// println!("{}", vec[i]);
|
||||
/// # let s = "...";
|
||||
/// for (idx, c) in s.char_indices() {
|
||||
/// let _ = s[idx..];
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let vec = vec!['a', 'b', 'c'];
|
||||
/// for i in vec {
|
||||
/// println!("{}", i);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_RANGE_LOOP,
|
||||
style,
|
||||
"for-looping over a range of indices where an iterator over items would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops on `x.iter()` where `&x` will do, and
|
||||
/// suggests the latter.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives. We currently only warn on some known
|
||||
/// types.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // with `y` a `Vec` or slice:
|
||||
/// # let y = vec![1];
|
||||
/// for x in y.iter() {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let y = vec![1];
|
||||
/// for x in &y {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EXPLICIT_ITER_LOOP,
|
||||
pedantic,
|
||||
"for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops on `y.into_iter()` where `y` will do, and
|
||||
/// suggests the latter.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let y = vec![1];
|
||||
/// // with `y` a `Vec` or slice:
|
||||
/// for x in y.into_iter() {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten to
|
||||
/// ```no_run
|
||||
/// # let y = vec![1];
|
||||
/// for x in y {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EXPLICIT_INTO_ITER_LOOP,
|
||||
pedantic,
|
||||
"for-looping over `_.into_iter()` when `_` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops on `x.next()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `next()` returns either `Some(value)` if there was a
|
||||
/// value, or `None` otherwise. The insidious thing is that `Option<_>`
|
||||
/// implements `IntoIterator`, so that possibly one value will be iterated,
|
||||
/// leading to some hard to find bugs. No one will want to write such code
|
||||
/// [except to win an Underhanded Rust
|
||||
/// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// for x in y.next() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub ITER_NEXT_LOOP,
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub CHAR_INDICES_AS_BYTE_INDICES,
|
||||
correctness,
|
||||
"for-looping over `_.next()` which is probably not intended"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects `loop + match` combinations that are easier
|
||||
/// written as a `while let` loop.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `while let` loop is usually shorter and more
|
||||
/// readable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// let y = Some(1);
|
||||
/// loop {
|
||||
/// let x = match y {
|
||||
/// Some(x) => x,
|
||||
/// None => break,
|
||||
/// };
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// let y = Some(1);
|
||||
/// while let Some(x) = y {
|
||||
/// // ..
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WHILE_LET_LOOP,
|
||||
complexity,
|
||||
"`loop { if let { ... } else break }`, which can be written as a `while let` loop"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks `for` loops over slices with an explicit counter
|
||||
/// and suggests the use of `.enumerate()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using `.enumerate()` makes the intent more clear,
|
||||
/// declutters the code and may be faster in some instances.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let v = vec![1];
|
||||
/// # fn bar(bar: usize, baz: usize) {}
|
||||
/// let mut i = 0;
|
||||
/// for item in &v {
|
||||
/// bar(i, *item);
|
||||
/// i += 1;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let v = vec![1];
|
||||
/// # fn bar(bar: usize, baz: usize) {}
|
||||
/// for (i, item) in v.iter().enumerate() { bar(i, *item); }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EXPLICIT_COUNTER_LOOP,
|
||||
complexity,
|
||||
"for-looping with an explicit counter when `_.enumerate()` would do"
|
||||
"using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -283,29 +114,97 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `while let` expressions on iterators.
|
||||
/// Checks `for` loops over slices with an explicit counter
|
||||
/// and suggests the use of `.enumerate()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability. A simple `for` loop is shorter and conveys
|
||||
/// the intent better.
|
||||
/// Using `.enumerate()` makes the intent more clear,
|
||||
/// declutters the code and may be faster in some instances.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// while let Some(val) = iter.next() {
|
||||
/// ..
|
||||
/// ```no_run
|
||||
/// # let v = vec![1];
|
||||
/// # fn bar(bar: usize, baz: usize) {}
|
||||
/// let mut i = 0;
|
||||
/// for item in &v {
|
||||
/// bar(i, *item);
|
||||
/// i += 1;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// for val in &mut iter {
|
||||
/// ..
|
||||
/// ```no_run
|
||||
/// # let v = vec![1];
|
||||
/// # fn bar(bar: usize, baz: usize) {}
|
||||
/// for (i, item) in v.iter().enumerate() { bar(i, *item); }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EXPLICIT_COUNTER_LOOP,
|
||||
complexity,
|
||||
"for-looping with an explicit counter when `_.enumerate()` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops on `y.into_iter()` where `y` will do, and
|
||||
/// suggests the latter.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let y = vec![1];
|
||||
/// // with `y` a `Vec` or slice:
|
||||
/// for x in y.into_iter() {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// can be rewritten to
|
||||
/// ```no_run
|
||||
/// # let y = vec![1];
|
||||
/// for x in y {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WHILE_LET_ON_ITERATOR,
|
||||
style,
|
||||
"using a `while let` loop instead of a for loop on an iterator"
|
||||
pub EXPLICIT_INTO_ITER_LOOP,
|
||||
pedantic,
|
||||
"for-looping over `_.into_iter()` when `_` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops on `x.iter()` where `&x` will do, and
|
||||
/// suggests the latter.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// False negatives. We currently only warn on some known
|
||||
/// types.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// // with `y` a `Vec` or slice:
|
||||
/// # let y = vec![1];
|
||||
/// for x in y.iter() {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let y = vec![1];
|
||||
/// for x in &y {
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub EXPLICIT_ITER_LOOP,
|
||||
pedantic,
|
||||
"for-looping over `_.iter()` or `_.iter_mut()` when `&_` or `&mut _` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -339,24 +238,251 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops that will always `break`, `return` or
|
||||
/// `continue` an outer loop.
|
||||
/// Checks for infinite loops in a function where the return type is not `!`
|
||||
/// and lint accordingly.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This loop never loops, all it does is obfuscating the
|
||||
/// code.
|
||||
/// ### Why restrict this?
|
||||
/// Making the return type `!` serves as documentation that the function does not return.
|
||||
/// If the function is not intended to loop infinitely, then this lint may detect a bug.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// loop {
|
||||
/// ..;
|
||||
/// break;
|
||||
/// ```no_run,ignore
|
||||
/// fn run_forever() {
|
||||
/// loop {
|
||||
/// // do something
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// If infinite loops are as intended:
|
||||
/// ```no_run,ignore
|
||||
/// fn run_forever() -> ! {
|
||||
/// loop {
|
||||
/// // do something
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Otherwise add a `break` or `return` condition:
|
||||
/// ```no_run,ignore
|
||||
/// fn run_forever() {
|
||||
/// loop {
|
||||
/// // do something
|
||||
/// if condition {
|
||||
/// break;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub INFINITE_LOOP,
|
||||
restriction,
|
||||
"possibly unintended infinite loop"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops on `x.next()`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// `next()` returns either `Some(value)` if there was a
|
||||
/// value, or `None` otherwise. The insidious thing is that `Option<_>`
|
||||
/// implements `IntoIterator`, so that possibly one value will be iterated,
|
||||
/// leading to some hard to find bugs. No one will want to write such code
|
||||
/// [except to win an Underhanded Rust
|
||||
/// Contest](https://www.reddit.com/r/rust/comments/3hb0wm/underhanded_rust_contest/cu5yuhr).
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// for x in y.next() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEVER_LOOP,
|
||||
pub ITER_NEXT_LOOP,
|
||||
correctness,
|
||||
"any loop that will always `break` or `return`"
|
||||
"for-looping over `_.next()` which is probably not intended"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for manual implementations of Iterator::find
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It doesn't affect performance, but using `find` is shorter and easier to read.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// fn example(arr: Vec<i32>) -> Option<i32> {
|
||||
/// for el in arr {
|
||||
/// if el == 1 {
|
||||
/// return Some(el);
|
||||
/// }
|
||||
/// }
|
||||
/// None
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn example(arr: Vec<i32>) -> Option<i32> {
|
||||
/// arr.into_iter().find(|&el| el == 1)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub MANUAL_FIND,
|
||||
complexity,
|
||||
"manual implementation of `Iterator::find`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for unnecessary `if let` usage in a for loop
|
||||
/// where only the `Some` or `Ok` variant of the iterator element is used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is verbose and can be simplified
|
||||
/// by first calling the `flatten` method on the `Iterator`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// let x = vec![Some(1), Some(2), Some(3)];
|
||||
/// for n in x {
|
||||
/// if let Some(n) = n {
|
||||
/// println!("{}", n);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x = vec![Some(1), Some(2), Some(3)];
|
||||
/// for n in x.into_iter().flatten() {
|
||||
/// println!("{}", n);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
pub MANUAL_FLATTEN,
|
||||
complexity,
|
||||
"for loops over `Option`s or `Result`s with a single expression can be simplified"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for for-loops that manually copy items between
|
||||
/// slices that could be optimized by having a memcpy.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is not as fast as a memcpy.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let src = vec![1];
|
||||
/// # let mut dst = vec![0; 65];
|
||||
/// for i in 0..src.len() {
|
||||
/// dst[i + 64] = src[i];
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let src = vec![1];
|
||||
/// # let mut dst = vec![0; 65];
|
||||
/// dst[64..(src.len() + 64)].clone_from_slice(&src[..]);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub MANUAL_MEMCPY,
|
||||
perf,
|
||||
"manually copying items between slices"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for manually filling a slice with a value.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using the `fill` method is more idiomatic and concise.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut some_slice = [1, 2, 3, 4, 5];
|
||||
/// for i in 0..some_slice.len() {
|
||||
/// some_slice[i] = 0;
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut some_slice = [1, 2, 3, 4, 5];
|
||||
/// some_slice.fill(0);
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub MANUAL_SLICE_FILL,
|
||||
style,
|
||||
"manually filling a slice with a value"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for loops that check for emptiness of a `Vec` in the condition and pop an element
|
||||
/// in the body as a separate operation.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Such loops can be written in a more idiomatic way by using a while-let loop and directly
|
||||
/// pattern matching on the return value of `Vec::pop()`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut numbers = vec![1, 2, 3, 4, 5];
|
||||
/// while !numbers.is_empty() {
|
||||
/// let number = numbers.pop().unwrap();
|
||||
/// // use `number`
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut numbers = vec![1, 2, 3, 4, 5];
|
||||
/// while let Some(number) = numbers.pop() {
|
||||
/// // use `number`
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub MANUAL_WHILE_LET_SOME,
|
||||
style,
|
||||
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty spin loops
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The loop body should have something like `thread::park()` or at least
|
||||
/// `std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
|
||||
/// energy. Perhaps even better use an actual lock, if possible.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint doesn't currently trigger on `while let` or
|
||||
/// `loop { match .. { .. } }` loops, which would be considered idiomatic in
|
||||
/// combination with e.g. `AtomicBool::compare_exchange_weak`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use core::sync::atomic::{AtomicBool, Ordering};
|
||||
/// let b = AtomicBool::new(true);
|
||||
/// // give a ref to `b` to another thread,wait for it to become false
|
||||
/// while b.load(Ordering::Acquire) {};
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
///# use core::sync::atomic::{AtomicBool, Ordering};
|
||||
///# let b = AtomicBool::new(true);
|
||||
/// while b.load(Ordering::Acquire) {
|
||||
/// std::hint::spin_loop()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub MISSING_SPIN_LOOP,
|
||||
perf,
|
||||
"An empty busy waiting loop"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -397,62 +523,55 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks whether variables used within while loop condition
|
||||
/// can be (and are) mutated in the body.
|
||||
/// Checks for looping over the range of `0..len` of some
|
||||
/// collection just to get the values by index.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If the condition is unchanged, entering the body of the loop
|
||||
/// will lead to an infinite loop.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If the `while`-loop is in a closure, the check for mutation of the
|
||||
/// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
|
||||
/// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
|
||||
/// Just iterating the collection itself makes the intent
|
||||
/// more clear and is probably faster because it eliminates
|
||||
/// the bounds check that is done when indexing.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let i = 0;
|
||||
/// while i > 10 {
|
||||
/// println!("let me loop forever!");
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WHILE_IMMUTABLE_CONDITION,
|
||||
correctness,
|
||||
"variables used within while expression are not mutated in the body"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for while loops comparing floating point values.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If you increment floating point values, errors can compound,
|
||||
/// so, use integers instead if possible.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint will catch all while loops comparing floating point
|
||||
/// values without regarding the increment.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut x = 0.0;
|
||||
/// while x < 42.0 {
|
||||
/// x += 1.0;
|
||||
/// let vec = vec!['a', 'b', 'c'];
|
||||
/// for i in 0..vec.len() {
|
||||
/// println!("{}", vec[i]);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut x = 0;
|
||||
/// while x < 42 {
|
||||
/// x += 1;
|
||||
/// let vec = vec!['a', 'b', 'c'];
|
||||
/// for i in vec {
|
||||
/// println!("{}", i);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub WHILE_FLOAT,
|
||||
nursery,
|
||||
"while loops comparing floating point values"
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEEDLESS_RANGE_LOOP,
|
||||
style,
|
||||
"for-looping over a range of indices where an iterator over items would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for loops that will always `break`, `return` or
|
||||
/// `continue` an outer loop.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This loop never loops, all it does is obfuscating the
|
||||
/// code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// loop {
|
||||
/// ..;
|
||||
/// break;
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub NEVER_LOOP,
|
||||
correctness,
|
||||
"any loop that will always `break` or `return`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -519,105 +638,6 @@
|
||||
"there is no reason to have a single element loop"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for unnecessary `if let` usage in a for loop
|
||||
/// where only the `Some` or `Ok` variant of the iterator element is used.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It is verbose and can be simplified
|
||||
/// by first calling the `flatten` method on the `Iterator`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// let x = vec![Some(1), Some(2), Some(3)];
|
||||
/// for n in x {
|
||||
/// if let Some(n) = n {
|
||||
/// println!("{}", n);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let x = vec![Some(1), Some(2), Some(3)];
|
||||
/// for n in x.into_iter().flatten() {
|
||||
/// println!("{}", n);
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.52.0"]
|
||||
pub MANUAL_FLATTEN,
|
||||
complexity,
|
||||
"for loops over `Option`s or `Result`s with a single expression can be simplified"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for empty spin loops
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The loop body should have something like `thread::park()` or at least
|
||||
/// `std::hint::spin_loop()` to avoid needlessly burning cycles and conserve
|
||||
/// energy. Perhaps even better use an actual lock, if possible.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// This lint doesn't currently trigger on `while let` or
|
||||
/// `loop { match .. { .. } }` loops, which would be considered idiomatic in
|
||||
/// combination with e.g. `AtomicBool::compare_exchange_weak`.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use core::sync::atomic::{AtomicBool, Ordering};
|
||||
/// let b = AtomicBool::new(true);
|
||||
/// // give a ref to `b` to another thread,wait for it to become false
|
||||
/// while b.load(Ordering::Acquire) {};
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
///# use core::sync::atomic::{AtomicBool, Ordering};
|
||||
///# let b = AtomicBool::new(true);
|
||||
/// while b.load(Ordering::Acquire) {
|
||||
/// std::hint::spin_loop()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.61.0"]
|
||||
pub MISSING_SPIN_LOOP,
|
||||
perf,
|
||||
"An empty busy waiting loop"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for manual implementations of Iterator::find
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// It doesn't affect performance, but using `find` is shorter and easier to read.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```no_run
|
||||
/// fn example(arr: Vec<i32>) -> Option<i32> {
|
||||
/// for el in arr {
|
||||
/// if el == 1 {
|
||||
/// return Some(el);
|
||||
/// }
|
||||
/// }
|
||||
/// None
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// fn example(arr: Vec<i32>) -> Option<i32> {
|
||||
/// arr.into_iter().find(|&el| el == 1)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub MANUAL_FIND,
|
||||
complexity,
|
||||
"manual implementation of `Iterator::find`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for uses of the `enumerate` method where the index is unused (`_`)
|
||||
@@ -647,144 +667,151 @@
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Looks for loops that check for emptiness of a `Vec` in the condition and pop an element
|
||||
/// in the body as a separate operation.
|
||||
/// Checks for while loops comparing floating point values.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Such loops can be written in a more idiomatic way by using a while-let loop and directly
|
||||
/// pattern matching on the return value of `Vec::pop()`.
|
||||
/// If you increment floating point values, errors can compound,
|
||||
/// so, use integers instead if possible.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The lint will catch all while loops comparing floating point
|
||||
/// values without regarding the increment.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut numbers = vec![1, 2, 3, 4, 5];
|
||||
/// while !numbers.is_empty() {
|
||||
/// let number = numbers.pop().unwrap();
|
||||
/// // use `number`
|
||||
/// let mut x = 0.0;
|
||||
/// while x < 42.0 {
|
||||
/// x += 1.0;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut numbers = vec![1, 2, 3, 4, 5];
|
||||
/// while let Some(number) = numbers.pop() {
|
||||
/// // use `number`
|
||||
/// let mut x = 0;
|
||||
/// while x < 42 {
|
||||
/// x += 1;
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.71.0"]
|
||||
pub MANUAL_WHILE_LET_SOME,
|
||||
style,
|
||||
"checking for emptiness of a `Vec` in the loop condition and popping an element in the body"
|
||||
#[clippy::version = "1.80.0"]
|
||||
pub WHILE_FLOAT,
|
||||
nursery,
|
||||
"while loops comparing floating point values"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for infinite loops in a function where the return type is not `!`
|
||||
/// and lint accordingly.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Making the return type `!` serves as documentation that the function does not return.
|
||||
/// If the function is not intended to loop infinitely, then this lint may detect a bug.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run,ignore
|
||||
/// fn run_forever() {
|
||||
/// loop {
|
||||
/// // do something
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// If infinite loops are as intended:
|
||||
/// ```no_run,ignore
|
||||
/// fn run_forever() -> ! {
|
||||
/// loop {
|
||||
/// // do something
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
/// Otherwise add a `break` or `return` condition:
|
||||
/// ```no_run,ignore
|
||||
/// fn run_forever() {
|
||||
/// loop {
|
||||
/// // do something
|
||||
/// if condition {
|
||||
/// break;
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.76.0"]
|
||||
pub INFINITE_LOOP,
|
||||
restriction,
|
||||
"possibly unintended infinite loop"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for manually filling a slice with a value.
|
||||
/// Checks whether variables used within while loop condition
|
||||
/// can be (and are) mutated in the body.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Using the `fill` method is more idiomatic and concise.
|
||||
/// If the condition is unchanged, entering the body of the loop
|
||||
/// will lead to an infinite loop.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// If the `while`-loop is in a closure, the check for mutation of the
|
||||
/// condition variables in the body can cause false negatives. For example when only `Upvar` `a` is
|
||||
/// in the condition and only `Upvar` `b` gets mutated in the body, the lint will not trigger.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let mut some_slice = [1, 2, 3, 4, 5];
|
||||
/// for i in 0..some_slice.len() {
|
||||
/// some_slice[i] = 0;
|
||||
/// let i = 0;
|
||||
/// while i > 10 {
|
||||
/// println!("let me loop forever!");
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let mut some_slice = [1, 2, 3, 4, 5];
|
||||
/// some_slice.fill(0);
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub MANUAL_SLICE_FILL,
|
||||
style,
|
||||
"manually filling a slice with a value"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for usage of a character position yielded by `.chars().enumerate()` in a context where a **byte index** is expected,
|
||||
/// such as an argument to a specific `str` method or indexing into a `str` or `String`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A character (more specifically, a Unicode scalar value) that is yielded by `str::chars` can take up multiple bytes,
|
||||
/// so a character position does not necessarily have the same byte index at which the character is stored.
|
||||
/// Thus, using the character position where a byte index is expected can unexpectedly return wrong values
|
||||
/// or panic when the string consists of multibyte characters.
|
||||
///
|
||||
/// For example, the character `a` in `äa` is stored at byte index 2 but has the character position 1.
|
||||
/// Using the character position 1 to index into the string will lead to a panic as it is in the middle of the first character.
|
||||
///
|
||||
/// Instead of `.chars().enumerate()`, the correct iterator to use is `.char_indices()`, which yields byte indices.
|
||||
///
|
||||
/// This pattern is technically fine if the strings are known to only use the ASCII subset,
|
||||
/// though in those cases it would be better to use `bytes()` directly to make the intent clearer,
|
||||
/// but there is also no downside to just using `.char_indices()` directly and supporting non-ASCII strings.
|
||||
///
|
||||
/// You may also want to read the [chapter on strings in the Rust Book](https://doc.rust-lang.org/book/ch08-02-strings.html)
|
||||
/// which goes into this in more detail.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// # let s = "...";
|
||||
/// for (idx, c) in s.chars().enumerate() {
|
||||
/// let _ = s[idx..]; // ⚠️ Panics for strings consisting of multibyte characters
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// # let s = "...";
|
||||
/// for (idx, c) in s.char_indices() {
|
||||
/// let _ = s[idx..];
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub CHAR_INDICES_AS_BYTE_INDICES,
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WHILE_IMMUTABLE_CONDITION,
|
||||
correctness,
|
||||
"using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected"
|
||||
"variables used within while expression are not mutated in the body"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Detects `loop + match` combinations that are easier
|
||||
/// written as a `while let` loop.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The `while let` loop is usually shorter and more
|
||||
/// readable.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,no_run
|
||||
/// let y = Some(1);
|
||||
/// loop {
|
||||
/// let x = match y {
|
||||
/// Some(x) => x,
|
||||
/// None => break,
|
||||
/// };
|
||||
/// // ..
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,no_run
|
||||
/// let y = Some(1);
|
||||
/// while let Some(x) = y {
|
||||
/// // ..
|
||||
/// };
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WHILE_LET_LOOP,
|
||||
complexity,
|
||||
"`loop { if let { ... } else break }`, which can be written as a `while let` loop"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for `while let` expressions on iterators.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Readability. A simple `for` loop is shorter and conveys
|
||||
/// the intent better.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```ignore
|
||||
/// while let Some(val) = iter.next() {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// for val in &mut iter {
|
||||
/// ..
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub WHILE_LET_ON_ITERATOR,
|
||||
style,
|
||||
"using a `while let` loop instead of a for loop on an iterator"
|
||||
}
|
||||
|
||||
impl_lint_pass!(Loops => [
|
||||
CHAR_INDICES_AS_BYTE_INDICES,
|
||||
EMPTY_LOOP,
|
||||
EXPLICIT_COUNTER_LOOP,
|
||||
EXPLICIT_INTO_ITER_LOOP,
|
||||
EXPLICIT_ITER_LOOP,
|
||||
FOR_KV_MAP,
|
||||
INFINITE_LOOP,
|
||||
ITER_NEXT_LOOP,
|
||||
MANUAL_FIND,
|
||||
MANUAL_FLATTEN,
|
||||
MANUAL_MEMCPY,
|
||||
MANUAL_SLICE_FILL,
|
||||
MANUAL_WHILE_LET_SOME,
|
||||
MISSING_SPIN_LOOP,
|
||||
MUT_RANGE_BOUND,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
NEVER_LOOP,
|
||||
SAME_ITEM_PUSH,
|
||||
SINGLE_ELEMENT_LOOP,
|
||||
UNUSED_ENUMERATE_INDEX,
|
||||
WHILE_FLOAT,
|
||||
WHILE_IMMUTABLE_CONDITION,
|
||||
WHILE_LET_LOOP,
|
||||
WHILE_LET_ON_ITERATOR,
|
||||
]);
|
||||
|
||||
pub struct Loops {
|
||||
msrv: Msrv,
|
||||
enforce_iter_loop_reborrow: bool,
|
||||
@@ -798,33 +825,6 @@ pub fn new(conf: &'static Conf) -> Self {
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(Loops => [
|
||||
MANUAL_MEMCPY,
|
||||
MANUAL_FLATTEN,
|
||||
NEEDLESS_RANGE_LOOP,
|
||||
EXPLICIT_ITER_LOOP,
|
||||
EXPLICIT_INTO_ITER_LOOP,
|
||||
ITER_NEXT_LOOP,
|
||||
WHILE_LET_LOOP,
|
||||
EXPLICIT_COUNTER_LOOP,
|
||||
EMPTY_LOOP,
|
||||
WHILE_LET_ON_ITERATOR,
|
||||
FOR_KV_MAP,
|
||||
NEVER_LOOP,
|
||||
MUT_RANGE_BOUND,
|
||||
WHILE_IMMUTABLE_CONDITION,
|
||||
WHILE_FLOAT,
|
||||
SAME_ITEM_PUSH,
|
||||
SINGLE_ELEMENT_LOOP,
|
||||
MISSING_SPIN_LOOP,
|
||||
MANUAL_FIND,
|
||||
MANUAL_WHILE_LET_SOME,
|
||||
UNUSED_ENUMERATE_INDEX,
|
||||
INFINITE_LOOP,
|
||||
MANUAL_SLICE_FILL,
|
||||
CHAR_INDICES_AS_BYTE_INDICES,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Loops {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
let for_loop = higher::ForLoop::hir(expr);
|
||||
|
||||
@@ -84,6 +84,7 @@
|
||||
suspicious,
|
||||
"expanding macro metavariables in an unsafe block"
|
||||
}
|
||||
|
||||
impl_lint_pass!(ExprMetavarsInUnsafe => [MACRO_METAVARS_IN_UNSAFE]);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user