Auto merge of #101249 - matthiaskrgr:rollup-wahnoz8, r=matthiaskrgr

Rollup of 10 pull requests

Successful merges:

 - #100787 (Pretty printing give proper error message without panic)
 - #100838 (Suggest moving redundant generic args of an assoc fn to its trait)
 - #100844 (migrate rustc_query_system to use SessionDiagnostic)
 - #101140 (Update Clippy)
 - #101161 (Fix uintended diagnostic caused by `drain(..)`)
 - #101165 (Use more `into_iter` rather than `drain(..)`)
 - #101229 (Link “? operator” to relevant chapter in The Book)
 - #101230 (lint: avoid linting diag functions with diag lints)
 - #101236 (Avoid needless buffer zeroing in `std::sys::windows::fs`)
 - #101240 (Fix a typo on `wasm64-unknown-unknown` doc)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2022-08-31 21:45:18 +00:00
commit db00199d99
280 changed files with 10192 additions and 4919 deletions

View File

@ -727,6 +727,7 @@ version = "0.1.65"
dependencies = [
"arrayvec",
"if_chain",
"itertools",
"rustc-semver",
]

View File

@ -555,7 +555,7 @@ impl TokenStreamBuilder {
// Get the first stream, which will become the result stream.
// If it's `None`, create an empty stream.
let mut iter = streams.drain(..);
let mut iter = streams.into_iter();
let mut res_stream_lrc = iter.next().unwrap().0;
// Append the subsequent elements to the result stream, after

View File

@ -164,7 +164,7 @@ impl<K: Ord, V> SortedMap<K, V> {
/// It is up to the caller to make sure that the elements are sorted by key
/// and that there are no duplicates.
#[inline]
pub fn insert_presorted(&mut self, mut elements: Vec<(K, V)>) {
pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) {
if elements.is_empty() {
return;
}
@ -173,28 +173,28 @@ impl<K: Ord, V> SortedMap<K, V> {
let start_index = self.lookup_index_for(&elements[0].0);
let drain = match start_index {
let elements = match start_index {
Ok(index) => {
let mut drain = elements.drain(..);
self.data[index] = drain.next().unwrap();
drain
let mut elements = elements.into_iter();
self.data[index] = elements.next().unwrap();
elements
}
Err(index) => {
if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 {
// We can copy the whole range without having to mix with
// existing elements.
self.data.splice(index..index, elements.drain(..));
self.data.splice(index..index, elements.into_iter());
return;
}
let mut drain = elements.drain(..);
self.data.insert(index, drain.next().unwrap());
drain
let mut elements = elements.into_iter();
self.data.insert(index, elements.next().unwrap());
elements
}
};
// Insert the rest
for (k, v) in drain {
for (k, v) in elements {
self.insert(k, v);
}
}

View File

@ -1,5 +1,6 @@
//! The various pretty-printing routines.
use crate::session_diagnostics::UnprettyDumpFail;
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
use rustc_errors::ErrorGuaranteed;
@ -357,12 +358,15 @@ fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
(src, src_name)
}
fn write_or_print(out: &str, ofile: Option<&Path>) {
fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) {
match ofile {
None => print!("{}", out),
Some(p) => {
if let Err(e) = std::fs::write(p, out) {
panic!("print-print failed to write {} due to {}", p.display(), e);
sess.emit_fatal(UnprettyDumpFail {
path: p.display().to_string(),
err: e.to_string(),
});
}
}
}
@ -402,7 +406,7 @@ pub fn print_after_parsing(
_ => unreachable!(),
};
write_or_print(&out, ofile);
write_or_print(&out, ofile, sess);
}
pub fn print_after_hir_lowering<'tcx>(
@ -468,7 +472,7 @@ pub fn print_after_hir_lowering<'tcx>(
_ => unreachable!(),
};
write_or_print(&out, ofile);
write_or_print(&out, ofile, tcx.sess);
}
// In an ideal world, this would be a public function called by the driver after
@ -512,7 +516,7 @@ fn print_with_analysis(
_ => unreachable!(),
};
write_or_print(&out, ofile);
write_or_print(&out, ofile, tcx.sess);
Ok(())
}

View File

@ -31,3 +31,10 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> {
#[derive(SessionDiagnostic)]
#[diag(driver::rlink_no_a_file)]
pub(crate) struct RlinkNotAFile;
#[derive(SessionDiagnostic)]
#[diag(driver::unpretty_dump_fail)]
pub(crate) struct UnprettyDumpFail {
pub path: String,
pub err: String,
}

View File

@ -9,3 +9,5 @@ driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding
driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
driver_rlink_no_a_file = rlink must be a file
driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`

View File

@ -0,0 +1,25 @@
query_system_reentrant = internal compiler error: re-entrant incremental verify failure, suppressing message
query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node}
.help = This is a known issue with the compiler. Run {$run_cmd} to allow your project to compile
query_system_increment_compilation_note1 = Please follow the instructions below to create a bug report with the provided information
query_system_increment_compilation_note2 = See <https://github.com/rust-lang/rust/issues/84970> for more information
query_system_cycle = cycle detected when {$stack_bottom}
query_system_cycle_usage = cycle used when {$usage}
query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again
query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle
query_system_cycle_recursive_ty_alias = type aliases cannot be recursive
query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle
query_system_cycle_recursive_ty_alias_help2 = see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information
query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive
query_system_cycle_which_requires = ...which requires {$desc}...
query_system_query_overflow = queries overflow the depth limit!

View File

@ -50,6 +50,7 @@ fluent_messages! {
passes => "../locales/en-US/passes.ftl",
plugin_impl => "../locales/en-US/plugin_impl.ftl",
privacy => "../locales/en-US/privacy.ftl",
query_system => "../locales/en-US/query_system.ftl",
save_analysis => "../locales/en-US/save_analysis.ftl",
ty_utils => "../locales/en-US/ty_utils.ftl",
typeck => "../locales/en-US/typeck.ftl",

View File

@ -974,12 +974,12 @@ impl Diagnostic {
fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
&mut self,
level: Level,
mut message: Vec<(M, Style)>,
message: Vec<(M, Style)>,
span: MultiSpan,
render_span: Option<MultiSpan>,
) {
let message = message
.drain(..)
.into_iter()
.map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
.collect();
let sub = SubDiagnostic { level, message, span, render_span };

View File

@ -21,7 +21,7 @@ pub trait Translate {
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
/// passed around as a reference thereafter.
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
FromIterator::from_iter(args.to_vec().drain(..))
FromIterator::from_iter(args.iter().cloned())
}
/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.

View File

@ -393,8 +393,14 @@ impl LateLintPass<'_> for Diagnostics {
return;
}
let mut found_parent_with_attr = false;
let mut found_impl = false;
for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
if let Some(owner_did) = hir_id.as_owner() {
found_parent_with_attr = found_parent_with_attr
|| cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics);
}
debug!(?parent);
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
let Impl { of_trait: Some(of_trait), .. } = impl_ &&
@ -407,7 +413,7 @@ impl LateLintPass<'_> for Diagnostics {
}
}
debug!(?found_impl);
if !found_impl {
if !found_parent_with_attr && !found_impl {
cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
lint.build(fluent::lint::diag_out_of_impl).emit();
})
@ -425,7 +431,7 @@ impl LateLintPass<'_> for Diagnostics {
}
}
debug!(?found_diagnostic_message);
if !found_diagnostic_message {
if !found_parent_with_attr && !found_diagnostic_message {
cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
lint.build(fluent::lint::untranslatable_diag).emit();
})

View File

@ -239,7 +239,7 @@ impl DiagnosticDeriveBuilder {
}
}
Ok(tokens.drain(..).collect())
Ok(tokens.into_iter().collect())
}
fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream {

View File

@ -12,7 +12,7 @@ use quote::{format_ident, quote};
use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
use synstructure::{BindingInfo, Structure, VariantInfo};
/// Which kind of suggestion is being created?
@ -28,41 +28,8 @@ enum SubdiagnosticSuggestionKind {
Verbose,
}
impl FromStr for SubdiagnosticSuggestionKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"" => Ok(SubdiagnosticSuggestionKind::Normal),
"_short" => Ok(SubdiagnosticSuggestionKind::Short),
"_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden),
"_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose),
_ => Err(()),
}
}
}
impl SubdiagnosticSuggestionKind {
pub fn to_suggestion_style(&self) -> TokenStream {
match self {
SubdiagnosticSuggestionKind::Normal => {
quote! { rustc_errors::SuggestionStyle::ShowCode }
}
SubdiagnosticSuggestionKind::Short => {
quote! { rustc_errors::SuggestionStyle::HideCodeInline }
}
SubdiagnosticSuggestionKind::Hidden => {
quote! { rustc_errors::SuggestionStyle::HideCodeAlways }
}
SubdiagnosticSuggestionKind::Verbose => {
quote! { rustc_errors::SuggestionStyle::ShowAlways }
}
}
}
}
/// Which kind of subdiagnostic is being created from a variant?
#[derive(Clone)]
#[derive(Clone, Copy)]
enum SubdiagnosticKind {
/// `#[label(...)]`
Label,
@ -73,9 +40,31 @@ enum SubdiagnosticKind {
/// `#[warning(...)]`
Warn,
/// `#[suggestion{,_short,_hidden,_verbose}]`
Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream },
/// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind },
Suggestion(SubdiagnosticSuggestionKind),
}
impl FromStr for SubdiagnosticKind {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"label" => Ok(SubdiagnosticKind::Label),
"note" => Ok(SubdiagnosticKind::Note),
"help" => Ok(SubdiagnosticKind::Help),
"warning" => Ok(SubdiagnosticKind::Warn),
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
"suggestion_short" => {
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
}
"suggestion_hidden" => {
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden))
}
"suggestion_verbose" => {
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose))
}
_ => Err(()),
}
}
}
impl quote::IdentFragment for SubdiagnosticKind {
@ -85,9 +74,17 @@ impl quote::IdentFragment for SubdiagnosticKind {
SubdiagnosticKind::Note => write!(f, "note"),
SubdiagnosticKind::Help => write!(f, "help"),
SubdiagnosticKind::Warn => write!(f, "warn"),
SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"),
SubdiagnosticKind::MultipartSuggestion { .. } => {
write!(f, "multipart_suggestion_with_style")
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
write!(f, "suggestion")
}
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => {
write!(f, "suggestion_short")
}
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => {
write!(f, "suggestion_hidden")
}
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => {
write!(f, "suggestion_verbose")
}
}
}
@ -151,9 +148,11 @@ impl<'a> SessionSubdiagnosticDerive<'a> {
variant,
span,
fields: fields_map,
kinds: Vec::new(),
slugs: Vec::new(),
code: None,
span_field: None,
applicability: None,
has_suggestion_parts: false,
};
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
});
@ -194,15 +193,21 @@ struct SessionSubdiagnosticDeriveBuilder<'a> {
/// derive builder.
fields: HashMap<String, TokenStream>,
/// Subdiagnostic kind of the type/variant.
kinds: Vec<(SubdiagnosticKind, proc_macro::Span)>,
/// Slugs of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
/// `#[kind(slug)]` attribute on the type or variant.
slugs: Vec<(Path, proc_macro::Span)>,
/// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
/// attribute on the type or variant.
code: Option<(TokenStream, proc_macro::Span)>,
/// Identifier for the binding to the `#[primary_span]` field.
span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
/// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
/// `rustc_errors::Applicability::*` variant directly.
applicability: Option<(TokenStream, proc_macro::Span)>,
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
/// during finalization if still `false`.
has_suggestion_parts: bool,
}
impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
@ -212,133 +217,124 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
}
impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
fn identify_kind(
&mut self,
) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
let mut kind_slug = None;
for attr in self.variant.ast().attrs {
fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> {
for (i, attr) in self.variant.ast().attrs.iter().enumerate() {
let span = attr.span().unwrap();
let name = attr.path.segments.last().unwrap().ident.to_string();
let name = name.as_str();
let meta = attr.parse_meta()?;
let Meta::List(MetaList { ref nested, .. }) = meta else {
throw_invalid_attr!(attr, &meta);
};
let mut kind = match name {
"label" => SubdiagnosticKind::Label,
"note" => SubdiagnosticKind::Note,
"help" => SubdiagnosticKind::Help,
"warning" => SubdiagnosticKind::Warn,
_ => {
if let Some(suggestion_kind) =
name.strip_prefix("suggestion").and_then(|s| s.parse().ok())
{
SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() }
} else if let Some(suggestion_kind) =
name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
{
SubdiagnosticKind::MultipartSuggestion { suggestion_kind }
} else {
throw_invalid_attr!(attr, &meta);
let kind = match meta {
Meta::List(MetaList { ref nested, .. }) => {
let mut nested_iter = nested.into_iter();
if let Some(nested_attr) = nested_iter.next() {
match nested_attr {
NestedMeta::Meta(Meta::Path(path)) => {
self.slugs.push((path.clone(), span));
}
NestedMeta::Meta(meta @ Meta::NameValue(_))
if matches!(
meta.path().segments.last().unwrap().ident.to_string().as_str(),
"code" | "applicability"
) =>
{
// don't error for valid follow-up attributes
}
nested_attr => {
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help(
"first argument of the attribute should be the diagnostic \
slug",
)
})
}
};
}
}
};
let mut slug = None;
let mut code = None;
for nested_attr in nested_iter {
let meta = match nested_attr {
NestedMeta::Meta(ref meta) => meta,
_ => throw_invalid_nested_attr!(attr, &nested_attr),
};
let mut nested_iter = nested.into_iter();
if let Some(nested_attr) = nested_iter.next() {
match nested_attr {
NestedMeta::Meta(Meta::Path(path)) => {
slug.set_once((path.clone(), span));
}
NestedMeta::Meta(meta @ Meta::NameValue(_))
if matches!(
meta.path().segments.last().unwrap().ident.to_string().as_str(),
"code" | "applicability"
) =>
{
// Don't error for valid follow-up attributes.
}
nested_attr => {
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help(
"first argument of the attribute should be the diagnostic \
slug",
)
})
}
};
}
let span = meta.span().unwrap();
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
for nested_attr in nested_iter {
let meta = match nested_attr {
NestedMeta::Meta(ref meta) => meta,
_ => throw_invalid_nested_attr!(attr, &nested_attr),
};
let span = meta.span().unwrap();
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
let value = match meta {
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value,
Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help("a diagnostic slug must be the first argument to the attribute")
}),
_ => throw_invalid_nested_attr!(attr, &nested_attr),
};
match nested_name {
"code" => {
if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
let formatted_str = self.build_format(&value.value(), value.span());
code.set_once((formatted_str, span));
} else {
span_err(
span,
&format!(
"`code` is not a valid nested attribute of a `{}` attribute",
name
),
)
.emit();
match meta {
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
match nested_name {
"code" => {
let formatted_str = self.build_format(&s.value(), s.span());
self.code.set_once((formatted_str, span));
}
"applicability" => {
let value = match Applicability::from_str(&s.value()) {
Ok(v) => v,
Err(()) => {
span_err(span, "invalid applicability").emit();
Applicability::Unspecified
}
};
self.applicability.set_once((quote! { #value }, span));
}
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help(
"only `code` and `applicability` are valid nested \
attributes",
)
}),
}
}
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
if matches!(meta, Meta::Path(_)) {
diag.help(
"a diagnostic slug must be the first argument to the \
attribute",
)
} else {
diag
}
}),
}
}
"applicability" => {
if matches!(
kind,
SubdiagnosticKind::Suggestion { .. }
| SubdiagnosticKind::MultipartSuggestion { .. }
) {
let value =
Applicability::from_str(&value.value()).unwrap_or_else(|()| {
span_err(span, "invalid applicability").emit();
Applicability::Unspecified
});
self.applicability.set_once((quote! { #value }, span));
} else {
span_err(
span,
&format!(
"`applicability` is not a valid nested attribute of a `{}` attribute",
name
)
).emit();
}
}
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help("only `code` and `applicability` are valid nested attributes")
}),
let Ok(kind) = SubdiagnosticKind::from_str(name) else {
throw_invalid_attr!(attr, &meta)
};
kind
}
_ => throw_invalid_attr!(attr, &meta),
};
if matches!(
kind,
SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
) && self.code.is_some()
{
throw_span_err!(
span,
&format!("`code` is not a valid nested attribute of a `{}` attribute", name)
);
}
let Some((slug, _)) = slug else {
if matches!(
kind,
SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
) && self.applicability.is_some()
{
throw_span_err!(
span,
&format!(
"`applicability` is not a valid nested attribute of a `{}` attribute",
name
)
);
}
if self.slugs.len() != i + 1 {
throw_span_err!(
span,
&format!(
@ -346,338 +342,146 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
name
)
);
};
match kind {
SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => {
let Some((code, _)) = code else {
throw_span_err!(span, "suggestion without `code = \"...\"`");
};
*code_field = code;
}
SubdiagnosticKind::Label
| SubdiagnosticKind::Note
| SubdiagnosticKind::Help
| SubdiagnosticKind::Warn
| SubdiagnosticKind::MultipartSuggestion { .. } => {}
}
kind_slug.set_once(((kind, slug), span))
self.kinds.push((kind, span));
}
Ok(kind_slug.map(|(kind_slug, _)| kind_slug))
Ok(())
}
/// Generates the code for a field with no attributes.
fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
fn generate_field_code(
&mut self,
binding: &BindingInfo<'_>,
have_suggestion: bool,
) -> Result<TokenStream, DiagnosticDeriveError> {
let ast = binding.ast();
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
let inner_ty = FieldInnerTy::from_type(&ast.ty);
let info = FieldInfo {
binding: binding,
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
span: &ast.span(),
};
for attr in &ast.attrs {
let name = attr.path.segments.last().unwrap().ident.to_string();
let name = name.as_str();
let span = attr.span().unwrap();
let meta = attr.parse_meta()?;
match meta {
Meta::Path(_) => match name {
"primary_span" => {
report_error_if_not_applied_to_span(attr, &info)?;
self.span_field.set_once((binding.binding.clone(), span));
return Ok(quote! {});
}
"applicability" if have_suggestion => {
report_error_if_not_applied_to_applicability(attr, &info)?;
let binding = binding.binding.clone();
self.applicability.set_once((quote! { #binding }, span));
return Ok(quote! {});
}
"applicability" => {
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
return Ok(quote! {});
}
"skip_arg" => {
return Ok(quote! {});
}
_ => throw_invalid_attr!(attr, &meta, |diag| {
diag.help(
"only `primary_span`, `applicability` and `skip_arg` are valid field \
attributes",
)
}),
},
_ => throw_invalid_attr!(attr, &meta),
}
}
let ident = ast.ident.as_ref().unwrap();
let diag = &self.diag;
let ident = ast.ident.as_ref().unwrap();
quote! {
let generated = quote! {
#diag.set_arg(
stringify!(#ident),
#binding
);
}
};
Ok(inner_ty.with(binding, generated))
}
/// Generates the necessary code for all attributes on a field.
fn generate_field_attr_code(
&mut self,
binding: &BindingInfo<'_>,
kind: &SubdiagnosticKind,
) -> TokenStream {
let ast = binding.ast();
assert!(ast.attrs.len() > 0, "field without attributes generating attr code");
// Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will
// apply the generated code on each element in the `Vec` or `Option`.
let inner_ty = FieldInnerTy::from_type(&ast.ty);
ast.attrs
.iter()
.map(|attr| {
let info = FieldInfo {
binding,
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
span: &ast.span(),
};
let generated = self
.generate_field_code_inner(kind, attr, info)
.unwrap_or_else(|v| v.to_compile_error());
inner_ty.with(binding, generated)
})
.collect()
}
fn generate_field_code_inner(
&mut self,
kind: &SubdiagnosticKind,
attr: &Attribute,
info: FieldInfo<'_>,
) -> Result<TokenStream, DiagnosticDeriveError> {
let meta = attr.parse_meta()?;
match meta {
Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path),
Meta::List(list @ MetaList { .. }) => {
self.generate_field_code_inner_list(kind, attr, info, list)
}
_ => throw_invalid_attr!(attr, &meta),
}
}
/// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
fn generate_field_code_inner_path(
&mut self,
kind: &SubdiagnosticKind,
attr: &Attribute,
info: FieldInfo<'_>,
path: Path,
) -> Result<TokenStream, DiagnosticDeriveError> {
let span = attr.span().unwrap();
let ident = &path.segments.last().unwrap().ident;
let name = ident.to_string();
let name = name.as_str();
match name {
"skip_arg" => Ok(quote! {}),
"primary_span" => {
if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
diag.help(
"multipart suggestions use one or more `#[suggestion_part]`s rather \
than one `#[primary_span]`",
)
})
}
report_error_if_not_applied_to_span(attr, &info)?;
let binding = info.binding.binding.clone();
self.span_field.set_once((binding, span));
Ok(quote! {})
}
"suggestion_part" => {
self.has_suggestion_parts = true;
match kind {
SubdiagnosticKind::MultipartSuggestion { .. } => {
span_err(
span,
"`#[suggestion_part(...)]` attribute without `code = \"...\"`",
)
.emit();
Ok(quote! {})
}
SubdiagnosticKind::Label
| SubdiagnosticKind::Note
| SubdiagnosticKind::Help
| SubdiagnosticKind::Warn
| SubdiagnosticKind::Suggestion { .. } => {
throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
diag.help(
"`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead",
)
});
}
}
}
"applicability" => {
if let SubdiagnosticKind::Suggestion { .. }
| SubdiagnosticKind::MultipartSuggestion { .. } = kind
{
report_error_if_not_applied_to_applicability(attr, &info)?;
let binding = info.binding.binding.clone();
self.applicability.set_once((quote! { #binding }, span));
} else {
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
}
Ok(quote! {})
}
_ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
"suggestion_part"
} else {
"primary_span"
};
diag.help(format!(
"only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
))
}),
}
}
/// Generates the code for a `[Meta::List]`-like attribute on a field (e.g.
/// `#[suggestion_part(code = "...")]`).
fn generate_field_code_inner_list(
&mut self,
kind: &SubdiagnosticKind,
attr: &Attribute,
info: FieldInfo<'_>,
list: MetaList,
) -> Result<TokenStream, DiagnosticDeriveError> {
let span = attr.span().unwrap();
let ident = &list.path.segments.last().unwrap().ident;
let name = ident.to_string();
let name = name.as_str();
match name {
"suggestion_part" => {
if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
throw_invalid_attr!(attr, &Meta::List(list), |diag| {
diag.help(
"`#[suggestion_part(...)]` is only valid in multipart suggestions",
)
})
}
self.has_suggestion_parts = true;
report_error_if_not_applied_to_span(attr, &info)?;
let mut code = None;
for nested_attr in list.nested.iter() {
let NestedMeta::Meta(ref meta) = nested_attr else {
throw_invalid_nested_attr!(attr, &nested_attr);
};
let span = meta.span().unwrap();
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else {
throw_invalid_nested_attr!(attr, &nested_attr);
};
match nested_name {
"code" => {
let formatted_str = self.build_format(&value.value(), value.span());
code.set_once((formatted_str, span));
}
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
diag.help("`code` is the only valid nested attribute")
}),
}
}
let Some((code, _)) = code else {
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
.emit();
return Ok(quote! {});
};
let binding = info.binding;
Ok(quote! { suggestions.push((#binding, #code)); })
}
_ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
"suggestion_part"
} else {
"primary_span"
};
diag.help(format!(
"only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
))
}),
}
}
pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
let Some((kind, slug)) = self.identify_kind()? else {
fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
self.identify_kind()?;
if self.kinds.is_empty() {
throw_span_err!(
self.variant.ast().ident.span().unwrap(),
"subdiagnostic kind not specified"
);
};
let have_suggestion =
self.kinds.iter().any(|(k, _)| matches!(k, SubdiagnosticKind::Suggestion(_)));
let mut args = TokenStream::new();
for binding in self.variant.bindings() {
let arg = self
.generate_field_code(binding, have_suggestion)
.unwrap_or_else(|v| v.to_compile_error());
args.extend(arg);
}
let mut tokens = TokenStream::new();
for ((kind, _), (slug, _)) in self.kinds.iter().zip(&self.slugs) {
let code = match self.code.as_ref() {
Some((code, _)) => Some(quote! { #code }),
None if have_suggestion => {
span_err(self.span, "suggestion without `code = \"...\"`").emit();
Some(quote! { /* macro error */ "..." })
}
None => None,
};
let init = match &kind {
SubdiagnosticKind::Label
| SubdiagnosticKind::Note
| SubdiagnosticKind::Help
| SubdiagnosticKind::Warn
| SubdiagnosticKind::Suggestion { .. } => quote! {},
SubdiagnosticKind::MultipartSuggestion { .. } => {
quote! { let mut suggestions = Vec::new(); }
}
};
let span_field = self.span_field.as_ref().map(|(span, _)| span);
let applicability = match self.applicability.clone() {
Some((applicability, _)) => Some(applicability),
None if have_suggestion => {
span_err(self.span, "suggestion without `applicability`").emit();
Some(quote! { rustc_errors::Applicability::Unspecified })
}
None => None,
};
let attr_args: TokenStream = self
.variant
.bindings()
.iter()
.filter(|binding| !binding.ast().attrs.is_empty())
.map(|binding| self.generate_field_attr_code(binding, &kind))
.collect();
let span_field = self.span_field.as_ref().map(|(span, _)| span);
let applicability = self.applicability.take().map_or_else(
|| quote! { rustc_errors::Applicability::Unspecified },
|(applicability, _)| applicability,
);
let diag = &self.diag;
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
let message = quote! { rustc_errors::fluent::#slug };
let call = match kind {
SubdiagnosticKind::Suggestion { suggestion_kind, code } => {
let diag = &self.diag;
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
let message = quote! { rustc_errors::fluent::#slug };
let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
if let Some(span) = span_field {
let style = suggestion_kind.to_suggestion_style();
quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
quote! { #diag.#name(#span, #message, #code, #applicability); }
} else {
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
quote! { unreachable!(); }
}
}
SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => {
if !self.has_suggestion_parts {
span_err(
self.span,
"multipart suggestion without any `#[suggestion_part(...)]` fields",
)
.emit();
}
let style = suggestion_kind.to_suggestion_style();
quote! { #diag.#name(#message, suggestions, #applicability, #style); }
}
SubdiagnosticKind::Label => {
} else if matches!(kind, SubdiagnosticKind::Label) {
if let Some(span) = span_field {
quote! { #diag.#name(#span, #message); }
} else {
span_err(self.span, "label without `#[primary_span]` field").emit();
quote! { unreachable!(); }
}
}
_ => {
} else {
if let Some(span) = span_field {
quote! { #diag.#name(#span, #message); }
} else {
quote! { #diag.#name(#message); }
}
}
};
};
tokens.extend(quote! {
#call
#args
});
}
let plain_args: TokenStream = self
.variant
.bindings()
.iter()
.filter(|binding| binding.ast().attrs.is_empty())
.map(|binding| self.generate_field_set_arg(binding))
.collect();
Ok(quote! {
#init
#attr_args
#call
#plain_args
})
Ok(tokens)
}
}

View File

@ -0,0 +1,73 @@
use rustc_errors::AddSubdiagnostic;
use rustc_span::Span;
pub struct CycleStack {
pub span: Span,
pub desc: String,
}
impl AddSubdiagnostic for CycleStack {
fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) {
diag.span_note(self.span, &format!("...which requires {}...", self.desc));
}
}
#[derive(SessionSubdiagnostic)]
pub enum StackCount {
#[note(query_system::cycle_stack_single)]
Single,
#[note(query_system::cycle_stack_multiple)]
Multiple,
}
#[derive(SessionSubdiagnostic)]
pub enum Alias {
#[note(query_system::cycle_recursive_ty_alias)]
#[help(query_system::cycle_recursive_ty_alias_help1)]
#[help(query_system::cycle_recursive_ty_alias_help2)]
Ty,
#[note(query_system::cycle_recursive_trait_alias)]
Trait,
}
#[derive(SessionSubdiagnostic)]
#[note(query_system::cycle_usage)]
pub struct CycleUsage {
#[primary_span]
pub span: Span,
pub usage: String,
}
#[derive(SessionDiagnostic)]
#[diag(query_system::cycle, code = "E0391")]
pub struct Cycle {
#[primary_span]
pub span: Span,
pub stack_bottom: String,
#[subdiagnostic]
pub cycle_stack: Vec<CycleStack>,
#[subdiagnostic]
pub stack_count: StackCount,
#[subdiagnostic]
pub alias: Option<Alias>,
#[subdiagnostic]
pub cycle_usage: Option<CycleUsage>,
}
#[derive(SessionDiagnostic)]
#[diag(query_system::reentrant)]
pub struct Reentrant;
#[derive(SessionDiagnostic)]
#[diag(query_system::increment_compilation)]
#[help]
#[note(query_system::increment_compilation_note1)]
#[note(query_system::increment_compilation_note2)]
pub struct IncrementCompilation {
pub run_cmd: String,
pub dep_node: String,
}
#[derive(SessionDiagnostic)]
#[diag(query_system::query_overflow)]
pub struct QueryOverflow;

View File

@ -5,6 +5,8 @@
#![feature(min_specialization)]
#![feature(extern_types)]
#![allow(rustc::potential_query_instability)]
// #![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
#[macro_use]
extern crate tracing;
@ -15,5 +17,6 @@ extern crate rustc_macros;
pub mod cache;
pub mod dep_graph;
mod error;
pub mod ich;
pub mod query;

View File

@ -1,12 +1,11 @@
use crate::error::CycleStack;
use crate::query::plumbing::CycleError;
use crate::query::{QueryContext, QueryStackFrame};
use rustc_hir::def::DefKind;
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{
struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level,
};
use rustc_session::Session;
use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level};
use rustc_hir::def::DefKind;
use rustc_session::{Session, SessionDiagnostic};
use rustc_span::Span;
use std::hash::Hash;
@ -536,46 +535,44 @@ pub(crate) fn report_cycle<'a>(
assert!(!stack.is_empty());
let span = stack[0].query.default_span(stack[1 % stack.len()].span);
let mut err =
struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description);
let mut cycle_stack = Vec::new();
use crate::error::StackCount;
let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
for i in 1..stack.len() {
let query = &stack[i].query;
let span = query.default_span(stack[(i + 1) % stack.len()].span);
err.span_note(span, &format!("...which requires {}...", query.description));
}
if stack.len() == 1 {
err.note(&format!("...which immediately requires {} again", stack[0].query.description));
} else {
err.note(&format!(
"...which again requires {}, completing the cycle",
stack[0].query.description
));
}
if stack.iter().all(|entry| {
entry
.query
.def_kind
.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias))
}) {
if stack.iter().all(|entry| {
entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias))
}) {
err.note("type aliases cannot be recursive");
err.help("consider using a struct, enum, or union instead to break the cycle");
err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
} else {
err.note("trait aliases cannot be recursive");
}
cycle_stack.push(CycleStack { span, desc: query.description.to_owned() });
}
let mut cycle_usage = None;
if let Some((span, query)) = usage {
err.span_note(query.default_span(span), &format!("cycle used when {}", query.description));
cycle_usage = Some(crate::error::CycleUsage {
span: query.default_span(span),
usage: query.description,
});
}
err
let alias = if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TyAlias)) {
Some(crate::error::Alias::Ty)
} else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) {
Some(crate::error::Alias::Trait)
} else {
None
};
let cycle_diag = crate::error::Cycle {
span,
cycle_stack,
stack_bottom: stack[0].query.description.to_owned(),
alias,
cycle_usage: cycle_usage,
stack_count,
};
cycle_diag.into_diagnostic(&sess.parse_sess)
}
pub fn print_query_stack<CTX: QueryContext>(

View File

@ -125,6 +125,6 @@ pub trait QueryContext: HasDepContext {
) -> R;
fn depth_limit_error(&self) {
self.dep_context().sess().fatal("queries overflow the depth limit!");
self.dep_context().sess().emit_fatal(crate::error::QueryOverflow);
}
}

View File

@ -618,16 +618,12 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D
let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
if old_in_panic {
sess.struct_err(
"internal compiler error: re-entrant incremental verify failure, suppressing message",
)
.emit();
sess.emit_err(crate::error::Reentrant);
} else {
sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node))
.help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd))
.note("Please follow the instructions below to create a bug report with the provided information")
.note("See <https://github.com/rust-lang/rust/issues/84970> for more information")
.emit();
sess.emit_err(crate::error::IncrementCompilation {
run_cmd,
dep_node: format!("{:?}", dep_node),
});
panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result);
}

View File

@ -1792,7 +1792,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
}
};
let mut suggestable_variants = variants
let suggestable_variants = variants
.iter()
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
@ -1802,8 +1802,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
CtorKind::Fictive => format!("({} {{}})", variant),
})
.collect::<Vec<_>>();
let no_suggestable_variant = suggestable_variants.is_empty();
if !suggestable_variants.is_empty() {
if !no_suggestable_variant {
let msg = if suggestable_variants.len() == 1 {
"you might have meant to use the following enum variant"
} else {
@ -1813,7 +1814,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err.span_suggestions(
span,
msg,
suggestable_variants.drain(..),
suggestable_variants.into_iter(),
Applicability::MaybeIncorrect,
);
}
@ -1830,15 +1831,15 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.collect::<Vec<_>>();
if !suggestable_variants_with_placeholders.is_empty() {
let msg = match (
suggestable_variants.is_empty(),
suggestable_variants_with_placeholders.len(),
) {
(true, 1) => "the following enum variant is available",
(true, _) => "the following enum variants are available",
(false, 1) => "alternatively, the following enum variant is available",
(false, _) => "alternatively, the following enum variants are also available",
};
let msg =
match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) {
(true, 1) => "the following enum variant is available",
(true, _) => "the following enum variants are available",
(false, 1) => "alternatively, the following enum variant is available",
(false, _) => {
"alternatively, the following enum variants are also available"
}
};
err.span_suggestions(
span,

View File

@ -501,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
if !errors_buffer.is_empty() {
errors_buffer.sort_by_key(|diag| diag.span.primary_span());
for mut diag in errors_buffer.drain(..) {
for mut diag in errors_buffer {
self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
}
}

View File

@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
if self.not_enough_args_provided() {
self.suggest_adding_args(err);
} else if self.too_many_args_provided() {
self.suggest_moving_args_from_assoc_fn_to_trait(err);
self.suggest_removing_args_or_generics(err);
} else {
unreachable!();
@ -653,6 +654,123 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
/// Suggests moving redundant argument(s) of an associate function to the
/// trait it belongs to.
///
/// ```compile_fail
/// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
/// ```
fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
let trait_ = match self.tcx.trait_of_item(self.def_id) {
Some(def_id) => def_id,
None => return,
};
// Skip suggestion when the associated function is itself generic, it is unclear
// how to split the provided parameters between those to suggest to the trait and
// those to remain on the associated type.
let num_assoc_fn_expected_args =
self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
if num_assoc_fn_expected_args > 0 {
return;
}
let num_assoc_fn_excess_args =
self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
let trait_generics = self.tcx.generics_of(trait_);
let num_trait_generics_except_self =
trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
let msg = format!(
"consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
these = pluralize!("this", num_assoc_fn_excess_args),
s = pluralize!(num_assoc_fn_excess_args),
name = self.tcx.item_name(trait_),
num = num_trait_generics_except_self,
);
if let Some(hir_id) = self.path_segment.hir_id
&& let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id)
&& let Some(parent_node) = self.tcx.hir().find(parent_node)
&& let hir::Node::Expr(expr) = parent_node {
match expr.kind {
hir::ExprKind::Path(ref qpath) => {
self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
err,
qpath,
msg,
num_assoc_fn_excess_args,
num_trait_generics_except_self
)
},
hir::ExprKind::MethodCall(..) => {
self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
err,
trait_,
expr,
msg,
num_assoc_fn_excess_args,
num_trait_generics_except_self
)
},
_ => return,
}
}
}
fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
&self,
err: &mut Diagnostic,
qpath: &'tcx hir::QPath<'tcx>,
msg: String,
num_assoc_fn_excess_args: usize,
num_trait_generics_except_self: usize,
) {
if let hir::QPath::Resolved(_, path) = qpath
&& let Some(trait_path_segment) = path.segments.get(0) {
let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
if let Some(span) = self.gen_args.span_ext()
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
let sugg = vec![
(self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
(span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
];
err.multipart_suggestion(
msg,
sugg,
Applicability::MaybeIncorrect
);
}
}
}
}
fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
&self,
err: &mut Diagnostic,
trait_: DefId,
expr: &'tcx hir::Expr<'tcx>,
msg: String,
num_assoc_fn_excess_args: usize,
num_trait_generics_except_self: usize,
) {
if let hir::ExprKind::MethodCall(_, args, _) = expr.kind {
assert_eq!(args.len(), 1);
if num_assoc_fn_excess_args == num_trait_generics_except_self {
if let Some(gen_args) = self.gen_args.span_ext()
&& let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args)
&& let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) {
let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args);
err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect);
}
}
}
}
/// Suggests to remove redundant argument(s):
///
/// ```text

View File

@ -350,10 +350,12 @@ macro_rules! matches {
/// Unwraps a result or propagates its error.
///
/// The `?` operator was added to replace `try!` and should be used instead.
/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use
/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`.
/// The [`?` operator][propagating-errors] was added to replace `try!`
/// and should be used instead. Furthermore, `try` is a reserved word
/// in Rust 2018, so if you must use it, you will need to use the
/// [raw-identifier syntax][ris]: `r#try`.
///
/// [propagating-errors]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
/// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html
///
/// `try!` matches the given [`Result`]. In case of the `Ok` variant, the

View File

@ -3,7 +3,7 @@ use crate::os::windows::prelude::*;
use crate::ffi::OsString;
use crate::fmt;
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
use crate::mem;
use crate::mem::{self, MaybeUninit};
use crate::os::windows::io::{AsHandle, BorrowedHandle};
use crate::path::{Path, PathBuf};
use crate::ptr;
@ -326,7 +326,8 @@ impl File {
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
let mut reparse_tag = 0;
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let mut b =
Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
if let Ok((_, buf)) = self.reparse_point(&mut b) {
reparse_tag = (*buf).ReparseTag;
}
@ -389,7 +390,8 @@ impl File {
attr.file_size = info.AllocationSize as u64;
attr.number_of_links = Some(info.NumberOfLinks);
if attr.file_type().is_reparse_point() {
let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let mut b =
Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
if let Ok((_, buf)) = self.reparse_point(&mut b) {
attr.reparse_tag = (*buf).ReparseTag;
}
@ -463,7 +465,7 @@ impl File {
// avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
fn reparse_point(
&self,
space: &mut Align8<[u8]>,
space: &mut Align8<[MaybeUninit<u8>]>,
) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
unsafe {
let mut bytes = 0;
@ -488,7 +490,7 @@ impl File {
}
fn readlink(&self) -> io::Result<PathBuf> {
let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let (_bytes, buf) = self.reparse_point(&mut space)?;
unsafe {
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
@ -658,12 +660,16 @@ impl File {
/// A buffer for holding directory entries.
struct DirBuff {
buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>,
buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
}
impl DirBuff {
const BUFFER_SIZE: usize = 1024;
fn new() -> Self {
Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) }
Self {
// Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
// initialization.
buffer: unsafe { Box::new_uninit().assume_init() },
}
}
fn capacity(&self) -> usize {
self.buffer.0.len()
@ -676,8 +682,8 @@ impl DirBuff {
DirBuffIter::new(self)
}
}
impl AsRef<[u8]> for DirBuff {
fn as_ref(&self) -> &[u8] {
impl AsRef<[MaybeUninit<u8>]> for DirBuff {
fn as_ref(&self) -> &[MaybeUninit<u8>] {
&self.buffer.0
}
}
@ -686,7 +692,7 @@ impl AsRef<[u8]> for DirBuff {
///
/// Currently only returns file names (UTF-16 encoded).
struct DirBuffIter<'a> {
buffer: Option<&'a [u8]>,
buffer: Option<&'a [MaybeUninit<u8>]>,
cursor: usize,
}
impl<'a> DirBuffIter<'a> {
@ -701,9 +707,13 @@ impl<'a> Iterator for DirBuffIter<'a> {
let buffer = &self.buffer?[self.cursor..];
// Get the name and next entry from the buffer.
// SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
// last field (the file name) is unsized. So an offset has to be
// used to get the file name slice.
// SAFETY:
// - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
// field (the file name) is unsized. So an offset has to be used to
// get the file name slice.
// - The OS has guaranteed initialization of the fields of
// `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
// `FileNameLength` bytes)
let (name, is_directory, next_entry) = unsafe {
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
// Guaranteed to be aligned in documentation for
@ -1349,7 +1359,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
let h = f.as_inner().as_raw_handle();
unsafe {
let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
let data_ptr = data.0.as_mut_ptr();
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();

View File

@ -30,7 +30,7 @@ is 8-bytes large as well as pointers. The tradeoff, though, is that the maximum
memory size is now the full 64-bit address space instead of the 4GB as limited
by the 32-bit address space for `wasm32-unknown-unknown`.
This target is not a stable target. The [memory64] WebAssembly proposal is stil
This target is not a stable target. The [memory64] WebAssembly proposal is still
in-progress and not standardized. This means that there are not many engines
which implement the `memory64` feature and if they do they're likely behind a
flag, for example:

View File

@ -1,6 +1,7 @@
// compile-flags: -Z unstable-options
#![crate_type = "lib"]
#![feature(rustc_attrs)]
#![feature(rustc_private)]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
@ -71,3 +72,10 @@ pub fn make_diagnostics<'a>(sess: &'a ParseSess) {
//~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
//~^^ ERROR diagnostics should be created using translatable messages
}
// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted.
#[rustc_lint_diagnostics]
pub fn skipped_because_of_annotation<'a>(sess: &'a ParseSess) {
let _diag = sess.struct_err("untranslatable diagnostic"); // okay!
}

View File

@ -1,41 +1,41 @@
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:36:14
--> $DIR/diagnostics.rs:37:14
|
LL | sess.struct_err("untranslatable diagnostic")
| ^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/diagnostics.rs:5:9
--> $DIR/diagnostics.rs:6:9
|
LL | #![deny(rustc::untranslatable_diagnostic)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:53:14
--> $DIR/diagnostics.rs:54:14
|
LL | diag.note("untranslatable diagnostic");
| ^^^^
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
--> $DIR/diagnostics.rs:67:22
--> $DIR/diagnostics.rs:68:22
|
LL | let _diag = sess.struct_err(fluent::parser::expect_path);
| ^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/diagnostics.rs:6:9
--> $DIR/diagnostics.rs:7:9
|
LL | #![deny(rustc::diagnostic_outside_of_impl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
--> $DIR/diagnostics.rs:70:22
--> $DIR/diagnostics.rs:71:22
|
LL | let _diag = sess.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:70:22
--> $DIR/diagnostics.rs:71:22
|
LL | let _diag = sess.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^

View File

@ -167,8 +167,8 @@ enum P {
#[derive(SessionSubdiagnostic)]
enum Q {
#[bar]
//~^ ERROR `#[bar]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
//~^ ERROR `#[bar]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
A {
#[primary_span]
span: Span,
@ -179,8 +179,8 @@ enum Q {
#[derive(SessionSubdiagnostic)]
enum R {
#[bar = "..."]
//~^ ERROR `#[bar = ...]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
//~^ ERROR `#[bar = ...]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
A {
#[primary_span]
span: Span,
@ -191,8 +191,8 @@ enum R {
#[derive(SessionSubdiagnostic)]
enum S {
#[bar = 4]
//~^ ERROR `#[bar = ...]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
//~^ ERROR `#[bar = ...]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
A {
#[primary_span]
span: Span,
@ -203,8 +203,8 @@ enum S {
#[derive(SessionSubdiagnostic)]
enum T {
#[bar("...")]
//~^ ERROR `#[bar(...)]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
//~^ ERROR `#[bar("...")]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope
A {
#[primary_span]
span: Span,
@ -215,7 +215,7 @@ enum T {
#[derive(SessionSubdiagnostic)]
enum U {
#[label(code = "...")]
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
A {
#[primary_span]
span: Span,
@ -232,7 +232,7 @@ enum V {
var: String,
},
B {
//~^ ERROR subdiagnostic kind not specified
//~^ ERROR subdiagnostic kind not specified
#[primary_span]
span: Span,
var: String,
@ -307,16 +307,6 @@ union AC {
b: u64
}
#[derive(SessionSubdiagnostic)]
#[label(parser::add_paren)]
//~^ NOTE previously specified here
#[label(parser::add_paren)]
//~^ ERROR specified multiple times
struct AD {
#[primary_span]
span: Span,
}
#[derive(SessionSubdiagnostic)]
#[label(parser::add_paren, parser::add_paren)]
//~^ ERROR `#[label(parser::add_paren)]` is not a valid attribute
@ -329,16 +319,16 @@ struct AE {
#[label(parser::add_paren)]
struct AF {
#[primary_span]
//~^ NOTE previously specified here
//~^ NOTE previously specified here
span_a: Span,
#[primary_span]
//~^ ERROR specified multiple times
//~^ ERROR specified multiple times
span_b: Span,
}
#[derive(SessionSubdiagnostic)]
struct AG {
//~^ ERROR subdiagnostic kind not specified
//~^ ERROR subdiagnostic kind not specified
#[primary_span]
span: Span,
}
@ -390,25 +380,27 @@ struct AK {
#[primary_span]
span: Span,
#[applicability]
//~^ NOTE previously specified here
//~^ NOTE previously specified here
applicability_a: Applicability,
#[applicability]
//~^ ERROR specified multiple times
//~^ ERROR specified multiple times
applicability_b: Applicability,
}
#[derive(SessionSubdiagnostic)]
#[suggestion(parser::add_paren, code = "...")]
//~^ ERROR suggestion without `applicability`
struct AL {
#[primary_span]
span: Span,
#[applicability]
//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
applicability: Span,
}
#[derive(SessionSubdiagnostic)]
#[suggestion(parser::add_paren, code = "...")]
//~^ ERROR suggestion without `applicability`
struct AM {
#[primary_span]
span: Span,
@ -444,7 +436,8 @@ struct AQ;
#[derive(SessionSubdiagnostic)]
#[suggestion(parser::add_paren, code = "...")]
//~^ ERROR suggestion without `#[primary_span]` field
//~^ ERROR suggestion without `applicability`
//~^^ ERROR suggestion without `#[primary_span]` field
struct AR {
var: String,
}
@ -514,120 +507,3 @@ struct AZ {
#[primary_span]
span: Span,
}
#[derive(SessionSubdiagnostic)]
#[suggestion(parser::add_paren, code = "...")]
//~^ ERROR suggestion without `#[primary_span]` field
struct BA {
#[suggestion_part]
//~^ ERROR `#[suggestion_part]` is not a valid attribute
span: Span,
#[suggestion_part(code = "...")]
//~^ ERROR `#[suggestion_part(...)]` is not a valid attribute
span2: Span,
#[applicability]
applicability: Applicability,
var: String,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute
struct BBa {
var: String,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
struct BBb {
#[suggestion_part]
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
span1: Span,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
struct BBc {
#[suggestion_part()]
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
span1: Span,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren)]
//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
struct BC {
#[primary_span]
//~^ ERROR `#[primary_span]` is not a valid attribute
span: Span,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren)]
struct BD {
#[suggestion_part]
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
span1: Span,
#[suggestion_part()]
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
span2: Span,
#[suggestion_part(foo = "bar")]
//~^ ERROR `#[suggestion_part(foo = ...)]` is not a valid attribute
span4: Span,
#[suggestion_part(code = "...")]
//~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
s1: String,
#[suggestion_part()]
//~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
s2: String,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
struct BE {
#[suggestion_part(code = "...", code = ",,,")]
//~^ ERROR specified multiple times
//~| NOTE previously specified here
span: Span,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
struct BF {
#[suggestion_part(code = "(")]
first: Span,
#[suggestion_part(code = ")")]
second: Span,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren)]
struct BG {
#[applicability]
appl: Applicability,
#[suggestion_part(code = "(")]
first: Span,
#[suggestion_part(code = ")")]
second: Span,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
//~^ NOTE previously specified here
struct BH {
#[applicability]
//~^ ERROR specified multiple times
appl: Applicability,
#[suggestion_part(code = "(")]
first: Span,
#[suggestion_part(code = ")")]
second: Span,
}
#[derive(SessionSubdiagnostic)]
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
struct BI {
#[suggestion_part(code = "")]
spans: Vec<Span>,
}

View File

@ -65,16 +65,16 @@ LL | #[label()]
| ^^^^^^^^^^
error: `code` is not a valid nested attribute of a `label` attribute
--> $DIR/subdiagnostic-derive.rs:137:28
--> $DIR/subdiagnostic-derive.rs:137:1
|
LL | #[label(parser::add_paren, code = "...")]
| ^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `applicability` is not a valid nested attribute of a `label` attribute
--> $DIR/subdiagnostic-derive.rs:146:28
--> $DIR/subdiagnostic-derive.rs:146:1
|
LL | #[label(parser::add_paren, applicability = "machine-applicable")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: unsupported type attribute for subdiagnostic enum
--> $DIR/subdiagnostic-derive.rs:155:1
@ -100,11 +100,13 @@ error: `#[bar = ...]` is not a valid attribute
LL | #[bar = 4]
| ^^^^^^^^^^
error: `#[bar(...)]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:205:5
error: `#[bar("...")]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:205:11
|
LL | #[bar("...")]
| ^^^^^^^^^^^^^
| ^^^^^
|
= help: first argument of the attribute should be the diagnostic slug
error: diagnostic slug must be first argument of a `#[label(...)]` attribute
--> $DIR/subdiagnostic-derive.rs:217:5
@ -161,8 +163,6 @@ error: `#[bar(...)]` is not a valid attribute
|
LL | #[bar("...")]
| ^^^^^^^^^^^^^
|
= help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
error: unexpected unsupported untagged union
--> $DIR/subdiagnostic-derive.rs:304:1
@ -174,20 +174,8 @@ LL | | b: u64
LL | | }
| |_^
error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:313:1
|
LL | #[label(parser::add_paren)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/subdiagnostic-derive.rs:311:1
|
LL | #[label(parser::add_paren)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `#[label(parser::add_paren)]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:321:28
--> $DIR/subdiagnostic-derive.rs:311:28
|
LL | #[label(parser::add_paren, parser::add_paren)]
| ^^^^^^^^^^^^^^^^^
@ -195,226 +183,134 @@ LL | #[label(parser::add_paren, parser::add_paren)]
= help: a diagnostic slug must be the first argument to the attribute
error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:334:5
--> $DIR/subdiagnostic-derive.rs:324:5
|
LL | #[primary_span]
| ^^^^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/subdiagnostic-derive.rs:331:5
--> $DIR/subdiagnostic-derive.rs:321:5
|
LL | #[primary_span]
| ^^^^^^^^^^^^^^^
error: subdiagnostic kind not specified
--> $DIR/subdiagnostic-derive.rs:340:8
--> $DIR/subdiagnostic-derive.rs:330:8
|
LL | struct AG {
| ^^
error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:377:47
--> $DIR/subdiagnostic-derive.rs:367:47
|
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
| ^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/subdiagnostic-derive.rs:377:33
--> $DIR/subdiagnostic-derive.rs:367:33
|
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
| ^^^^^^^^^^^^
error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:395:5
--> $DIR/subdiagnostic-derive.rs:385:5
|
LL | #[applicability]
| ^^^^^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/subdiagnostic-derive.rs:392:5
--> $DIR/subdiagnostic-derive.rs:382:5
|
LL | #[applicability]
| ^^^^^^^^^^^^^^^^
error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
--> $DIR/subdiagnostic-derive.rs:405:5
--> $DIR/subdiagnostic-derive.rs:396:5
|
LL | #[applicability]
| ^^^^^^^^^^^^^^^^
error: suggestion without `code = "..."`
--> $DIR/subdiagnostic-derive.rs:418:1
error: suggestion without `applicability`
--> $DIR/subdiagnostic-derive.rs:391:1
|
LL | #[suggestion(parser::add_paren)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
LL | | struct AL {
LL | | #[primary_span]
... |
LL | | applicability: Span,
LL | | }
| |_^
error: suggestion without `applicability`
--> $DIR/subdiagnostic-derive.rs:402:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
LL | | struct AM {
LL | | #[primary_span]
LL | | span: Span,
LL | | }
| |_^
error: suggestion without `code = "..."`
--> $DIR/subdiagnostic-derive.rs:410:1
|
LL | / #[suggestion(parser::add_paren)]
LL | |
LL | | struct AN {
LL | | #[primary_span]
... |
LL | | applicability: Applicability,
LL | | }
| |_^
error: invalid applicability
--> $DIR/subdiagnostic-derive.rs:428:46
--> $DIR/subdiagnostic-derive.rs:420:46
|
LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^
error: suggestion without `#[primary_span]` field
--> $DIR/subdiagnostic-derive.rs:446:1
error: suggestion without `applicability`
--> $DIR/subdiagnostic-derive.rs:438:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
LL | |
LL | | struct AR {
LL | | var: String,
LL | | }
| |_^
error: suggestion without `#[primary_span]` field
--> $DIR/subdiagnostic-derive.rs:438:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
LL | |
LL | | struct AR {
LL | | var: String,
LL | | }
| |_^
error: unsupported type attribute for subdiagnostic enum
--> $DIR/subdiagnostic-derive.rs:460:1
--> $DIR/subdiagnostic-derive.rs:453:1
|
LL | #[label]
| ^^^^^^^^
error: `var` doesn't refer to a field on this type
--> $DIR/subdiagnostic-derive.rs:480:39
--> $DIR/subdiagnostic-derive.rs:473:39
|
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
| ^^^^^^^
error: `var` doesn't refer to a field on this type
--> $DIR/subdiagnostic-derive.rs:499:43
--> $DIR/subdiagnostic-derive.rs:492:43
|
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
| ^^^^^^^
error: `#[suggestion_part]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:522:5
|
LL | #[suggestion_part]
| ^^^^^^^^^^^^^^^^^^
|
= help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead
error: `#[suggestion_part(...)]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:525:5
|
LL | #[suggestion_part(code = "...")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: `#[suggestion_part(...)]` is only valid in multipart suggestions
error: suggestion without `#[primary_span]` field
--> $DIR/subdiagnostic-derive.rs:519:1
|
LL | / #[suggestion(parser::add_paren, code = "...")]
LL | |
LL | | struct BA {
LL | | #[suggestion_part]
... |
LL | | var: String,
LL | | }
| |_^
error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute
--> $DIR/subdiagnostic-derive.rs:534:43
|
LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
| ^^^^^^^^^^^^
error: multipart suggestion without any `#[suggestion_part(...)]` fields
--> $DIR/subdiagnostic-derive.rs:534:1
|
LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
LL | |
LL | |
LL | | struct BBa {
LL | | var: String,
LL | | }
| |_^
error: `#[suggestion_part(...)]` attribute without `code = "..."`
--> $DIR/subdiagnostic-derive.rs:544:5
|
LL | #[suggestion_part]
| ^^^^^^^^^^^^^^^^^^
error: `#[suggestion_part(...)]` attribute without `code = "..."`
--> $DIR/subdiagnostic-derive.rs:552:5
|
LL | #[suggestion_part()]
| ^^^^^^^^^^^^^^^^^^^^
error: `#[primary_span]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:561:5
|
LL | #[primary_span]
| ^^^^^^^^^^^^^^^
|
= help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]`
error: multipart suggestion without any `#[suggestion_part(...)]` fields
--> $DIR/subdiagnostic-derive.rs:558:1
|
LL | / #[multipart_suggestion(parser::add_paren)]
LL | |
LL | | struct BC {
LL | | #[primary_span]
LL | |
LL | | span: Span,
LL | | }
| |_^
error: `#[suggestion_part(...)]` attribute without `code = "..."`
--> $DIR/subdiagnostic-derive.rs:569:5
|
LL | #[suggestion_part]
| ^^^^^^^^^^^^^^^^^^
error: `#[suggestion_part(...)]` attribute without `code = "..."`
--> $DIR/subdiagnostic-derive.rs:572:5
|
LL | #[suggestion_part()]
| ^^^^^^^^^^^^^^^^^^^^
error: `#[suggestion_part(foo = ...)]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:575:23
|
LL | #[suggestion_part(foo = "bar")]
| ^^^^^^^^^^^
|
= help: `code` is the only valid nested attribute
error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
--> $DIR/subdiagnostic-derive.rs:578:5
|
LL | #[suggestion_part(code = "...")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
--> $DIR/subdiagnostic-derive.rs:581:5
|
LL | #[suggestion_part()]
| ^^^^^^^^^^^^^^^^^^^^
error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:589:37
|
LL | #[suggestion_part(code = "...", code = ",,,")]
| ^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/subdiagnostic-derive.rs:589:23
|
LL | #[suggestion_part(code = "...", code = ",,,")]
| ^^^^^^^^^^^^
error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:619:5
|
LL | #[applicability]
| ^^^^^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/subdiagnostic-derive.rs:616:43
|
LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: cannot find attribute `foo` in this scope
--> $DIR/subdiagnostic-derive.rs:63:3
|
@ -475,6 +371,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
LL | #[label(slug)]
| ^^^^ not found in `rustc_errors::fluent`
error: aborting due to 64 previous errors
error: aborting due to 50 previous errors
For more information about this error, try `rustc --explain E0425`.

View File

@ -2,15 +2,22 @@ error[E0107]: this associated function takes 0 generic arguments but 1 generic a
--> $DIR/invalid-const-arg-for-type-param.rs:6:23
|
LL | let _: u32 = 5i32.try_into::<32>().unwrap();
| ^^^^^^^^------ help: remove these generics
| |
| expected 0 generic arguments
| ^^^^^^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
LL | fn try_into(self) -> Result<T, Self::Error>;
| ^^^^^^^^
help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument
|
LL | let _: u32 = TryInto::<32>::try_into(5i32).unwrap();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: remove these generics
|
LL - let _: u32 = 5i32.try_into::<32>().unwrap();
LL + let _: u32 = 5i32.try_into().unwrap();
|
error[E0599]: no method named `f` found for struct `S` in the current scope
--> $DIR/invalid-const-arg-for-type-param.rs:9:7

View File

@ -22,6 +22,10 @@ enum D {
Unit,
}
enum E {
TupleWithFields(()),
}
fn main() {
// Only variants without fields are suggested (and others mentioned in a note) where an enum
// is used rather than a variant.
@ -34,6 +38,8 @@ fn main() {
//~^ ERROR expected value, found enum `C`
D.foo();
//~^ ERROR expected value, found enum `D`
E.foo();
//~^ ERROR expected value, found enum `E`
// Only tuple variants are suggested in calls or tuple struct pattern matching.

View File

@ -1,5 +1,5 @@
error[E0423]: expected value, found enum `A`
--> $DIR/issue-73427.rs:29:5
--> $DIR/issue-73427.rs:33:5
|
LL | A.foo();
| ^
@ -23,7 +23,7 @@ LL | (A::Tuple()).foo();
| ~~~~~~~~~~~~
LL | A::Unit.foo();
| ~~~~~~~
help: the following enum variants are available
help: alternatively, the following enum variants are also available
|
LL | (A::StructWithFields { /* fields */ }).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -31,7 +31,7 @@ LL | (A::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `B`
--> $DIR/issue-73427.rs:31:5
--> $DIR/issue-73427.rs:35:5
|
LL | B.foo();
| ^
@ -52,7 +52,7 @@ LL | (B::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `C`
--> $DIR/issue-73427.rs:33:5
--> $DIR/issue-73427.rs:37:5
|
LL | C.foo();
| ^
@ -70,7 +70,7 @@ help: you might have meant to use the following enum variant
|
LL | C::Unit.foo();
| ~~~~~~~
help: the following enum variants are available
help: alternatively, the following enum variants are also available
|
LL | (C::StructWithFields { /* fields */ }).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -78,7 +78,7 @@ LL | (C::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `D`
--> $DIR/issue-73427.rs:35:5
--> $DIR/issue-73427.rs:39:5
|
LL | D.foo();
| ^
@ -95,13 +95,37 @@ help: you might have meant to use the following enum variant
|
LL | D::Unit.foo();
| ~~~~~~~
help: the following enum variant is available
help: alternatively, the following enum variant is available
|
LL | (D::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `E`
--> $DIR/issue-73427.rs:41:5
|
LL | E.foo();
| ^
|
note: the enum is defined here
--> $DIR/issue-73427.rs:25:1
|
LL | / enum E {
LL | | TupleWithFields(()),
LL | | }
| |_^
help: the following enum variant is available
|
LL | (E::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: consider importing one of these items instead
|
LL | use std::f32::consts::E;
|
LL | use std::f64::consts::E;
|
error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
--> $DIR/issue-73427.rs:40:13
--> $DIR/issue-73427.rs:46:13
|
LL | let x = A(3);
| ^
@ -126,7 +150,7 @@ LL | let x = A::TupleWithFields(3);
| ~~~~~~~~~~~~~~~~~~
error[E0532]: expected tuple struct or tuple variant, found enum `A`
--> $DIR/issue-73427.rs:42:12
--> $DIR/issue-73427.rs:48:12
|
LL | if let A(3) = x { }
| ^
@ -150,7 +174,7 @@ LL | if let A::Tuple(3) = x { }
LL | if let A::TupleWithFields(3) = x { }
| ~~~~~~~~~~~~~~~~~~
error: aborting due to 6 previous errors
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0423, E0532.
For more information about an error, try `rustc --explain E0423`.

View File

@ -19,7 +19,7 @@ help: you might have meant to use the following enum variant
|
LL | m::Z::Unit;
| ~~~~~~~~~~
help: the following enum variants are available
help: alternatively, the following enum variants are also available
|
LL | (m::Z::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~~~~
@ -47,7 +47,7 @@ help: you might have meant to use the following enum variant
|
LL | m::Z::Unit;
| ~~~~~~~~~~
help: the following enum variants are available
help: alternatively, the following enum variants are also available
|
LL | (m::Z::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~~~~
@ -89,7 +89,7 @@ help: you might have meant to use the following enum variant
|
LL | let _: E = E::Unit;
| ~~~~~~~
help: the following enum variants are available
help: alternatively, the following enum variants are also available
|
LL | let _: E = (E::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~
@ -143,7 +143,7 @@ help: you might have meant to use the following enum variant
|
LL | let _: E = E::Unit;
| ~~~~~~~
help: the following enum variants are available
help: alternatively, the following enum variants are also available
|
LL | let _: E = (E::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~
@ -203,7 +203,7 @@ help: you might have meant to use the following enum variant
|
LL | let _: Z = m::Z::Unit;
| ~~~~~~~~~~
help: the following enum variants are available
help: alternatively, the following enum variants are also available
|
LL | let _: Z = (m::Z::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -0,0 +1,35 @@
use std::convert::TryInto;
trait A<T> {
fn foo() {}
}
trait B<T, U> {
fn bar() {}
}
struct S;
impl<T> A<T> for S {}
impl<T, U> B<T, U> for S {}
fn main() {
let _ = A::foo::<S>();
//~^ ERROR
//~| HELP remove these generics
//~| HELP consider moving this generic argument
let _ = B::bar::<S, S>();
//~^ ERROR
//~| HELP remove these generics
//~| HELP consider moving these generic arguments
let _ = A::<S>::foo::<S>();
//~^ ERROR
//~| HELP remove these generics
let _ = 42.into::<Option<_>>();
//~^ ERROR
//~| HELP remove these generics
//~| HELP consider moving this generic argument
}

View File

@ -0,0 +1,82 @@
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
--> $DIR/issue-89064.rs:17:16
|
LL | let _ = A::foo::<S>();
| ^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $DIR/issue-89064.rs:4:8
|
LL | fn foo() {}
| ^^^
help: consider moving this generic argument to the `A` trait, which takes up to 1 argument
|
LL - let _ = A::foo::<S>();
LL + let _ = A::<S>::foo();
|
help: remove these generics
|
LL - let _ = A::foo::<S>();
LL + let _ = A::foo();
|
error[E0107]: this associated function takes 0 generic arguments but 2 generic arguments were supplied
--> $DIR/issue-89064.rs:22:16
|
LL | let _ = B::bar::<S, S>();
| ^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $DIR/issue-89064.rs:8:8
|
LL | fn bar() {}
| ^^^
help: consider moving these generic arguments to the `B` trait, which takes up to 2 arguments
|
LL - let _ = B::bar::<S, S>();
LL + let _ = B::<S, S>::bar();
|
help: remove these generics
|
LL - let _ = B::bar::<S, S>();
LL + let _ = B::bar();
|
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
--> $DIR/issue-89064.rs:27:21
|
LL | let _ = A::<S>::foo::<S>();
| ^^^----- help: remove these generics
| |
| expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $DIR/issue-89064.rs:4:8
|
LL | fn foo() {}
| ^^^
error[E0107]: this associated function takes 0 generic arguments but 1 generic argument was supplied
--> $DIR/issue-89064.rs:31:16
|
LL | let _ = 42.into::<Option<_>>();
| ^^^^ expected 0 generic arguments
|
note: associated function defined here, with 0 generic parameters
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
LL | fn into(self) -> T;
| ^^^^
help: consider moving this generic argument to the `Into` trait, which takes up to 1 argument
|
LL | let _ = Into::<Option<_>>::into(42);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
help: remove these generics
|
LL - let _ = 42.into::<Option<_>>();
LL + let _ = 42.into();
|
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0107`.

View File

@ -0,0 +1,4 @@
// normalize-stderr-test "error `.*`" -> "$$ERROR_MESSAGE"
// compile-flags: -o/tmp/ -Zunpretty=ast-tree
fn main() {}

View File

@ -0,0 +1,4 @@
error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE
error: aborting due to previous error

View File

@ -24,6 +24,7 @@ env:
RUST_BACKTRACE: 1
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
CARGO_INCREMENTAL: 0
jobs:
base:

View File

@ -10,6 +10,7 @@ env:
RUST_BACKTRACE: 1
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1
CARGO_INCREMENTAL: 0
defaults:
run:

View File

@ -6,11 +6,157 @@ document.
## Unreleased / In Rust Nightly
[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master)
## Rust 1.63
Current stable, released 2022-08-11
[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
### New Lints
* [`borrow_deref_ref`]
[#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
* [`doc_link_with_quotes`]
[#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
* [`no_effect_replace`]
[#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
* [`rc_clone_in_vec_init`]
[#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
* [`derive_partial_eq_without_eq`]
[#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
* [`mismatching_type_param_order`]
[#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
* [`unused_rounding`]
[#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
* [`swap_ptr_to_ref`]
[#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
* [`almost_complete_letter_range`]
[#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
* [`needless_parens_on_range_literals`]
[#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
### Moves and Deprecations
* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
`nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
### Enhancements
* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
[#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
[#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
* [`disallowed_methods`]: Now also lints indirect usages
[#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
[#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
[#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
[#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
[#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
[#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
method chains inside `map`
[#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
* [`needless_return`]: Now also lints on macro expressions in return statements
[#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
should extend the default and not replace it
[#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
config should extend the default and not replace it
[#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
* [`never_loop`]: Now checks for `continue` in struct expression
[#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
### False Positive Fixes
* [`useless_transmute`]: No longer lints on types with erased regions
[#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
* [`vec_init_then_push`]: No longer lints when further extended
[#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
[#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
* [`redundant_allocation`]: No longer lints on fat pointers that would become
thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
* [`derive_partial_eq_without_eq`]:
* Handle differing predicates applied by `#[derive(PartialEq)]` and
`#[derive(Eq)]`
[#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
* No longer lints on non-public types and better handles generics
[#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
* [`branches_sharing_code`]: No longer lints when using different binding names
[#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
* [`checked_conversions`]: No longer lints in `const` contexts
[#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
`T::Item` doesn't implement `IntoIterator`
[#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
### Suggestion Fixes/Improvements
* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
[#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
* [`manual_range_contains`]: Fix suggestion for integers with different signs
[#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
* [`identity_op`]: Add parenthesis to suggestions where required
[#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
[#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
* [`rc_clone_in_vec_init`]: Add suggestion
[#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
* The "unknown field" error messages for config files now wraps the field names
[#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
[#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
references and not trivially clone-able
[#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
`iter_mut()` or `into_iter()`
[#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
### ICE Fixes
* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
[#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
* Fix ICEs on callable `static`/`const`s
[#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
* [`needless_late_init`]
[#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
* Fix ICE in shadow lints
[#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
### Documentation Improvements
* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
[#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
* Add a *copy lint name*-button to Clippy's lint list
[#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
* Display past names of renamed lints on Clippy's lint list
[#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
* Add the ability to show the lint output in the lint list
[#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
## Rust 1.62
Current stable, released 2022-06-30
Released 2022-06-30
[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
@ -3481,6 +3627,7 @@ Released 2018-09-13
[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
@ -3496,6 +3643,7 @@ Released 2018-09-13
[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
@ -3656,6 +3804,8 @@ Released 2018-09-13
[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
@ -3697,6 +3847,7 @@ Released 2018-09-13
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
@ -3747,6 +3898,7 @@ Released 2018-09-13
[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
@ -3827,6 +3979,7 @@ Released 2018-09-13
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
@ -3872,6 +4025,7 @@ Released 2018-09-13
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
@ -3930,6 +4084,7 @@ Released 2018-09-13
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
@ -4002,6 +4157,7 @@ Released 2018-09-13
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit

View File

@ -37,7 +37,7 @@ fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
return;
}
let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
if test_output_file != reference_file {

View File

@ -46,7 +46,7 @@ pub fn run(check: bool, verbose: bool) {
// dependency
if fs::read_to_string(project_root.join("Cargo.toml"))
.expect("Failed to read clippy Cargo.toml")
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
{
return Err(CliError::IntellijSetupActive);
}
@ -193,10 +193,10 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
let args = &["--version"];
if context.verbose {
println!("{}", format_command(&program, &dir, args));
println!("{}", format_command(program, &dir, args));
}
let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?;
if output.status.success() {
Ok(())
@ -207,7 +207,7 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
Err(CliError::RustfmtNotInstalled)
} else {
Err(CliError::CommandFailed(
format_command(&program, &dir, args),
format_command(program, &dir, args),
std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
))
}

View File

@ -155,7 +155,7 @@ fn to_camel_case(name: &str) -> String {
name.split('_')
.map(|s| {
if s.is_empty() {
String::from("")
String::new()
} else {
[&s[0..1].to_uppercase(), &s[1..]].concat()
}

View File

@ -418,7 +418,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
.expect("failed to find `impl_lint_pass` terminator");
impl_lint_pass_end += impl_lint_pass_start;
if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
for c in content[lint_name_end..impl_lint_pass_end].chars() {
// Remove trailing whitespace
@ -451,7 +451,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
}
let mut content =
fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
eprintln!(
"warn: you will have to manually remove any code related to `{}` from `{}`",

View File

@ -1,74 +0,0 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Check for the usage of `as _` conversion using inferred type.
///
/// ### Why is this bad?
/// The conversion might include lossy conversion and dangerous cast that might go
/// undetected du to the type being inferred.
///
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
///
/// ### Example
/// ```rust
/// fn foo(n: usize) {}
/// let n: u16 = 256;
/// foo(n as _);
/// ```
/// Use instead:
/// ```rust
/// fn foo(n: usize) {}
/// let n: u16 = 256;
/// foo(n as usize);
/// ```
#[clippy::version = "1.63.0"]
pub AS_UNDERSCORE,
restriction,
"detects `as _` conversion"
}
declare_lint_pass!(AsUnderscore => [AS_UNDERSCORE]);
impl<'tcx> LateLintPass<'tcx> for AsUnderscore {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
if in_external_macro(cx.sess(), expr.span) {
return;
}
if let ExprKind::Cast(_, ty) = expr.kind && let TyKind::Infer = ty.kind {
let ty_resolved = cx.typeck_results().expr_ty(expr);
if let ty::Error(_) = ty_resolved.kind() {
span_lint_and_help(
cx,
AS_UNDERSCORE,
expr.span,
"using `as _` conversion",
None,
"consider giving the type explicitly",
);
} else {
span_lint_and_then(
cx,
AS_UNDERSCORE,
expr.span,
"using `as _` conversion",
|diag| {
diag.span_suggestion(
ty.span,
"consider giving the type explicitly",
ty_resolved,
Applicability::MachineApplicable,
);
}
);
}
}
}
}

View File

@ -1,99 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_no_std_crate;
use clippy_utils::source::snippet_opt;
use clippy_utils::{meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of `&expr as *const T` or
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
/// `ptr::addr_of_mut` instead.
///
/// ### Why is this bad?
/// This would improve readability and avoid creating a reference
/// that points to an uninitialized value or unaligned place.
/// Read the `ptr::addr_of` docs for more information.
///
/// ### Example
/// ```rust
/// let val = 1;
/// let p = &val as *const i32;
///
/// let mut val_mut = 1;
/// let p_mut = &mut val_mut as *mut i32;
/// ```
/// Use instead:
/// ```rust
/// let val = 1;
/// let p = std::ptr::addr_of!(val);
///
/// let mut val_mut = 1;
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
/// ```
#[clippy::version = "1.60.0"]
pub BORROW_AS_PTR,
pedantic,
"borrowing just to cast to a raw pointer"
}
impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]);
pub struct BorrowAsPtr {
msrv: Option<RustcVersion>,
}
impl BorrowAsPtr {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if !meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
return;
}
if expr.span.from_expansion() {
return;
}
if_chain! {
if let ExprKind::Cast(left_expr, ty) = &expr.kind;
if let TyKind::Ptr(_) = ty.kind;
if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind;
then {
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
let macro_name = match mutability {
Mutability::Not => "addr_of",
Mutability::Mut => "addr_of_mut",
};
span_lint_and_sugg(
cx,
BORROW_AS_PTR,
expr.span,
"borrow as raw pointer",
"try",
format!(
"{}::ptr::{}!({})",
core_or_std,
macro_name,
snippet_opt(cx, e.span).unwrap()
),
Applicability::MachineApplicable,
);
}
}
}
extract_msrv_attr!(LateContext);
}

View File

@ -29,22 +29,17 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// fn foo(_x: &str) {}
///
/// let s = &String::new();
///
/// let a: &String = &* s;
/// foo(&*s);
/// ```
///
/// Use instead:
/// ```rust
/// # fn foo(_x: &str) {}
/// # let s = &String::new();
/// let a: &String = s;
/// foo(&**s);
/// ```
#[clippy::version = "1.59.0"]
#[clippy::version = "1.63.0"]
pub BORROW_DEREF_REF,
complexity,
"deref on an immutable reference returns the same type as itself"

View File

@ -1,103 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::match_type;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, UintTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks for naive byte counts
///
/// ### Why is this bad?
/// The [`bytecount`](https://crates.io/crates/bytecount)
/// crate has methods to count your bytes faster, especially for large slices.
///
/// ### Known problems
/// If you have predominantly small slices, the
/// `bytecount::count(..)` method may actually be slower. However, if you can
/// ensure that less than 2³²-1 matches arise, the `naive_count_32(..)` can be
/// faster in those cases.
///
/// ### Example
/// ```rust
/// # let vec = vec![1_u8];
/// let count = vec.iter().filter(|x| **x == 0u8).count();
/// ```
///
/// Use instead:
/// ```rust,ignore
/// # let vec = vec![1_u8];
/// let count = bytecount::count(&vec, 0u8);
/// ```
#[clippy::version = "pre 1.29.0"]
pub NAIVE_BYTECOUNT,
pedantic,
"use of naive `<slice>.filter(|&x| x == y).count()` to count byte values"
}
declare_lint_pass!(ByteCount => [NAIVE_BYTECOUNT]);
impl<'tcx> LateLintPass<'tcx> for ByteCount {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::MethodCall(count, [count_recv], _) = expr.kind;
if count.ident.name == sym::count;
if let ExprKind::MethodCall(filter, [filter_recv, filter_arg], _) = count_recv.kind;
if filter.ident.name == sym!(filter);
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
let body = cx.tcx.hir().body(body);
if let [param] = body.params;
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
if op.node == BinOpKind::Eq;
if match_type(cx,
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
&paths::SLICE_ITER);
let operand_is_arg = |expr| {
let expr = peel_ref_operators(cx, peel_blocks(expr));
path_to_local_id(expr, arg_id)
};
let needle = if operand_is_arg(l) {
r
} else if operand_is_arg(r) {
l
} else {
return;
};
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
if !is_local_used(cx, needle, arg_id);
then {
let haystack = if let ExprKind::MethodCall(path, args, _) =
filter_recv.kind {
let p = path.ident.name;
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
&args[0]
} else {
filter_recv
}
} else {
filter_recv
};
let mut applicability = Applicability::MaybeIncorrect;
span_lint_and_sugg(
cx,
NAIVE_BYTECOUNT,
expr.span,
"you appear to be counting bytes the naive way",
"consider using the bytecount crate",
format!("bytecount::count({}, {})",
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
applicability,
);
}
};
}
}

View File

@ -1,70 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// It checks for `str::bytes().count()` and suggests replacing it with
/// `str::len()`.
///
/// ### Why is this bad?
/// `str::bytes().count()` is longer and may not be as performant as using
/// `str::len()`.
///
/// ### Example
/// ```rust
/// "hello".bytes().count();
/// String::from("hello").bytes().count();
/// ```
/// Use instead:
/// ```rust
/// "hello".len();
/// String::from("hello").len();
/// ```
#[clippy::version = "1.62.0"]
pub BYTES_COUNT_TO_LEN,
complexity,
"Using `bytes().count()` when `len()` performs the same functionality"
}
declare_lint_pass!(BytesCountToLen => [BYTES_COUNT_TO_LEN]);
impl<'tcx> LateLintPass<'tcx> for BytesCountToLen {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
if let hir::ExprKind::MethodCall(_, expr_args, _) = &expr.kind;
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, expr_def_id, &paths::ITER_COUNT);
if let [bytes_expr] = &**expr_args;
if let hir::ExprKind::MethodCall(_, bytes_args, _) = &bytes_expr.kind;
if let Some(bytes_def_id) = cx.typeck_results().type_dependent_def_id(bytes_expr.hir_id);
if match_def_path(cx, bytes_def_id, &paths::STR_BYTES);
if let [str_expr] = &**bytes_args;
let ty = cx.typeck_results().expr_ty(str_expr).peel_refs();
if is_type_diagnostic_item(cx, ty, sym::String) || ty.kind() == &ty::Str;
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
BYTES_COUNT_TO_LEN,
expr.span,
"using long and hard to read `.bytes().count()`",
"consider calling `.len()` instead",
format!("{}.len()", snippet_with_applicability(cx, str_expr.span, "..", &mut applicability)),
applicability
);
}
};
}
}

View File

@ -1,86 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_help;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind, PathSegment};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{source_map::Spanned, symbol::sym, Span};
declare_clippy_lint! {
/// ### What it does
/// Checks for calls to `ends_with` with possible file extensions
/// and suggests to use a case-insensitive approach instead.
///
/// ### Why is this bad?
/// `ends_with` is case-sensitive and may not detect files with a valid extension.
///
/// ### Example
/// ```rust
/// fn is_rust_file(filename: &str) -> bool {
/// filename.ends_with(".rs")
/// }
/// ```
/// Use instead:
/// ```rust
/// fn is_rust_file(filename: &str) -> bool {
/// let filename = std::path::Path::new(filename);
/// filename.extension()
/// .map(|ext| ext.eq_ignore_ascii_case("rs"))
/// .unwrap_or(false)
/// }
/// ```
#[clippy::version = "1.51.0"]
pub CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
pedantic,
"Checks for calls to ends_with with case-sensitive file extensions"
}
declare_lint_pass!(CaseSensitiveFileExtensionComparisons => [CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS]);
fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
if_chain! {
if let ExprKind::MethodCall(PathSegment { ident, .. }, [obj, extension, ..], span) = expr.kind;
if ident.as_str() == "ends_with";
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = extension.kind;
if (2..=6).contains(&ext_literal.as_str().len());
if ext_literal.as_str().starts_with('.');
if ext_literal.as_str().chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|| ext_literal.as_str().chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
then {
let mut ty = ctx.typeck_results().expr_ty(obj);
ty = match ty.kind() {
ty::Ref(_, ty, ..) => *ty,
_ => ty
};
match ty.kind() {
ty::Str => {
return Some(span);
},
ty::Adt(def, _) => {
if ctx.tcx.is_diagnostic_item(sym::String, def.did()) {
return Some(span);
}
},
_ => { return None; }
}
}
}
None
}
impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons {
fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) {
span_lint_and_help(
ctx,
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
span,
"case-sensitive file extension comparison",
None,
"consider using a case-insensitive comparison instead",
);
}
}
}

View File

@ -0,0 +1,25 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Ty, TyKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use super::AS_UNDERSCORE;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tcx Ty<'_>) {
if matches!(ty.kind, TyKind::Infer) {
span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
let ty_resolved = cx.typeck_results().expr_ty(expr);
if let ty::Error(_) = ty_resolved.kind() {
diag.help("consider giving the type explicitly");
} else {
diag.span_suggestion(
ty.span,
"consider giving the type explicitly",
ty_resolved,
Applicability::MachineApplicable,
);
}
});
}
}

View File

@ -0,0 +1,37 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_no_std_crate;
use clippy_utils::source::snippet_with_context;
use rustc_errors::Applicability;
use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
use rustc_lint::LateContext;
use super::BORROW_AS_PTR;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
cast_expr: &'tcx Expr<'_>,
cast_to: &'tcx Ty<'_>,
) {
if matches!(cast_to.kind, TyKind::Ptr(_))
&& let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
{
let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
let macro_name = match mutability {
Mutability::Not => "addr_of",
Mutability::Mut => "addr_of_mut",
};
let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
span_lint_and_sugg(
cx,
BORROW_AS_PTR,
expr.span,
"borrow as raw pointer",
"try",
format!("{}::ptr::{}!({})", core_or_std, macro_name, snip),
Applicability::MachineApplicable,
);
}
}

View File

@ -0,0 +1,63 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{match_def_path, meets_msrv, msrvs, paths};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{def_id::DefId, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty};
use rustc_semver::RustcVersion;
use super::CAST_SLICE_FROM_RAW_PARTS;
enum RawPartsKind {
Immutable,
Mutable,
}
fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS) {
Some(RawPartsKind::Immutable)
} else if match_def_path(cx, did, &paths::SLICE_FROM_RAW_PARTS_MUT) {
Some(RawPartsKind::Mutable)
} else {
None
}
}
pub(super) fn check(
cx: &LateContext<'_>,
expr: &Expr<'_>,
cast_expr: &Expr<'_>,
cast_to: Ty<'_>,
msrv: Option<RustcVersion>,
) {
if_chain! {
if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS);
if let ty::RawPtr(ptrty) = cast_to.kind();
if let ty::Slice(_) = ptrty.ty.kind();
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind;
if let ExprKind::Path(ref qpath) = fun.kind;
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
if let Some(rpk) = raw_parts_kind(cx, fun_def_id);
then {
let func = match rpk {
RawPartsKind::Immutable => "from_raw_parts",
RawPartsKind::Mutable => "from_raw_parts_mut"
};
let span = expr.span;
let mut applicability = Applicability::MachineApplicable;
let ptr = snippet_with_applicability(cx, ptr_arg.span, "ptr", &mut applicability);
let len = snippet_with_applicability(cx, len_arg.span, "len", &mut applicability);
span_lint_and_sugg(
cx,
CAST_SLICE_FROM_RAW_PARTS,
span,
&format!("casting the result of `{func}` to {cast_to}"),
"replace with",
format!("core::ptr::slice_{func}({ptr}, {len})"),
applicability
);
}
}
}

View File

@ -1,3 +1,5 @@
mod as_underscore;
mod borrow_as_ptr;
mod cast_abs_to_unsigned;
mod cast_enum_constructor;
mod cast_lossless;
@ -8,6 +10,7 @@ mod cast_ptr_alignment;
mod cast_ref_to_mut;
mod cast_sign_loss;
mod cast_slice_different_sizes;
mod cast_slice_from_raw_parts;
mod char_lit_as_u8;
mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any;
@ -16,7 +19,7 @@ mod ptr_as_ptr;
mod unnecessary_cast;
mod utils;
use clippy_utils::is_hir_ty_cfg_dependant;
use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
@ -506,6 +509,93 @@ declare_clippy_lint! {
"casting the result of `abs()` to an unsigned integer can panic"
}
declare_clippy_lint! {
/// ### What it does
/// Check for the usage of `as _` conversion using inferred type.
///
/// ### Why is this bad?
/// The conversion might include lossy conversion and dangerous cast that might go
/// undetected due to the type being inferred.
///
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
///
/// ### Example
/// ```rust
/// fn foo(n: usize) {}
/// let n: u16 = 256;
/// foo(n as _);
/// ```
/// Use instead:
/// ```rust
/// fn foo(n: usize) {}
/// let n: u16 = 256;
/// foo(n as usize);
/// ```
#[clippy::version = "1.63.0"]
pub AS_UNDERSCORE,
restriction,
"detects `as _` conversion"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of `&expr as *const T` or
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
/// `ptr::addr_of_mut` instead.
///
/// ### Why is this bad?
/// This would improve readability and avoid creating a reference
/// that points to an uninitialized value or unaligned place.
/// Read the `ptr::addr_of` docs for more information.
///
/// ### Example
/// ```rust
/// let val = 1;
/// let p = &val as *const i32;
///
/// let mut val_mut = 1;
/// let p_mut = &mut val_mut as *mut i32;
/// ```
/// Use instead:
/// ```rust
/// let val = 1;
/// let p = std::ptr::addr_of!(val);
///
/// let mut val_mut = 1;
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
/// ```
#[clippy::version = "1.60.0"]
pub BORROW_AS_PTR,
pedantic,
"borrowing just to cast to a raw pointer"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for a raw slice being cast to a slice pointer
///
/// ### Why is this bad?
/// This can result in multiple `&mut` references to the same location when only a pointer is
/// required.
/// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
/// the same [safety requirements] to be upheld.
///
/// ### Example
/// ```rust,ignore
/// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
/// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
/// ```
/// Use instead:
/// ```rust,ignore
/// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
/// ```
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
#[clippy::version = "1.64.0"]
pub CAST_SLICE_FROM_RAW_PARTS,
suspicious,
"casting a slice created from a pointer and length to a slice pointer"
}
pub struct Casts {
msrv: Option<RustcVersion>,
}
@ -534,7 +624,10 @@ impl_lint_pass!(Casts => [
PTR_AS_PTR,
CAST_ENUM_TRUNCATION,
CAST_ENUM_CONSTRUCTOR,
CAST_ABS_TO_UNSIGNED
CAST_ABS_TO_UNSIGNED,
AS_UNDERSCORE,
BORROW_AS_PTR,
CAST_SLICE_FROM_RAW_PARTS
]);
impl<'tcx> LateLintPass<'tcx> for Casts {
@ -547,8 +640,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
return;
}
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
if is_hir_ty_cfg_dependant(cx, cast_to) {
if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
return;
}
let (cast_from, cast_to) = (
@ -559,7 +652,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
return;
}
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
@ -575,6 +668,12 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
}
as_underscore::check(cx, expr, cast_to_hir);
if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
}
}
cast_ref_to_mut::check(cx, expr);

View File

@ -90,13 +90,20 @@ pub(super) fn check<'tcx>(
fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
let replaced_literal;
let matchless = if literal_str.contains(['(', ')']) {
replaced_literal = literal_str.replace(['(', ')'], "");
&replaced_literal
} else {
literal_str
};
span_lint_and_sugg(
cx,
UNNECESSARY_CAST,
expr.span,
&format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
"try",
format!("{}_{}", literal_str.trim_end_matches('.'), cast_to),
format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
Applicability::MachineApplicable,
);
}

View File

@ -1,24 +1,34 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage};
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
use clippy_utils::{
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
walk_to_expr_usage,
};
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
TraitItemKind, TyKind, UnOp,
self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
};
use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
use rustc_middle::ty::{
self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
};
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
use rustc_trait_selection::infer::InferCtxtExt;
use rustc_trait_selection::infer::InferCtxtExt as _;
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
use std::collections::VecDeque;
declare_clippy_lint! {
/// ### What it does
@ -151,6 +161,7 @@ pub struct Dereferencing {
/// been finished. Note we can't lint at the end of every body as they can be nested within each
/// other.
current_body: Option<BodyId>,
/// The list of locals currently being checked by the lint.
/// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
/// This is needed for or patterns where one of the branches can be linted, but another can not
@ -158,6 +169,19 @@ pub struct Dereferencing {
///
/// e.g. `m!(x) | Foo::Bar(ref x)`
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
// `IntoIterator` for arrays requires Rust 1.53.
msrv: Option<RustcVersion>,
}
impl Dereferencing {
#[must_use]
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self {
msrv,
..Dereferencing::default()
}
}
}
struct StateData {
@ -170,6 +194,7 @@ struct StateData {
struct DerefedBorrow {
count: usize,
msg: &'static str,
snip_expr: Option<HirId>,
}
enum State {
@ -250,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
match (self.state.take(), kind) {
(None, kind) => {
let expr_ty = typeck.expr_ty(expr);
let (position, adjustments) = walk_parents(cx, expr);
let (position, adjustments) = walk_parents(cx, expr, self.msrv);
match kind {
RefOp::Deref => {
@ -331,20 +356,23 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
let deref_msg =
"this expression creates a reference which is immediately dereferenced by the compiler";
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
let impl_msg = "the borrowed expression implements the required traits";
let (required_refs, msg) = if position.can_auto_borrow() {
(1, if deref_count == 1 { borrow_msg } else { deref_msg })
let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
(1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
} else if let Position::ImplArg(hir_id) = position {
(0, impl_msg, Some(hir_id))
} else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
next_adjust.map(|a| &a.kind)
{
if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
{
(3, deref_msg)
(3, deref_msg, None)
} else {
(2, deref_msg)
(2, deref_msg, None)
}
} else {
(2, deref_msg)
(2, deref_msg, None)
};
if deref_count >= required_refs {
@ -354,6 +382,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
// can't be removed without breaking the code. See earlier comment.
count: deref_count - required_refs,
msg,
snip_expr,
}),
StateData { span: expr.span, hir_id: expr.hir_id, position },
));
@ -510,7 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
spans: vec![pat.span],
app,
replacements: vec![(pat.span, snip.into())],
hir_id: pat.hir_id
hir_id: pat.hir_id,
}),
);
}
@ -542,6 +571,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
self.current_body = None;
}
}
extract_msrv_attr!(LateContext);
}
fn try_parse_ref_op<'tcx>(
@ -594,6 +625,7 @@ enum Position {
/// The method is defined on a reference type. e.g. `impl Foo for &T`
MethodReceiverRefImpl,
Callee,
ImplArg(HirId),
FieldAccess(Symbol),
Postfix,
Deref,
@ -630,7 +662,7 @@ impl Position {
| Self::Callee
| Self::FieldAccess(_)
| Self::Postfix => PREC_POSTFIX,
Self::Deref => PREC_PREFIX,
Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
}
}
@ -639,8 +671,12 @@ impl Position {
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
/// locations as those follow different rules.
#[allow(clippy::too_many_lines)]
fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
#[expect(clippy::too_many_lines)]
fn walk_parents<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>,
msrv: Option<RustcVersion>,
) -> (Position, &'tcx [Adjustment<'tcx>]) {
let mut adjustments = [].as_slice();
let mut precedence = 0i8;
let ctxt = e.span.ctxt();
@ -745,13 +781,20 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
.iter()
.position(|arg| arg.hir_id == child_id)
.zip(expr_sig(cx, func))
.and_then(|(i, sig)| sig.input_with_hir(i))
.map(|(hir_ty, ty)| match hir_ty {
// Type inference for closures can depend on how they're called. Only go by the explicit
// types here.
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
.position_for_arg(),
.and_then(|(i, sig)| {
sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
// Type inference for closures can depend on how they're called. Only go by the explicit
// types here.
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
None => {
if let ty::Param(param_ty) = ty.skip_binder().kind() {
needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
} else {
ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
.position_for_arg()
}
},
})
}),
ExprKind::MethodCall(_, args, _) => {
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
@ -773,7 +816,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
.and_then(|subs| subs.get(1..))
{
Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
None => cx.tcx.mk_substs([].iter()),
None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
} && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
// Trait methods taking `&self`
sub_ty
@ -792,12 +835,17 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
Position::MethodReceiver
}
} else {
ty_auto_deref_stability(
cx,
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
precedence,
)
.position_for_arg()
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
if let ty::Param(param_ty) = ty.kind() {
needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
} else {
ty_auto_deref_stability(
cx,
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
precedence,
)
.position_for_arg()
}
}
})
},
@ -948,6 +996,205 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
v.0
}
// Checks whether:
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
// * `e`'s type implements `Trait` and is copyable
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
// be moved, but it cannot be.
fn needless_borrow_impl_arg_position<'tcx>(
cx: &LateContext<'tcx>,
parent: &Expr<'tcx>,
arg_index: usize,
param_ty: ParamTy,
mut expr: &Expr<'tcx>,
precedence: i8,
msrv: Option<RustcVersion>,
) -> Position {
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
let substs_with_expr_ty = cx
.typeck_results()
.node_substs(if let ExprKind::Call(callee, _) = parent.kind {
callee.hir_id
} else {
parent.hir_id
});
let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
let projection_predicates = predicates
.iter()
.filter_map(|predicate| {
if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
Some(projection_predicate)
} else {
None
}
})
.collect::<Vec<_>>();
let mut trait_with_ref_mut_self_method = false;
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
if predicates
.iter()
.filter_map(|predicate| {
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
{
Some(trait_predicate.trait_ref.def_id)
} else {
None
}
})
.inspect(|trait_def_id| {
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
})
.all(|trait_def_id| {
Some(trait_def_id) == destruct_trait_def_id
|| Some(trait_def_id) == sized_trait_def_id
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
})
{
return Position::Other(precedence);
}
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
// elements are modified each time `check_referent` is called.
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
let mut check_referent = |referent| {
let referent_ty = cx.typeck_results().expr_ty(referent);
if !is_copy(cx, referent_ty) {
return false;
}
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
return false;
}
if !replace_types(
cx,
param_ty,
referent_ty,
fn_sig,
arg_index,
&projection_predicates,
&mut substs_with_referent_ty,
) {
return false;
}
predicates.iter().all(|predicate| {
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
&& let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
&& ty.is_array()
&& !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
{
return false;
}
let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
cx.tcx
.infer_ctxt()
.enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
})
};
let mut needless_borrow = false;
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
if !check_referent(referent) {
break;
}
expr = referent;
needless_borrow = true;
}
if needless_borrow {
Position::ImplArg(expr.hir_id)
} else {
Position::Other(precedence)
}
}
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
cx.tcx
.associated_items(trait_def_id)
.in_definition_order()
.any(|assoc_item| {
if assoc_item.fn_has_self_parameter {
let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
} else {
false
}
})
}
// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
// projected type that is a type parameter. Returns `false` if replacing the types would have an
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
fn replace_types<'tcx>(
cx: &LateContext<'tcx>,
param_ty: ParamTy,
new_ty: Ty<'tcx>,
fn_sig: FnSig<'tcx>,
arg_index: usize,
projection_predicates: &[ProjectionPredicate<'tcx>],
substs: &mut [ty::GenericArg<'tcx>],
) -> bool {
let mut replaced = BitSet::new_empty(substs.len());
let mut deque = VecDeque::with_capacity(substs.len());
deque.push_back((param_ty, new_ty));
while let Some((param_ty, new_ty)) = deque.pop_front() {
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
if !fn_sig
.inputs_and_output
.iter()
.enumerate()
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
{
return false;
}
substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
// The `replaced.insert(...)` check provides some protection against infinite loops.
if replaced.insert(param_ty.index) {
for projection_predicate in projection_predicates {
if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
&& let ty::Term::Ty(term_ty) = projection_predicate.term
&& let ty::Param(term_param_ty) = term_ty.kind()
{
let item_def_id = projection_predicate.projection_ty.item_def_id;
let assoc_item = cx.tcx.associated_item(item_def_id);
let projection = cx.tcx
.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
&& substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
{
deque.push_back((*term_param_ty, projected_ty));
}
}
}
}
}
true
}
struct TyPosition<'tcx> {
position: Position,
ty: Option<Ty<'tcx>>,
@ -1086,7 +1333,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
},
State::DerefedBorrow(state) => {
let mut app = Applicability::MachineApplicable;
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
let sugg = if !snip_is_macro

View File

@ -516,7 +516,10 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
trait_ref: TraitRef::new(
eq_trait_id,
tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
),
constness: BoundConstness::NotConst,
polarity: ImplPolarity::Positive,
})))

View File

@ -21,7 +21,7 @@ declare_clippy_lint! {
/// /// See also: [`foo`]
/// fn bar() {}
/// ```
#[clippy::version = "1.60.0"]
#[clippy::version = "1.63.0"]
pub DOC_LINK_WITH_QUOTES,
pedantic,
"possible typo for an intra-doc link"

View File

@ -39,7 +39,7 @@ declare_clippy_lint! {
/// // a.rs
/// use crate::b;
/// ```
#[clippy::version = "1.62.0"]
#[clippy::version = "1.63.0"]
pub DUPLICATE_MOD,
suspicious,
"file loaded as module multiple times"

View File

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_hir;
use clippy_utils::ty::contains_ty;
use rustc_hir::intravisit;
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
use rustc_infer::infer::TyCtxtInferExt;
@ -30,18 +29,12 @@ declare_clippy_lint! {
///
/// ### Example
/// ```rust
/// # fn foo(bar: usize) {}
/// let x = Box::new(1);
/// foo(*x);
/// println!("{}", *x);
/// fn foo(x: Box<u32>) {}
/// ```
///
/// Use instead:
/// ```rust
/// # fn foo(bar: usize) {}
/// let x = 1;
/// foo(x);
/// println!("{}", x);
/// fn foo(x: u32) {}
/// ```
#[clippy::version = "pre 1.29.0"]
pub BOXED_LOCAL,
@ -172,7 +165,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
// skip if there is a `self` parameter binding to a type
// that contains `Self` (i.e.: `self: Box<Self>`), see #4804
if let Some(trait_self_ty) = self.trait_self_ty {
if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
return;
}
}

View File

@ -172,7 +172,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
expr.span,
"logarithm for bases 2, 10 and e can be computed more accurately",
"consider using",
format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
Applicability::MachineApplicable,
);
}
@ -263,13 +263,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
(
SUBOPTIMAL_FLOPS,
"square-root of a number can be computed more efficiently and accurately",
format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
)
} else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
(
IMPRECISE_FLOPS,
"cube-root of a number can be computed more accurately",
format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
)
} else if let Some(exponent) = get_integer_from_float_constant(&value) {
(
@ -277,7 +277,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
"exponentiation with integer powers can be computed more efficiently",
format!(
"{}.powi({})",
Sugg::hir(cx, &args[0], ".."),
Sugg::hir(cx, &args[0], "..").maybe_par(),
numeric_literal::format(&exponent.to_string(), None, false)
),
)
@ -327,7 +327,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
"consider using",
format!(
"{}.mul_add({}, {})",
Sugg::hir(cx, &args[0], ".."),
Sugg::hir(cx, &args[0], "..").maybe_par(),
Sugg::hir(cx, &args[0], ".."),
Sugg::hir(cx, other_addend, ".."),
),
@ -418,7 +418,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
"consider using",
format!(
"{}.exp_m1()",
Sugg::hir(cx, self_arg, "..")
Sugg::hir(cx, self_arg, "..").maybe_par()
),
Applicability::MachineApplicable,
);
@ -550,11 +550,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
then {
let positive_abs_sugg = (
"manual implementation of `abs` method",
format!("{}.abs()", Sugg::hir(cx, body, "..")),
format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
);
let negative_abs_sugg = (
"manual implementation of negation of `abs` method",
format!("-{}.abs()", Sugg::hir(cx, body, "..")),
format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
);
let sugg = if is_testing_positive(cx, cond, body) {
if if_expr_positive {
@ -621,7 +621,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
expr.span,
"log base can be expressed more clearly",
"consider using",
format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
Applicability::MachineApplicable,
);
}
@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
(F32(180_f32) == lvalue || F64(180_f64) == lvalue)
{
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;
@ -677,7 +677,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
{
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node;

View File

@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -56,29 +56,27 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
};
let mut applicability = Applicability::MachineApplicable;
if format_args.value_args.is_empty() {
match *format_args.format_string_parts {
if format_args.args.is_empty() {
match *format_args.format_string.parts {
[] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
[_] => {
if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
// Simulate macro expansion, converting {{ and }} to { and }.
let s_expand = s_src.replace("{{", "{").replace("}}", "}");
let sugg = format!("{}.to_string()", s_expand);
span_useless_format(cx, call_site, sugg, applicability);
}
// Simulate macro expansion, converting {{ and }} to { and }.
let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
let sugg = format!("{}.to_string()", s_expand);
span_useless_format(cx, call_site, sugg, applicability);
},
[..] => {},
}
} else if let [value] = *format_args.value_args {
} else if let [arg] = &*format_args.args {
let value = arg.param.value;
if_chain! {
if format_args.format_string_parts == [kw::Empty];
if format_args.format_string.parts == [kw::Empty];
if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
ty::Str => true,
_ => false,
};
if let Some(args) = format_args.args();
if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
if !arg.format.has_string_formatting();
then {
let is_new_string = match value.kind {
ExprKind::Binary(..) => true,

View File

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item;
use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::implements_trait;
use if_chain::if_chain;
use itertools::Itertools;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty;
@ -74,20 +75,16 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
if is_format_macro(cx, macro_def_id);
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
if let Some(args) = format_args.args();
then {
for (i, arg) in args.iter().enumerate() {
if arg.format_trait != sym::Display {
for arg in &format_args.args {
if arg.format.has_string_formatting() {
continue;
}
if arg.has_string_formatting() {
if is_aliased(&format_args, arg.param.value.hir_id) {
continue;
}
if is_aliased(&args, i) {
continue;
}
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
check_to_string_in_format_args(cx, name, arg.value);
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
check_to_string_in_format_args(cx, name, arg.param.value);
}
}
}
@ -134,45 +131,56 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
if is_diag_trait_item(cx, method_def_id, sym::ToString);
let receiver_ty = cx.typeck_results().expr_ty(receiver);
if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
let (n_needed_derefs, target) =
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
if implements_trait(cx, target, display_trait_id, &[]);
if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then {
let (n_needed_derefs, target) = count_needed_derefs(
receiver_ty,
cx.typeck_results().expr_adjustments(receiver).iter(),
);
if implements_trait(cx, target, display_trait_id, &[]) {
if n_needed_derefs == 0 {
span_lint_and_sugg(
cx,
TO_STRING_IN_FORMAT_ARGS,
value.span.with_lo(receiver.span.hi()),
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
"remove this",
String::new(),
Applicability::MachineApplicable,
);
} else {
span_lint_and_sugg(
cx,
TO_STRING_IN_FORMAT_ARGS,
value.span,
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
"use this",
format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs),
Applicability::MachineApplicable,
);
}
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
if n_needed_derefs == 0 && !needs_ref {
span_lint_and_sugg(
cx,
TO_STRING_IN_FORMAT_ARGS,
value.span.with_lo(receiver.span.hi()),
&format!(
"`to_string` applied to a type that implements `Display` in `{}!` args",
name
),
"remove this",
String::new(),
Applicability::MachineApplicable,
);
} else {
span_lint_and_sugg(
cx,
TO_STRING_IN_FORMAT_ARGS,
value.span,
&format!(
"`to_string` applied to a type that implements `Display` in `{}!` args",
name
),
"use this",
format!(
"{}{:*>width$}{}",
if needs_ref { "&" } else { "" },
"",
receiver_snippet,
width = n_needed_derefs
),
Applicability::MachineApplicable,
);
}
}
}
}
// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
let value = args[i].value;
args.iter()
.enumerate()
.any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
// Returns true if `hir_id` is referred to by multiple format params
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
args.params()
.filter(|param| param.value.hir_id == hir_id)
.at_most_one()
.is_err()
}
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
use if_chain::if_chain;
use rustc_errors::Applicability;
@ -168,10 +168,9 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
if let macro_def_id = outer_macro.def_id;
if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
if is_format_macro(cx, macro_def_id);
if let Some(args) = format_args.args();
then {
for arg in args {
if arg.format_trait != impl_trait.name {
for arg in format_args.args {
if arg.format.r#trait != impl_trait.name {
continue;
}
check_format_arg_self(cx, expr, &arg, impl_trait);
@ -180,11 +179,11 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
}
}
fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
// Handle multiple dereferencing of references e.g. &&self
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
// Since the argument to fmt is itself a reference: &self
let reference = peel_ref_operators(cx, arg.value);
let reference = peel_ref_operators(cx, arg.param.value);
let map = cx.tcx.hir();
// Is the reference self?
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {

View File

@ -1,6 +1,6 @@
mod must_use;
mod not_unsafe_ptr_arg_deref;
mod result_unit_err;
mod result;
mod too_many_arguments;
mod too_many_lines;
@ -217,17 +217,62 @@ declare_clippy_lint! {
"public function returning `Result` with an `Err` type of `()`"
}
declare_clippy_lint! {
/// ### What it does
/// Checks for functions that return `Result` with an unusually large
/// `Err`-variant.
///
/// ### Why is this bad?
/// A `Result` is at least as large as the `Err`-variant. While we
/// expect that variant to be seldomly used, the compiler needs to reserve
/// and move that much memory every single time.
///
/// ### Known problems
/// The size determined by Clippy is platform-dependent.
///
/// ### Examples
/// ```rust
/// pub enum ParseError {
/// UnparsedBytes([u8; 512]),
/// UnexpectedEof,
/// }
///
/// // The `Result` has at least 512 bytes, even in the `Ok`-case
/// pub fn parse() -> Result<(), ParseError> {
/// Ok(())
/// }
/// ```
/// should be
/// ```
/// pub enum ParseError {
/// UnparsedBytes(Box<[u8; 512]>),
/// UnexpectedEof,
/// }
///
/// // The `Result` is slightly larger than a pointer
/// pub fn parse() -> Result<(), ParseError> {
/// Ok(())
/// }
/// ```
#[clippy::version = "1.64.0"]
pub RESULT_LARGE_ERR,
perf,
"function returning `Result` with large `Err` type"
}
#[derive(Copy, Clone)]
pub struct Functions {
too_many_arguments_threshold: u64,
too_many_lines_threshold: u64,
large_error_threshold: u64,
}
impl Functions {
pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
Self {
too_many_arguments_threshold,
too_many_lines_threshold,
large_error_threshold,
}
}
}
@ -240,6 +285,7 @@ impl_lint_pass!(Functions => [
DOUBLE_MUST_USE,
MUST_USE_CANDIDATE,
RESULT_UNIT_ERR,
RESULT_LARGE_ERR,
]);
impl<'tcx> LateLintPass<'tcx> for Functions {
@ -259,18 +305,18 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
must_use::check_item(cx, item);
result_unit_err::check_item(cx, item);
result::check_item(cx, item, self.large_error_threshold);
}
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
must_use::check_impl_item(cx, item);
result_unit_err::check_impl_item(cx, item);
result::check_impl_item(cx, item, self.large_error_threshold);
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
must_use::check_trait_item(cx, item);
result_unit_err::check_trait_item(cx, item);
result::check_trait_item(cx, item, self.large_error_threshold);
}
}

View File

@ -0,0 +1,100 @@
use rustc_errors::Diagnostic;
use rustc_hir as hir;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::{self, Ty};
use rustc_span::{sym, Span};
use rustc_typeck::hir_ty_to_ty;
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
use clippy_utils::trait_ref_of_method;
use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item};
use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR};
/// The type of the `Err`-variant in a `std::result::Result` returned by the
/// given `FnDecl`
fn result_err_ty<'tcx>(
cx: &LateContext<'tcx>,
decl: &hir::FnDecl<'tcx>,
item_span: Span,
) -> Option<(&'tcx hir::Ty<'tcx>, Ty<'tcx>)> {
if !in_external_macro(cx.sess(), item_span)
&& let hir::FnRetTy::Return(hir_ty) = decl.output
&& let ty = hir_ty_to_ty(cx.tcx, hir_ty)
&& is_type_diagnostic_item(cx, ty, sym::Result)
&& let ty::Adt(_, substs) = ty.kind()
{
let err_ty = substs.type_at(1);
Some((hir_ty, err_ty))
} else {
None
}
}
pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::Item<'tcx>, large_err_threshold: u64) {
if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
{
if cx.access_levels.is_exported(item.def_id) {
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
check_result_unit_err(cx, err_ty, fn_header_span);
}
check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
}
}
pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::ImplItem<'tcx>, large_err_threshold: u64) {
// Don't lint if method is a trait's implementation, we can't do anything about those
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind
&& let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span)
&& trait_ref_of_method(cx, item.def_id).is_none()
{
if cx.access_levels.is_exported(item.def_id) {
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
check_result_unit_err(cx, err_ty, fn_header_span);
}
check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
}
}
pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &hir::TraitItem<'tcx>, large_err_threshold: u64) {
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if let Some((hir_ty, err_ty)) = result_err_ty(cx, sig.decl, item.span) {
if cx.access_levels.is_exported(item.def_id) {
check_result_unit_err(cx, err_ty, fn_header_span);
}
check_result_large_err(cx, err_ty, hir_ty.span, large_err_threshold);
}
}
}
fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: Span) {
if err_ty.is_unit() {
span_lint_and_help(
cx,
RESULT_UNIT_ERR,
fn_header_span,
"this returns a `Result<_, ()>`",
None,
"use a custom `Error` type instead",
);
}
}
fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
let ty_size = approx_ty_size(cx, err_ty);
if ty_size >= large_err_threshold {
span_lint_and_then(
cx,
RESULT_LARGE_ERR,
hir_ty_span,
"the `Err`-variant returned from this function is very large",
|diag: &mut Diagnostic| {
diag.span_label(hir_ty_span, format!("the `Err`-variant is at least {ty_size} bytes"));
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
},
);
}
}

View File

@ -1,66 +0,0 @@
use rustc_hir as hir;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_span::{sym, Span};
use rustc_typeck::hir_ty_to_ty;
use if_chain::if_chain;
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::trait_ref_of_method;
use clippy_utils::ty::is_type_diagnostic_item;
use super::RESULT_UNIT_ERR;
pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
if let hir::ItemKind::Fn(ref sig, _generics, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if is_public {
check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
}
}
}
pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) {
if let hir::ImplItemKind::Fn(ref sig, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if is_public && trait_ref_of_method(cx, item.def_id).is_none() {
check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
}
}
}
pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) {
if let hir::TraitItemKind::Fn(ref sig, _) = item.kind {
let is_public = cx.access_levels.is_exported(item.def_id);
let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
if is_public {
check_result_unit_err(cx, sig.decl, item.span, fn_header_span);
}
}
}
fn check_result_unit_err(cx: &LateContext<'_>, decl: &hir::FnDecl<'_>, item_span: Span, fn_header_span: Span) {
if_chain! {
if !in_external_macro(cx.sess(), item_span);
if let hir::FnRetTy::Return(ty) = decl.output;
let ty = hir_ty_to_ty(cx.tcx, ty);
if is_type_diagnostic_item(cx, ty, sym::Result);
if let ty::Adt(_, substs) = ty.kind();
let err_ty = substs.type_at(1);
if err_ty.is_unit();
then {
span_lint_and_help(
cx,
RESULT_UNIT_ERR,
fn_header_span,
"this returns a `Result<_, ()>`",
None,
"use a custom `Error` type instead",
);
}
}
}

View File

@ -1,68 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_slice_of_primitives, match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Spanned;
declare_clippy_lint! {
/// ### What it does
/// Checks for using `x.get(0)` instead of
/// `x.first()`.
///
/// ### Why is this bad?
/// Using `x.first()` is easier to read and has the same
/// result.
///
/// ### Example
/// ```rust
/// let x = vec![2, 3, 5];
/// let first_element = x.get(0);
/// ```
///
/// Use instead:
/// ```rust
/// let x = vec![2, 3, 5];
/// let first_element = x.first();
/// ```
#[clippy::version = "1.63.0"]
pub GET_FIRST,
style,
"Using `x.get(0)` when `x.first()` is simpler"
}
declare_lint_pass!(GetFirst => [GET_FIRST]);
impl<'tcx> LateLintPass<'tcx> for GetFirst {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! {
if let hir::ExprKind::MethodCall(_, [struct_calling_on, method_arg], _) = &expr.kind;
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if match_def_path(cx, expr_def_id, &paths::SLICE_GET);
if let Some(_) = is_slice_of_primitives(cx, struct_calling_on);
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = method_arg.kind;
then {
let mut applicability = Applicability::MachineApplicable;
let slice_name = snippet_with_applicability(
cx,
struct_calling_on.span, "..",
&mut applicability,
);
span_lint_and_sugg(
cx,
GET_FIRST,
expr.span,
&format!("accessing first element with `{0}.get(0)`", slice_name),
"try",
format!("{}.first()", slice_name),
applicability,
);
}
}
}
}

View File

@ -1,8 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::SpanlessEq;
use if_chain::if_chain;
use rustc_errors::Diagnostic;
use rustc_hir::intravisit::{self as visit, Visitor};
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let mut arm_visit = ArmVisitor {
mutex_lock_called: false,
found_mutex: None,
cx,
};
let mut op_visit = OppVisitor {
mutex_lock_called: false,
found_mutex: None,
cx,
};
let mut arm_visit = ArmVisitor { found_mutex: None, cx };
let mut op_visit = OppVisitor { found_mutex: None, cx };
if let Some(higher::IfLet {
let_expr,
if_then,
@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
}) = higher::IfLet::hir(cx, expr)
{
op_visit.visit_expr(let_expr);
if op_visit.mutex_lock_called {
if let Some(op_mutex) = op_visit.found_mutex {
arm_visit.visit_expr(if_then);
arm_visit.visit_expr(if_else);
if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
span_lint_and_help(
if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
let diag = |diag: &mut Diagnostic| {
diag.span_label(
op_mutex.span,
"this Mutex will remain locked for the entire `if let`-block...",
);
diag.span_label(
arm_mutex.span,
"... and is tried to lock again here, which will always deadlock.",
);
diag.help("move the lock call outside of the `if let ...` expression");
};
span_lint_and_then(
cx,
IF_LET_MUTEX,
expr.span,
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
None,
"move the lock call outside of the `if let ...` expression",
diag,
);
}
}
@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
/// Checks if `Mutex::lock` is called in the `if let` expr.
pub struct OppVisitor<'a, 'tcx> {
mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>,
cx: &'a LateContext<'tcx>,
}
@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
self.found_mutex = Some(mutex);
self.mutex_lock_called = true;
return;
}
visit::walk_expr(self, expr);
@ -102,7 +103,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
/// Checks if `Mutex::lock` is called in any of the branches.
pub struct ArmVisitor<'a, 'tcx> {
mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>,
cx: &'a LateContext<'tcx>,
}
@ -111,7 +111,6 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
self.found_mutex = Some(mutex);
self.mutex_lock_called = true;
return;
}
visit::walk_expr(self, expr);
@ -119,9 +118,12 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
}
impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
self.found_mutex
.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
self.found_mutex.and_then(|arm_mutex| {
SpanlessEq::new(self.cx)
.eq_expr(op_mutex, arm_mutex)
.then_some(arm_mutex)
})
}
}
@ -129,7 +131,7 @@ fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Opt
if_chain! {
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
if path.ident.as_str() == "lock";
let ty = cx.typeck_results().expr_ty(self_arg);
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
if is_type_diagnostic_item(cx, ty, sym::Mutex);
then {
Some(self_arg)

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
use clippy_utils::source::snippet_with_macro_callsite;
use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
use if_chain::if_chain;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! {
/// ### What it does
/// Checks for if-else that could be written to `bool::then`.
/// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
///
/// ### Why is this bad?
/// Looks a little redundant. Using `bool::then` helps it have less lines of code.
/// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
/// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
/// in comparison to `bool::then`.
///
/// ### Example
/// ```rust
@ -39,7 +41,7 @@ declare_clippy_lint! {
#[clippy::version = "1.53.0"]
pub IF_THEN_SOME_ELSE_NONE,
restriction,
"Finds if-else that could be written using `bool::then`"
"Finds if-else that could be written using either `bool::then` or `bool::then_some`"
}
pub struct IfThenSomeElseNone {
@ -56,7 +58,7 @@ impl IfThenSomeElseNone {
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
return;
}
@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
return;
}
if_chain! {
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
if let ExprKind::Block(then_block, _) = then.kind;
if let Some(then_expr) = then_block.expr;
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
if is_lang_ctor(cx, then_call_qpath, OptionSome);
if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
if is_lang_ctor(cx, qpath, OptionNone);
if !stmts_contains_early_return(then_block.stmts);
then {
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
format!("({})", cond_snip)
} else {
cond_snip.into_owned()
};
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
let closure_body = if then_block.stmts.is_empty() {
arg_snip.into_owned()
} else {
format!("{{ /* snippet */ {} }}", arg_snip)
};
let help = format!(
"consider using `bool::then` like: `{}.then(|| {})`",
cond_snip,
closure_body,
);
span_lint_and_help(
cx,
IF_THEN_SOME_ELSE_NONE,
expr.span,
"this could be simplified with `bool::then`",
None,
&help,
);
}
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
&& let ExprKind::Block(then_block, _) = then.kind
&& let Some(then_expr) = then_block.expr
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
&& let ExprKind::Path(ref then_call_qpath) = then_call.kind
&& is_lang_ctor(cx, then_call_qpath, OptionSome)
&& let ExprKind::Path(ref qpath) = peel_blocks(els).kind
&& is_lang_ctor(cx, qpath, OptionNone)
&& !stmts_contains_early_return(then_block.stmts)
{
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
format!("({})", cond_snip)
} else {
cond_snip.into_owned()
};
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
let mut method_body = if then_block.stmts.is_empty() {
arg_snip.into_owned()
} else {
format!("{{ /* snippet */ {} }}", arg_snip)
};
let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
"then_some"
} else {
method_body.insert_str(0, "|| ");
"then"
};
let help = format!(
"consider using `bool::{}` like: `{}.{}({})`",
method_name, cond_snip, method_name, method_body,
);
span_lint_and_help(
cx,
IF_THEN_SOME_ELSE_NONE,
expr.span,
&format!("this could be simplified with `bool::{}`", method_name),
None,
&help,
);
}
}

View File

@ -20,12 +20,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
@ -80,9 +80,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(functions::RESULT_LARGE_ERR),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(functions::TOO_MANY_ARGUMENTS),
LintId::of(get_first::GET_FIRST),
LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER),
@ -128,7 +128,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
LintId::of(manual_retain::MANUAL_RETAIN),
LintId::of(manual_strip::MANUAL_STRIP),
LintId::of(map_clone::MAP_CLONE),
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(match_result_ok::MATCH_RESULT_OK),
@ -150,17 +149,20 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::CLONE_ON_COPY),
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::ERR_EXPECT),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT),
LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::GET_FIRST),
LintId::of(methods::GET_LAST_WITH_LEN),
LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF),
@ -178,13 +180,16 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MANUAL_SPLIT_ONCE),
LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
LintId::of(methods::NEEDLESS_OPTION_TAKE),
LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT),
@ -193,6 +198,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::OPTION_MAP_OR_NONE),
LintId::of(methods::OR_FUN_CALL),
LintId::of(methods::OR_THEN_UNWRAP),
LintId::of(methods::RANGE_ZIP_WITH_LEN),
LintId::of(methods::REPEAT_ONCE),
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
@ -202,14 +209,18 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(methods::STRING_EXTEND_CHARS),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::SUSPICIOUS_TO_OWNED),
LintId::of(methods::UNINIT_ASSUMED_INIT),
LintId::of(methods::UNIT_HASH),
LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_FOLD),
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::UNNECESSARY_TO_OWNED),
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
LintId::of(methods::USELESS_ASREF),
LintId::of(methods::VEC_RESIZE_TO_ZERO),
LintId::of(methods::WRONG_SELF_CONVENTION),
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
@ -224,8 +235,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON),
@ -245,7 +256,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::ASSIGN_OP_PATTERN),
LintId::of(operators::BAD_BIT_MASK),
@ -275,7 +286,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(question_mark::QUESTION_MARK),
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
@ -286,7 +296,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(reference::DEREF_ADDROF),
LintId::of(regex::INVALID_REGEX),
LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(returns::LET_AND_RETURN),
LintId::of(returns::NEEDLESS_RETURN),
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
@ -314,10 +323,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(types::BORROWED_BOX),
LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION),
@ -325,7 +334,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(types::VEC_BOX),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_hash::UNIT_HASH),
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::LET_UNIT_VALUE),
LintId::of(unit_types::UNIT_ARG),
@ -333,9 +341,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(unused_peekable::UNUSED_PEEKABLE),
LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(unwrap::PANICKING_UNWRAP),
LintId::of(unwrap::UNNECESSARY_UNWRAP),
@ -343,7 +351,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
LintId::of(write::PRINTLN_EMPTY_STRING),
LintId::of(write::PRINT_LITERAL),
LintId::of(write::PRINT_WITH_NEWLINE),

View File

@ -6,7 +6,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(attrs::DEPRECATED_CFG_ATTR),
LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
LintId::of(casts::CHAR_LIT_AS_U8),
LintId::of(casts::UNNECESSARY_CAST),
LintId::of(dereference::EXPLICIT_AUTO_DEREF),
@ -33,6 +32,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(matches::NEEDLESS_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::CLONE_ON_COPY),
LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT),
@ -51,10 +51,13 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(methods::OPTION_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::OR_THEN_UNWRAP),
LintId::of(methods::RANGE_ZIP_WITH_LEN),
LintId::of(methods::REPEAT_ONCE),
LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::USELESS_ASREF),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
@ -69,6 +72,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::DOUBLE_COMPARISONS),
LintId::of(operators::DURATION_SUBSEC),
LintId::of(operators::IDENTITY_OP),
@ -76,11 +80,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(precedence::PRECEDENCE),
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(reference::DEREF_ADDROF),
LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(swap::MANUAL_SWAP),
@ -99,7 +101,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
LintId::of(types::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX),
LintId::of(unit_types::UNIT_ARG),
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unwrap::UNNECESSARY_UNWRAP),
LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),

View File

@ -39,12 +39,14 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::UNINIT_ASSUMED_INIT),
LintId::of(methods::UNIT_HASH),
LintId::of(methods::VEC_RESIZE_TO_ZERO),
LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::BAD_BIT_MASK),
LintId::of(operators::CMP_NAN),
@ -62,17 +64,15 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
LintId::of(serde_api::SERDE_API_MISUSE),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(swap::ALMOST_SWAPPED),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC),
LintId::of(unit_hash::UNIT_HASH),
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(unwrap::PANICKING_UNWRAP),
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
])

View File

@ -38,7 +38,6 @@ store.register_lints(&[
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
approx_const::APPROX_CONSTANT,
as_conversions::AS_CONVERSIONS,
as_underscore::AS_UNDERSCORE,
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
@ -59,16 +58,14 @@ store.register_lints(&[
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
booleans::NONMINIMAL_BOOL,
booleans::OVERLY_COMPLEX_BOOL_EXPR,
borrow_as_ptr::BORROW_AS_PTR,
borrow_deref_ref::BORROW_DEREF_REF,
bytecount::NAIVE_BYTECOUNT,
bytes_count_to_len::BYTES_COUNT_TO_LEN,
cargo::CARGO_COMMON_METADATA,
cargo::MULTIPLE_CRATE_VERSIONS,
cargo::NEGATIVE_FEATURE_NAMES,
cargo::REDUNDANT_FEATURE_NAMES,
cargo::WILDCARD_DEPENDENCIES,
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
casts::AS_UNDERSCORE,
casts::BORROW_AS_PTR,
casts::CAST_ABS_TO_UNSIGNED,
casts::CAST_ENUM_CONSTRUCTOR,
casts::CAST_ENUM_TRUNCATION,
@ -80,6 +77,7 @@ store.register_lints(&[
casts::CAST_REF_TO_MUT,
casts::CAST_SIGN_LOSS,
casts::CAST_SLICE_DIFFERENT_SIZES,
casts::CAST_SLICE_FROM_RAW_PARTS,
casts::CHAR_LIT_AS_U8,
casts::FN_TO_NUMERIC_CAST,
casts::FN_TO_NUMERIC_CAST_ANY,
@ -173,11 +171,11 @@ store.register_lints(&[
functions::MUST_USE_CANDIDATE,
functions::MUST_USE_UNIT,
functions::NOT_UNSAFE_PTR_ARG_DEREF,
functions::RESULT_LARGE_ERR,
functions::RESULT_UNIT_ERR,
functions::TOO_MANY_ARGUMENTS,
functions::TOO_MANY_LINES,
future_not_send::FUTURE_NOT_SEND,
get_first::GET_FIRST,
if_let_mutex::IF_LET_MUTEX,
if_not_else::IF_NOT_ELSE,
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
@ -246,12 +244,10 @@ store.register_lints(&[
manual_bits::MANUAL_BITS,
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
manual_ok_or::MANUAL_OK_OR,
manual_rem_euclid::MANUAL_REM_EUCLID,
manual_retain::MANUAL_RETAIN,
manual_string_new::MANUAL_STRING_NEW,
manual_strip::MANUAL_STRIP,
map_clone::MAP_CLONE,
map_err_ignore::MAP_ERR_IGNORE,
map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN,
match_result_ok::MATCH_RESULT_OK,
@ -284,13 +280,16 @@ store.register_lints(&[
mem_replace::MEM_REPLACE_WITH_DEFAULT,
mem_replace::MEM_REPLACE_WITH_UNINIT,
methods::BIND_INSTEAD_OF_MAP,
methods::BYTES_COUNT_TO_LEN,
methods::BYTES_NTH,
methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
methods::CHARS_LAST_CMP,
methods::CHARS_NEXT_CMP,
methods::CLONED_INSTEAD_OF_COPIED,
methods::CLONE_DOUBLE_REF,
methods::CLONE_ON_COPY,
methods::CLONE_ON_REF_PTR,
methods::COLLAPSIBLE_STR_REPLACE,
methods::ERR_EXPECT,
methods::EXPECT_FUN_CALL,
methods::EXPECT_USED,
@ -302,6 +301,7 @@ store.register_lints(&[
methods::FLAT_MAP_IDENTITY,
methods::FLAT_MAP_OPTION,
methods::FROM_ITER_INSTEAD_OF_COLLECT,
methods::GET_FIRST,
methods::GET_LAST_WITH_LEN,
methods::GET_UNWRAP,
methods::IMPLICIT_CLONE,
@ -315,22 +315,30 @@ store.register_lints(&[
methods::ITER_NEXT_SLICE,
methods::ITER_NTH,
methods::ITER_NTH_ZERO,
methods::ITER_ON_EMPTY_COLLECTIONS,
methods::ITER_ON_SINGLE_ITEMS,
methods::ITER_OVEREAGER_CLONED,
methods::ITER_SKIP_NEXT,
methods::ITER_WITH_DRAIN,
methods::MANUAL_FILTER_MAP,
methods::MANUAL_FIND_MAP,
methods::MANUAL_OK_OR,
methods::MANUAL_SATURATING_ARITHMETIC,
methods::MANUAL_SPLIT_ONCE,
methods::MANUAL_STR_REPEAT,
methods::MAP_CLONE,
methods::MAP_COLLECT_RESULT_UNIT,
methods::MAP_ERR_IGNORE,
methods::MAP_FLATTEN,
methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR,
methods::MUT_MUTEX_LOCK,
methods::NAIVE_BYTECOUNT,
methods::NEEDLESS_OPTION_AS_DEREF,
methods::NEEDLESS_OPTION_TAKE,
methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF,
methods::NONSENSICAL_OPEN_OPTIONS,
methods::NO_EFFECT_REPLACE,
methods::OBFUSCATED_IF_ELSE,
methods::OK_EXPECT,
@ -339,25 +347,34 @@ store.register_lints(&[
methods::OPTION_MAP_OR_NONE,
methods::OR_FUN_CALL,
methods::OR_THEN_UNWRAP,
methods::PATH_BUF_PUSH_OVERWRITE,
methods::RANGE_ZIP_WITH_LEN,
methods::REPEAT_ONCE,
methods::RESULT_MAP_OR_INTO_OPTION,
methods::SEARCH_IS_SOME,
methods::SHOULD_IMPLEMENT_TRAIT,
methods::SINGLE_CHAR_ADD_STR,
methods::SINGLE_CHAR_PATTERN,
methods::SKIP_WHILE_NEXT,
methods::STABLE_SORT_PRIMITIVE,
methods::STRING_EXTEND_CHARS,
methods::SUSPICIOUS_MAP,
methods::SUSPICIOUS_SPLITN,
methods::SUSPICIOUS_TO_OWNED,
methods::UNINIT_ASSUMED_INIT,
methods::UNIT_HASH,
methods::UNNECESSARY_FILTER_MAP,
methods::UNNECESSARY_FIND_MAP,
methods::UNNECESSARY_FOLD,
methods::UNNECESSARY_JOIN,
methods::UNNECESSARY_LAZY_EVALUATIONS,
methods::UNNECESSARY_SORT_BY,
methods::UNNECESSARY_TO_OWNED,
methods::UNWRAP_OR_ELSE_DEFAULT,
methods::UNWRAP_USED,
methods::USELESS_ASREF,
methods::VEC_RESIZE_TO_ZERO,
methods::VERBOSE_FILE_READS,
methods::WRONG_SELF_CONVENTION,
methods::ZST_OFFSET,
minmax::MIN_MAX,
@ -384,9 +401,9 @@ store.register_lints(&[
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
module_style::MOD_MODULE_FILES,
module_style::SELF_NAMED_MODULE_FILES,
multi_assignments::MULTI_ASSIGNMENTS,
mut_key::MUTABLE_KEY_TYPE,
mut_mut::MUT_MUT,
mut_mutex_lock::MUT_MUTEX_LOCK,
mut_reference::UNNECESSARY_MUT_PASSED,
mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
mutex_atomic::MUTEX_ATOMIC,
@ -418,7 +435,6 @@ store.register_lints(&[
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
octal_escapes::OCTAL_ESCAPES,
only_used_in_recursion::ONLY_USED_IN_RECURSION,
open_options::NONSENSICAL_OPEN_OPTIONS,
operators::ABSURD_EXTREME_COMPARISONS,
operators::ARITHMETIC,
operators::ASSIGN_OP_PATTERN,
@ -457,7 +473,6 @@ store.register_lints(&[
partialeq_to_none::PARTIALEQ_TO_NONE,
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
precedence::PRECEDENCE,
ptr::CMP_NULL,
@ -470,7 +485,6 @@ store.register_lints(&[
ranges::MANUAL_RANGE_CONTAINS,
ranges::RANGE_MINUS_ONE,
ranges::RANGE_PLUS_ONE,
ranges::RANGE_ZIP_WITH_LEN,
ranges::REVERSED_EMPTY_RANGES,
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
@ -486,7 +500,6 @@ store.register_lints(&[
reference::DEREF_ADDROF,
regex::INVALID_REGEX,
regex::TRIVIAL_REGEX,
repeat_once::REPEAT_ONCE,
return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
returns::LET_AND_RETURN,
returns::NEEDLESS_RETURN,
@ -501,7 +514,6 @@ store.register_lints(&[
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
stable_sort_primitive::STABLE_SORT_PRIMITIVE,
std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
std_instead_of_core::STD_INSTEAD_OF_ALLOC,
std_instead_of_core::STD_INSTEAD_OF_CORE,
@ -537,10 +549,10 @@ store.register_lints(&[
transmute::TRANSMUTE_PTR_TO_PTR,
transmute::TRANSMUTE_PTR_TO_REF,
transmute::TRANSMUTE_UNDEFINED_REPR,
transmute::TRANSMUTING_NULL,
transmute::UNSOUND_COLLECTION_TRANSMUTE,
transmute::USELESS_TRANSMUTE,
transmute::WRONG_TRANSMUTE,
transmuting_null::TRANSMUTING_NULL,
types::BORROWED_BOX,
types::BOX_COLLECTION,
types::LINKEDLIST,
@ -555,7 +567,6 @@ store.register_lints(&[
unicode::NON_ASCII_LITERAL,
unicode::UNICODE_NOT_NFC,
uninit_vec::UNINIT_VEC,
unit_hash::UNIT_HASH,
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
unit_types::LET_UNIT_VALUE,
unit_types::UNIT_ARG,
@ -564,12 +575,12 @@ store.register_lints(&[
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
unnecessary_sort_by::UNNECESSARY_SORT_BY,
unnecessary_wraps::UNNECESSARY_WRAPS,
unnested_or_patterns::UNNESTED_OR_PATTERNS,
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
unused_async::UNUSED_ASYNC,
unused_io_amount::UNUSED_IO_AMOUNT,
unused_peekable::UNUSED_PEEKABLE,
unused_rounding::UNUSED_ROUNDING,
unused_self::UNUSED_SELF,
unused_unit::UNUSED_UNIT,
@ -581,10 +592,9 @@ store.register_lints(&[
useless_conversion::USELESS_CONVERSION,
vec::USELESS_VEC,
vec_init_then_push::VEC_INIT_THEN_PUSH,
vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
verbose_file_reads::VERBOSE_FILE_READS,
wildcard_imports::ENUM_GLOB_USE,
wildcard_imports::WILDCARD_IMPORTS,
write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
write::PRINTLN_EMPTY_STRING,
write::PRINT_LITERAL,
write::PRINT_STDERR,

View File

@ -14,16 +14,17 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
LintId::of(methods::ITER_ON_SINGLE_ITEMS),
LintId::of(methods::ITER_WITH_DRAIN),
LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(mutex_atomic::MUTEX_INTEGER),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
LintId::of(regex::TRIVIAL_REGEX),
LintId::of(strings::STRING_LIT_AS_BYTES),

View File

@ -4,9 +4,7 @@
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(attrs::INLINE_ALWAYS),
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
LintId::of(bytecount::NAIVE_BYTECOUNT),
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(casts::BORROW_AS_PTR),
LintId::of(casts::CAST_LOSSLESS),
LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
LintId::of(casts::CAST_POSSIBLE_WRAP),
@ -50,20 +48,24 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(macro_use::MACRO_USE_IMPORTS),
LintId::of(manual_assert::MANUAL_ASSERT),
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
LintId::of(manual_ok_or::MANUAL_OK_OR),
LintId::of(manual_string_new::MANUAL_STRING_NEW),
LintId::of(matches::MATCH_BOOL),
LintId::of(matches::MATCH_ON_VEC_ITEMS),
LintId::of(matches::MATCH_SAME_ARMS),
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
LintId::of(matches::MATCH_WILD_ERR_ARM),
LintId::of(matches::SINGLE_MATCH_ELSE),
LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
LintId::of(methods::FILTER_MAP_NEXT),
LintId::of(methods::FLAT_MAP_OPTION),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MANUAL_OK_OR),
LintId::of(methods::MAP_UNWRAP_OR),
LintId::of(methods::NAIVE_BYTECOUNT),
LintId::of(methods::STABLE_SORT_PRIMITIVE),
LintId::of(methods::UNNECESSARY_JOIN),
LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
@ -85,7 +87,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(ref_option_ref::REF_OPTION_REF),
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
LintId::of(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),

View File

@ -7,12 +7,14 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
LintId::of(escape::BOXED_LOCAL),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
LintId::of(functions::RESULT_LARGE_ERR),
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::MISSING_SPIN_LOOP),
LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(manual_retain::MANUAL_RETAIN),
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::ITER_NTH),

View File

@ -4,11 +4,11 @@
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(as_conversions::AS_CONVERSIONS),
LintId::of(as_underscore::AS_UNDERSCORE),
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
LintId::of(casts::AS_UNDERSCORE),
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO),
@ -30,7 +30,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
LintId::of(map_err_ignore::MAP_ERR_IGNORE),
LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
LintId::of(matches::TRY_ERR),
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
@ -39,7 +38,9 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(methods::EXPECT_USED),
LintId::of(methods::FILETYPE_IS_FILE),
LintId::of(methods::GET_UNWRAP),
LintId::of(methods::MAP_ERR_IGNORE),
LintId::of(methods::UNWRAP_USED),
LintId::of(methods::VERBOSE_FILE_READS),
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
@ -81,7 +82,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
LintId::of(unicode::NON_ASCII_LITERAL),
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
LintId::of(write::PRINT_STDERR),
LintId::of(write::PRINT_STDOUT),
LintId::of(write::USE_DEBUG),

View File

@ -29,7 +29,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(functions::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(get_first::GET_FIRST),
LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(len_zero::COMPARISON_TO_EMPTY),
@ -45,7 +44,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(map_clone::MAP_CLONE),
LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::COLLAPSIBLE_MATCH),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
@ -61,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::ERR_EXPECT),
LintId::of(methods::GET_FIRST),
LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITER_CLONED_COLLECT),
@ -68,7 +67,9 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(methods::ITER_NTH_ZERO),
LintId::of(methods::ITER_SKIP_NEXT),
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT),
@ -88,7 +89,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
LintId::of(misc_early::REDUNDANT_PATTERN),
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),

View File

@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(drop_forget_ref::DROP_NON_DROP),
LintId::of(drop_forget_ref::FORGET_NON_DROP),
@ -24,6 +25,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
LintId::of(loops::MUT_RANGE_BOUND),
LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::SUSPICIOUS_TO_OWNED),
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
LintId::of(mut_key::MUTABLE_KEY_TYPE),
LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
@ -32,4 +35,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
LintId::of(unused_peekable::UNUSED_PEEKABLE),
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
])

View File

@ -171,7 +171,6 @@ mod renamed_lints;
mod almost_complete_letter_range;
mod approx_const;
mod as_conversions;
mod as_underscore;
mod asm_syntax;
mod assertions_on_constants;
mod assertions_on_result_states;
@ -181,12 +180,8 @@ mod await_holding_invalid;
mod blocks_in_if_conditions;
mod bool_assert_comparison;
mod booleans;
mod borrow_as_ptr;
mod borrow_deref_ref;
mod bytecount;
mod bytes_count_to_len;
mod cargo;
mod case_sensitive_file_extension_comparisons;
mod casts;
mod checked_conversions;
mod cognitive_complexity;
@ -239,7 +234,6 @@ mod from_over_into;
mod from_str_radix_10;
mod functions;
mod future_not_send;
mod get_first;
mod if_let_mutex;
mod if_not_else;
mod if_then_some_else_none;
@ -276,12 +270,10 @@ mod manual_async_fn;
mod manual_bits;
mod manual_instant_elapsed;
mod manual_non_exhaustive;
mod manual_ok_or;
mod manual_rem_euclid;
mod manual_retain;
mod manual_string_new;
mod manual_strip;
mod map_clone;
mod map_err_ignore;
mod map_unit_fn;
mod match_result_ok;
mod matches;
@ -298,9 +290,9 @@ mod missing_enforced_import_rename;
mod missing_inline;
mod mixed_read_write_in_expression;
mod module_style;
mod multi_assignments;
mod mut_key;
mod mut_mut;
mod mut_mutex_lock;
mod mut_reference;
mod mutable_debug_assertion;
mod mutex_atomic;
@ -325,7 +317,6 @@ mod non_send_fields_in_send_ty;
mod nonstandard_macro_braces;
mod octal_escapes;
mod only_used_in_recursion;
mod open_options;
mod operators;
mod option_env_unwrap;
mod option_if_let_else;
@ -335,7 +326,6 @@ mod panic_unimplemented;
mod partialeq_ne_impl;
mod partialeq_to_none;
mod pass_by_ref_or_value;
mod path_buf_push_overwrite;
mod pattern_type_mismatch;
mod precedence;
mod ptr;
@ -355,7 +345,6 @@ mod redundant_static_lifetimes;
mod ref_option_ref;
mod reference;
mod regex;
mod repeat_once;
mod return_self_not_must_use;
mod returns;
mod same_name_method;
@ -367,7 +356,6 @@ mod single_char_lifetime_names;
mod single_component_path_imports;
mod size_of_in_element_count;
mod slow_vector_initialization;
mod stable_sort_primitive;
mod std_instead_of_core;
mod strings;
mod strlen_on_c_strings;
@ -381,23 +369,21 @@ mod to_digit_is_some;
mod trailing_empty_array;
mod trait_bounds;
mod transmute;
mod transmuting_null;
mod types;
mod undocumented_unsafe_blocks;
mod unicode;
mod uninit_vec;
mod unit_hash;
mod unit_return_expecting_ord;
mod unit_types;
mod unnamed_address;
mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports;
mod unnecessary_sort_by;
mod unnecessary_wraps;
mod unnested_or_patterns;
mod unsafe_removed_from_name;
mod unused_async;
mod unused_io_amount;
mod unused_peekable;
mod unused_rounding;
mod unused_self;
mod unused_unit;
@ -408,8 +394,6 @@ mod use_self;
mod useless_conversion;
mod vec;
mod vec_init_then_push;
mod vec_resize_to_zero;
mod verbose_file_reads;
mod wildcard_imports;
mod write;
mod zero_div_zero;
@ -597,7 +581,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
store.register_late_pass(|| Box::new(unicode::Unicode));
store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
store.register_late_pass(|| Box::new(unit_hash::UnitHash));
store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
store.register_late_pass(|| Box::new(strings::StringAdd));
store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
@ -635,8 +618,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
@ -646,7 +627,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
msrv,
))
});
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
store.register_late_pass(|| Box::new(shadow::Shadow::default()));
store.register_late_pass(|| Box::new(unit_types::UnitTypes));
store.register_late_pass(|| Box::new(loops::Loops));
@ -654,7 +634,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
store.register_late_pass(|| Box::new(entry::HashMapPass));
store.register_late_pass(|| Box::new(minmax::MinMaxPass));
store.register_late_pass(|| Box::new(open_options::OpenOptions));
store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
@ -690,10 +669,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
let too_many_arguments_threshold = conf.too_many_arguments_threshold;
let too_many_lines_threshold = conf.too_many_lines_threshold;
let large_error_threshold = conf.large_error_threshold;
store.register_late_pass(move || {
Box::new(functions::Functions::new(
too_many_arguments_threshold,
too_many_lines_threshold,
large_error_threshold,
))
});
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
@ -720,7 +701,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
);
store.register_late_pass(move || Box::new(pass_by_ref_or_value));
store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
store.register_late_pass(|| Box::new(bytecount::ByteCount));
store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
@ -738,12 +718,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
let max_trait_bounds = conf.max_trait_bounds;
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
@ -819,18 +796,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
store.register_early_pass(move || {
@ -842,10 +816,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
let disallowed_methods = conf.disallowed_methods.clone();
@ -857,9 +828,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(strings::StringToString));
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
store.register_late_pass(|| {
Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
});
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
@ -894,11 +862,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish;
@ -912,18 +879,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
store.register_early_pass(|| Box::new(pub_use::PubUse));
store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
let max_include_file_size = conf.max_include_file_size;
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
store.register_late_pass(|| Box::new(get_first::GetFirst));
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
@ -934,6 +898,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View File

@ -1,5 +1,6 @@
use super::NEEDLESS_COLLECT;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::higher;
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
if check_loop_kind(expr).is_some() {
continue;
}
self.visit_block_expr(expr, hir_id);
}
if let Some(expr) = block.expr {
self.visit_block_expr(expr, None);
if let Some(loop_kind) = check_loop_kind(expr) {
if let LoopKind::Conditional(block_expr) = loop_kind {
self.visit_block_expr(block_expr, None);
}
} else {
self.visit_block_expr(expr, None);
}
}
}
@ -264,6 +274,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
}
}
enum LoopKind<'tcx> {
Conditional(&'tcx Expr<'tcx>),
Loop,
}
fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
return Some(LoopKind::Conditional(let_expr));
}
if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
return Some(LoopKind::Conditional(condition));
}
if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
return Some(LoopKind::Conditional(arg));
}
if let ExprKind::Loop { .. } = expr.kind {
return Some(LoopKind::Loop);
}
None
}
impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
self.current_statement_hir_id = hir_id;

View File

@ -192,7 +192,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
match output.kind {
TyKind::Tup(tys) if tys.is_empty() => {
let sugg = "remove the return type";
Some((sugg, "".into()))
Some((sugg, String::new()))
},
_ => {
let sugg = "return the output of the future directly";

View File

@ -1,95 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_lang_ctor, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::symbol::sym;
declare_clippy_lint! {
/// ### What it does
///
/// Finds patterns that reimplement `Option::ok_or`.
///
/// ### Why is this bad?
///
/// Concise code helps focusing on behavior instead of boilerplate.
///
/// ### Examples
/// ```rust
/// let foo: Option<i32> = None;
/// foo.map_or(Err("error"), |v| Ok(v));
/// ```
///
/// Use instead:
/// ```rust
/// let foo: Option<i32> = None;
/// foo.ok_or("error");
/// ```
#[clippy::version = "1.49.0"]
pub MANUAL_OK_OR,
pedantic,
"finds patterns that can be encoded more concisely with `Option::ok_or`"
}
declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]);
impl<'tcx> LateLintPass<'tcx> for ManualOkOr {
fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) {
if in_external_macro(cx.sess(), scrutinee.span) {
return;
}
if_chain! {
if let ExprKind::MethodCall(method_segment, [receiver, or_expr, map_expr], _) = scrutinee.kind;
if method_segment.ident.name == sym!(map_or);
let ty = cx.typeck_results().expr_ty(receiver);
if is_type_diagnostic_item(cx, ty, sym::Option);
if is_ok_wrapping(cx, map_expr);
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, &[ref err_arg]) = or_expr.kind;
if is_lang_ctor(cx, err_path, ResultErr);
if let Some(method_receiver_snippet) = snippet_opt(cx, receiver.span);
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
if let Some(indent) = indent_of(cx, scrutinee.span);
then {
let reindented_err_arg_snippet =
reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
span_lint_and_sugg(
cx,
MANUAL_OK_OR,
scrutinee.span,
"this pattern reimplements `Option::ok_or`",
"replace with",
format!(
"{}.ok_or({})",
method_receiver_snippet,
reindented_err_arg_snippet
),
Applicability::MachineApplicable,
);
}
}
}
}
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
if let ExprKind::Path(ref qpath) = map_expr.kind {
if is_lang_ctor(cx, qpath, ResultOk) {
return true;
}
}
if_chain! {
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
let body = cx.tcx.hir().body(body);
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
if is_lang_ctor(cx, ok_path, ResultOk);
then { path_to_local_id(ok_arg, param_id) } else { false }
}
}

View File

@ -0,0 +1,140 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use rustc_ast::LitKind;
use rustc_errors::Applicability::MachineApplicable;
use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{sym, symbol, Span};
declare_clippy_lint! {
/// ### What it does
///
/// Checks for usage of `""` to create a `String`, such as `"".to_string()`, `"".to_owned()`,
/// `String::from("")` and others.
///
/// ### Why is this bad?
///
/// Different ways of creating an empty string makes your code less standardized, which can
/// be confusing.
///
/// ### Example
/// ```rust
/// let a = "".to_string();
/// let b: String = "".into();
/// ```
/// Use instead:
/// ```rust
/// let a = String::new();
/// let b = String::new();
/// ```
#[clippy::version = "1.65.0"]
pub MANUAL_STRING_NEW,
pedantic,
"empty String is being created manually"
}
declare_lint_pass!(ManualStringNew => [MANUAL_STRING_NEW]);
impl LateLintPass<'_> for ManualStringNew {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if expr.span.from_expansion() {
return;
}
let ty = cx.typeck_results().expr_ty(expr);
match ty.kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => {
if !cx.tcx.is_diagnostic_item(sym::String, adt_def.did()) {
return;
}
},
_ => return,
}
match expr.kind {
ExprKind::Call(func, args) => {
parse_call(cx, expr.span, func, args);
},
ExprKind::MethodCall(path_segment, args, _) => {
parse_method_call(cx, expr.span, path_segment, args);
},
_ => (),
}
}
}
/// Checks if an expression's kind corresponds to an empty &str.
fn is_expr_kind_empty_str(expr_kind: &ExprKind<'_>) -> bool {
if let ExprKind::Lit(lit) = expr_kind &&
let LitKind::Str(value, _) = lit.node &&
value == symbol::kw::Empty
{
return true;
}
false
}
fn warn_then_suggest(cx: &LateContext<'_>, span: Span) {
span_lint_and_sugg(
cx,
MANUAL_STRING_NEW,
span,
"empty String is being created manually",
"consider using",
"String::new()".into(),
MachineApplicable,
);
}
/// Tries to parse an expression as a method call, emitting the warning if necessary.
fn parse_method_call(cx: &LateContext<'_>, span: Span, path_segment: &PathSegment<'_>, args: &[Expr<'_>]) {
if args.is_empty() {
// When parsing TryFrom::try_from(...).expect(...), we will have more than 1 arg.
return;
}
let ident = path_segment.ident.as_str();
let method_arg_kind = &args[0].kind;
if ["to_string", "to_owned", "into"].contains(&ident) && is_expr_kind_empty_str(method_arg_kind) {
warn_then_suggest(cx, span);
} else if let ExprKind::Call(func, args) = method_arg_kind {
// If our first argument is a function call itself, it could be an `unwrap`-like function.
// E.g. String::try_from("hello").unwrap(), TryFrom::try_from("").expect("hello"), etc.
parse_call(cx, span, func, args);
}
}
/// Tries to parse an expression as a function call, emitting the warning if necessary.
fn parse_call(cx: &LateContext<'_>, span: Span, func: &Expr<'_>, args: &[Expr<'_>]) {
if args.len() != 1 {
return;
}
let arg_kind = &args[0].kind;
if let ExprKind::Path(qpath) = &func.kind {
if let QPath::TypeRelative(_, _) = qpath {
// String::from(...) or String::try_from(...)
if let QPath::TypeRelative(ty, path_seg) = qpath &&
[sym::from, sym::try_from].contains(&path_seg.ident.name) &&
let TyKind::Path(qpath) = &ty.kind &&
let QPath::Resolved(_, path) = qpath &&
let [path_seg] = path.segments &&
path_seg.ident.name == sym::String &&
is_expr_kind_empty_str(arg_kind)
{
warn_then_suggest(cx, span);
}
} else if let QPath::Resolved(_, path) = qpath {
// From::from(...) or TryFrom::try_from(...)
if let [path_seg1, path_seg2] = path.segments &&
is_expr_kind_empty_str(arg_kind) && (
(path_seg1.ident.name == sym::From && path_seg2.ident.name == sym::from) ||
(path_seg1.ident.name == sym::TryFrom && path_seg2.ident.name == sym::try_from)
)
{
warn_then_suggest(cx, span);
}
}
}
}

View File

@ -1,167 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use clippy_utils::{is_trait_method, meets_msrv, msrvs, peel_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::mir::Mutability;
use rustc_middle::ty;
use rustc_middle::ty::adjustment::Adjust;
use rustc_semver::RustcVersion;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `map(|x| x.clone())` or
/// dereferencing closures for `Copy` types, on `Iterator` or `Option`,
/// and suggests `cloned()` or `copied()` instead
///
/// ### Why is this bad?
/// Readability, this can be written more concisely
///
/// ### Example
/// ```rust
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.map(|i| *i);
/// ```
///
/// The correct use would be:
///
/// ```rust
/// let x = vec![42, 43];
/// let y = x.iter();
/// let z = y.cloned();
/// ```
#[clippy::version = "pre 1.29.0"]
pub MAP_CLONE,
style,
"using `iterator.map(|x| x.clone())`, or dereferencing closures for `Copy` types"
}
pub struct MapClone {
msrv: Option<RustcVersion>,
}
impl_lint_pass!(MapClone => [MAP_CLONE]);
impl MapClone {
pub fn new(msrv: Option<RustcVersion>) -> Self {
Self { msrv }
}
}
impl<'tcx> LateLintPass<'tcx> for MapClone {
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
if e.span.from_expansion() {
return;
}
if_chain! {
if let hir::ExprKind::MethodCall(method, args, _) = e.kind;
if args.len() == 2;
if method.ident.name == sym::map;
let ty = cx.typeck_results().expr_ty(&args[0]);
if is_type_diagnostic_item(cx, ty, sym::Option) || is_trait_method(cx, e, sym::Iterator);
if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = args[1].kind;
then {
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(&closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
hir::BindingAnnotation::Unannotated, .., name, None
) = inner.kind {
if ident_eq(name, closure_expr) {
self.lint_explicit_closure(cx, e.span, args[0].span, true);
}
},
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
match closure_expr.kind {
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
if ident_eq(name, inner) {
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
self.lint_explicit_closure(cx, e.span, args[0].span, true);
}
}
},
hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
if ident_eq(name, obj) && method.ident.name == sym::clone;
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
// no autoderefs
if !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
then {
let obj_ty = cx.typeck_results().expr_ty(obj);
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
if matches!(mutability, Mutability::Not) {
let copy = is_copy(cx, *ty);
self.lint_explicit_closure(cx, e.span, args[0].span, copy);
}
} else {
lint_needless_cloning(cx, e.span, args[0].span);
}
}
},
_ => {},
}
},
_ => {},
}
}
}
}
extract_msrv_attr!(LateContext);
}
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
path.segments.len() == 1 && path.segments[0].ident == name
} else {
false
}
}
fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
span_lint_and_sugg(
cx,
MAP_CLONE,
root.trim_start(receiver).unwrap(),
"you are needlessly cloning iterator elements",
"remove the `map` call",
String::new(),
Applicability::MachineApplicable,
);
}
impl MapClone {
fn lint_explicit_closure(&self, cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool) {
let mut applicability = Applicability::MachineApplicable;
let (message, sugg_method) = if is_copy && meets_msrv(self.msrv, msrvs::ITERATOR_COPIED) {
("you are using an explicit closure for copying elements", "copied")
} else {
("you are using an explicit closure for cloning elements", "cloned")
};
span_lint_and_sugg(
cx,
MAP_CLONE,
replace,
message,
&format!("consider calling the dedicated `{}` method", sugg_method),
format!(
"{}.{}()",
snippet_with_applicability(cx, root, "..", &mut applicability),
sugg_method,
),
applicability,
);
}
}

View File

@ -1,154 +0,0 @@
use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
declare_clippy_lint! {
/// ### What it does
/// Checks for instances of `map_err(|_| Some::Enum)`
///
/// ### Why is this bad?
/// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
///
/// ### Example
/// Before:
/// ```rust
/// use std::fmt;
///
/// #[derive(Debug)]
/// enum Error {
/// Indivisible,
/// Remainder(u8),
/// }
///
/// impl fmt::Display for Error {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// match self {
/// Error::Indivisible => write!(f, "could not divide input by three"),
/// Error::Remainder(remainder) => write!(
/// f,
/// "input is not divisible by three, remainder = {}",
/// remainder
/// ),
/// }
/// }
/// }
///
/// impl std::error::Error for Error {}
///
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
/// input
/// .parse::<i32>()
/// .map_err(|_| Error::Indivisible)
/// .map(|v| v % 3)
/// .and_then(|remainder| {
/// if remainder == 0 {
/// Ok(())
/// } else {
/// Err(Error::Remainder(remainder as u8))
/// }
/// })
/// }
/// ```
///
/// After:
/// ```rust
/// use std::{fmt, num::ParseIntError};
///
/// #[derive(Debug)]
/// enum Error {
/// Indivisible(ParseIntError),
/// Remainder(u8),
/// }
///
/// impl fmt::Display for Error {
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
/// match self {
/// Error::Indivisible(_) => write!(f, "could not divide input by three"),
/// Error::Remainder(remainder) => write!(
/// f,
/// "input is not divisible by three, remainder = {}",
/// remainder
/// ),
/// }
/// }
/// }
///
/// impl std::error::Error for Error {
/// fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
/// match self {
/// Error::Indivisible(source) => Some(source),
/// _ => None,
/// }
/// }
/// }
///
/// fn divisible_by_3(input: &str) -> Result<(), Error> {
/// input
/// .parse::<i32>()
/// .map_err(Error::Indivisible)
/// .map(|v| v % 3)
/// .and_then(|remainder| {
/// if remainder == 0 {
/// Ok(())
/// } else {
/// Err(Error::Remainder(remainder as u8))
/// }
/// })
/// }
/// ```
#[clippy::version = "1.48.0"]
pub MAP_ERR_IGNORE,
restriction,
"`map_err` should not ignore the original error"
}
declare_lint_pass!(MapErrIgnore => [MAP_ERR_IGNORE]);
impl<'tcx> LateLintPass<'tcx> for MapErrIgnore {
// do not try to lint if this is from a macro or desugaring
fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
if e.span.from_expansion() {
return;
}
// check if this is a method call (e.g. x.foo())
if let ExprKind::MethodCall(method, [_, arg], _) = e.kind {
// only work if the method name is `map_err` and there are only 2 arguments (e.g. x.map_err(|_|[1]
// Enum::Variant[2]))
if method.ident.name == sym!(map_err) {
// make sure the first argument is a closure, and grab the CaptureRef, BodyId, and fn_decl_span
// fields
if let ExprKind::Closure(&Closure {
capture_clause,
body,
fn_decl_span,
..
}) = arg.kind
{
// check if this is by Reference (meaning there's no move statement)
if capture_clause == CaptureBy::Ref {
// Get the closure body to check the parameters and values
let closure_body = cx.tcx.hir().body(body);
// make sure there's only one parameter (`|_|`)
if closure_body.params.len() == 1 {
// make sure that parameter is the wild token (`_`)
if let PatKind::Wild = closure_body.params[0].pat.kind {
// span the area of the closure capture and warn that the
// original error will be thrown away
span_lint_and_help(
cx,
MAP_ERR_IGNORE,
fn_decl_span,
"`map_err(|_|...` wildcard pattern discards the original error",
None,
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
);
}
}
}
}
}
}
}
}

View File

@ -1,10 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_wild;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::span_contains_comment;
use rustc_ast::{Attribute, LitKind};
use rustc_errors::Applicability;
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
use rustc_lint::LateContext;
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty;
use rustc_span::source_map::Spanned;
@ -76,6 +77,7 @@ where
>,
{
if_chain! {
if !span_contains_comment(cx.sess().source_map(), expr.span);
if iter.len() >= 2;
if cx.typeck_results().expr_ty(expr).is_bool();
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();

View File

@ -8,7 +8,7 @@ use clippy_utils::{
};
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
use rustc_lint::LateContext;
use rustc_span::sym;
use rustc_typeck::hir_ty_to_ty;
@ -65,8 +65,26 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let:
fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
for arm in arms {
let arm_expr = peel_blocks_with_stmt(arm.body);
if let Some(guard_expr) = &arm.guard {
match guard_expr {
// gives up if `pat if expr` can have side effects
Guard::If(if_cond) => {
if if_cond.can_have_side_effects() {
return false;
}
},
// gives up `pat if let ...` arm
Guard::IfLet(_) => {
return false;
},
};
}
if let PatKind::Wild = arm.pat.kind {
return eq_expr_value(cx, match_expr, strip_return(arm_expr));
if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) {
return false;
}
} else if !pat_same_as_expr(arm.pat, arm_expr) {
return false;
}

View File

@ -0,0 +1,70 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::match_type;
use clippy_utils::visitors::is_local_used;
use clippy_utils::{path_to_local_id, paths, peel_blocks, peel_ref_operators, strip_pat_refs};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, UintTy};
use rustc_span::sym;
use super::NAIVE_BYTECOUNT;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
filter_recv: &'tcx Expr<'_>,
filter_arg: &'tcx Expr<'_>,
) {
if_chain! {
if let ExprKind::Closure(&Closure { body, .. }) = filter_arg.kind;
let body = cx.tcx.hir().body(body);
if let [param] = body.params;
if let PatKind::Binding(_, arg_id, _, _) = strip_pat_refs(param.pat).kind;
if let ExprKind::Binary(ref op, l, r) = body.value.kind;
if op.node == BinOpKind::Eq;
if match_type(cx,
cx.typeck_results().expr_ty(filter_recv).peel_refs(),
&paths::SLICE_ITER);
let operand_is_arg = |expr| {
let expr = peel_ref_operators(cx, peel_blocks(expr));
path_to_local_id(expr, arg_id)
};
let needle = if operand_is_arg(l) {
r
} else if operand_is_arg(r) {
l
} else {
return;
};
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(needle).peel_refs().kind();
if !is_local_used(cx, needle, arg_id);
then {
let haystack = if let ExprKind::MethodCall(path, args, _) =
filter_recv.kind {
let p = path.ident.name;
if (p == sym::iter || p == sym!(iter_mut)) && args.len() == 1 {
&args[0]
} else {
filter_recv
}
} else {
filter_recv
};
let mut applicability = Applicability::MaybeIncorrect;
span_lint_and_sugg(
cx,
NAIVE_BYTECOUNT,
expr.span,
"you appear to be counting bytes the naive way",
"consider using the bytecount crate",
format!("bytecount::count({}, {})",
snippet_with_applicability(cx, haystack.span, "..", &mut applicability),
snippet_with_applicability(cx, needle.span, "..", &mut applicability)),
applicability,
);
}
};
}

View File

@ -0,0 +1,37 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::sym;
use super::BYTES_COUNT_TO_LEN;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
count_recv: &'tcx hir::Expr<'_>,
bytes_recv: &'tcx hir::Expr<'_>,
) {
if_chain! {
if let Some(bytes_id) = cx.typeck_results().type_dependent_def_id(count_recv.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(bytes_id);
if cx.tcx.type_of(impl_id).is_str();
let ty = cx.typeck_results().expr_ty(bytes_recv).peel_refs();
if ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String);
then {
let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
BYTES_COUNT_TO_LEN,
expr.span,
"using long and hard to read `.bytes().count()`",
"consider calling `.len()` instead",
format!("{}.len()", snippet_with_applicability(cx, bytes_recv.span, "..", &mut applicability)),
applicability
);
}
};
}

View File

@ -0,0 +1,41 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::{source_map::Spanned, symbol::sym, Span};
use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
call_span: Span,
recv: &'tcx Expr<'_>,
arg: &'tcx Expr<'_>,
) {
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
if cx.tcx.type_of(impl_id).is_str();
if let ExprKind::Lit(Spanned { node: LitKind::Str(ext_literal, ..), ..}) = arg.kind;
if (2..=6).contains(&ext_literal.as_str().len());
let ext_str = ext_literal.as_str();
if ext_str.starts_with('.');
if ext_str.chars().skip(1).all(|c| c.is_uppercase() || c.is_ascii_digit())
|| ext_str.chars().skip(1).all(|c| c.is_lowercase() || c.is_ascii_digit());
let recv_ty = cx.typeck_results().expr_ty(recv).peel_refs();
if recv_ty.is_str() || is_type_diagnostic_item(cx, recv_ty, sym::String);
then {
span_lint_and_help(
cx,
CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
call_span,
"case-sensitive file extension comparison",
None,
"consider using a case-insensitive comparison instead",
);
}
}
}

View File

@ -0,0 +1,96 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::visitors::for_each_expr;
use clippy_utils::{eq_expr_value, get_parent_expr};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use std::collections::VecDeque;
use super::method_call;
use super::COLLAPSIBLE_STR_REPLACE;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
from: &'tcx hir::Expr<'tcx>,
to: &'tcx hir::Expr<'tcx>,
) {
let replace_methods = collect_replace_calls(cx, expr, to);
if replace_methods.methods.len() > 1 {
let from_kind = cx.typeck_results().expr_ty(from).peel_refs().kind();
// If the parent node's `to` argument is the same as the `to` argument
// of the last replace call in the current chain, don't lint as it was already linted
if let Some(parent) = get_parent_expr(cx, expr)
&& let Some(("replace", [_, current_from, current_to], _)) = method_call(parent)
&& eq_expr_value(cx, to, current_to)
&& from_kind == cx.typeck_results().expr_ty(current_from).peel_refs().kind()
{
return;
}
check_consecutive_replace_calls(cx, expr, &replace_methods, to);
}
}
struct ReplaceMethods<'tcx> {
methods: VecDeque<&'tcx hir::Expr<'tcx>>,
from_args: VecDeque<&'tcx hir::Expr<'tcx>>,
}
fn collect_replace_calls<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
to_arg: &'tcx hir::Expr<'tcx>,
) -> ReplaceMethods<'tcx> {
let mut methods = VecDeque::new();
let mut from_args = VecDeque::new();
let _: Option<()> = for_each_expr(expr, |e| {
if let Some(("replace", [_, from, to], _)) = method_call(e) {
if eq_expr_value(cx, to_arg, to) && cx.typeck_results().expr_ty(from).peel_refs().is_char() {
methods.push_front(e);
from_args.push_front(from);
ControlFlow::Continue(())
} else {
ControlFlow::BREAK
}
} else {
ControlFlow::Continue(())
}
});
ReplaceMethods { methods, from_args }
}
/// Check a chain of `str::replace` calls for `collapsible_str_replace` lint.
fn check_consecutive_replace_calls<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'tcx>,
replace_methods: &ReplaceMethods<'tcx>,
to_arg: &'tcx hir::Expr<'tcx>,
) {
let from_args = &replace_methods.from_args;
let from_arg_reprs: Vec<String> = from_args
.iter()
.map(|from_arg| snippet(cx, from_arg.span, "..").to_string())
.collect();
let app = Applicability::MachineApplicable;
let earliest_replace_call = replace_methods.methods.front().unwrap();
if let Some((_, [..], span_lo)) = method_call(earliest_replace_call) {
span_lint_and_sugg(
cx,
COLLAPSIBLE_STR_REPLACE,
expr.span.with_lo(span_lo.lo()),
"used consecutive `str::replace` call",
"replace with",
format!(
"replace([{}], {})",
from_arg_reprs.join(", "),
snippet(cx, to_arg.span, ".."),
),
app,
);
}
}

View File

@ -7,18 +7,26 @@ use rustc_span::sym;
use super::EXPECT_USED;
/// lint use of `expect()` for `Option`s and `Result`s
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
pub(super) fn check(
cx: &LateContext<'_>,
expr: &hir::Expr<'_>,
recv: &hir::Expr<'_>,
is_err: bool,
allow_expect_in_tests: bool,
) {
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
Some((EXPECT_USED, "an Option", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
Some((EXPECT_USED, "a Result", "Err", "an "))
Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
} else {
None
};
let method = if is_err { "expect_err" } else { "expect" };
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
return;
}
@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
cx,
lint,
expr.span,
&format!("used `expect()` on `{kind}` value"),
&format!("used `{method}()` on `{kind}` value"),
None,
&format!("if this value is {none_prefix}`{none_value}`, it will panic"),
);

View File

@ -0,0 +1,39 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_slice_of_primitives;
use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::LitKind;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_span::source_map::Spanned;
use super::GET_FIRST;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx hir::Expr<'_>,
recv: &'tcx hir::Expr<'_>,
arg: &'tcx hir::Expr<'_>,
) {
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
if cx.tcx.type_of(impl_id).is_slice();
if let Some(_) = is_slice_of_primitives(cx, recv);
if let hir::ExprKind::Lit(Spanned { node: LitKind::Int(0, _), .. }) = arg.kind;
then {
let mut app = Applicability::MachineApplicable;
let slice_name = snippet_with_applicability(cx, recv.span, "..", &mut app);
span_lint_and_sugg(
cx,
GET_FIRST,
expr.span,
&format!("accessing first element with `{0}.get(0)`", slice_name),
"try",
format!("{}.first()", slice_name),
app,
);
}
}
}

View File

@ -0,0 +1,107 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::{get_expr_use_or_unification_node, is_lang_ctor, is_no_std_crate};
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext;
use super::{ITER_ON_EMPTY_COLLECTIONS, ITER_ON_SINGLE_ITEMS};
enum IterType {
Iter,
IterMut,
IntoIter,
}
impl IterType {
fn ref_prefix(&self) -> &'static str {
match self {
Self::Iter => "&",
Self::IterMut => "&mut ",
Self::IntoIter => "",
}
}
}
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
let item = match &recv.kind {
ExprKind::Array(v) if v.len() <= 1 => v.first(),
ExprKind::Path(p) => {
if is_lang_ctor(cx, p, OptionNone) {
None
} else {
return;
}
},
ExprKind::Call(f, some_args) if some_args.len() == 1 => {
if let ExprKind::Path(p) = &f.kind {
if is_lang_ctor(cx, p, OptionSome) {
Some(&some_args[0])
} else {
return;
}
} else {
return;
}
},
_ => return,
};
let iter_type = match method_name {
"iter" => IterType::Iter,
"iter_mut" => IterType::IterMut,
"into_iter" => IterType::IntoIter,
_ => return,
};
let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
Some((Node::Expr(parent), child_id)) => match parent.kind {
ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
ExprKind::If(_, _, _)
| ExprKind::Match(_, _, _)
| ExprKind::Closure(_)
| ExprKind::Ret(_)
| ExprKind::Break(_, _) => true,
_ => false,
},
Some((Node::Stmt(_) | Node::Local(_), _)) => false,
_ => true,
};
if is_unified {
return;
}
if let Some(i) = item {
let sugg = format!(
"{}::iter::once({}{})",
if is_no_std_crate(cx) { "core" } else { "std" },
iter_type.ref_prefix(),
snippet(cx, i.span, "...")
);
span_lint_and_sugg(
cx,
ITER_ON_SINGLE_ITEMS,
expr.span,
&format!("`{method_name}` call on a collection with only one item"),
"try",
sugg,
Applicability::MaybeIncorrect,
);
} else {
span_lint_and_sugg(
cx,
ITER_ON_EMPTY_COLLECTIONS,
expr.span,
&format!("`{method_name}` call on an empty collection"),
"try",
if is_no_std_crate(cx) {
"core::iter::empty()".to_string()
} else {
"std::iter::empty()".to_string()
},
Applicability::MaybeIncorrect,
);
}
}

View File

@ -0,0 +1,64 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{is_lang_ctor, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{ResultErr, ResultOk};
use rustc_hir::{Closure, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym;
use super::MANUAL_OK_OR;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
recv: &'tcx Expr<'_>,
or_expr: &'tcx Expr<'_>,
map_expr: &'tcx Expr<'_>,
) {
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Option);
if let ExprKind::Call(Expr { kind: ExprKind::Path(err_path), .. }, [err_arg]) = or_expr.kind;
if is_lang_ctor(cx, err_path, ResultErr);
if is_ok_wrapping(cx, map_expr);
if let Some(recv_snippet) = snippet_opt(cx, recv.span);
if let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span);
if let Some(indent) = indent_of(cx, expr.span);
then {
let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
span_lint_and_sugg(
cx,
MANUAL_OK_OR,
expr.span,
"this pattern reimplements `Option::ok_or`",
"replace with",
format!(
"{}.ok_or({})",
recv_snippet,
reindented_err_arg_snippet
),
Applicability::MachineApplicable,
);
}
}
}
fn is_ok_wrapping(cx: &LateContext<'_>, map_expr: &Expr<'_>) -> bool {
if let ExprKind::Path(ref qpath) = map_expr.kind {
if is_lang_ctor(cx, qpath, ResultOk) {
return true;
}
}
if_chain! {
if let ExprKind::Closure(&Closure { body, .. }) = map_expr.kind;
let body = cx.tcx.hir().body(body);
if let PatKind::Binding(_, param_id, ..) = body.params[0].pat.kind;
if let ExprKind::Call(Expr { kind: ExprKind::Path(ok_path), .. }, &[ref ok_arg]) = body.value.kind;
if is_lang_ctor(cx, ok_path, ResultOk);
then { path_to_local_id(ok_arg, param_id) } else { false }
}
}

View File

@ -0,0 +1,122 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_lint::LateContext;
use rustc_middle::mir::Mutability;
use rustc_middle::ty;
use rustc_middle::ty::adjustment::Adjust;
use rustc_semver::RustcVersion;
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span};
use super::MAP_CLONE;
pub(super) fn check<'tcx>(
cx: &LateContext<'_>,
e: &hir::Expr<'_>,
recv: &hir::Expr<'_>,
arg: &'tcx hir::Expr<'_>,
msrv: Option<RustcVersion>,
) {
if_chain! {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id);
if cx.tcx.impl_of_method(method_id)
.map_or(false, |id| is_type_diagnostic_item(cx, cx.tcx.type_of(id), sym::Option))
|| is_diag_trait_item(cx, method_id, sym::Iterator);
if let hir::ExprKind::Closure(&hir::Closure{ body, .. }) = arg.kind;
then {
let closure_body = cx.tcx.hir().body(body);
let closure_expr = peel_blocks(&closure_body.value);
match closure_body.params[0].pat.kind {
hir::PatKind::Ref(inner, hir::Mutability::Not) => if let hir::PatKind::Binding(
hir::BindingAnnotation::Unannotated, .., name, None
) = inner.kind {
if ident_eq(name, closure_expr) {
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
},
hir::PatKind::Binding(hir::BindingAnnotation::Unannotated, .., name, None) => {
match closure_expr.kind {
hir::ExprKind::Unary(hir::UnOp::Deref, inner) => {
if ident_eq(name, inner) {
if let ty::Ref(.., Mutability::Not) = cx.typeck_results().expr_ty(inner).kind() {
lint_explicit_closure(cx, e.span, recv.span, true, msrv);
}
}
},
hir::ExprKind::MethodCall(method, [obj], _) => if_chain! {
if ident_eq(name, obj) && method.ident.name == sym::clone;
if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id);
if let Some(trait_id) = cx.tcx.trait_of_item(fn_id);
if cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id);
// no autoderefs
if !cx.typeck_results().expr_adjustments(obj).iter()
.any(|a| matches!(a.kind, Adjust::Deref(Some(..))));
then {
let obj_ty = cx.typeck_results().expr_ty(obj);
if let ty::Ref(_, ty, mutability) = obj_ty.kind() {
if matches!(mutability, Mutability::Not) {
let copy = is_copy(cx, *ty);
lint_explicit_closure(cx, e.span, recv.span, copy, msrv);
}
} else {
lint_needless_cloning(cx, e.span, recv.span);
}
}
},
_ => {},
}
},
_ => {},
}
}
}
}
fn ident_eq(name: Ident, path: &hir::Expr<'_>) -> bool {
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = path.kind {
path.segments.len() == 1 && path.segments[0].ident == name
} else {
false
}
}
fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) {
span_lint_and_sugg(
cx,
MAP_CLONE,
root.trim_start(receiver).unwrap(),
"you are needlessly cloning iterator elements",
"remove the `map` call",
String::new(),
Applicability::MachineApplicable,
);
}
fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) {
let mut applicability = Applicability::MachineApplicable;
let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) {
("you are using an explicit closure for copying elements", "copied")
} else {
("you are using an explicit closure for cloning elements", "cloned")
};
span_lint_and_sugg(
cx,
MAP_CLONE,
replace,
message,
&format!("consider calling the dedicated `{}` method", sugg_method),
format!(
"{}.{}()",
snippet_with_applicability(cx, root, "..", &mut applicability),
sugg_method,
),
applicability,
);
}

View File

@ -0,0 +1,34 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use super::MAP_ERR_IGNORE;
pub(super) fn check<'tcx>(cx: &LateContext<'_>, e: &Expr<'_>, arg: &'tcx Expr<'_>) {
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id)
&& let Some(impl_id) = cx.tcx.impl_of_method(method_id)
&& is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Result)
&& let ExprKind::Closure(&Closure {
capture_clause: CaptureBy::Ref,
body,
fn_decl_span,
..
}) = arg.kind
&& let closure_body = cx.tcx.hir().body(body)
&& let [param] = closure_body.params
&& let PatKind::Wild = param.pat.kind
{
// span the area of the closure capture and warn that the
// original error will be thrown away
span_lint_and_help(
cx,
MAP_ERR_IGNORE,
fn_decl_span,
"`map_err(|_|...` wildcard pattern discards the original error",
None,
"consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::{sym, Span};
use super::MUT_MUTEX_LOCK;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
if_chain! {
if let ty::Ref(_, _, Mutability::Mut) = cx.typeck_results().expr_ty(recv).kind();
if let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id);
if let Some(impl_id) = cx.tcx.impl_of_method(method_id);
if is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id), sym::Mutex);
then {
span_lint_and_sugg(
cx,
MUT_MUTEX_LOCK,
name_span,
"calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
"change this to",
"get_mut".to_owned(),
Applicability::MaybeIncorrect,
);
}
}
}

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