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] [alias]
bless = "test --config env.RUSTC_BLESS='1'"
uitest = "test --test compile-test" uitest = "test --test compile-test"
uibless = "test --test compile-test -- -- --bless" uibless = "bless --test compile-test"
bless = "test -- -- --bless"
dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --" 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 -- " 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] [build]
# -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests # -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/index.html out/master
cp util/gh-pages/script.js out/master cp util/gh-pages/script.js out/master
cp util/gh-pages/lints.json out/master cp util/gh-pages/lints.json out/master
cp util/gh-pages/style.css out/master
if [[ -n $TAG_NAME ]]; then if [[ -n $TAG_NAME ]]; then
echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it" 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 - name: Test metadata collection
run: cargo collect-metadata 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: integration_build:
needs: changelog needs: changelog
runs-on: ubuntu-latest 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_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 [`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 [`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_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 [`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 [`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" anstream = "0.6.0"
[dev-dependencies] [dev-dependencies]
cargo_metadata = "0.18.1"
ui_test = "0.25" ui_test = "0.25"
regex = "1.5.5" regex = "1.5.5"
serde = { version = "1.0.145", features = ["derive"] }
serde_json = "1.0.122"
toml = "0.7.3" toml = "0.7.3"
walkdir = "2.3" walkdir = "2.3"
filetime = "0.2.9" filetime = "0.2.9"
@ -41,7 +44,6 @@ itertools = "0.12"
clippy_utils = { path = "clippy_utils" } clippy_utils = { path = "clippy_utils" }
if_chain = "1.0" if_chain = "1.0"
quote = "1.0.25" quote = "1.0.25"
serde = { version = "1.0.145", features = ["derive"] }
syn = { version = "2.0", features = ["full"] } syn = { version = "2.0", features = ["full"] }
futures = "0.3" futures = "0.3"
parking_lot = "0.12" 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) 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_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 [`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. 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 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. 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:** **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 replace the default list with `["ClipPy"]`.
* `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list. * `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:** **Affected lints:**
@ -949,5 +949,3 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in
--- ---
**Affected lints:** **Affected lints:**
* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe) * [`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] [dependencies]
itertools = "0.12" itertools = "0.12"
rustc-semver = "1.1"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
toml = "0.7.3" toml = "0.7.3"

View file

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

View file

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

View file

@ -1,6 +1,6 @@
use rustc_ast::Attribute; use rustc_ast::Attribute;
use rustc_semver::RustcVersion; use rustc_attr::parse_version;
use rustc_session::Session; use rustc_session::{RustcVersion, Session};
use rustc_span::{sym, Symbol}; use rustc_span::{sym, Symbol};
use serde::Deserialize; use serde::Deserialize;
use std::fmt; use std::fmt;
@ -10,7 +10,7 @@ macro_rules! msrv_aliases {
$($name:ident),* $(,)? $($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 // names may refer to stabilized feature flags or library items
msrv_aliases! { msrv_aliases! {
1,81,0 { LINT_REASONS_STABILIZATION } 1,81,0 { LINT_REASONS_STABILIZATION }
1,80,0 { BOX_INTO_ITER}
1,77,0 { C_STR_LITERALS } 1,77,0 { C_STR_LITERALS }
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
@ -81,9 +82,9 @@ impl<'de> Deserialize<'de> for Msrv {
D: serde::Deserializer<'de>, D: serde::Deserializer<'de>,
{ {
let v = String::deserialize(deserializer)?; let v = String::deserialize(deserializer)?;
RustcVersion::parse(&v) parse_version(Symbol::intern(&v))
.map(|v| Msrv { stack: vec![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) { pub fn read_cargo(&mut self, sess: &Session) {
let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
.ok() .ok()
.and_then(|v| RustcVersion::parse(&v).ok()); .and_then(|v| parse_version(Symbol::intern(&v)));
match (self.current(), cargo_msrv) { match (self.current(), cargo_msrv) {
(None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv], (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv],
@ -115,7 +116,7 @@ impl Msrv {
} }
pub fn meets(&self, required: RustcVersion) -> bool { 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> { 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 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); 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 on lints, that are included in `rust-lang/rust`s bootstrap
#![warn(rust_2018_idioms, unused_lifetimes)] #![warn(rust_2018_idioms, unused_lifetimes)]

View file

@ -604,7 +604,7 @@ fn gen_declared_lints<'a>(
details.sort_unstable(); details.sort_unstable();
let mut output = GENERATED_FILE_COMMENT.to_string(); 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 { for (is_public, module_name, lint_name) in details {
if !is_public { if !is_public {

View file

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

View file

@ -1,6 +1,6 @@
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint; 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_data_structures::fx::FxHashSet;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; 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_lint::{LateContext, LateLintPass};
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::symbol::kw; use rustc_span::symbol::kw;
use rustc_span::Symbol;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### 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 /// 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. /// 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 /// ### Example
/// ```no_run /// ```no_run
/// let x = std::f64::consts::PI; /// let x = std::f64::consts::PI;
@ -48,56 +56,60 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
pub struct AbsolutePaths { pub struct AbsolutePaths {
pub absolute_paths_max_segments: u64, pub absolute_paths_max_segments: u64,
pub absolute_paths_allowed_crates: &'static FxHashSet<String>, pub absolute_paths_allowed_crates: FxHashSet<Symbol>,
} }
impl AbsolutePaths { impl AbsolutePaths {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
absolute_paths_max_segments: conf.absolute_paths_max_segments, 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 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 // 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` // a `Use`
#[expect(clippy::cast_possible_truncation)] fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, hir_id: HirId) {
fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) { let segments = match path.segments {
let Self { [] | [_] => return,
absolute_paths_max_segments, // Don't count enum variants and trait items as part of the length.
absolute_paths_allowed_crates, [rest @ .., _]
} = self; if let [.., s] = rest
&& matches!(s.res, Res::Def(DefKind::Enum | DefKind::Trait | DefKind::TraitAlias, _)) =>
if !path.span.from_expansion() {
&& let node = cx.tcx.hir_node(hir_id) rest
&& !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _))) },
&& let [first, rest @ ..] = path.segments path => path,
// Handle `::std` };
&& let (segment, len) = if first.ident.name == kw::PathRoot { if let [s1, s2, ..] = segments
// Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1` && let has_root = s1.ident.name == kw::PathRoot
// is fine here for the same reason && let first = if has_root { s2 } else { s1 }
(&rest[0], path.segments.len() - 1) && 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 { } 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; return;
} }
&& !path.span.from_expansion()
if is_abs_external || is_abs_crate { && 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( span_lint(
cx, cx,
ABSOLUTE_PATHS, 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_ast::ast::{FloatTy, LitFloatType, LitKind};
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion; use rustc_session::{impl_lint_pass, RustcVersion};
use rustc_session::impl_lint_pass;
use rustc_span::symbol; use rustc_span::symbol;
use std::f64::consts as f64; 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::diagnostics::span_lint_and_then;
use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap}; use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
use clippy_utils::sugg::Sugg; 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_errors::Applicability;
use rustc_hir::{self as hir, Expr, ExprKind}; use rustc_hir::{self as hir, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
} }
) )
&& !clone_source_borrows_from_dest(cx, lhs, rhs.span) && !clone_source_borrows_from_dest(cx, lhs, rhs.span)
&& !is_in_test(cx.tcx, e.hir_id)
{ {
span_lint_and_then( span_lint_and_then(
cx, cx,

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,7 @@
use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::utils::{extract_clippy_lint, is_lint_level, is_word};
use super::{Attribute, USELESS_ATTRIBUTE}; use super::{Attribute, USELESS_ATTRIBUTE};
use clippy_utils::diagnostics::span_lint_and_then; 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_ast::NestedMetaItem;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind}; 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); let line_span = first_line_of_span(cx, attr.span);
if let Some(mut sugg) = snippet_opt(cx, line_span) { if let Some(src) = line_span.get_source_text(cx) {
if sugg.contains("#[") { if src.contains("#[") {
#[expect(clippy::collapsible_span_lint_calls)]
span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| { span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
sugg = sugg.replacen("#[", "#![", 1);
diag.span_suggestion( diag.span_suggestion(
line_span, line_span,
"if you just forgot a `!`, use", "if you just forgot a `!`, use",
sugg, src.replacen("#[", "#![", 1),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
}); });

View file

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

View file

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

View file

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

View file

@ -205,7 +205,7 @@ declare_clippy_lint! {
} }
pub struct Cargo { pub struct Cargo {
allowed_duplicate_crates: &'static FxHashSet<String>, allowed_duplicate_crates: FxHashSet<String>,
ignore_publish: bool, ignore_publish: bool,
} }
@ -221,7 +221,7 @@ impl_lint_pass!(Cargo => [
impl Cargo { impl Cargo {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
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, ignore_publish: conf.cargo_ignore_publish,
} }
} }
@ -263,7 +263,7 @@ impl LateLintPass<'_> for Cargo {
{ {
match MetadataCommand::new().exec() { match MetadataCommand::new().exec() {
Ok(metadata) => { Ok(metadata) => {
multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates); multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates);
}, },
Err(e) => { Err(e) => {
for lint in WITH_DEPS_LINTS { 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 cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use itertools::Itertools; use itertools::Itertools;

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext; 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 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 Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
&& let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() && 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 // `as_mut_ptr` might not exist
let applicability = Applicability::MaybeIncorrect; let applicability = Applicability::MaybeIncorrect;

View file

@ -1,7 +1,7 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::is_in_const_context; 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::sugg::Sugg;
use clippy_utils::ty::is_isize_or_usize; use clippy_utils::ty::is_isize_or_usize;
use rustc_errors::Applicability; 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"); diag.help("an `as` cast can become silently lossy if the types change in the future");
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability); 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; return;
}; };
match cast_to_hir.kind { 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::source::snippet;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; 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::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -190,12 +190,10 @@ fn offer_suggestion(
format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, "..")) format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
}; };
diag.span_suggestion_with_style( diag.span_suggestion_verbose(
expr.span, expr.span,
"... or use `try_from` and handle the error accordingly", "... or use `try_from` and handle the error accordingly",
suggestion, suggestion,
Applicability::Unspecified, 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::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::Applicability;
use rustc_hir::Expr; use rustc_hir::Expr;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
@ -24,12 +24,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
expr.span, expr.span,
format!("casting function pointer `{from_snippet}` to `{cast_to}`"), format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
|diag| { |diag| {
diag.span_suggestion_with_style( diag.span_suggestion_verbose(
expr.span, expr.span,
"did you mean to invoke the function?", "did you mean to invoke the function?",
format!("{from_snippet}() as {cast_to}"), format!("{from_snippet}() as {cast_to}"),
applicability, applicability,
SuggestionStyle::ShowAlways,
); );
}, },
); );

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::NumericLiteral; 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::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 clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_ast::{LitFloatType, LitIntType, LitKind};
@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
let literal_str = &cast_str; let literal_str = &cast_str;
if let LitKind::Int(n, _) = lit.node 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() && cast_to.is_floating_point()
&& let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
&& let from_nbits = 128 - n.get().leading_zeros() && let from_nbits = 128 - n.get().leading_zeros()
@ -131,7 +131,7 @@ pub(super) fn check<'tcx>(
| LitKind::Float(_, LitFloatType::Suffixed(_)) | LitKind::Float(_, LitFloatType::Suffixed(_))
if cast_from.kind() == cast_to.kind() => 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) { if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
return true; 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); let res = cx.qpath_res(&qpath, expr.hir_id);
// Function call // Function call
if let Res::Def(DefKind::Fn, def_id) = res { 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(()); return ControlFlow::Continue(());
}; };
// This is the worst part of this entire function. This is the only way I know of to // 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::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 clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability, Ty, TyKind}; 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 { let sugg = if let TyKind::Infer = mut_ty.ty.kind {
format!("{std_or_core}::{sugg_fn}()") 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}>()") format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
} else { } else {
return; return;

View file

@ -5,7 +5,7 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### 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? /// ### Why is this bad?
/// This may give the false impression that a codebase has 100% coverage, yet actually has untested code. /// 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::msrvs::{self, Msrv};
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg; 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_config::Conf;
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::{IntoSpan, SpanRangeExt}; 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::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint; 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::visitors::{for_each_expr, Visitable};
use clippy_utils::{get_enclosing_block, path_to_local_id}; use clippy_utils::{get_enclosing_block, path_to_local_id};
use core::ops::ControlFlow; 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_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Symbol;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -44,24 +43,11 @@ declare_clippy_lint! {
} }
declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]); 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 { impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { 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. // Look for local variables whose type is a container. Search surrounding block for read access.
if let PatKind::Binding(_, local_id, _, _) = local.pat.kind 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) && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
&& has_no_read_access(cx, local_id, enclosing_block) && 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); let ty = cx.typeck_results().pat_ty(local.pat);
collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym)) matches!(
// String type is a lang item but not a diagnostic item for now so we need a separate check get_type_diagnostic_name(cx, ty),
|| is_type_lang_item(cx, ty, LangItem::String) 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 { 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::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; 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", "calling `std::fs::create_dir` where there may be a better way",
|diag| { |diag| {
let mut app = Applicability::MaybeIncorrect; let mut app = Applicability::MaybeIncorrect;
diag.span_suggestion_with_style( diag.span_suggestion_verbose(
expr.span, expr.span,
"consider calling `std::fs::create_dir_all` instead", "consider calling `std::fs::create_dir_all` instead",
format!( format!(
@ -54,7 +54,6 @@ impl LateLintPass<'_> for CreateDir {
snippet_with_applicability(cx, arg.span, "..", &mut app) snippet_with_applicability(cx, arg.span, "..", &mut app)
), ),
app, app,
SuggestionStyle::ShowAlways,
); );
}, },
); );

View file

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

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use itertools::Itertools; use itertools::Itertools;
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::Applicability;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::{BytePos, Span}; use rustc_span::{BytePos, Span};
use std::ops::Range; use std::ops::Range;
@ -59,12 +59,11 @@ pub(super) fn check(
&& (doc_comment == "///" || doc_comment == "//!") && (doc_comment == "///" || doc_comment == "//!")
{ {
// suggest filling in a blank line // suggest filling in a blank line
diag.span_suggestion_with_style( diag.span_suggestion_verbose(
line_break_span.shrink_to_lo(), line_break_span.shrink_to_lo(),
"if this should be its own paragraph, add a blank doc comment line", "if this should be its own paragraph, add a blank doc comment line",
format!("\n{doc_comment}"), format!("\n{doc_comment}"),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
SuggestionStyle::ShowAlways,
); );
if ccount > 0 || blockquote_level > 0 { if ccount > 0 || blockquote_level > 0 {
diag.help("if this not intended to be a quote at all, escape it with `\\>`"); 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 { if ccount == 0 && blockquote_level == 0 {
// simpler suggestion style for indentation // simpler suggestion style for indentation
let indent = list_indentation - lcount; let indent = list_indentation - lcount;
diag.span_suggestion_with_style( diag.span_suggestion_verbose(
span.shrink_to_hi(), span.shrink_to_hi(),
"indent this line", "indent this line",
std::iter::repeat(" ").take(indent).join(""), std::iter::repeat(" ").take(indent).join(""),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
SuggestionStyle::ShowAlways,
); );
diag.help("if this is supposed to be its own paragraph, add a blank line"); diag.help("if this is supposed to be its own paragraph, add a blank line");
return; return;
@ -107,12 +105,11 @@ pub(super) fn check(
suggested.push_str(text); suggested.push_str(text);
} }
} }
diag.span_suggestion_with_style( diag.span_suggestion_verbose(
span, span,
"add markers to start of line", "add markers to start of line",
suggested, suggested,
Applicability::MachineApplicable, Applicability::MachineApplicable,
SuggestionStyle::ShowAlways,
); );
diag.help("if this not intended to be a quote at all, escape it with `\\>`"); 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::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::Applicability;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::{BytePos, Pos, Span}; use rustc_span::{BytePos, Pos, Span};
use url::Url; 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')) && matches!(prefix.chars().last(), Some('S' | 'X'))
{ {
prefix prefix
} else if let Some(prefix) = s.strip_suffix("ified")
&& prefix.chars().all(|c| c.is_ascii_uppercase())
{
prefix
} else { } else {
s.strip_suffix('s').unwrap_or(s) 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("()") { if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_then( span_lint_and_then(
cx, cx,
DOC_MARKDOWN, DOC_MARKDOWN,
span, span,
"item in documentation is missing backticks", "item in documentation is missing backticks",
|diag| { |diag| {
let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, span, "..", &mut applicability); let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
diag.span_suggestion_with_style( diag.span_suggestion_verbose(span, "try", format!("`{snippet}`"), applicability);
span,
"try",
format!("`{snippet}`"),
applicability,
// always show the suggestion in a separate line, since the
// inline presentation adds another pair of backticks
SuggestionStyle::ShowAlways,
);
}, },
); );
} }

View file

@ -1,4 +1,6 @@
mod lazy_continuation; mod lazy_continuation;
mod too_long_first_doc_paragraph;
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::attrs::is_doc_hidden; use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
@ -309,7 +311,7 @@ declare_clippy_lint! {
/// ### Known problems /// ### Known problems
/// Inner doc comments can only appear before items, so there are certain cases where the suggestion /// 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: /// made by this lint is not valid code. For example:
/// ```rs /// ```rust
/// fn foo() {} /// fn foo() {}
/// ///! /// ///!
/// fn bar() {} /// fn bar() {}
@ -422,15 +424,47 @@ declare_clippy_lint! {
"require every line of a paragraph to be indented and marked" "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 { pub struct Documentation {
valid_idents: &'static FxHashSet<String>, valid_idents: FxHashSet<String>,
check_private_items: bool, check_private_items: bool,
} }
impl Documentation { impl Documentation {
pub fn new(conf: &'static Conf) -> Self { pub fn new(conf: &'static Conf) -> Self {
Self { Self {
valid_idents: &conf.doc_valid_idents, valid_idents: conf.doc_valid_idents.iter().cloned().collect(),
check_private_items: conf.check_private_items, check_private_items: conf.check_private_items,
} }
} }
@ -448,18 +482,29 @@ impl_lint_pass!(Documentation => [
SUSPICIOUS_DOC_COMMENTS, SUSPICIOUS_DOC_COMMENTS,
EMPTY_DOCS, EMPTY_DOCS,
DOC_LAZY_CONTINUATION, DOC_LAZY_CONTINUATION,
TOO_LONG_FIRST_DOC_PARAGRAPH,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Documentation { impl<'tcx> LateLintPass<'tcx> for Documentation {
fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) { 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; return;
}; };
match cx.tcx.hir_node(cx.last_node_with_lint_attrs) { 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) => { 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 body = cx.tcx.hir().body(body_id);
let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value); 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) => { Node::TraitItem(trait_item) => {
if let TraitItemKind::Fn(sig, ..) = trait_item.kind if let TraitItemKind::Fn(sig, ..) = trait_item.kind
@ -547,6 +593,7 @@ struct DocHeaders {
safety: bool, safety: bool,
errors: bool, errors: bool,
panics: bool, panics: bool,
first_paragraph_len: usize,
} }
/// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and /// 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 paragraph_range = 0..0;
let mut code_level = 0; let mut code_level = 0;
let mut blockquote_level = 0; let mut blockquote_level = 0;
let mut is_first_paragraph = true;
let mut containers = Vec::new(); 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; ticks_unbalanced = false;
paragraph_range = range; 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) => { End(TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::Item) => {
if let End(TagEnd::Heading(_)) = event { 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 clippy_utils::diagnostics::span_lint_and_then;
use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::ast::{Expr, ExprKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; 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 clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; 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::consts::{mir_to_const, Constant};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use rustc_hir::{Item, ItemKind}; 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::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::higher::VecArgs; use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt; 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::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 clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
use rustc_errors::Applicability; 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_raw = typeck.expr_ty(callee);
let callee_ty = callee_ty_raw.peel_refs(); 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) || !check_inputs(typeck, body.params, None, args)
{ {
return; return;

View file

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

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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::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 clippy_utils::sugg::Sugg;
use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
use rustc_errors::Applicability; 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), ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
([], [_]) => { ([], [_]) => {
// Simulate macro expansion, converting {{ and }} to { and }. // 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; return;
}; };
let s_expand = snippet.replace("{{", "{").replace("}}", "}"); let s_expand = snippet.replace("{{", "{").replace("}}", "}");
@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
span_useless_format(cx, call_site, sugg, applicability); span_useless_format(cx, call_site, sugg, applicability);
}, },
([arg], [piece]) => { ([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 && let FormatArgsPiece::Placeholder(placeholder) = piece
&& placeholder.format_trait == FormatTrait::Display && placeholder.format_trait == FormatTrait::Display
&& placeholder.format_options == FormatOptions::default() && 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, 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, 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 clippy_utils::ty::{implements_trait, is_type_lang_item};
use itertools::Itertools; use itertools::Itertools;
use rustc_ast::{ use rustc_ast::{
@ -224,13 +224,11 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
if let FormatArgsPiece::Placeholder(placeholder) = piece if let FormatArgsPiece::Placeholder(placeholder) = piece
&& let Ok(index) = placeholder.argument.index && let Ok(index) = placeholder.argument.index
&& let Some(arg) = self.format_args.arguments.all_args().get(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); self.check_unused_format_specifier(placeholder, arg_expr);
if let Ok(arg_expr) = arg_expr if placeholder.format_trait == FormatTrait::Display
&& placeholder.format_trait == FormatTrait::Display
&& placeholder.format_options == FormatOptions::default() && placeholder.format_options == FormatOptions::default()
&& !self.is_aliased(index) && !self.is_aliased(index)
{ {
@ -242,28 +240,13 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
} }
} }
fn check_unused_format_specifier( fn check_unused_format_specifier(&self, placeholder: &FormatPlaceholder, arg: &Expr<'_>) {
&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(_)),
};
let options = &placeholder.format_options; 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 if let Some(placeholder_span) = placeholder.span
&& is_format_args
&& *options != FormatOptions::default() && *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( span_lint_and_then(
self.cx, self.cx,
@ -274,7 +257,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
let mut suggest_format = |spec| { let mut suggest_format = |spec| {
let message = format!("for the {spec} to apply consider using `format!()`"); 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( diag.span_suggestion(
self.cx.sess().source_map().span_until_char(mac_call.span, '!'), self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
message, message,
@ -424,7 +407,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
&& implements_trait(cx, target, display_trait_id, &[]) && implements_trait(cx, target, display_trait_id, &[])
&& let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() && 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, &[]); let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
if n_needed_derefs == 0 && !needs_ref { 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 && trait_name == self.format_trait_impl.name
&& let Ok(index) = placeholder.argument.index && let Ok(index) = placeholder.argument.index
&& let Some(arg) = format_args.arguments.all_args().get(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); 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::diagnostics::span_lint_and_then;
use clippy_utils::macros::span_is_local; use clippy_utils::macros::span_is_local;
use clippy_utils::path_def_id; use clippy_utils::path_def_id;
use clippy_utils::source::snippet_opt; use clippy_utils::source::SpanRangeExt;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_path, Visitor}; use rustc_hir::intravisit::{walk_path, Visitor};
use rustc_hir::{ use rustc_hir::{
@ -178,8 +178,8 @@ fn convert_to_from(
return None; return None;
}; };
let from = snippet_opt(cx, self_ty.span)?; let from = self_ty.span.get_source_text(cx)?;
let into = snippet_opt(cx, target_ty.span)?; let into = target_ty.span.get_source_text(cx)?;
let mut suggestions = vec![ let mut suggestions = vec![
// impl Into<T> for U -> impl From<T> for U // 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")), (into_trait_seg.ident.span, String::from("From")),
// impl Into<T> for U -> impl Into<U> for U // 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 // 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 // fn into(self) -> T -> fn from(self) -> T
// ~~~~ ~~~~ // ~~~~ ~~~~
(impl_item.ident.span, String::from("from")), (impl_item.ident.span, String::from("from")),
@ -223,7 +223,7 @@ fn convert_to_from(
} }
for span in finder.upper { for span in finder.upper {
suggestions.push((span, from.clone())); suggestions.push((span, from.to_owned()));
} }
for span in finder.lower { for span in finder.lower {
suggestions.push((span, String::from("val"))); suggestions.push((span, String::from("val")));

View file

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

View file

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

View file

@ -8,11 +8,11 @@ use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty}; 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::attrs::is_proc_macro;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; 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::ty::is_must_use_ty;
use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::visitors::for_each_expr_without_closures;
use clippy_utils::{return_ty, trait_ref_of_method}; use clippy_utils::{return_ty, trait_ref_of_method};
@ -129,9 +129,9 @@ fn check_needless_must_use(
cx, cx,
DOUBLE_MUST_USE, DOUBLE_MUST_USE,
fn_header_span, 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, 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; return;
} }
span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| { 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( diag.span_suggestion(
fn_span, fn_span,
"add the attribute", "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 { fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
match *ty.kind() { match *ty.kind() {
// primitive types are never mutable // primitive types are never mutable
ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false, ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
ty::Adt(adt, args) => { ty::Adt(adt, args) => {
tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env) tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
|| KNOWN_WRAPPER_TYS || matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc))
.iter()
.any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
&& args.types().any(|ty| is_mutable_ty(cx, ty, tys)) && args.types().any(|ty| is_mutable_ty(cx, ty, tys))
}, },
ty::Tuple(args) => args.iter().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 as hir;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_span::Span; use rustc_span::Span;
use clippy_utils::diagnostics::span_lint;
use clippy_utils::source::snippet_opt;
use super::TOO_MANY_LINES; use super::TOO_MANY_LINES;
pub(super) fn check_fn( pub(super) fn check_fn(
@ -22,21 +21,19 @@ pub(super) fn check_fn(
return; return;
} }
let Some(code_snippet) = snippet_opt(cx, body.value.span) else {
return;
};
let mut line_count: u64 = 0; let mut line_count: u64 = 0;
let too_many = body.value.span.check_source_text(cx, |src| {
let mut in_comment = false; let mut in_comment = false;
let mut code_in_line; let mut code_in_line;
let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..)) let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
&& code_snippet.as_bytes().first().copied() == Some(b'{') && src.as_bytes().first().copied() == Some(b'{')
&& code_snippet.as_bytes().last().copied() == Some(b'}') && src.as_bytes().last().copied() == Some(b'}')
{ {
// Removing the braces from the enclosing block // Removing the braces from the enclosing block
&code_snippet[1..code_snippet.len() - 1] &src[1..src.len() - 1]
} else { } else {
&code_snippet src
} }
.trim() // Remove leading and trailing blank lines .trim() // Remove leading and trailing blank lines
.lines(); .lines();
@ -71,8 +68,10 @@ pub(super) fn check_fn(
line_count += 1; line_count += 1;
} }
} }
line_count > too_many_lines_threshold
});
if line_count > too_many_lines_threshold { if too_many {
span_lint( span_lint(
cx, cx,
TOO_MANY_LINES, 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::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::is_else_clause; 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::visitors::for_each_expr_without_closures;
use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro}; use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId}; use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -54,13 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
|diag| { |diag| {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let snip = snippet_with_applicability(cx, span, "..", &mut app); let snip = snippet_with_applicability(cx, span, "..", &mut app);
diag.span_suggestion_with_style( diag.span_suggestion_verbose(span, "add `return` as shown", format!("return {snip}"), app);
span,
"add `return` as shown",
format!("return {snip}"),
app,
SuggestionStyle::ShowAlways,
);
}, },
); );
} }
@ -75,12 +69,11 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp
|diag| { |diag| {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0; 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, break_span,
"change `break` to `return` as shown", "change `break` to `return` as shown",
format!("return {snip}"), format!("return {snip}"),
app, app,
SuggestionStyle::ShowAlways,
); );
}, },
); );

View file

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

View file

@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::fulfill_or_allowed;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -71,6 +72,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
&& let ty = cx.typeck_results().expr_ty(expr) && let ty = cx.typeck_results().expr_ty(expr)
&& let Some(adt_def) = ty.ty_adt_def() && let Some(adt_def) = ty.ty_adt_def()
&& adt_def.is_struct() && 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 Some(variant) = adt_def.variants().iter().next()
{ {
let mut def_order_map = FxHashMap::default(); let mut def_order_map = FxHashMap::default();
@ -103,6 +106,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
snippet(cx, qpath.span(), ".."), snippet(cx, qpath.span(), ".."),
); );
if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
INCONSISTENT_STRUCT_CONSTRUCTOR, 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 // 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_config::Conf;
use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; 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::diagnostics::span_lint;
use clippy_utils::higher; 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_hir::{BorrowKind, Closure, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass; use rustc_session::declare_lint_pass;
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -210,18 +210,6 @@ const COMPLETING_METHODS: [(&str, usize); 12] = [
("product", 0), ("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 { fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
match expr.kind { match expr.kind {
ExprKind::MethodCall(method, receiver, args, _) => { 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) { } else if method.ident.name == sym!(collect) {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
if INFINITE_COLLECTORS if matches!(
.iter() get_type_diagnostic_name(cx, ty),
.any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item)) Some(
{ sym::BinaryHeap
| sym::BTreeMap
| sym::BTreeSet
| sym::HashMap
| sym::HashSet
| sym::LinkedList
| sym::Vec
| sym::VecDeque,
)
) {
return is_infinite(cx, receiver); 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::diagnostics::span_lint_and_then;
use clippy_utils::is_lint_allowed; use clippy_utils::is_lint_allowed;
use rustc_data_structures::fx::FxHashMap; 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::diagnostics::span_lint_and_then;
use clippy_utils::sugg::DiagExt; use clippy_utils::sugg::DiagExt;
use rustc_errors::Applicability; 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::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::ast::{BinOpKind, Expr, ExprKind, LitKind};
use rustc_ast::token; use rustc_ast::token;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -132,8 +130,8 @@ impl IntPlusOne {
BinOpKind::Le => "<", BinOpKind::Le => "<",
_ => return None, _ => return None,
}; };
if let Some(snippet) = snippet_opt(cx, node.span) { if let Some(snippet) = node.span.get_source_text(cx) {
if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) { if let Some(other_side_snippet) = other_side.span.get_source_text(cx) {
let rec = match side { let rec = match side {
Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")), Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {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_config::Conf;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
use clippy_utils::is_bool; 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 clippy_utils::diagnostics::span_lint_hir;
use rustc_hir::{Block, ItemKind, StmtKind}; use rustc_hir::{Block, ItemKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};

View file

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

View file

@ -3,7 +3,7 @@ use std::{fmt, ops};
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::fn_has_unsatisfiable_preds; 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::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl}; 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? // TODO: Is there a cleaner, robust way to ask this question?
// The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", // 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. // 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) && is_ident(&name)
{ {
// If the local is an ordinary named variable, // 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::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::{get_parent_expr, is_from_proc_macro}; use clippy_utils::{get_parent_expr, is_from_proc_macro};
use hir::def_id::DefId; use hir::def_id::DefId;
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind}; use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -143,12 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
&& !is_from_proc_macro(cx, expr) && !is_from_proc_macro(cx, expr)
{ {
span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| { 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, span,
"use the associated constant instead", "use the associated constant instead",
sugg, sugg,
Applicability::MaybeIncorrect, 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::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::sugg::{has_enclosing_paren, Sugg};
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
use rustc_ast::ast::LitKind; 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 { 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; return span;
}; };
if has_enclosing_paren(snippet) { 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))] #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils; mod utils;
mod declared_lints; pub mod declared_lints;
mod deprecated_lints; pub mod deprecated_lints;
// begin lints modules, do not remove this comment, its used in `update_lints` // begin lints modules, do not remove this comment, its used in `update_lints`
mod absolute_paths; mod absolute_paths;
@ -440,7 +440,7 @@ impl RegistrationGroups {
} }
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone, Debug)]
pub(crate) enum LintCategory { pub(crate) enum LintCategory {
Cargo, Cargo,
Complexity, Complexity,
@ -479,11 +479,39 @@ impl LintCategory {
} }
} }
pub(crate) struct LintInfo { pub struct LintInfo {
/// Double reference to maintain pointer equality /// Double reference to maintain pointer equality
lint: &'static &'static Lint, pub lint: &'static &'static Lint,
category: LintCategory, 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 { 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); 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_storage = FormatArgsStorage::default();
let format_args = format_args_storage.clone(); let format_args = format_args_storage.clone();
store.register_early_pass(move || { 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_config::Conf;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::numeric_literal::{NumericLiteral, Radix}; 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::ast::{Expr, ExprKind, LitKind};
use rustc_ast::token; use rustc_ast::token;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -228,7 +225,7 @@ impl LiteralDigitGrouping {
} }
fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { 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 Ok(lit_kind) = LitKind::from_token_lit(lit)
&& let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
{ {
@ -442,7 +439,7 @@ impl DecimalLiteralRepresentation {
// Lint integral literals. // Lint integral literals.
if let Ok(lit_kind) = LitKind::from_token_lit(lit) if let Ok(lit_kind) = LitKind::from_token_lit(lit)
&& let LitKind::Int(val, _) = lit_kind && 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) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
&& num_lit.radix == Radix::Decimal && num_lit.radix == Radix::Decimal
&& val >= u128::from(self.threshold) && 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::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{ 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, make_normalized_projection_with_regions, normalize_with_regions,
}; };
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -20,9 +20,10 @@ pub(super) fn check(
msrv: &Msrv, msrv: &Msrv,
enforce_iter_loop_reborrow: bool, 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; return;
}; };
if let ty::Array(_, count) = *ty.peel_refs().kind() { if let ty::Array(_, count) = *ty.peel_refs().kind() {
if !ty.is_ref() { if !ty.is_ref() {
if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
@ -109,6 +110,7 @@ fn is_ref_iterable<'tcx>(
self_arg: &Expr<'_>, self_arg: &Expr<'_>,
call_expr: &Expr<'_>, call_expr: &Expr<'_>,
enforce_iter_loop_reborrow: bool, enforce_iter_loop_reborrow: bool,
msrv: &Msrv,
) -> Option<(AdjustKind, Ty<'tcx>)> { ) -> Option<(AdjustKind, Ty<'tcx>)> {
let typeck = cx.typeck_results(); let typeck = cx.typeck_results();
if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator) 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_ty = typeck.expr_ty(self_arg);
let self_is_copy = is_copy(cx, self_ty); 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 { if adjustments.is_empty() && self_is_copy {
// Exact type match, already checked earlier // Exact type match, already checked earlier
return Some((AdjustKind::None, self_ty)); return Some((AdjustKind::None, self_ty));

View file

@ -122,8 +122,23 @@ struct BodyVisitor<'a, 'tcx> {
/// within a relevant macro. /// within a relevant macro.
macro_unsafe_blocks: Vec<HirId>, macro_unsafe_blocks: Vec<HirId>,
/// When this is >0, it means that the node currently being visited is "within" a /// 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 /// macro definition.
/// of spans we need to insert into the map, since only spans from macros are relevant. /// 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, expn_depth: u32,
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
lint: &'a mut ExprMetavarsInUnsafe, 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.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.macro_unsafe_blocks.push(block.hir_id);
self.expn_depth += 1;
walk_block(self, block); walk_block(self, block);
self.expn_depth -= 1;
self.macro_unsafe_blocks.pop(); self.macro_unsafe_blocks.pop();
} else if ctxt.is_root() && self.expn_depth > 0 { } else if ctxt.is_root() && self.expn_depth > 0 {
let unsafe_block = self.macro_unsafe_blocks.last().copied(); 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::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_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
@ -68,8 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
header_span, header_span,
"this function can be simplified using the `async fn` syntax", "this function can be simplified using the `async fn` syntax",
|diag| { |diag| {
if let Some(vis_snip) = snippet_opt(cx, *vis_span) if let Some(vis_snip) = vis_span.get_source_text(cx)
&& let Some(header_snip) = snippet_opt(cx, header_span) && let Some(header_snip) = header_span.get_source_text(cx)
&& let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some(ret_pos) = position_before_rarrow(&header_snip)
&& let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output) && 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())) Some((sugg, String::new()))
} else { } else {
let sugg = "return the output of the future directly"; 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::consts::{ConstEvalCtxt, Constant};
use clippy_utils::diagnostics::span_lint_and_then; 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 clippy_utils::{is_from_proc_macro, path_to_local};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Constness, Expr, ExprKind}; use rustc_hir::{BinOpKind, Constness, Expr, ExprKind};
@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
// case somebody does that for some reason // case somebody does that for some reason
&& (is_infinity(&const_1) && is_neg_infinity(&const_2) && (is_infinity(&const_1) && is_neg_infinity(&const_2)
|| is_neg_infinity(&const_1) && is_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) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
(BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite,

View file

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

View file

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

View file

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

View file

@ -2,14 +2,13 @@ use clippy_config::msrvs::{self, Msrv};
use clippy_config::Conf; use clippy_config::Conf;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; 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 clippy_utils::{match_def_path, paths, SpanlessEq};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::ExprKind::Assign; use rustc_hir::ExprKind::Assign;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Span; use rustc_span::Span;
@ -21,16 +20,6 @@ const ACCEPTABLE_METHODS: [&[&str]; 5] = [
&paths::SLICE_INTO, &paths::SLICE_INTO,
&paths::VEC_DEQUE_ITER, &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! { declare_clippy_lint! {
/// ### What it does /// ### 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 { fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); let ty = cx.typeck_results().expr_ty(expr).peel_refs();
ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| { let required = match get_type_diagnostic_name(cx, ty) {
is_type_diagnostic_item(cx, expr_ty, *ty) Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN,
&& acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv)) 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 { fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); let ty = cx.typeck_results().expr_ty(expr).peel_refs();
MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty)) matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap))
} }
fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) { 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 // collapsing patterns need an explicit field name in struct pattern matching
// ex: Struct {x: Some(1)} // ex: Struct {x: Some(1)}
let replace_msg = if is_innermost_parent_pat_struct { 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 { } else {
String::new() String::new()
}; };

View file

@ -1,6 +1,6 @@
use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::consts::ConstEvalCtxt;
use clippy_utils::diagnostics::span_lint_and_sugg; 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::ty::is_type_diagnostic_item;
use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::usage::contains_return_break_continue_macro;
use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; 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) && path_to_local_id(peel_blocks(then_expr), binding_hir_id)
&& cx.typeck_results().expr_adjustments(then_expr).is_empty() && cx.typeck_results().expr_adjustments(then_expr).is_empty()
&& let Some(ty_name) = find_type_name(cx, ty) && 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) && let Some(indent) = indent_of(cx, expr.span)
&& ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some() && 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>, expr: &Expr<'tcx>,
scrutinee: &'tcx Expr<'_>, scrutinee: &'tcx Expr<'_>,
ty_name: &str, ty_name: &str,
or_body_snippet: String, or_body_snippet: &str,
indent: usize, indent: usize,
) { ) {
let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent)); 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 /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
/// match arms. /// match arms.
fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { 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() && let Some(text) = ff.as_str()
{ {
text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*") 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::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 clippy_utils::ty::is_type_lang_item;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -49,10 +49,12 @@ pub(super) fn check<'tcx>(
"case-sensitive file extension comparison", "case-sensitive file extension comparison",
|diag| { |diag| {
diag.help("consider using a case-insensitive comparison instead"); diag.help("consider using a case-insensitive comparison instead");
if let Some(mut recv_source) = snippet_opt(cx, recv.span) { if let Some(recv_source) = recv.span.get_source_text(cx) {
if !cx.typeck_results().expr_ty(recv).is_ref() { let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() {
recv_source = format!("&{recv_source}"); recv_source.to_owned()
} } else {
format!("&{recv_source}")
};
let suggestion_source = reindent_multiline( let suggestion_source = reindent_multiline(
format!( format!(

View file

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

View file

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; 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::ty::implements_trait;
use clippy_utils::{is_path_diagnostic_item, sugg}; use clippy_utils::{is_path_diagnostic_item, sugg};
use rustc_errors::Applicability; 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(); 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 snippet_split = snippet.split("::").collect::<Vec<_>>()
&& let Some((_, elements)) = snippet_split.split_last() && 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, span,
"using `[]` is clearer and more concise", "using `[]` is clearer and more concise",
format!( format!(
@ -82,7 +82,6 @@ pub(super) fn check<'tcx>(
snippet_with_applicability(cx, recv.span, "..", &mut applicability) snippet_with_applicability(cx, recv.span, "..", &mut applicability)
), ),
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 super::IS_DIGIT_ASCII_RADIX;
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::consts::{ConstEvalCtxt, FullInt};

View file

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::expr_or_init; 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 clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; 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, join_arg.span,
"argument to `Path::join` starts with a path separator", "argument to `Path::join` starts with a path separator",
|diag| { |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('/') { let no_separator = if sym_str.starts_with('/') {
arg_str.replacen('/', "", 1) arg_str.replacen('/', "", 1)

View file

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

View file

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg; 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::ty::implements_trait;
use clippy_utils::{is_from_proc_macro, is_trait_method}; use clippy_utils::{is_from_proc_macro, is_trait_method};
use rustc_errors::Applicability; 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 Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id)
&& let ExprKind::Closure(closure) = acc.kind && let ExprKind::Closure(closure) = acc.kind
&& !is_from_proc_macro(cx, expr) && !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 let init_snip = rest
.is_empty() .is_empty()
.then_some(first.span) .then_some(first.span)
.and_then(|span| snippet_opt(cx, span)) .and_then(|span| span.get_source_text(cx))
.unwrap_or("...".to_owned()); .map_or_else(|| "...".to_owned(), |src| src.to_owned());
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,

View file

@ -7,7 +7,7 @@ use rustc_span::Span;
use super::utils::get_last_chain_binding_hir_id; use super::utils::get_last_chain_binding_hir_id;
use super::NEEDLESS_CHARACTER_ITERATION; use super::NEEDLESS_CHARACTER_ITERATION;
use clippy_utils::diagnostics::span_lint_and_sugg; 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}; 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> { 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) && path_to_local_id(receiver, first_param)
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
&& *char_arg_ty.kind() == ty::Char && *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( span_lint_and_sugg(
cx, cx,
@ -79,7 +79,7 @@ fn handle_expr(
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id() && 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"]) && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
&& path_to_local_id(peels_expr_ref(arg), first_param) && 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( span_lint_and_sugg(
cx, 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::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg; 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::{ 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, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id,
CaptureKind, CaptureKind,
@ -88,9 +88,10 @@ pub(super) fn check<'tcx>(
Node::LetStmt(l) => { Node::LetStmt(l) => {
if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
&& let ty = cx.typeck_results().expr_ty(collect_expr) && let ty = cx.typeck_results().expr_ty(collect_expr)
&& [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList] && matches!(
.into_iter() get_type_diagnostic_name(cx, ty),
.any(|item| is_type_diagnostic_item(cx, ty, item)) Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList)
)
&& let iter_ty = cx.typeck_results().expr_ty(iter_expr) && let iter_ty = cx.typeck_results().expr_ty(iter_expr)
&& let Some(block) = get_enclosing_block(cx, l.hir_id) && 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)) && 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::diagnostics::span_lint_and_sugg;
use clippy_utils::path_res; 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::ty::is_type_diagnostic_item;
use clippy_utils::usage::local_used_after_expr; use clippy_utils::usage::local_used_after_expr;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name
expr.span, expr.span,
"derefed type is same as origin", "derefed type is same as origin",
"try", "try",
snippet_opt(cx, recv.span).unwrap(), recv.span.get_source_text(cx).unwrap().to_owned(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

View file

@ -1,6 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then; 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 clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
use itertools::Itertools; use itertools::Itertools;
use rustc_ast::LitKind; use rustc_ast::LitKind;
@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
_ => return, _ => return,
} }
&& !is_from_proc_macro(cx, expr) && !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 // 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 // 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::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 clippy_utils::ty::is_type_diagnostic_item;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -38,11 +38,11 @@ pub(super) fn check(
return; return;
}; };
let both_calls_span = get_call_span.with_hi(call_span.hi()); let both_calls_span = get_call_span.with_hi(call_span.hi());
if let Some(snippet) = snippet_opt(cx, both_calls_span) if let Some(snippet) = both_calls_span.get_source_text(cx)
&& let Some(arg_snippet) = snippet_opt(cx, arg.span) && let Some(arg_snippet) = arg.span.get_source_text(cx)
{ {
let generics_snippet = if let Some(generics) = path.args 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}") format!("::{generics_snippet}")
} else { } else {
@ -63,7 +63,7 @@ pub(super) fn check(
suggestion, suggestion,
Applicability::MaybeIncorrect, 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()); let full_span = get_caller.span.with_hi(call_span.hi());
span_lint_and_then( span_lint_and_then(

View file

@ -1,7 +1,7 @@
use super::utils::clone_or_copy_needed; use super::utils::clone_or_copy_needed;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::ForLoop; 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::ty::{get_iterator_item_ty, implements_trait};
use clippy_utils::visitors::for_each_expr_without_closures; 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}; 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 Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent)
&& let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body) && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body)
&& !clone_or_copy_needed && !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 // Issue 12098
// https://github.com/rust-lang/rust-clippy/issues/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, &[]) && 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") && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item")
&& iter_item_ty == into_iter_item_ty && 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 collection_snippet
} else { } else {
@ -122,7 +122,7 @@ pub fn check_for_loop_iter(
} else { } else {
Applicability::MachineApplicable 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() { if !references_to_binding.is_empty() {
diag.multipart_suggestion( diag.multipart_suggestion(
"remove any references to the binding", "remove any references to the binding",

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