Merge commit '0f8eabd6231366bfc1bb1464601297c2d48f8f68' into clippyup

This commit is contained in:
Jason Newcomb 2024-08-24 17:34:45 -04:00
parent 43e1145c80
commit 173d5a6af0
216 changed files with 3052 additions and 2969 deletions

View file

@ -1,10 +1,10 @@
[alias]
bless = "test --config env.RUSTC_BLESS='1'"
uitest = "test --test compile-test"
uibless = "test --test compile-test -- -- --bless"
bless = "test -- -- --bless"
uibless = "bless --test compile-test"
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml -- "
collect-metadata = "test --test dogfood --features internal -- collect_metadata"
collect-metadata = "test --test compile-test --config env.COLLECT_METADATA='1'"
[build]
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests

1
.github/deploy.sh vendored
View file

@ -10,6 +10,7 @@ mkdir out/master/
cp util/gh-pages/index.html out/master
cp util/gh-pages/script.js out/master
cp util/gh-pages/lints.json out/master
cp util/gh-pages/style.css out/master
if [[ -n $TAG_NAME ]]; then
echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it"

View file

@ -136,11 +136,6 @@ jobs:
- name: Test metadata collection
run: cargo collect-metadata
- name: Test lint_configuration.md is up-to-date
run: |
echo "run \`cargo collect-metadata\` if this fails"
git update-index --refresh
integration_build:
needs: changelog
runs-on: ubuntu-latest

View file

@ -5914,6 +5914,7 @@ Released 2018-09-13
[`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
[`to_string_trait_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl
[`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
[`too_long_first_doc_paragraph`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_long_first_doc_paragraph
[`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
[`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
[`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg

View file

@ -30,8 +30,11 @@ color-print = "0.3.4"
anstream = "0.6.0"
[dev-dependencies]
cargo_metadata = "0.18.1"
ui_test = "0.25"
regex = "1.5.5"
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.122"
toml = "0.7.3"
walkdir = "2.3"
filetime = "0.2.9"
@ -41,7 +44,6 @@ itertools = "0.12"
clippy_utils = { path = "clippy_utils" }
if_chain = "1.0"
quote = "1.0.25"
serde = { version = "1.0.145", features = ["derive"] }
syn = { version = "2.0", features = ["full"] }
futures = "0.3"
parking_lot = "0.12"

View file

@ -739,7 +739,7 @@ for some users. Adding a configuration is done in the following steps:
5. Update [Lint Configuration](../lint_configuration.md)
Run `cargo collect-metadata` to generate documentation changes for the book.
Run `cargo bless --test config-metadata` to generate documentation changes for the book.
[`clippy_config::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_config/src/conf.rs
[`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs

View file

@ -1,5 +1,5 @@
<!--
This file is generated by `cargo collect-metadata`.
This file is generated by `cargo bless --test config-metadata`.
Please use that command to update the file and do not edit it by hand.
-->
@ -199,7 +199,7 @@ Allowed names below the minimum allowed characters. The value `".."` can be used
the list to indicate, that the configured values should be appended to the default
configuration of Clippy. By default, any configuration will replace the default value.
**Default Value:** `["j", "z", "i", "y", "n", "x", "w"]`
**Default Value:** `["i", "j", "x", "y", "z", "w", "n"]`
---
**Affected lints:**
@ -455,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the
* `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
**Default Value:** `["TiB", "CoreGraphics", "CoffeeScript", "TeX", "Direct2D", "PiB", "DirectX", "NetBSD", "OAuth", "NaN", "OpenType", "WebGL2", "WebTransport", "JavaScript", "OpenSSL", "OpenSSH", "EiB", "PureScript", "OpenAL", "MiB", "WebAssembly", "MinGW", "CoreFoundation", "WebGPU", "ClojureScript", "CamelCase", "OpenDNS", "NaNs", "OpenMP", "GitLab", "KiB", "sRGB", "CoreText", "macOS", "TypeScript", "GiB", "OpenExr", "YCbCr", "OpenTelemetry", "OpenBSD", "FreeBSD", "GPLv2", "PostScript", "WebP", "LaTeX", "TensorFlow", "AccessKit", "TrueType", "OpenStreetMap", "OpenGL", "DevOps", "OCaml", "WebRTC", "WebGL", "BibLaTeX", "GitHub", "GraphQL", "iOS", "Direct3D", "BibTeX", "DirectWrite", "GPLv3", "IPv6", "WebSocket", "IPv4", "ECMAScript"]`
**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
---
**Affected lints:**
@ -949,5 +949,3 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in
---
**Affected lints:**
* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe)

View file

@ -7,7 +7,6 @@ edition = "2021"
[dependencies]
itertools = "0.12"
rustc-semver = "1.1"
serde = { version = "1.0", features = ["derive"] }
toml = "0.7.3"

View file

@ -1,7 +1,6 @@
use crate::msrvs::Msrv;
use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
use crate::ClippyConfiguration;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_session::Session;
use rustc_span::edit_distance::edit_distance;
@ -218,7 +217,7 @@ macro_rules! define_Conf {
define_Conf! {
/// Which crates to allow absolute paths from
#[lints(absolute_paths)]
absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(),
absolute_paths_allowed_crates: Vec<String> = Vec::new(),
/// The maximum number of segments a path can have before being linted, anything above this will
/// be linted.
#[lints(absolute_paths)]
@ -280,12 +279,12 @@ define_Conf! {
allowed_dotfiles: Vec<String> = Vec::default(),
/// A list of crate names to allow duplicates of
#[lints(multiple_crate_versions)]
allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(),
allowed_duplicate_crates: Vec<String> = Vec::new(),
/// Allowed names below the minimum allowed characters. The value `".."` can be used as part of
/// the list to indicate, that the configured values should be appended to the default
/// configuration of Clippy. By default, any configuration will replace the default value.
#[lints(min_ident_chars)]
allowed_idents_below_min_chars: FxHashSet<String> =
allowed_idents_below_min_chars: Vec<String> =
DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(),
/// List of prefixes to allow when determining whether an item's name ends with the module's name.
/// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
@ -323,7 +322,7 @@ define_Conf! {
/// 2. Paths with any segment that containing the word 'prelude'
/// are already allowed by default.
#[lints(wildcard_imports)]
allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(),
allowed_wildcard_imports: Vec<String> = Vec::new(),
/// Suppress checking of the passed type names in all types of operations.
///
/// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
@ -355,7 +354,7 @@ define_Conf! {
/// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
/// ```
#[lints(arithmetic_side_effects)]
arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(),
arithmetic_side_effects_allowed_binary: Vec<(String, String)> = <_>::default(),
/// Suppress checking of the passed type names in unary operations like "negation" (`-`).
///
/// #### Example
@ -431,7 +430,7 @@ define_Conf! {
/// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
/// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
#[lints(doc_markdown)]
doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
doc_valid_idents: Vec<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
/// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
#[lints(non_send_fields_in_send_ty)]
enable_raw_pointer_heuristic_for_send: bool = true,
@ -706,12 +705,12 @@ fn deserialize(file: &SourceFile) -> TryConf {
DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
);
// TODO: THIS SHOULD BE TESTED, this comment will be gone soon
if conf.conf.allowed_idents_below_min_chars.contains("..") {
if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") {
conf.conf
.allowed_idents_below_min_chars
.extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string));
}
if conf.conf.doc_valid_idents.contains("..") {
if conf.conf.doc_valid_idents.iter().any(|e| e == "..") {
conf.conf
.doc_valid_idents
.extend(DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string));
@ -890,14 +889,14 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
#[cfg(test)]
mod tests {
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use serde::de::IgnoredAny;
use std::collections::{HashMap, HashSet};
use std::fs;
use walkdir::WalkDir;
#[test]
fn configs_are_tested() {
let mut names: FxHashSet<String> = crate::get_configuration_metadata()
let mut names: HashSet<String> = crate::get_configuration_metadata()
.into_iter()
.map(|meta| meta.name.replace('_', "-"))
.collect();
@ -910,7 +909,7 @@ mod tests {
for entry in toml_files {
let file = fs::read_to_string(entry.path()).unwrap();
#[allow(clippy::zero_sized_map_values)]
if let Ok(map) = toml::from_str::<FxHashMap<String, IgnoredAny>>(&file) {
if let Ok(map) = toml::from_str::<HashMap<String, IgnoredAny>>(&file) {
for name in map.keys() {
names.remove(name.as_str());
}

View file

@ -14,7 +14,7 @@
)]
extern crate rustc_ast;
extern crate rustc_data_structures;
extern crate rustc_attr;
#[allow(unused_extern_crates)]
extern crate rustc_driver;
extern crate rustc_errors;

View file

@ -1,6 +1,6 @@
use rustc_ast::Attribute;
use rustc_semver::RustcVersion;
use rustc_session::Session;
use rustc_attr::parse_version;
use rustc_session::{RustcVersion, Session};
use rustc_span::{sym, Symbol};
use serde::Deserialize;
use std::fmt;
@ -10,7 +10,7 @@ macro_rules! msrv_aliases {
$($name:ident),* $(,)?
})*) => {
$($(
pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
pub const $name: RustcVersion = RustcVersion { major: $major, minor :$minor, patch: $patch };
)*)*
};
}
@ -18,6 +18,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,81,0 { LINT_REASONS_STABILIZATION }
1,80,0 { BOX_INTO_ITER}
1,77,0 { C_STR_LITERALS }
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
@ -81,9 +82,9 @@ impl<'de> Deserialize<'de> for Msrv {
D: serde::Deserializer<'de>,
{
let v = String::deserialize(deserializer)?;
RustcVersion::parse(&v)
parse_version(Symbol::intern(&v))
.map(|v| Msrv { stack: vec![v] })
.map_err(|_| serde::de::Error::custom("not a valid Rust version"))
.ok_or_else(|| serde::de::Error::custom("not a valid Rust version"))
}
}
@ -95,7 +96,7 @@ impl Msrv {
pub fn read_cargo(&mut self, sess: &Session) {
let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
.ok()
.and_then(|v| RustcVersion::parse(&v).ok());
.and_then(|v| parse_version(Symbol::intern(&v)));
match (self.current(), cargo_msrv) {
(None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv],
@ -115,7 +116,7 @@ impl Msrv {
}
pub fn meets(&self, required: RustcVersion) -> bool {
self.current().map_or(true, |version| version.meets(required))
self.current().map_or(true, |msrv| msrv >= required)
}
fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
@ -131,7 +132,7 @@ impl Msrv {
}
if let Some(msrv) = msrv_attr.value_str() {
if let Ok(version) = RustcVersion::parse(msrv.as_str()) {
if let Some(version) = parse_version(msrv) {
return Some(version);
}

View file

@ -1,3 +1,4 @@
#![feature(rustc_private)]
// warn on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)]

View file

@ -604,7 +604,7 @@ fn gen_declared_lints<'a>(
details.sort_unstable();
let mut output = GENERATED_FILE_COMMENT.to_string();
output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n");
output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
for (is_public, module_name, lint_name) in details {
if !is_public {

View file

@ -25,7 +25,6 @@ regex = { version = "1.5", optional = true }
unicode-normalization = "0.1"
unicode-script = { version = "0.5", default-features = false }
semver = "1.0"
rustc-semver = "1.1"
url = "2.2"
[dev-dependencies]

View file

@ -1,6 +1,6 @@
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::snippet_opt;
use clippy_utils::is_from_proc_macro;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
@ -8,6 +8,7 @@ use rustc_hir::{HirId, ItemKind, Node, Path};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass;
use rustc_span::symbol::kw;
use rustc_span::Symbol;
declare_clippy_lint! {
/// ### What it does
@ -24,6 +25,13 @@ declare_clippy_lint! {
/// Note: One exception to this is code from macro expansion - this does not lint such cases, as
/// using absolute paths is the proper way of referencing items in one.
///
/// ### Known issues
///
/// There are currently a few cases which are not caught by this lint:
/// * Macro calls. e.g. `path::to::macro!()`
/// * Derive macros. e.g. `#[derive(path::to::macro)]`
/// * Attribute macros. e.g. `#[path::to::macro]`
///
/// ### Example
/// ```no_run
/// let x = std::f64::consts::PI;
@ -48,56 +56,60 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
pub struct AbsolutePaths {
pub absolute_paths_max_segments: u64,
pub absolute_paths_allowed_crates: &'static FxHashSet<String>,
pub absolute_paths_allowed_crates: FxHashSet<Symbol>,
}
impl AbsolutePaths {
pub fn new(conf: &'static Conf) -> Self {
Self {
absolute_paths_max_segments: conf.absolute_paths_max_segments,
absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates,
absolute_paths_allowed_crates: conf
.absolute_paths_allowed_crates
.iter()
.map(|x| Symbol::intern(x))
.collect(),
}
}
}
impl LateLintPass<'_> for AbsolutePaths {
impl<'tcx> LateLintPass<'tcx> for AbsolutePaths {
// We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
// we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
// a `Use`
#[expect(clippy::cast_possible_truncation)]
fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
let Self {
absolute_paths_max_segments,
absolute_paths_allowed_crates,
} = self;
if !path.span.from_expansion()
&& let node = cx.tcx.hir_node(hir_id)
&& !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _)))
&& let [first, rest @ ..] = path.segments
// Handle `::std`
&& let (segment, len) = if first.ident.name == kw::PathRoot {
// Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1`
// is fine here for the same reason
(&rest[0], path.segments.len() - 1)
fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, hir_id: HirId) {
let segments = match path.segments {
[] | [_] => return,
// Don't count enum variants and trait items as part of the length.
[rest @ .., _]
if let [.., s] = rest
&& matches!(s.res, Res::Def(DefKind::Enum | DefKind::Trait | DefKind::TraitAlias, _)) =>
{
rest
},
path => path,
};
if let [s1, s2, ..] = segments
&& let has_root = s1.ident.name == kw::PathRoot
&& let first = if has_root { s2 } else { s1 }
&& let len = segments.len() - usize::from(has_root)
&& len as u64 > self.absolute_paths_max_segments
&& let crate_name = if let Res::Def(DefKind::Mod, DefId { index, .. }) = first.res
&& index == CRATE_DEF_INDEX
{
// `other_crate::foo` or `::other_crate::foo`
first.ident.name
} else if first.ident.name == kw::Crate || has_root {
// `::foo` or `crate::foo`
kw::Crate
} else {
(first, path.segments.len())
}
&& len > *absolute_paths_max_segments as usize
&& let Some(segment_snippet) = snippet_opt(cx, segment.ident.span)
&& segment_snippet == segment.ident.as_str()
{
let is_abs_external =
matches!(segment.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX);
let is_abs_crate = segment.ident.name == kw::Crate;
if is_abs_external && absolute_paths_allowed_crates.contains(segment.ident.name.as_str())
|| is_abs_crate && absolute_paths_allowed_crates.contains("crate")
{
return;
}
if is_abs_external || is_abs_crate {
&& !path.span.from_expansion()
&& let node = cx.tcx.hir_node(hir_id)
&& !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(..)))
&& !self.absolute_paths_allowed_crates.contains(&crate_name)
&& !is_from_proc_macro(cx, path)
{
span_lint(
cx,
ABSOLUTE_PATHS,
@ -106,5 +118,4 @@ impl LateLintPass<'_> for AbsolutePaths {
);
}
}
}
}

View file

@ -4,8 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::impl_lint_pass;
use rustc_session::{impl_lint_pass, RustcVersion};
use rustc_span::symbol;
use std::f64::consts as f64;

View file

@ -3,7 +3,7 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
use clippy_utils::sugg::Sugg;
use clippy_utils::{is_diag_trait_item, last_path_segment, local_is_initialized, path_to_local};
use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local};
use rustc_errors::Applicability;
use rustc_hir::{self as hir, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
}
)
&& !clone_source_borrows_from_dest(cx, lhs, rhs.span)
&& !is_in_test(cx.tcx, e.hir_id)
{
span_lint_and_then(
cx,

View file

@ -1,6 +1,6 @@
use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments};
use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt};
use rustc_ast::{AttrKind, AttrStyle};
use rustc_lint::EarlyContext;
use rustc_span::Span;
@ -26,7 +26,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
item.span.parent(),
);
if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) {
let lines = snippet.split('\n').collect::<Vec<_>>();
let lines = without_block_comments(lines);

View file

@ -1,5 +1,3 @@
//! checks for attributes
mod allow_attributes;
mod allow_attributes_without_reason;
mod blanket_clippy_restriction_lints;

View file

@ -1,6 +1,6 @@
use super::{Attribute, NON_MINIMAL_CFG};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_ast::{MetaItemKind, NestedMetaItem};
use rustc_errors::Applicability;
use rustc_lint::EarlyContext;
@ -29,8 +29,13 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
meta.span,
"unneeded sub `cfg` when there is only one condition",
|diag| {
if let Some(snippet) = snippet_opt(cx, list[0].span()) {
diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect);
if let Some(snippet) = list[0].span().get_source_text(cx) {
diag.span_suggestion(
meta.span,
"try",
snippet.to_owned(),
Applicability::MaybeIncorrect,
);
}
},
);

View file

@ -1,6 +1,7 @@
use super::{Attribute, UNNECESSARY_CLIPPY_CFG};
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use itertools::Itertools;
use rustc_ast::AttrStyle;
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, Level};
@ -31,7 +32,7 @@ pub(super) fn check(
return;
}
if nb_items == clippy_lints.len() {
if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) {
if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) {
span_lint_and_sugg(
cx,
UNNECESSARY_CLIPPY_CFG,
@ -47,11 +48,7 @@ pub(super) fn check(
);
}
} else {
let snippet = clippy_lints
.iter()
.filter_map(|sp| snippet_opt(cx, *sp))
.collect::<Vec<_>>()
.join(",");
let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(",");
span_lint_and_note(
cx,
UNNECESSARY_CLIPPY_CFG,

View file

@ -1,7 +1,7 @@
use super::utils::{extract_clippy_lint, is_lint_level, is_word};
use super::{Attribute, USELESS_ATTRIBUTE};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{first_line_of_span, snippet_opt};
use clippy_utils::source::{first_line_of_span, SpanRangeExt};
use rustc_ast::NestedMetaItem;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
@ -69,14 +69,14 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
}
let line_span = first_line_of_span(cx, attr.span);
if let Some(mut sugg) = snippet_opt(cx, line_span) {
if sugg.contains("#[") {
if let Some(src) = line_span.get_source_text(cx) {
if src.contains("#[") {
#[expect(clippy::collapsible_span_lint_calls)]
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
sugg = sugg.replacen("#[", "#![", 1);
diag.span_suggestion(
line_span,
"if you just forgot a `!`, use",
sugg,
src.replacen("#[", "#![", 1),
Applicability::MaybeIncorrect,
);
});

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::eq_expr_value;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -134,28 +134,30 @@ fn check_inverted_bool_in_condition(
let suggestion = match (left.kind, right.kind) {
(ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => {
let Some(left) = snippet_opt(cx, left_sub.span) else {
let Some(left) = left_sub.span.get_source_text(cx) else {
return;
};
let Some(right) = snippet_opt(cx, right_sub.span) else {
let Some(right) = right_sub.span.get_source_text(cx) else {
return;
};
let Some(op) = bin_op_eq_str(op) else { return };
format!("{left} {op} {right}")
},
(ExprKind::Unary(UnOp::Not, left_sub), _) => {
let Some(left) = snippet_opt(cx, left_sub.span) else {
let Some(left) = left_sub.span.get_source_text(cx) else {
return;
};
let Some(right) = snippet_opt(cx, right.span) else {
let Some(right) = right.span.get_source_text(cx) else {
return;
};
let Some(op) = inverted_bin_op_eq_str(op) else { return };
format!("{left} {op} {right}")
},
(_, ExprKind::Unary(UnOp::Not, right_sub)) => {
let Some(left) = snippet_opt(cx, left.span) else { return };
let Some(right) = snippet_opt(cx, right_sub.span) else {
let Some(left) = left.span.get_source_text(cx) else {
return;
};
let Some(right) = right_sub.span.get_source_text(cx) else {
return;
};
let Some(op) = inverted_bin_op_eq_str(op) else { return };
@ -313,8 +315,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
self.output.push_str(&str);
} else {
self.output.push('!');
let snip = snippet_opt(self.cx, terminal.span)?;
self.output.push_str(&snip);
self.output.push_str(&terminal.span.get_source_text(self.cx)?);
}
},
True | False | Not(_) => {
@ -345,8 +346,12 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
}
},
&Term(n) => {
let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?;
self.output.push_str(&snip);
self.output.push_str(
&self.terminals[n as usize]
.span
.source_callsite()
.get_source_text(self.cx)?,
);
},
}
Some(())
@ -370,8 +375,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
_ => None,
}
.and_then(|op| {
let lhs_snippet = snippet_opt(cx, lhs.span)?;
let rhs_snippet = snippet_opt(cx, rhs.span)?;
let lhs_snippet = lhs.span.get_source_text(cx)?;
let rhs_snippet = rhs.span.get_source_text(cx)?;
if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) {
@ -399,7 +404,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
let path: &str = path.ident.name.as_str();
a == path
})
.and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?)))
.and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?)))
},
_ => None,
}

View file

@ -1,6 +1,6 @@
use crate::reference::DEREF_ADDROF;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed};
use rustc_errors::Applicability;
@ -73,6 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
}
})
&& !is_from_proc_macro(cx, e)
&& let Some(deref_text) = deref_target.span.get_source_text(cx)
{
span_lint_and_then(
cx,
@ -83,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
diag.span_suggestion(
e.span,
"if you would like to reborrow, try removing `&*`",
snippet_opt(cx, deref_target.span).unwrap(),
deref_text.as_str(),
Applicability::MachineApplicable,
);
@ -98,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
diag.span_suggestion(
e.span,
"if you would like to deref, try using `&**`",
format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()),
format!("&**{deref_text}"),
Applicability::MaybeIncorrect,
);
},

View file

@ -1,5 +1,3 @@
//! lint on missing cargo common metadata
use cargo_metadata::Metadata;
use clippy_utils::diagnostics::span_lint;
use rustc_lint::LateContext;

View file

@ -205,7 +205,7 @@ declare_clippy_lint! {
}
pub struct Cargo {
allowed_duplicate_crates: &'static FxHashSet<String>,
allowed_duplicate_crates: FxHashSet<String>,
ignore_publish: bool,
}
@ -221,7 +221,7 @@ impl_lint_pass!(Cargo => [
impl Cargo {
pub fn new(conf: &'static Conf) -> Self {
Self {
allowed_duplicate_crates: &conf.allowed_duplicate_crates,
allowed_duplicate_crates: conf.allowed_duplicate_crates.iter().cloned().collect(),
ignore_publish: conf.cargo_ignore_publish,
}
}
@ -263,7 +263,7 @@ impl LateLintPass<'_> for Cargo {
{
match MetadataCommand::new().exec() {
Ok(metadata) => {
multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates);
multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates);
},
Err(e) => {
for lint in WITH_DEPS_LINTS {

View file

@ -1,5 +1,3 @@
//! lint on multiple versions of a crate being used
use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
use clippy_utils::diagnostics::span_lint;
use itertools::Itertools;

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
&& let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
&& let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
&& let Some(recv) = snippet_opt(cx, receiver.span)
&& let Some(recv) = receiver.span.get_source_text(cx)
{
// `as_mut_ptr` might not exist
let applicability = Applicability::MaybeIncorrect;

View file

@ -1,7 +1,7 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_in_const_context;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_isize_or_usize;
use rustc_errors::Applicability;
@ -34,7 +34,7 @@ pub(super) fn check(
diag.help("an `as` cast can become silently lossy if the types change in the future");
let mut applicability = Applicability::MachineApplicable;
let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability);
let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else {
let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else {
return;
};
match cast_to_hir.kind {

View file

@ -4,7 +4,7 @@ use clippy_utils::expr_or_init;
use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
use rustc_errors::{Applicability, Diag, SuggestionStyle};
use rustc_errors::{Applicability, Diag};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
@ -190,12 +190,10 @@ fn offer_suggestion(
format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
};
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
expr.span,
"... or use `try_from` and handle the error accordingly",
suggestion,
Applicability::Unspecified,
// always show the suggestion in a separate line
SuggestionStyle::ShowAlways,
);
}

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_errors::Applicability;
use rustc_hir::Expr;
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
@ -24,12 +24,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
expr.span,
format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
|diag| {
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
expr.span,
"did you mean to invoke the function?",
format!("{from_snippet}() as {cast_to}"),
applicability,
SuggestionStyle::ShowAlways,
);
},
);

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::{snippet_opt, SpanRangeExt};
use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
use rustc_ast::{LitFloatType, LitIntType, LitKind};
@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
let literal_str = &cast_str;
if let LitKind::Int(n, _) = lit.node
&& let Some(src) = snippet_opt(cx, cast_expr.span)
&& let Some(src) = cast_expr.span.get_source_text(cx)
&& cast_to.is_floating_point()
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
&& let from_nbits = 128 - n.get().leading_zeros()
@ -131,7 +131,7 @@ pub(super) fn check<'tcx>(
| LitKind::Float(_, LitFloatType::Suffixed(_))
if cast_from.kind() == cast_to.kind() =>
{
if let Some(src) = snippet_opt(cx, cast_expr.span) {
if let Some(src) = cast_expr.span.get_source_text(cx) {
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
return true;
@ -253,7 +253,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
let res = cx.qpath_res(&qpath, expr.hir_id);
// Function call
if let Res::Def(DefKind::Fn, def_id) = res {
let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else {
let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else {
return ControlFlow::Continue(());
};
// This is the worst part of this entire function. This is the only way I know of to

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability, Ty, TyKind};
@ -20,7 +20,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>
let sugg = if let TyKind::Infer = mut_ty.ty.kind {
format!("{std_or_core}::{sugg_fn}()")
} else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
} else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
} else {
return;

View file

@ -5,7 +5,7 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#{cfg(not(test))]`)
/// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#[cfg(not(test))]`)
///
/// ### Why is this bad?
/// This may give the false impression that a codebase has 100% coverage, yet actually has untested code.

View file

@ -1,5 +1,3 @@
//! lint on manually implemented checked conversions that could be transformed into `try_from`
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;

View file

@ -1,5 +1,3 @@
//! calculate cognitive complexity and warn about overly complex functions
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::{IntoSpan, SpanRangeExt};

View file

@ -1,17 +1,3 @@
//! Checks for if expressions that contain only an if expression.
//!
//! For example, the lint would catch:
//!
//! ```rust,ignore
//! if x {
//! if y {
//! println!("Hello world");
//! }
//! }
//! ```
//!
//! This lint is **warn** by default
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
use clippy_utils::sugg::Sugg;

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
use clippy_utils::visitors::{for_each_expr, Visitable};
use clippy_utils::{get_enclosing_block, path_to_local_id};
use core::ops::ControlFlow;
@ -7,7 +7,6 @@ use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym;
use rustc_span::Symbol;
declare_clippy_lint! {
/// ### What it does
@ -44,24 +43,11 @@ declare_clippy_lint! {
}
declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
// Add `String` here when it is added to diagnostic items
static COLLECTIONS: [Symbol; 9] = [
sym::BTreeMap,
sym::BTreeSet,
sym::BinaryHeap,
sym::HashMap,
sym::HashSet,
sym::LinkedList,
sym::Option,
sym::Vec,
sym::VecDeque,
];
impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
// Look for local variables whose type is a container. Search surrounding block for read access.
if let PatKind::Binding(_, local_id, _, _) = local.pat.kind
&& match_acceptable_type(cx, local, &COLLECTIONS)
&& match_acceptable_type(cx, local)
&& let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
&& has_no_read_access(cx, local_id, enclosing_block)
{
@ -70,11 +56,22 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
}
}
fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[Symbol]) -> bool {
fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool {
let ty = cx.typeck_results().pat_ty(local.pat);
collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
// String type is a lang item but not a diagnostic item for now so we need a separate check
|| is_type_lang_item(cx, ty, LangItem::String)
matches!(
get_type_diagnostic_name(cx, ty),
Some(
sym::BTreeMap
| sym::BTreeSet
| sym::BinaryHeap
| sym::HashMap
| sym::HashSet
| sym::LinkedList
| sym::Option
| sym::Vec
| sym::VecDeque
)
) || is_type_lang_item(cx, ty, LangItem::String)
}
fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool {

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
@ -46,7 +46,7 @@ impl LateLintPass<'_> for CreateDir {
"calling `std::fs::create_dir` where there may be a better way",
|diag| {
let mut app = Applicability::MaybeIncorrect;
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
expr.span,
"consider calling `std::fs::create_dir_all` instead",
format!(
@ -54,7 +54,6 @@ impl LateLintPass<'_> for CreateDir {
snippet_with_applicability(cx, arg.span, "..", &mut app)
),
app,
SuggestionStyle::ShowAlways,
);
},
);

View file

@ -2,7 +2,7 @@
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
pub(crate) static LINTS: &[&crate::LintInfo] = &[
pub static LINTS: &[&crate::LintInfo] = &[
#[cfg(feature = "internal")]
crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO,
#[cfg(feature = "internal")]
@ -22,8 +22,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
#[cfg(feature = "internal")]
crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::metadata_collector::METADATA_COLLECTOR_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO,
@ -146,6 +144,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::doc::NEEDLESS_DOCTEST_MAIN_INFO,
crate::doc::SUSPICIOUS_DOC_COMMENTS_INFO,
crate::doc::TEST_ATTR_IN_DOCTEST_INFO,
crate::doc::TOO_LONG_FIRST_DOC_PARAGRAPH_INFO,
crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
crate::double_parens::DOUBLE_PARENS_INFO,
crate::drop_forget_ref::DROP_NON_DROP_INFO,

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use itertools::Itertools;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::{BytePos, Span};
use std::ops::Range;
@ -59,12 +59,11 @@ pub(super) fn check(
&& (doc_comment == "///" || doc_comment == "//!")
{
// suggest filling in a blank line
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
line_break_span.shrink_to_lo(),
"if this should be its own paragraph, add a blank doc comment line",
format!("\n{doc_comment}"),
Applicability::MaybeIncorrect,
SuggestionStyle::ShowAlways,
);
if ccount > 0 || blockquote_level > 0 {
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
@ -79,12 +78,11 @@ pub(super) fn check(
if ccount == 0 && blockquote_level == 0 {
// simpler suggestion style for indentation
let indent = list_indentation - lcount;
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
span.shrink_to_hi(),
"indent this line",
std::iter::repeat(" ").take(indent).join(""),
Applicability::MaybeIncorrect,
SuggestionStyle::ShowAlways,
);
diag.help("if this is supposed to be its own paragraph, add a blank line");
return;
@ -107,12 +105,11 @@ pub(super) fn check(
suggested.push_str(text);
}
}
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
span,
"add markers to start of line",
suggested,
Applicability::MachineApplicable,
SuggestionStyle::ShowAlways,
);
diag.help("if this not intended to be a quote at all, escape it with `\\>`");
});

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::{BytePos, Pos, Span};
use url::Url;
@ -92,6 +92,10 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
&& matches!(prefix.chars().last(), Some('S' | 'X'))
{
prefix
} else if let Some(prefix) = s.strip_suffix("ified")
&& prefix.chars().all(|c| c.is_ascii_uppercase())
{
prefix
} else {
s.strip_suffix('s').unwrap_or(s)
};
@ -133,24 +137,15 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
}
if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_then(
cx,
DOC_MARKDOWN,
span,
"item in documentation is missing backticks",
|diag| {
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
diag.span_suggestion_with_style(
span,
"try",
format!("`{snippet}`"),
applicability,
// always show the suggestion in a separate line, since the
// inline presentation adds another pair of backticks
SuggestionStyle::ShowAlways,
);
diag.span_suggestion_verbose(span, "try", format!("`{snippet}`"), applicability);
},
);
}

View file

@ -1,4 +1,6 @@
mod lazy_continuation;
mod too_long_first_doc_paragraph;
use clippy_config::Conf;
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
@ -309,7 +311,7 @@ declare_clippy_lint! {
/// ### Known problems
/// Inner doc comments can only appear before items, so there are certain cases where the suggestion
/// made by this lint is not valid code. For example:
/// ```rs
/// ```rust
/// fn foo() {}
/// ///!
/// fn bar() {}
@ -422,15 +424,47 @@ declare_clippy_lint! {
"require every line of a paragraph to be indented and marked"
}
declare_clippy_lint! {
/// ### What it does
/// Checks if the first line in the documentation of items listed in module page is too long.
///
/// ### Why is this bad?
/// Documentation will show the first paragraph of the doscstring in the summary page of a
/// module, so having a nice, short summary in the first paragraph is part of writing good docs.
///
/// ### Example
/// ```no_run
/// /// A very short summary.
/// /// A much longer explanation that goes into a lot more detail about
/// /// how the thing works, possibly with doclinks and so one,
/// /// and probably spanning a many rows.
/// struct Foo {}
/// ```
/// Use instead:
/// ```no_run
/// /// A very short summary.
/// ///
/// /// A much longer explanation that goes into a lot more detail about
/// /// how the thing works, possibly with doclinks and so one,
/// /// and probably spanning a many rows.
/// struct Foo {}
/// ```
#[clippy::version = "1.81.0"]
pub TOO_LONG_FIRST_DOC_PARAGRAPH,
style,
"ensure that the first line of a documentation paragraph isn't too long"
}
#[derive(Clone)]
pub struct Documentation {
valid_idents: &'static FxHashSet<String>,
valid_idents: FxHashSet<String>,
check_private_items: bool,
}
impl Documentation {
pub fn new(conf: &'static Conf) -> Self {
Self {
valid_idents: &conf.doc_valid_idents,
valid_idents: conf.doc_valid_idents.iter().cloned().collect(),
check_private_items: conf.check_private_items,
}
}
@ -448,18 +482,29 @@ impl_lint_pass!(Documentation => [
SUSPICIOUS_DOC_COMMENTS,
EMPTY_DOCS,
DOC_LAZY_CONTINUATION,
TOO_LONG_FIRST_DOC_PARAGRAPH,
]);
impl<'tcx> LateLintPass<'tcx> for Documentation {
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else {
let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
return;
};
match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {
Node::Item(item) => match item.kind {
Node::Item(item) => {
too_long_first_doc_paragraph::check(
cx,
item,
attrs,
headers.first_paragraph_len,
self.check_private_items,
);
match item.kind {
ItemKind::Fn(sig, _, body_id) => {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
|| in_external_macro(cx.tcx.sess, item.span))
{
let body = cx.tcx.hir().body(body_id);
let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
@ -490,6 +535,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
_ => (),
},
_ => (),
}
},
Node::TraitItem(trait_item) => {
if let TraitItemKind::Fn(sig, ..) = trait_item.kind
@ -547,6 +593,7 @@ struct DocHeaders {
safety: bool,
errors: bool,
panics: bool,
first_paragraph_len: usize,
}
/// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and
@ -653,6 +700,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
let mut paragraph_range = 0..0;
let mut code_level = 0;
let mut blockquote_level = 0;
let mut is_first_paragraph = true;
let mut containers = Vec::new();
@ -720,6 +768,10 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
}
ticks_unbalanced = false;
paragraph_range = range;
if is_first_paragraph {
headers.first_paragraph_len = doc[paragraph_range.clone()].chars().count();
is_first_paragraph = false;
}
},
End(TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::Item) => {
if let End(TagEnd::Heading(_)) = event {

View file

@ -0,0 +1,91 @@
use rustc_ast::ast::Attribute;
use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind};
use rustc_lint::LateContext;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_from_proc_macro;
use clippy_utils::source::snippet_opt;
use super::TOO_LONG_FIRST_DOC_PARAGRAPH;
pub(super) fn check(
cx: &LateContext<'_>,
item: &Item<'_>,
attrs: &[Attribute],
mut first_paragraph_len: usize,
check_private_items: bool,
) {
if !check_private_items && !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
return;
}
if first_paragraph_len <= 200
|| !matches!(
item.kind,
// This is the list of items which can be documented AND are displayed on the module
// page. So associated items or impl blocks are not part of this list.
ItemKind::Static(..)
| ItemKind::Const(..)
| ItemKind::Fn(..)
| ItemKind::Macro(..)
| ItemKind::Mod(..)
| ItemKind::TyAlias(..)
| ItemKind::Enum(..)
| ItemKind::Struct(..)
| ItemKind::Union(..)
| ItemKind::Trait(..)
| ItemKind::TraitAlias(..)
)
{
return;
}
let mut spans = Vec::new();
let mut should_suggest_empty_doc = false;
for attr in attrs {
if let Some(doc) = attr.doc_str() {
spans.push(attr.span);
let doc = doc.as_str();
let doc = doc.trim();
if spans.len() == 1 {
// We make this suggestion only if the first doc line ends with a punctuation
// because it might just need to add an empty line with `///`.
should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?');
}
let len = doc.chars().count();
if len >= first_paragraph_len {
break;
}
first_paragraph_len -= len;
}
}
let &[first_span, .., last_span] = spans.as_slice() else {
return;
};
if is_from_proc_macro(cx, item) {
return;
}
span_lint_and_then(
cx,
TOO_LONG_FIRST_DOC_PARAGRAPH,
first_span.with_hi(last_span.lo()),
"first doc comment paragraph is too long",
|diag| {
if should_suggest_empty_doc
&& let Some(second_span) = spans.get(1)
&& let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
&& let Some(snippet) = snippet_opt(cx, new_span)
{
diag.span_suggestion(
new_span,
"add an empty line",
format!("{snippet}///\n"),
Applicability::MachineApplicable,
);
}
},
);
}

View file

@ -1,5 +1,3 @@
//! Lint on if expressions with an else if, but without a final else branch.
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};

View file

@ -1,5 +1,3 @@
//! lint when there is an enum with no variants
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};

View file

@ -1,6 +1,3 @@
//! lint on C-like enums that are `repr(isize/usize)` and have values that
//! don't fit into an `i32`
use clippy_utils::consts::{mir_to_const, Constant};
use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Item, ItemKind};

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::type_diagnostic_name;
use clippy_utils::ty::get_type_diagnostic_name;
use clippy_utils::usage::{local_used_after_expr, local_used_in};
use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
use rustc_errors::Applicability;
@ -139,7 +139,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
{
let callee_ty_raw = typeck.expr_ty(callee);
let callee_ty = callee_ty_raw.peel_refs();
if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
|| !check_inputs(typeck, body.params, None, args)
{
return;

View file

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::numeric_literal;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, FloatTy};
@ -117,12 +117,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
if type_suffix.is_none() {
float_str.push_str(".0");
}
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
expr.span,
"consider changing the type or replacing it with",
numeric_literal::format(&float_str, type_suffix, true),
Applicability::MachineApplicable,
SuggestionStyle::ShowAlways,
);
},
);
@ -134,12 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
expr.span,
"float has excessive precision",
|diag| {
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
expr.span,
"consider changing the type or truncating it to",
numeric_literal::format(&float_str, type_suffix, true),
Applicability::MachineApplicable,
SuggestionStyle::ShowAlways,
);
},
);

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage};
use clippy_utils::source::{snippet_opt, snippet_with_context};
use clippy_utils::source::{snippet_with_context, SpanRangeExt};
use clippy_utils::sugg::Sugg;
use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
use rustc_errors::Applicability;
@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
([], [_]) => {
// Simulate macro expansion, converting {{ and }} to { and }.
let Some(snippet) = snippet_opt(cx, format_args.span) else {
let Some(snippet) = format_args.span.get_source_text(cx) else {
return;
};
let s_expand = snippet.replace("{{", "{").replace("}}", "}");
@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
span_useless_format(cx, call_site, sugg, applicability);
},
([arg], [piece]) => {
if let Ok(value) = find_format_arg_expr(expr, arg)
if let Some(value) = find_format_arg_expr(expr, arg)
&& let FormatArgsPiece::Placeholder(placeholder) = piece
&& placeholder.format_trait == FormatTrait::Display
&& placeholder.format_options == FormatOptions::default()

View file

@ -7,7 +7,7 @@ use clippy_utils::macros::{
find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
};
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::{implements_trait, is_type_lang_item};
use itertools::Itertools;
use rustc_ast::{
@ -224,13 +224,11 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
if let FormatArgsPiece::Placeholder(placeholder) = piece
&& let Ok(index) = placeholder.argument.index
&& let Some(arg) = self.format_args.arguments.all_args().get(index)
&& let Some(arg_expr) = find_format_arg_expr(self.expr, arg)
{
let arg_expr = find_format_arg_expr(self.expr, arg);
self.check_unused_format_specifier(placeholder, arg_expr);
if let Ok(arg_expr) = arg_expr
&& placeholder.format_trait == FormatTrait::Display
if placeholder.format_trait == FormatTrait::Display
&& placeholder.format_options == FormatOptions::default()
&& !self.is_aliased(index)
{
@ -242,28 +240,13 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
}
}
fn check_unused_format_specifier(
&self,
placeholder: &FormatPlaceholder,
arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
) {
let ty_or_ast_expr = arg_expr.map(|expr| self.cx.typeck_results().expr_ty(expr).peel_refs());
let is_format_args = match ty_or_ast_expr {
Ok(ty) => is_type_lang_item(self.cx, ty, LangItem::FormatArguments),
Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
};
fn check_unused_format_specifier(&self, placeholder: &FormatPlaceholder, arg: &Expr<'_>) {
let options = &placeholder.format_options;
let arg_span = match arg_expr {
Ok(expr) => expr.span,
Err(expr) => expr.span,
};
if let Some(placeholder_span) = placeholder.span
&& is_format_args
&& *options != FormatOptions::default()
&& let ty = self.cx.typeck_results().expr_ty(arg).peel_refs()
&& is_type_lang_item(self.cx, ty, LangItem::FormatArguments)
{
span_lint_and_then(
self.cx,
@ -274,7 +257,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
let mut suggest_format = |spec| {
let message = format!("for the {spec} to apply consider using `format!()`");
if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) {
if let Some(mac_call) = matching_root_macro_call(self.cx, arg.span, sym::format_args_macro) {
diag.span_suggestion(
self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
message,
@ -424,7 +407,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
&& implements_trait(cx, target, display_trait_id, &[])
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite())
&& let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx)
{
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
if n_needed_derefs == 0 && !needs_ref {

View file

@ -196,7 +196,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
&& trait_name == self.format_trait_impl.name
&& let Ok(index) = placeholder.argument.index
&& let Some(arg) = format_args.arguments.all_args().get(index)
&& let Ok(arg_expr) = find_format_arg_expr(self.expr, arg)
&& let Some(arg_expr) = find_format_arg_expr(self.expr, arg)
{
self.check_format_arg_self(arg_expr);
}

View file

@ -3,7 +3,7 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::span_is_local;
use clippy_utils::path_def_id;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_path, Visitor};
use rustc_hir::{
@ -178,8 +178,8 @@ fn convert_to_from(
return None;
};
let from = snippet_opt(cx, self_ty.span)?;
let into = snippet_opt(cx, target_ty.span)?;
let from = self_ty.span.get_source_text(cx)?;
let into = target_ty.span.get_source_text(cx)?;
let mut suggestions = vec![
// impl Into<T> for U -> impl From<T> for U
@ -187,10 +187,10 @@ fn convert_to_from(
(into_trait_seg.ident.span, String::from("From")),
// impl Into<T> for U -> impl Into<U> for U
// ~ ~
(target_ty.span, from.clone()),
(target_ty.span, from.to_owned()),
// impl Into<T> for U -> impl Into<T> for T
// ~ ~
(self_ty.span, into),
(self_ty.span, into.to_owned()),
// fn into(self) -> T -> fn from(self) -> T
// ~~~~ ~~~~
(impl_item.ident.span, String::from("from")),
@ -223,7 +223,7 @@ fn convert_to_from(
}
for span in finder.upper {
suggestions.push((span, from.clone()));
suggestions.push((span, from.to_owned()));
}
for span in finder.lower {
suggestions.push((span, String::from("val")));

View file

@ -22,8 +22,8 @@ declare_clippy_lint! {
///
/// ### Known problems
///
/// This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly
/// in some cases, which is correct but adds unnecessary complexity to the code.
/// This lint may suggest using `(&<expression>).parse()` instead of `<expression>.parse()`
/// directly in some cases, which is correct but adds unnecessary complexity to the code.
///
/// ### Example
/// ```ignore

View file

@ -1,6 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_in_test;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind};
@ -18,20 +19,18 @@ fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_
|diag| {
if let Some(gen_span) = generics.span_for_param_suggestion() {
// If there's already a generic param with the same bound, do not lint **this** suggestion.
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
gen_span,
"add a type parameter",
format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways,
Applicability::HasPlaceholders,
);
} else {
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
generics.span,
"add a type parameter",
format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders,
rustc_errors::SuggestionStyle::ShowAlways,
Applicability::HasPlaceholders,
);
}
},

View file

@ -8,11 +8,11 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_span::{sym, Span, Symbol};
use rustc_span::{sym, Span};
use clippy_utils::attrs::is_proc_macro;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_must_use_ty;
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{return_ty, trait_ref_of_method};
@ -129,9 +129,9 @@ fn check_needless_must_use(
cx,
DOUBLE_MUST_USE,
fn_header_span,
"this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
"this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`",
None,
"either add some descriptive text or remove the attribute",
"either add some descriptive message or remove the attribute",
);
}
}
@ -155,7 +155,7 @@ fn check_must_use_candidate<'tcx>(
return;
}
span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
if let Some(snippet) = snippet_opt(cx, fn_span) {
if let Some(snippet) = fn_span.get_source_text(cx) {
diag.span_suggestion(
fn_span,
"add the attribute",
@ -193,17 +193,13 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet)
}
}
static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
match *ty.kind() {
// primitive types are never mutable
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
ty::Adt(adt, args) => {
tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
|| KNOWN_WRAPPER_TYS
.iter()
.any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
|| matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc))
&& args.types().any(|ty| is_mutable_ty(cx, ty, tys))
},
ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)),

View file

@ -1,12 +1,11 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::SpanRangeExt;
use rustc_hir as hir;
use rustc_hir::intravisit::FnKind;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_span::Span;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::snippet_opt;
use super::TOO_MANY_LINES;
pub(super) fn check_fn(
@ -22,21 +21,19 @@ pub(super) fn check_fn(
return;
}
let Some(code_snippet) = snippet_opt(cx, body.value.span) else {
return;
};
let mut line_count: u64 = 0;
let too_many = body.value.span.check_source_text(cx, |src| {
let mut in_comment = false;
let mut code_in_line;
let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
&& code_snippet.as_bytes().first().copied() == Some(b'{')
&& code_snippet.as_bytes().last().copied() == Some(b'}')
&& src.as_bytes().first().copied() == Some(b'{')
&& src.as_bytes().last().copied() == Some(b'}')
{
// Removing the braces from the enclosing block
&code_snippet[1..code_snippet.len() - 1]
&src[1..src.len() - 1]
} else {
&code_snippet
src
}
.trim() // Remove leading and trailing blank lines
.lines();
@ -71,8 +68,10 @@ pub(super) fn check_fn(
line_count += 1;
}
}
line_count > too_many_lines_threshold
});
if line_count > too_many_lines_threshold {
if too_many {
span_lint(
cx,
TOO_MANY_LINES,

View file

@ -1,6 +1,3 @@
//! lint on if branches that could be swapped so no `!` operation is necessary
//! on the condition
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_else_clause;

View file

@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
use core::ops::ControlFlow;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -54,13 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
|diag| {
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, span, "..", &mut app);
diag.span_suggestion_with_style(
span,
"add `return` as shown",
format!("return {snip}"),
app,
SuggestionStyle::ShowAlways,
);
diag.span_suggestion_verbose(span, "add `return` as shown", format!("return {snip}"), app);
},
);
}
@ -75,12 +69,11 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp
|diag| {
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
break_span,
"change `break` to `return` as shown",
format!("return {snip}"),
app,
SuggestionStyle::ShowAlways,
);
},
);

View file

@ -7,8 +7,7 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::TyCtxt;
use rustc_semver::RustcVersion;
use rustc_session::impl_lint_pass;
use rustc_session::{impl_lint_pass, RustcVersion};
use rustc_span::def_id::DefId;
use rustc_span::{ExpnKind, Span};
@ -65,18 +64,18 @@ impl IncompatibleMsrv {
StabilityLevel::Stable {
since: StableSince::Version(version),
..
} => Some(RustcVersion::new(
version.major.into(),
version.minor.into(),
version.patch.into(),
)),
} => Some(version),
_ => None,
}) {
version
} else if let Some(parent_def_id) = tcx.opt_parent(def_id) {
self.get_def_id_version(tcx, parent_def_id)
} else {
RustcVersion::new(1, 0, 0)
RustcVersion {
major: 1,
minor: 0,
patch: 0,
}
};
self.is_above_msrv.insert(def_id, version);
version

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::fulfill_or_allowed;
use clippy_utils::source::snippet;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
@ -71,6 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
&& let ty = cx.typeck_results().expr_ty(expr)
&& let Some(adt_def) = ty.ty_adt_def()
&& adt_def.is_struct()
&& let Some(local_def_id) = adt_def.did().as_local()
&& let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
&& let Some(variant) = adt_def.variants().iter().next()
{
let mut def_order_map = FxHashMap::default();
@ -103,6 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
snippet(cx, qpath.span(), ".."),
);
if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) {
span_lint_and_sugg(
cx,
INCONSISTENT_STRUCT_CONSTRUCTOR,
@ -114,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
);
}
}
}
}
// Check whether the order of the fields in the constructor is consistent with the order in the

View file

@ -1,5 +1,3 @@
//! lint on indexing and slicing operations
use clippy_config::Conf;
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};

View file

@ -1,10 +1,10 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::higher;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
@ -210,18 +210,6 @@ const COMPLETING_METHODS: [(&str, usize); 12] = [
("product", 0),
];
/// the paths of types that are known to be infinitely allocating
const INFINITE_COLLECTORS: &[Symbol] = &[
sym::BinaryHeap,
sym::BTreeMap,
sym::BTreeSet,
sym::HashMap,
sym::HashSet,
sym::LinkedList,
sym::Vec,
sym::VecDeque,
];
fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
match expr.kind {
ExprKind::MethodCall(method, receiver, args, _) => {
@ -248,10 +236,19 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
}
} else if method.ident.name == sym!(collect) {
let ty = cx.typeck_results().expr_ty(expr);
if INFINITE_COLLECTORS
.iter()
.any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
{
if matches!(
get_type_diagnostic_name(cx, ty),
Some(
sym::BinaryHeap
| sym::BTreeMap
| sym::BTreeSet
| sym::HashMap
| sym::HashSet
| sym::LinkedList
| sym::Vec
| sym::VecDeque,
)
) {
return is_infinite(cx, receiver);
}
}

View file

@ -1,5 +1,3 @@
//! lint on inherent implementations
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_lint_allowed;
use rustc_data_structures::fx::FxHashMap;

View file

@ -1,5 +1,3 @@
//! checks for `#[inline]` on trait methods without bodies
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::sugg::DiagExt;
use rustc_errors::Applicability;

View file

@ -1,7 +1,5 @@
//! lint on blocks unnecessarily using >= with a + 1 or - 1
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind};
use rustc_ast::token;
use rustc_errors::Applicability;
@ -132,8 +130,8 @@ impl IntPlusOne {
BinOpKind::Le => "<",
_ => return None,
};
if let Some(snippet) = snippet_opt(cx, node.span) {
if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
if let Some(snippet) = node.span.get_source_text(cx) {
if let Some(other_side_snippet) = other_side.span.get_source_text(cx) {
let rec = match side {
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),

View file

@ -1,5 +1,3 @@
//! lint on enum variants that are prefixed or suffixed by the same characters
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
use clippy_utils::is_bool;

View file

@ -1,5 +1,3 @@
//! lint when items are used after statements
use clippy_utils::diagnostics::span_lint_hir;
use rustc_hir::{Block, ItemKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro};
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::{HirId, Item, ItemKind, Mod};
@ -93,11 +93,14 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
if let Some(prev) = mod_pos.checked_sub(1)
&& let prev = cx.tcx.hir().item(module.item_ids[prev])
&& let items_span = last.span.with_lo(test_mod.span.hi())
&& let Some(items) = snippet_opt(cx, items_span)
&& let Some(items) = items_span.get_source_text(cx)
{
diag.multipart_suggestion_with_style(
"move the items to before the test module was defined",
vec![(prev.span.shrink_to_hi(), items), (items_span, String::new())],
vec![
(prev.span.shrink_to_hi(), items.to_owned()),
(items_span, String::new()),
],
Applicability::MachineApplicable,
SuggestionStyle::HideCodeAlways,
);

View file

@ -1,5 +1,3 @@
//! lint when there is a large size difference between variants on an enum
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability;

View file

@ -3,7 +3,7 @@ use std::{fmt, ops};
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::fn_has_unsatisfiable_preds;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl};
@ -186,7 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
// TODO: Is there a cleaner, robust way to ask this question?
// The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data",
// and that doesn't get us the true name in scope rather than the span text either.
if let Some(name) = snippet_opt(cx, local_span)
if let Some(name) = local_span.get_source_text(cx)
&& is_ident(&name)
{
// If the local is an ordinary named variable,

View file

@ -3,7 +3,7 @@ use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::{get_parent_expr, is_from_proc_macro};
use hir::def_id::DefId;
use rustc_errors::{Applicability, SuggestionStyle};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -143,12 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
&& !is_from_proc_macro(cx, expr)
{
span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
span,
"use the associated constant instead",
sugg,
Applicability::MaybeIncorrect,
SuggestionStyle::ShowAlways,
);
});
}

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet_opt, snippet_with_context};
use clippy_utils::source::{snippet_with_context, SpanRangeExt};
use clippy_utils::sugg::{has_enclosing_paren, Sugg};
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
use rustc_ast::ast::LitKind;
@ -216,7 +216,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
}
fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span {
let Some(snippet) = snippet_opt(cx, span) else {
let Some(snippet) = span.get_source_text(cx) else {
return span;
};
if has_enclosing_paren(snippet) {

View file

@ -66,8 +66,8 @@ extern crate declare_clippy_lint;
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils;
mod declared_lints;
mod deprecated_lints;
pub mod declared_lints;
pub mod deprecated_lints;
// begin lints modules, do not remove this comment, its used in `update_lints`
mod absolute_paths;
@ -440,7 +440,7 @@ impl RegistrationGroups {
}
}
#[derive(Copy, Clone)]
#[derive(Copy, Clone, Debug)]
pub(crate) enum LintCategory {
Cargo,
Complexity,
@ -479,11 +479,39 @@ impl LintCategory {
}
}
pub(crate) struct LintInfo {
pub struct LintInfo {
/// Double reference to maintain pointer equality
lint: &'static &'static Lint,
pub lint: &'static &'static Lint,
category: LintCategory,
explanation: &'static str,
pub explanation: &'static str,
/// e.g. `clippy_lints/src/absolute_paths.rs#43`
pub location: &'static str,
pub version: Option<&'static str>,
}
impl LintInfo {
/// Returns the lint name in lowercase without the `clippy::` prefix
#[allow(clippy::missing_panics_doc)]
pub fn name_lower(&self) -> String {
self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
}
/// Returns the name of the lint's category in lowercase (`style`, `pedantic`)
pub fn category_str(&self) -> &'static str {
match self.category {
Cargo => "cargo",
Complexity => "complexity",
Correctness => "correctness",
Nursery => "nursery",
Pedantic => "pedantic",
Perf => "perf",
Restriction => "restriction",
Style => "style",
Suspicious => "suspicious",
#[cfg(feature = "internal")]
Internal => "internal",
}
}
}
pub fn explain(name: &str) -> i32 {
@ -538,14 +566,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
store.register_removed(name, reason);
}
#[cfg(feature = "internal")]
{
if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
return;
}
}
let format_args_storage = FormatArgsStorage::default();
let format_args = format_args_storage.clone();
store.register_early_pass(move || {

View file

@ -1,10 +1,7 @@
//! Lints concerned with the grouping of digits with underscores in integral or
//! floating-point literal expressions.
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::numeric_literal::{NumericLiteral, Radix};
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_ast::ast::{Expr, ExprKind, LitKind};
use rustc_ast::token;
use rustc_errors::Applicability;
@ -228,7 +225,7 @@ impl LiteralDigitGrouping {
}
fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
if let Some(src) = snippet_opt(cx, span)
if let Some(src) = span.get_source_text(cx)
&& let Ok(lit_kind) = LitKind::from_token_lit(lit)
&& let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
{
@ -442,7 +439,7 @@ impl DecimalLiteralRepresentation {
// Lint integral literals.
if let Ok(lit_kind) = LitKind::from_token_lit(lit)
&& let LitKind::Int(val, _) = lit_kind
&& let Some(src) = snippet_opt(cx, span)
&& let Some(src) = span.get_source_text(cx)
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
&& num_lit.radix == Radix::Decimal
&& val >= u128::from(self.threshold)

View file

@ -3,7 +3,7 @@ use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{
implements_trait, implements_trait_with_env, is_copy, make_normalized_projection,
implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection,
make_normalized_projection_with_regions, normalize_with_regions,
};
use rustc_errors::Applicability;
@ -20,9 +20,10 @@ pub(super) fn check(
msrv: &Msrv,
enforce_iter_loop_reborrow: bool,
) {
let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else {
let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow, msrv) else {
return;
};
if let ty::Array(_, count) = *ty.peel_refs().kind() {
if !ty.is_ref() {
if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
@ -109,6 +110,7 @@ fn is_ref_iterable<'tcx>(
self_arg: &Expr<'_>,
call_expr: &Expr<'_>,
enforce_iter_loop_reborrow: bool,
msrv: &Msrv,
) -> Option<(AdjustKind, Ty<'tcx>)> {
let typeck = cx.typeck_results();
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
@ -128,6 +130,12 @@ fn is_ref_iterable<'tcx>(
let self_ty = typeck.expr_ty(self_arg);
let self_is_copy = is_copy(cx, self_ty);
if !msrv.meets(msrvs::BOX_INTO_ITER)
&& is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox)
{
return None;
}
if adjustments.is_empty() && self_is_copy {
// Exact type match, already checked earlier
return Some((AdjustKind::None, self_ty));

View file

@ -122,8 +122,23 @@ struct BodyVisitor<'a, 'tcx> {
/// within a relevant macro.
macro_unsafe_blocks: Vec<HirId>,
/// When this is >0, it means that the node currently being visited is "within" a
/// macro definition. This is not necessary for correctness, it merely helps reduce the number
/// of spans we need to insert into the map, since only spans from macros are relevant.
/// macro definition.
/// This is used to detect if an expression represents a metavariable.
///
/// For example, the following pre-expansion code that we want to lint
/// ```ignore
/// macro_rules! m { ($e:expr) => { unsafe { $e; } } }
/// m!(1);
/// ```
/// would look like this post-expansion code:
/// ```ignore
/// unsafe { /* macro */
/// 1 /* root */; /* macro */
/// }
/// ```
/// Visiting the block and the statement will increment the `expn_depth` so that it is >0,
/// and visiting the expression with a root context while `expn_depth > 0` tells us
/// that it must be a metavariable.
expn_depth: u32,
cx: &'a LateContext<'tcx>,
lint: &'a mut ExprMetavarsInUnsafe,
@ -157,7 +172,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> {
&& (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id))
{
self.macro_unsafe_blocks.push(block.hir_id);
self.expn_depth += 1;
walk_block(self, block);
self.expn_depth -= 1;
self.macro_unsafe_blocks.pop();
} else if ctxt.is_root() && self.expn_depth > 0 {
let unsafe_block = self.macro_unsafe_blocks.last().copied();

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt};
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{
@ -68,8 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
header_span,
"this function can be simplified using the `async fn` syntax",
|diag| {
if let Some(vis_snip) = snippet_opt(cx, *vis_span)
&& let Some(header_snip) = snippet_opt(cx, header_span)
if let Some(vis_snip) = vis_span.get_source_text(cx)
&& let Some(header_snip) = header_span.get_source_text(cx)
&& let Some(ret_pos) = position_before_rarrow(&header_snip)
&& let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output)
{
@ -190,6 +190,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
Some((sugg, String::new()))
} else {
let sugg = "return the output of the future directly";
snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}")))
output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}")))
}
}

View file

@ -1,6 +1,6 @@
use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{is_from_proc_macro, path_to_local};
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Constness, Expr, ExprKind};
@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
// case somebody does that for some reason
&& (is_infinity(&const_1) && is_neg_infinity(&const_2)
|| is_neg_infinity(&const_1) && is_infinity(&const_2))
&& let Some(local_snippet) = snippet_opt(cx, first.span)
&& let Some(local_snippet) = first.span.get_source_text(cx)
{
let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite,

View file

@ -1,7 +1,7 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::visitors::{is_local_used, local_used_once};
use clippy_utils::{is_trait_method, path_to_local_id};
use rustc_errors::Applicability;
@ -107,8 +107,8 @@ impl LateLintPass<'_> for ManualHashOne {
finish_expr.span,
"manual implementation of `BuildHasher::hash_one`",
|diag| {
if let Some(build_hasher) = snippet_opt(cx, build_hasher.span)
&& let Some(hashed_value) = snippet_opt(cx, hashed_value.span)
if let Some(build_hasher) = build_hasher.span.get_source_text(cx)
&& let Some(hashed_value) = hashed_value.span.get_source_text(cx)
{
diag.multipart_suggestion(
"try",

View file

@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::is_doc_hidden;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_ast::ast::{self, VisibilityKind};
use rustc_ast::attr;
use rustc_data_structures::fx::FxHashSet;
@ -124,7 +124,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
|diag| {
if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
&& let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
&& let Some(snippet) = snippet_opt(cx, header_span)
&& let Some(snippet) = header_span.get_source_text(cx)
{
diag.span_suggestion(
header_span,
@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
"this seems like a manual implementation of the non-exhaustive pattern",
|diag| {
let header_span = cx.sess().source_map().span_until_char(enum_span, '{');
if let Some(snippet) = snippet_opt(cx, header_span) {
if let Some(snippet) = header_span.get_source_text(cx) {
diag.span_suggestion(
header_span,
"add the attribute",

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use rustc_ast::LitKind;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns {
pat.span,
"this OR pattern can be rewritten using a range",
|diag| {
if let Some(min) = snippet_opt(cx, min.span)
&& let Some(max) = snippet_opt(cx, max.span)
if let Some(min) = min.span.get_source_text(cx)
&& let Some(max) = max.span.get_source_text(cx)
{
diag.span_suggestion(
pat.span,

View file

@ -2,14 +2,13 @@ use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
use clippy_utils::{match_def_path, paths, SpanlessEq};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_hir::ExprKind::Assign;
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::impl_lint_pass;
use rustc_span::symbol::sym;
use rustc_span::Span;
@ -21,16 +20,6 @@ const ACCEPTABLE_METHODS: [&[&str]; 5] = [
&paths::SLICE_INTO,
&paths::VEC_DEQUE_ITER,
];
const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 7] = [
(sym::BinaryHeap, Some(msrvs::BINARY_HEAP_RETAIN)),
(sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
(sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
(sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
(sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)),
(sym::Vec, None),
(sym::VecDeque, None),
];
const MAP_TYPES: [rustc_span::Symbol; 2] = [sym::BTreeMap, sym::HashMap];
declare_clippy_lint! {
/// ### What it does
@ -265,16 +254,22 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo
}
fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
is_type_diagnostic_item(cx, expr_ty, *ty)
&& acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
})
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
let required = match get_type_diagnostic_name(cx, ty) {
Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN,
Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN,
Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN,
Some(sym::HashSet) => msrvs::HASH_SET_RETAIN,
Some(sym::HashMap) => msrvs::HASH_MAP_RETAIN,
Some(sym::Vec | sym::VecDeque) => return true,
_ => return false,
};
msrv.meets(required)
}
fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty))
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap))
}
fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) {

View file

@ -96,7 +96,7 @@ fn check_arm<'tcx>(
// collapsing patterns need an explicit field name in struct pattern matching
// ex: Struct {x: Some(1)}
let replace_msg = if is_innermost_parent_pat_struct {
format!(", prefixed by {}:", snippet(cx, binding_span, "their field name"))
format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name"))
} else {
String::new()
};

View file

@ -1,6 +1,6 @@
use clippy_utils::consts::ConstEvalCtxt;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::contains_return_break_continue_macro;
use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg};
@ -67,11 +67,11 @@ fn check_and_lint<'tcx>(
&& path_to_local_id(peel_blocks(then_expr), binding_hir_id)
&& cx.typeck_results().expr_adjustments(then_expr).is_empty()
&& let Some(ty_name) = find_type_name(cx, ty)
&& let Some(or_body_snippet) = snippet_opt(cx, else_expr.span)
&& let Some(or_body_snippet) = else_expr.span.get_source_text(cx)
&& let Some(indent) = indent_of(cx, expr.span)
&& ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some()
{
lint(cx, expr, let_expr, ty_name, or_body_snippet, indent);
lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent);
}
}
@ -110,7 +110,7 @@ fn lint<'tcx>(
expr: &Expr<'tcx>,
scrutinee: &'tcx Expr<'_>,
ty_name: &str,
or_body_snippet: String,
or_body_snippet: &str,
indent: usize,
) {
let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent));

View file

@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
/// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
/// match arms.
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
if let Some(ff) = span.get_source_text(cx)
if let Some(ff) = span.get_source_range(cx)
&& let Some(text) = ff.as_str()
{
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
use clippy_utils::ty::is_type_lang_item;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -49,10 +49,12 @@ pub(super) fn check<'tcx>(
"case-sensitive file extension comparison",
|diag| {
diag.help("consider using a case-insensitive comparison instead");
if let Some(mut recv_source) = snippet_opt(cx, recv.span) {
if !cx.typeck_results().expr_ty(recv).is_ref() {
recv_source = format!("&{recv_source}");
}
if let Some(recv_source) = recv.span.get_source_text(cx) {
let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() {
recv_source.to_owned()
} else {
format!("&{recv_source}")
};
let suggestion_source = reindent_multiline(
format!(

View file

@ -1,7 +1,7 @@
use super::FILTER_MAP_BOOL_THEN;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::paths::BOOL_THEN;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_copy;
use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks};
use rustc_errors::Applicability;
@ -42,9 +42,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
.iter()
.filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
.count()
&& let Some(param_snippet) = snippet_opt(cx, param.span)
&& let Some(filter) = snippet_opt(cx, recv.span)
&& let Some(map) = snippet_opt(cx, then_body.span)
&& let Some(param_snippet) = param.span.get_source_text(cx)
&& let Some(filter) = recv.span.get_source_text(cx)
&& let Some(map) = then_body.span.get_source_text(cx)
{
span_lint_and_sugg(
cx,

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
use clippy_utils::{is_path_diagnostic_item, sugg};
use rustc_errors::Applicability;
@ -39,7 +39,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) ->
}
let call_site = expr.span.source_callsite();
if let Some(snippet) = snippet_opt(cx, call_site)
if let Some(snippet) = call_site.get_source_text(cx)
&& let snippet_split = snippet.split("::").collect::<Vec<_>>()
&& let Some((_, elements)) = snippet_split.split_last()
{

View file

@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
"&"
};
diag.span_suggestion_with_style(
diag.span_suggestion_verbose(
span,
"using `[]` is clearer and more concise",
format!(
@ -82,7 +82,6 @@ pub(super) fn check<'tcx>(
snippet_with_applicability(cx, recv.span, "..", &mut applicability)
),
applicability,
rustc_errors::SuggestionStyle::ShowAlways,
);
},
);

View file

@ -1,5 +1,3 @@
//! Lint for `c.is_digit(10)`
use super::IS_DIGIT_ASCII_RADIX;
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{ConstEvalCtxt, FullInt};

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::expr_or_init;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a
join_arg.span,
"argument to `Path::join` starts with a path separator",
|diag| {
let arg_str = snippet_opt(cx, spanned.span).unwrap_or_else(|| "..".to_string());
let arg_str = snippet(cx, spanned.span, "..");
let no_separator = if sym_str.starts_with('/') {
arg_str.replacen('/', "", 1)

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
use rustc_errors::Applicability;
@ -23,11 +23,11 @@ pub(super) fn check<'tcx>(
&& let ExprKind::Call(err_path, [err_arg]) = or_expr.kind
&& is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr)
&& is_ok_wrapping(cx, map_expr)
&& let Some(recv_snippet) = snippet_opt(cx, recv.span)
&& let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span)
&& let Some(recv_snippet) = recv.span.get_source_text(cx)
&& let Some(err_arg_snippet) = err_arg.span.get_source_text(cx)
&& let Some(indent) = indent_of(cx, expr.span)
{
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str().into(), true, Some(indent + 4));
span_lint_and_sugg(
cx,
MANUAL_OK_OR,

View file

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
use clippy_utils::{is_from_proc_macro, is_trait_method};
use rustc_errors::Applicability;
@ -31,13 +31,15 @@ pub(super) fn check<'tcx>(
&& let Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id)
&& let ExprKind::Closure(closure) = acc.kind
&& !is_from_proc_macro(cx, expr)
&& let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| snippet_opt(cx, fn_arg_span))
&& let Some(args_snip) = closure
.fn_arg_span
.and_then(|fn_arg_span| fn_arg_span.get_source_text(cx))
{
let init_snip = rest
.is_empty()
.then_some(first.span)
.and_then(|span| snippet_opt(cx, span))
.unwrap_or("...".to_owned());
.and_then(|span| span.get_source_text(cx))
.map_or_else(|| "...".to_owned(), |src| src.to_owned());
span_lint_and_sugg(
cx,

View file

@ -7,7 +7,7 @@ use rustc_span::Span;
use super::utils::get_last_chain_binding_hir_id;
use super::NEEDLESS_CHARACTER_ITERATION;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
@ -35,7 +35,7 @@ fn handle_expr(
&& path_to_local_id(receiver, first_param)
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
&& *char_arg_ty.kind() == ty::Char
&& let Some(snippet) = snippet_opt(cx, before_chars)
&& let Some(snippet) = before_chars.get_source_text(cx)
{
span_lint_and_sugg(
cx,
@ -79,7 +79,7 @@ fn handle_expr(
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
&& match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
&& path_to_local_id(peels_expr_ref(arg), first_param)
&& let Some(snippet) = snippet_opt(cx, before_chars)
&& let Some(snippet) = before_chars.get_source_text(cx)
{
span_lint_and_sugg(
cx,

View file

@ -2,7 +2,7 @@ use super::NEEDLESS_COLLECT;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection};
use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection};
use clippy_utils::{
can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id,
CaptureKind,
@ -88,9 +88,10 @@ pub(super) fn check<'tcx>(
Node::LetStmt(l) => {
if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
&& let ty = cx.typeck_results().expr_ty(collect_expr)
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
.into_iter()
.any(|item| is_type_diagnostic_item(cx, ty, item))
&& matches!(
get_type_diagnostic_name(cx, ty),
Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList)
)
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr)
&& let Some(block) = get_enclosing_block(cx, l.hir_id)
&& let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::path_res;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::local_used_after_expr;
use rustc_errors::Applicability;
@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name
expr.span,
"derefed type is same as origin",
"try",
snippet_opt(cx, recv.span).unwrap(),
recv.span.get_source_text(cx).unwrap().to_owned(),
Applicability::MachineApplicable,
);
}

View file

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
use itertools::Itertools;
use rustc_ast::LitKind;
@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
_ => return,
}
&& !is_from_proc_macro(cx, expr)
&& let Some(scrutinee_snip) = snippet_opt(cx, scrutinee.span)
&& let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx)
{
// Normalize the char using `map` so `join` doesn't use `Display`, if we don't then
// something like `r"\"` will become `'\'`, which is of course invalid

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability;
@ -38,11 +38,11 @@ pub(super) fn check(
return;
};
let both_calls_span = get_call_span.with_hi(call_span.hi());
if let Some(snippet) = snippet_opt(cx, both_calls_span)
&& let Some(arg_snippet) = snippet_opt(cx, arg.span)
if let Some(snippet) = both_calls_span.get_source_text(cx)
&& let Some(arg_snippet) = arg.span.get_source_text(cx)
{
let generics_snippet = if let Some(generics) = path.args
&& let Some(generics_snippet) = snippet_opt(cx, generics.span_ext)
&& let Some(generics_snippet) = generics.span_ext.get_source_text(cx)
{
format!("::{generics_snippet}")
} else {
@ -63,7 +63,7 @@ pub(super) fn check(
suggestion,
Applicability::MaybeIncorrect,
);
} else if let Some(caller_snippet) = snippet_opt(cx, get_caller.span) {
} else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) {
let full_span = get_caller.span.with_hi(call_span.hi());
span_lint_and_then(

View file

@ -1,7 +1,7 @@
use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet_opt;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
@ -40,7 +40,7 @@ pub fn check_for_loop_iter(
&& let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent)
&& let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body)
&& !clone_or_copy_needed
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
&& let Some(receiver_snippet) = receiver.span.get_source_text(cx)
{
// Issue 12098
// https://github.com/rust-lang/rust-clippy/issues/12098
@ -100,7 +100,7 @@ pub fn check_for_loop_iter(
&& implements_trait(cx, collection_ty, into_iterator_trait_id, &[])
&& let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item")
&& iter_item_ty == into_iter_item_ty
&& let Some(collection_snippet) = snippet_opt(cx, collection.span)
&& let Some(collection_snippet) = collection.span.get_source_text(cx)
{
collection_snippet
} else {
@ -122,7 +122,7 @@ pub fn check_for_loop_iter(
} else {
Applicability::MachineApplicable
};
diag.span_suggestion(expr.span, "use", snippet, applicability);
diag.span_suggestion(expr.span, "use", snippet.to_owned(), applicability);
if !references_to_binding.is_empty() {
diag.multipart_suggestion(
"remove any references to the binding",

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