Merge commit 'e645f93552c3926a0bb481a777df120b7bce986f'

This commit is contained in:
Philipp Krones
2026-03-05 17:18:20 +01:00
parent 86e6165d72
commit b4d2445432
306 changed files with 11449 additions and 10159 deletions
+2 -1
View File
@@ -20,7 +20,8 @@ jobs:
- name: Check Changelog
if: ${{ github.event_name == 'pull_request' }}
run: |
if [[ -z $(grep -oP 'changelog: *\K\S+' <<< "$PR_BODY") ]]; then
# Checks that the PR body contains a changelog entry, ignoring the placeholder from the PR template.
if [[ -z $(grep -oP 'changelog: *\K(?!\[`lint_name`\])\S+' <<< "$PR_BODY") ]]; then
echo "::error::Pull request message must contain 'changelog: ...' with your changelog. Please add it."
exit 1
fi
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "clippy"
version = "0.1.95"
version = "0.1.96"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
+1 -1
View File
@@ -51,7 +51,7 @@ this group to help with triaging, which can include:
busy or wants to abandon it. If the reviewer is busy, the PR can be
reassigned to someone else.
Checkout: https://triage.rust-lang.org/triage/rust-lang/rust-clippy to
Checkout: <https://triage.rust-lang.org/triage/rust-lang/rust-clippy> to
monitor PRs.
While not part of their duties, contributors are encouraged to review PRs
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "clippy_config"
version = "0.1.95"
version = "0.1.96"
edition = "2024"
publish = false
+5 -6
View File
@@ -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
View File
@@ -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);
}
+316
View File
@@ -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)));
});
}
+1 -2
View File
@@ -1,6 +1,5 @@
#![feature(
exit_status_error,
if_let_guard,
new_range,
new_range_api,
os_str_slice,
@@ -33,8 +32,8 @@
pub mod serve;
pub mod setup;
pub mod sync;
pub mod update_lints;
mod generate;
mod parse;
mod utils;
+4 -3
View File
@@ -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
View File
@@ -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(),
});
}
},
_ => {},
}
}
}
+82 -4
View File
@@ -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.
///
-204
View File
@@ -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)
},
);
}
}
+27
View File
@@ -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 }
}
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "clippy_lints"
version = "0.1.95"
version = "0.1.96"
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md"
+1
View File
@@ -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 {
+2 -2
View File
@@ -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 {
+48 -48
View File
@@ -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,
}
+2 -2
View File
@@ -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
View File
@@ -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);
+42 -38
View File
@@ -11,6 +11,43 @@
use rustc_session::impl_lint_pass;
use rustc_span::Span;
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
@@ -135,44 +172,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)>,
+1
View File
@@ -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 {
+2 -2
View File
@@ -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,
+1 -1
View File
@@ -1,8 +1,8 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{is_default_equivalent, sym};
use clippy_utils::macros::macro_backtrace;
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::ty::expr_sig;
use clippy_utils::{is_default_equivalent, sym};
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
+1
View File
@@ -27,6 +27,7 @@
style,
"hard to read byte char slice"
}
declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]);
impl EarlyLintPass for ByteCharSlice {
+127 -127
View File
@@ -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
View File
@@ -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()) {
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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,
+34 -34
View File
@@ -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
+2 -1
View File
@@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::{get_enclosing_block, sym};
use clippy_utils::res::{MaybeDef, MaybeResPath};
use clippy_utils::visitors::{Visitable, for_each_expr};
use clippy_utils::{get_enclosing_block, sym};
use core::ops::ControlFlow;
use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
@@ -40,6 +40,7 @@
nursery,
"a collection is never queried"
}
declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
+1
View File
@@ -49,6 +49,7 @@
suspicious,
"using `crate` in a macro definition"
}
declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
impl EarlyLintPass for CrateInMacroDef {
+3 -3
View File
@@ -1,8 +1,8 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{is_in_test, sym};
use clippy_utils::macros::{MacroCall, macro_backtrace};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_in_test, sym};
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Arm, Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind};
@@ -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 {
+2 -2
View File
@@ -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 {
+27 -25
View File
@@ -3,7 +3,9 @@
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs};
use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym};
use clippy_utils::{
DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym,
};
use rustc_ast::util::parser::ExprPrecedence;
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
@@ -20,6 +22,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.
@@ -117,34 +142,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)]
+2 -2
View File
@@ -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(..), _))
+64 -64
View File
@@ -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 its 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 its 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 {
+2 -2
View File
@@ -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 {
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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() {
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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:
+2 -2
View File
@@ -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
@@ -94,17 +94,14 @@ fn is_missing_punctuation(doc_string: &str) -> Vec<MissingPunctuation> {
Event::Code(..) | Event::Start(Tag::Link { .. }) | Event::End(TagEnd::Link)
if no_report_depth == 0 && !offset.is_empty() =>
{
if doc_string[..offset.end]
.trim_end()
.ends_with(TERMINAL_PUNCTUATION_MARKS)
{
if trim_trailing_symbols(&doc_string[..offset.end]).ends_with(TERMINAL_PUNCTUATION_MARKS) {
current_paragraph = None;
} else {
current_paragraph = Some(MissingPunctuation::Fixable(offset.end));
}
},
Event::Text(..) if no_report_depth == 0 && !offset.is_empty() => {
let trimmed = doc_string[..offset.end].trim_end();
let trimmed = trim_trailing_symbols(&doc_string[..offset.end]);
if trimmed.ends_with(TERMINAL_PUNCTUATION_MARKS) {
current_paragraph = None;
} else if let Some(t) = trimmed.strip_suffix(|c| c == ')' || c == '"') {
@@ -125,6 +122,21 @@ fn is_missing_punctuation(doc_string: &str) -> Vec<MissingPunctuation> {
missing_punctuation
}
fn trim_trailing_symbols(s: &str) -> &str {
s.trim_end_matches(|c: char|
// Source: https://unicodeplus.com
matches!(c as u32,
0x1F300..=0x1F5FF | // Miscellaneous Symbols and Pictographs
0x1F600..=0x1F64F | // Emoticons
0x1F900..=0x1F9FF | // Supplemental Symbols and Pictographs
0x2700..=0x27BF | // Dingbats
0x1FA70..=0x1FAFF | // Symbols and Pictographs Extended-A
0x1F680..=0x1F6FF | // Transport and Map Symbols
0x2600..=0x26FF | // Miscellaneous Symbols
0xFE00..=0xFE0F // Variation selectors
) || c.is_whitespace())
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum MissingPunctuation {
Fixable(usize),
+416 -416
View File
@@ -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);
+2 -6
View File
@@ -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
+2 -2
View File
@@ -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
+1
View File
@@ -31,6 +31,7 @@
restriction,
"empty `Drop` implementations"
}
declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
impl LateLintPass<'_> for EmptyDrop {
+40 -40
View File
@@ -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() }
+33 -30
View File
@@ -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 {}`
+23 -19
View File
@@ -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 targets 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 targets 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];
+4 -1
View File
@@ -2,7 +2,10 @@
use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::is_copy;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{SpanlessEq, can_move_expr_to_closure_no_visit, desugar_await, higher, is_expr_final_block_expr, is_expr_used_or_unified, paths, peel_hir_expr_while, span_contains_non_whitespace, sym};
use clippy_utils::{
SpanlessEq, can_move_expr_to_closure_no_visit, desugar_await, higher, is_expr_final_block_expr,
is_expr_used_or_unified, paths, peel_hir_expr_while, span_contains_non_whitespace, sym,
};
use core::fmt::{self, Write};
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
+1
View File
@@ -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 {
+2 -2
View File
@@ -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,
+16 -13
View File
@@ -65,7 +65,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>) {
@@ -215,6 +218,18 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
"redundant closure",
|diag| {
if let Some(mut snippet) = snippet_opt(cx, callee.span) {
let n_refs = callee_ty_adjustments
.iter()
.rev()
.fold(0, |acc, adjustment| match adjustment.kind {
Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1,
Adjust::Deref(_) if acc > 0 => acc + 1,
_ => acc,
});
if n_refs > 0 {
snippet = format!("{}{snippet}", "*".repeat(n_refs));
}
if callee.res_local_id().is_some_and(|l| {
// FIXME: Do we really need this `local_used_in` check?
// Isn't it checking something like... `callee(callee)`?
@@ -230,18 +245,6 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
},
_ => (),
}
} else if let n_refs =
callee_ty_adjustments
.iter()
.rev()
.fold(0, |acc, adjustment| match adjustment.kind {
Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1,
Adjust::Deref(_) if acc > 0 => acc + 1,
_ => acc,
})
&& n_refs > 0
{
snippet = format!("{}{snippet}", "*".repeat(n_refs));
}
let replace_with = match callee_ty_adjusted.kind() {
+42 -39
View File
@@ -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);
+1
View File
@@ -61,6 +61,7 @@
complexity,
"checks for blocks nested beyond a certain threshold"
}
impl_lint_pass!(ExcessiveNesting => [EXCESSIVE_NESTING]);
pub struct ExcessiveNesting {
+2 -2
View File
@@ -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) {
+2 -4
View File
@@ -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<'_>) {
@@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::Sugg;
use clippy_utils::{get_parent_expr, has_ambiguous_literal_in_expr, sym};
use rustc_ast::AssignOpKind;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment};
use rustc_lint::LateContext;
@@ -26,69 +27,99 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'
}
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if let ExprKind::Binary(
Spanned {
node: op @ (BinOpKind::Add | BinOpKind::Sub),
..
},
lhs,
rhs,
) = &expr.kind
let (is_assign, op, lhs, rhs) = match &expr.kind {
ExprKind::AssignOp(
Spanned {
node: AssignOpKind::AddAssign,
..
},
lhs,
rhs,
) => (true, BinOpKind::Add, lhs, rhs),
ExprKind::AssignOp(
Spanned {
node: AssignOpKind::SubAssign,
..
},
lhs,
rhs,
) => (true, BinOpKind::Sub, lhs, rhs),
ExprKind::Binary(
Spanned {
node: op @ (BinOpKind::Add | BinOpKind::Sub),
..
},
lhs,
rhs,
) => (false, *op, lhs, rhs),
_ => return,
};
if !is_assign
&& let Some(parent) = get_parent_expr(cx, expr)
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind
&& method.name == sym::sqrt
// we don't care about the applicability as this is an early-return condition
&& super::hypot::detect(cx, receiver, &mut Applicability::Unspecified).is_some()
{
if let Some(parent) = get_parent_expr(cx, expr)
&& let ExprKind::MethodCall(PathSegment { ident: method, .. }, receiver, ..) = parent.kind
&& method.name == sym::sqrt
// we don't care about the applicability as this is an early-return condition
&& super::hypot::detect(cx, receiver, &mut Applicability::Unspecified).is_some()
{
return;
}
let maybe_neg_sugg = |expr, app: &mut _| {
let sugg = Sugg::hir_with_applicability(cx, expr, "_", app);
if let BinOpKind::Sub = op { -sugg } else { sugg }
};
let mut app = Applicability::MachineApplicable;
let (recv, arg1, arg2) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs)
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
{
(
inner_lhs,
Sugg::hir_with_applicability(cx, inner_rhs, "_", &mut app),
maybe_neg_sugg(rhs, &mut app),
)
} else if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs)
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
{
(
inner_lhs,
maybe_neg_sugg(inner_rhs, &mut app),
Sugg::hir_with_applicability(cx, lhs, "_", &mut app),
)
} else {
return;
};
// Check if any variable in the expression has an ambiguous type (could be f32 or f64)
// see: https://github.com/rust-lang/rust-clippy/issues/14897
if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _)))
&& has_ambiguous_literal_in_expr(cx, recv)
{
return;
}
span_lint_and_sugg(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"multiply and add expressions can be calculated more efficiently and accurately",
"consider using",
format!(
"{}.mul_add({arg1}, {arg2})",
super::lib::prepare_receiver_sugg(cx, recv, &mut app)
),
app,
);
return;
}
// Check if any variable in the expression has an ambiguous type (could be f32 or f64)
// see: https://github.com/rust-lang/rust-clippy/issues/14897
let has_ambiguous_type = |expr: &Expr<'_>| {
(matches!(expr.kind, ExprKind::Path(_)) || matches!(expr.kind, ExprKind::Call(_, _)))
&& has_ambiguous_literal_in_expr(cx, expr)
};
let (recv, arg1, arg2, is_from_rhs) = if let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, rhs)
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
&& !has_ambiguous_type(inner_lhs)
{
(inner_lhs, inner_rhs, lhs, true)
} else if !is_assign
&& let Some((inner_lhs, inner_rhs)) = is_float_mul_expr(cx, lhs)
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
&& !has_ambiguous_type(inner_lhs)
{
(inner_lhs, inner_rhs, rhs, false)
} else {
return;
};
span_lint_and_then(
cx,
SUBOPTIMAL_FLOPS,
expr.span,
"multiply and add expressions can be calculated more efficiently and accurately",
|diag| {
let maybe_neg_sugg = |expr, app: &mut _| {
let sugg = Sugg::hir_with_applicability(cx, expr, "_", app);
if let BinOpKind::Sub = op { -sugg } else { sugg }
};
let mut app = Applicability::MachineApplicable;
let recv_sugg = super::lib::prepare_receiver_sugg(cx, recv, &mut app);
let (arg1, arg2) = if is_from_rhs {
(
maybe_neg_sugg(arg1, &mut app),
Sugg::hir_with_applicability(cx, arg2, "_", &mut app),
)
} else {
(
Sugg::hir_with_applicability(cx, arg1, "_", &mut app),
maybe_neg_sugg(arg2, &mut app),
)
};
diag.span_suggestion(
expr.span,
"consider using",
if is_assign {
format!("{arg2} = {recv_sugg}.mul_add({arg1}, {arg2})")
} else {
format!("{recv_sugg}.mul_add({arg1}, {arg2})")
},
app,
);
},
);
}
+2 -2
View File
@@ -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)
+68 -68
View File
@@ -58,32 +58,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! {
@@ -171,61 +169,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! {
@@ -257,14 +226,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)]
+41 -41
View File
@@ -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);
+1
View File
@@ -41,6 +41,7 @@
pedantic,
"`format!(..)` appended to existing `String`"
}
impl_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
pub(crate) struct FormatPushString {
+69 -69
View File
@@ -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 {
+1
View File
@@ -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 {
+2 -2
View File
@@ -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
View File
@@ -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,
+1 -1
View File
@@ -217,7 +217,7 @@ fn check_must_use_candidate<'tcx>(
diag.span_suggestion(
item_span.shrink_to_lo(),
"add the attribute",
format!("#[must_use] \n{indent}"),
format!("#[must_use]\n{indent}"),
Applicability::MachineApplicable,
);
if let Some(msg) = match return_ty(cx, item_id).opt_diag_name(cx) {
+2 -2
View File
@@ -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
View File
@@ -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 {
+5 -2
View File
@@ -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(
+2 -2
View File
@@ -71,6 +71,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,
@@ -113,8 +115,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 {
+2 -2
View File
@@ -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)
+30 -30
View File
@@ -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 {
+1
View File
@@ -40,6 +40,7 @@
suspicious,
"TryFrom with infallible Error type"
}
declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
+4 -1
View File
@@ -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<'_>) {
+38 -37
View File
@@ -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 == &"_")
+48 -45
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+3 -3
View File
@@ -2,9 +2,9 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::{is_from_proc_macro, sym};
use clippy_utils::macros::macro_backtrace;
use clippy_utils::source::snippet;
use clippy_utils::{is_from_proc_macro, sym};
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
@@ -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(..)) {

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