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 = [ dependencies = [
"arrayvec", "arrayvec",
"if_chain", "if_chain",
"itertools",
"rustc-semver", "rustc-semver",
] ]

View File

@ -555,7 +555,7 @@ impl TokenStreamBuilder {
// Get the first stream, which will become the result stream. // Get the first stream, which will become the result stream.
// If it's `None`, create an empty 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; let mut res_stream_lrc = iter.next().unwrap().0;
// Append the subsequent elements to the result stream, after // 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 /// It is up to the caller to make sure that the elements are sorted by key
/// and that there are no duplicates. /// and that there are no duplicates.
#[inline] #[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() { if elements.is_empty() {
return; return;
} }
@ -173,28 +173,28 @@ impl<K: Ord, V> SortedMap<K, V> {
let start_index = self.lookup_index_for(&elements[0].0); let start_index = self.lookup_index_for(&elements[0].0);
let drain = match start_index { let elements = match start_index {
Ok(index) => { Ok(index) => {
let mut drain = elements.drain(..); let mut elements = elements.into_iter();
self.data[index] = drain.next().unwrap(); self.data[index] = elements.next().unwrap();
drain elements
} }
Err(index) => { Err(index) => {
if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 { if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 {
// We can copy the whole range without having to mix with // We can copy the whole range without having to mix with
// existing elements. // existing elements.
self.data.splice(index..index, elements.drain(..)); self.data.splice(index..index, elements.into_iter());
return; return;
} }
let mut drain = elements.drain(..); let mut elements = elements.into_iter();
self.data.insert(index, drain.next().unwrap()); self.data.insert(index, elements.next().unwrap());
drain elements
} }
}; };
// Insert the rest // Insert the rest
for (k, v) in drain { for (k, v) in elements {
self.insert(k, v); self.insert(k, v);
} }
} }

View File

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

View File

@ -31,3 +31,10 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> {
#[derive(SessionDiagnostic)] #[derive(SessionDiagnostic)]
#[diag(driver::rlink_no_a_file)] #[diag(driver::rlink_no_a_file)]
pub(crate) struct RlinkNotAFile; 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_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_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", passes => "../locales/en-US/passes.ftl",
plugin_impl => "../locales/en-US/plugin_impl.ftl", plugin_impl => "../locales/en-US/plugin_impl.ftl",
privacy => "../locales/en-US/privacy.ftl", privacy => "../locales/en-US/privacy.ftl",
query_system => "../locales/en-US/query_system.ftl",
save_analysis => "../locales/en-US/save_analysis.ftl", save_analysis => "../locales/en-US/save_analysis.ftl",
ty_utils => "../locales/en-US/ty_utils.ftl", ty_utils => "../locales/en-US/ty_utils.ftl",
typeck => "../locales/en-US/typeck.ftl", typeck => "../locales/en-US/typeck.ftl",

View File

@ -974,12 +974,12 @@ impl Diagnostic {
fn sub_with_highlights<M: Into<SubdiagnosticMessage>>( fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
&mut self, &mut self,
level: Level, level: Level,
mut message: Vec<(M, Style)>, message: Vec<(M, Style)>,
span: MultiSpan, span: MultiSpan,
render_span: Option<MultiSpan>, render_span: Option<MultiSpan>,
) { ) {
let message = message let message = message
.drain(..) .into_iter()
.map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1)) .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
.collect(); .collect();
let sub = SubDiagnostic { level, message, span, render_span }; 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 /// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
/// passed around as a reference thereafter. /// passed around as a reference thereafter.
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> { 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. /// Convert `DiagnosticMessage`s to a string, performing translation if necessary.

View File

@ -393,8 +393,14 @@ impl LateLintPass<'_> for Diagnostics {
return; return;
} }
let mut found_parent_with_attr = false;
let mut found_impl = 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); debug!(?parent);
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent && if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
let Impl { of_trait: Some(of_trait), .. } = impl_ && let Impl { of_trait: Some(of_trait), .. } = impl_ &&
@ -407,7 +413,7 @@ impl LateLintPass<'_> for Diagnostics {
} }
} }
debug!(?found_impl); debug!(?found_impl);
if !found_impl { if !found_parent_with_attr && !found_impl {
cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| { cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
lint.build(fluent::lint::diag_out_of_impl).emit(); lint.build(fluent::lint::diag_out_of_impl).emit();
}) })
@ -425,7 +431,7 @@ impl LateLintPass<'_> for Diagnostics {
} }
} }
debug!(?found_diagnostic_message); debug!(?found_diagnostic_message);
if !found_diagnostic_message { if !found_parent_with_attr && !found_diagnostic_message {
cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| { cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
lint.build(fluent::lint::untranslatable_diag).emit(); 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 { 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::collections::HashMap;
use std::fmt; use std::fmt;
use std::str::FromStr; 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}; use synstructure::{BindingInfo, Structure, VariantInfo};
/// Which kind of suggestion is being created? /// Which kind of suggestion is being created?
@ -28,41 +28,8 @@ enum SubdiagnosticSuggestionKind {
Verbose, 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? /// Which kind of subdiagnostic is being created from a variant?
#[derive(Clone)] #[derive(Clone, Copy)]
enum SubdiagnosticKind { enum SubdiagnosticKind {
/// `#[label(...)]` /// `#[label(...)]`
Label, Label,
@ -73,9 +40,31 @@ enum SubdiagnosticKind {
/// `#[warning(...)]` /// `#[warning(...)]`
Warn, Warn,
/// `#[suggestion{,_short,_hidden,_verbose}]` /// `#[suggestion{,_short,_hidden,_verbose}]`
Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream }, Suggestion(SubdiagnosticSuggestionKind),
/// `#[multipart_suggestion{,_short,_hidden,_verbose}]` }
MultipartSuggestion { suggestion_kind: 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 { impl quote::IdentFragment for SubdiagnosticKind {
@ -85,9 +74,17 @@ impl quote::IdentFragment for SubdiagnosticKind {
SubdiagnosticKind::Note => write!(f, "note"), SubdiagnosticKind::Note => write!(f, "note"),
SubdiagnosticKind::Help => write!(f, "help"), SubdiagnosticKind::Help => write!(f, "help"),
SubdiagnosticKind::Warn => write!(f, "warn"), SubdiagnosticKind::Warn => write!(f, "warn"),
SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"), SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
SubdiagnosticKind::MultipartSuggestion { .. } => { write!(f, "suggestion")
write!(f, "multipart_suggestion_with_style") }
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, variant,
span, span,
fields: fields_map, fields: fields_map,
kinds: Vec::new(),
slugs: Vec::new(),
code: None,
span_field: None, span_field: None,
applicability: None, applicability: None,
has_suggestion_parts: false,
}; };
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
}); });
@ -194,15 +193,21 @@ struct SessionSubdiagnosticDeriveBuilder<'a> {
/// derive builder. /// derive builder.
fields: HashMap<String, TokenStream>, 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. /// Identifier for the binding to the `#[primary_span]` field.
span_field: Option<(proc_macro2::Ident, proc_macro::Span)>, span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
/// If a suggestion, the identifier for the binding to the `#[applicability]` field or a /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
/// `rustc_errors::Applicability::*` variant directly. /// `rustc_errors::Applicability::*` variant directly.
applicability: Option<(TokenStream, proc_macro::Span)>, 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> { impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
@ -212,133 +217,124 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
} }
impl<'a> SessionSubdiagnosticDeriveBuilder<'a> { impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
fn identify_kind( fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> {
&mut self, for (i, attr) in self.variant.ast().attrs.iter().enumerate() {
) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
let mut kind_slug = None;
for attr in self.variant.ast().attrs {
let span = attr.span().unwrap(); let span = attr.span().unwrap();
let name = attr.path.segments.last().unwrap().ident.to_string(); let name = attr.path.segments.last().unwrap().ident.to_string();
let name = name.as_str(); let name = name.as_str();
let meta = attr.parse_meta()?; let meta = attr.parse_meta()?;
let Meta::List(MetaList { ref nested, .. }) = meta else { let kind = match meta {
throw_invalid_attr!(attr, &meta); Meta::List(MetaList { ref nested, .. }) => {
}; let mut nested_iter = nested.into_iter();
if let Some(nested_attr) = nested_iter.next() {
let mut kind = match name { match nested_attr {
"label" => SubdiagnosticKind::Label, NestedMeta::Meta(Meta::Path(path)) => {
"note" => SubdiagnosticKind::Note, self.slugs.push((path.clone(), span));
"help" => SubdiagnosticKind::Help, }
"warning" => SubdiagnosticKind::Warn, NestedMeta::Meta(meta @ Meta::NameValue(_))
_ => { if matches!(
if let Some(suggestion_kind) = meta.path().segments.last().unwrap().ident.to_string().as_str(),
name.strip_prefix("suggestion").and_then(|s| s.parse().ok()) "code" | "applicability"
{ ) =>
SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() } {
} else if let Some(suggestion_kind) = // don't error for valid follow-up attributes
name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) }
{ nested_attr => {
SubdiagnosticKind::MultipartSuggestion { suggestion_kind } throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
} else { diag.help(
throw_invalid_attr!(attr, &meta); "first argument of the attribute should be the diagnostic \
slug",
)
})
}
};
} }
}
};
let mut slug = None; for nested_attr in nested_iter {
let mut code = None; let meta = match nested_attr {
NestedMeta::Meta(ref meta) => meta,
_ => throw_invalid_nested_attr!(attr, &nested_attr),
};
let mut nested_iter = nested.into_iter(); let span = meta.span().unwrap();
if let Some(nested_attr) = nested_iter.next() { let nested_name = meta.path().segments.last().unwrap().ident.to_string();
match nested_attr { let nested_name = nested_name.as_str();
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",
)
})
}
};
}
for nested_attr in nested_iter { match meta {
let meta = match nested_attr { Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
NestedMeta::Meta(ref meta) => meta, match nested_name {
_ => throw_invalid_nested_attr!(attr, &nested_attr), "code" => {
}; let formatted_str = self.build_format(&s.value(), s.span());
self.code.set_once((formatted_str, span));
let span = meta.span().unwrap(); }
let nested_name = meta.path().segments.last().unwrap().ident.to_string(); "applicability" => {
let nested_name = nested_name.as_str(); let value = match Applicability::from_str(&s.value()) {
Ok(v) => v,
let value = match meta { Err(()) => {
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value, span_err(span, "invalid applicability").emit();
Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { Applicability::Unspecified
diag.help("a diagnostic slug must be the first argument to the attribute") }
}), };
_ => throw_invalid_nested_attr!(attr, &nested_attr), self.applicability.set_once((quote! { #value }, span));
}; }
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
match nested_name { diag.help(
"code" => { "only `code` and `applicability` are valid nested \
if matches!(kind, SubdiagnosticKind::Suggestion { .. }) { attributes",
let formatted_str = self.build_format(&value.value(), value.span()); )
code.set_once((formatted_str, span)); }),
} else { }
span_err( }
span, _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
&format!( if matches!(meta, Meta::Path(_)) {
"`code` is not a valid nested attribute of a `{}` attribute", diag.help(
name "a diagnostic slug must be the first argument to the \
), attribute",
) )
.emit(); } else {
diag
}
}),
} }
} }
"applicability" => {
if matches!( let Ok(kind) = SubdiagnosticKind::from_str(name) else {
kind, throw_invalid_attr!(attr, &meta)
SubdiagnosticKind::Suggestion { .. } };
| SubdiagnosticKind::MultipartSuggestion { .. }
) { kind
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")
}),
} }
_ => 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!( throw_span_err!(
span, span,
&format!( &format!(
@ -346,338 +342,146 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
name 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_code(
fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream { &mut self,
binding: &BindingInfo<'_>,
have_suggestion: bool,
) -> Result<TokenStream, DiagnosticDeriveError> {
let ast = binding.ast(); 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 diag = &self.diag;
let ident = ast.ident.as_ref().unwrap(); let generated = quote! {
quote! {
#diag.set_arg( #diag.set_arg(
stringify!(#ident), stringify!(#ident),
#binding #binding
); );
} };
Ok(inner_ty.with(binding, generated))
} }
/// Generates the necessary code for all attributes on a field. fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
fn generate_field_attr_code( self.identify_kind()?;
&mut self, if self.kinds.is_empty() {
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 {
throw_span_err!( throw_span_err!(
self.variant.ast().ident.span().unwrap(), self.variant.ast().ident.span().unwrap(),
"subdiagnostic kind not specified" "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 { let span_field = self.span_field.as_ref().map(|(span, _)| span);
SubdiagnosticKind::Label let applicability = match self.applicability.clone() {
| SubdiagnosticKind::Note Some((applicability, _)) => Some(applicability),
| SubdiagnosticKind::Help None if have_suggestion => {
| SubdiagnosticKind::Warn span_err(self.span, "suggestion without `applicability`").emit();
| SubdiagnosticKind::Suggestion { .. } => quote! {}, Some(quote! { rustc_errors::Applicability::Unspecified })
SubdiagnosticKind::MultipartSuggestion { .. } => { }
quote! { let mut suggestions = Vec::new(); } None => None,
} };
};
let attr_args: TokenStream = self let diag = &self.diag;
.variant let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
.bindings() let message = quote! { rustc_errors::fluent::#slug };
.iter() let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
.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 } => {
if let Some(span) = span_field { if let Some(span) = span_field {
let style = suggestion_kind.to_suggestion_style(); quote! { #diag.#name(#span, #message, #code, #applicability); }
quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
} else { } else {
span_err(self.span, "suggestion without `#[primary_span]` field").emit(); span_err(self.span, "suggestion without `#[primary_span]` field").emit();
quote! { unreachable!(); } quote! { unreachable!(); }
} }
} } else if matches!(kind, SubdiagnosticKind::Label) {
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 => {
if let Some(span) = span_field { if let Some(span) = span_field {
quote! { #diag.#name(#span, #message); } quote! { #diag.#name(#span, #message); }
} else { } else {
span_err(self.span, "label without `#[primary_span]` field").emit(); span_err(self.span, "label without `#[primary_span]` field").emit();
quote! { unreachable!(); } quote! { unreachable!(); }
} }
} } else {
_ => {
if let Some(span) = span_field { if let Some(span) = span_field {
quote! { #diag.#name(#span, #message); } quote! { #diag.#name(#span, #message); }
} else { } else {
quote! { #diag.#name(#message); } quote! { #diag.#name(#message); }
} }
} };
}; tokens.extend(quote! {
#call
#args
});
}
let plain_args: TokenStream = self Ok(tokens)
.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
})
} }
} }

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

View File

@ -1,12 +1,11 @@
use crate::error::CycleStack;
use crate::query::plumbing::CycleError; use crate::query::plumbing::CycleError;
use crate::query::{QueryContext, QueryStackFrame}; use crate::query::{QueryContext, QueryStackFrame};
use rustc_hir::def::DefKind;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{ use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level};
struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level, use rustc_hir::def::DefKind;
}; use rustc_session::{Session, SessionDiagnostic};
use rustc_session::Session;
use rustc_span::Span; use rustc_span::Span;
use std::hash::Hash; use std::hash::Hash;
@ -536,46 +535,44 @@ pub(crate) fn report_cycle<'a>(
assert!(!stack.is_empty()); assert!(!stack.is_empty());
let span = stack[0].query.default_span(stack[1 % stack.len()].span); 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() { for i in 1..stack.len() {
let query = &stack[i].query; let query = &stack[i].query;
let span = query.default_span(stack[(i + 1) % stack.len()].span); let span = query.default_span(stack[(i + 1) % stack.len()].span);
err.span_note(span, &format!("...which requires {}...", query.description)); cycle_stack.push(CycleStack { span, desc: query.description.to_owned() });
}
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");
}
} }
let mut cycle_usage = None;
if let Some((span, query)) = usage { 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>( pub fn print_query_stack<CTX: QueryContext>(

View File

@ -125,6 +125,6 @@ pub trait QueryContext: HasDepContext {
) -> R; ) -> R;
fn depth_limit_error(&self) { 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)); let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
if old_in_panic { if old_in_panic {
sess.struct_err( sess.emit_err(crate::error::Reentrant);
"internal compiler error: re-entrant incremental verify failure, suppressing message",
)
.emit();
} else { } else {
sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node)) sess.emit_err(crate::error::IncrementCompilation {
.help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd)) run_cmd,
.note("Please follow the instructions below to create a bug report with the provided information") dep_node: format!("{:?}", dep_node),
.note("See <https://github.com/rust-lang/rust/issues/84970> for more information") });
.emit();
panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result); 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() .iter()
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind)) .filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
.map(|(variant, _, kind)| (path_names_to_string(variant), 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), CtorKind::Fictive => format!("({} {{}})", variant),
}) })
.collect::<Vec<_>>(); .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 { let msg = if suggestable_variants.len() == 1 {
"you might have meant to use the following enum variant" "you might have meant to use the following enum variant"
} else { } else {
@ -1813,7 +1814,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
err.span_suggestions( err.span_suggestions(
span, span,
msg, msg,
suggestable_variants.drain(..), suggestable_variants.into_iter(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
@ -1830,15 +1831,15 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !suggestable_variants_with_placeholders.is_empty() { if !suggestable_variants_with_placeholders.is_empty() {
let msg = match ( let msg =
suggestable_variants.is_empty(), match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) {
suggestable_variants_with_placeholders.len(), (true, 1) => "the following enum variant is available",
) { (true, _) => "the following enum variants are available",
(true, 1) => "the following enum variant is available", (false, 1) => "alternatively, the following enum variant is available",
(true, _) => "the following enum variants are available", (false, _) => {
(false, 1) => "alternatively, the following enum variant is available", "alternatively, the following enum variants are also available"
(false, _) => "alternatively, the following enum variants are also available", }
}; };
err.span_suggestions( err.span_suggestions(
span, span,

View File

@ -501,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
if !errors_buffer.is_empty() { if !errors_buffer.is_empty() {
errors_buffer.sort_by_key(|diag| diag.span.primary_span()); 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); 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() { if self.not_enough_args_provided() {
self.suggest_adding_args(err); self.suggest_adding_args(err);
} else if self.too_many_args_provided() { } else if self.too_many_args_provided() {
self.suggest_moving_args_from_assoc_fn_to_trait(err);
self.suggest_removing_args_or_generics(err); self.suggest_removing_args_or_generics(err);
} else { } else {
unreachable!(); 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): /// Suggests to remove redundant argument(s):
/// ///
/// ```text /// ```text

View File

@ -350,10 +350,12 @@ macro_rules! matches {
/// Unwraps a result or propagates its error. /// Unwraps a result or propagates its error.
/// ///
/// The `?` operator was added to replace `try!` and should be used instead. /// The [`?` operator][propagating-errors] was added to replace `try!`
/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use /// and should be used instead. Furthermore, `try` is a reserved word
/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`. /// 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 /// [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 /// `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::ffi::OsString;
use crate::fmt; use crate::fmt;
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom}; 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::os::windows::io::{AsHandle, BorrowedHandle};
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::ptr; use crate::ptr;
@ -326,7 +326,8 @@ impl File {
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?; cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
let mut reparse_tag = 0; let mut reparse_tag = 0;
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 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) { if let Ok((_, buf)) = self.reparse_point(&mut b) {
reparse_tag = (*buf).ReparseTag; reparse_tag = (*buf).ReparseTag;
} }
@ -389,7 +390,8 @@ impl File {
attr.file_size = info.AllocationSize as u64; attr.file_size = info.AllocationSize as u64;
attr.number_of_links = Some(info.NumberOfLinks); attr.number_of_links = Some(info.NumberOfLinks);
if attr.file_type().is_reparse_point() { 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) { if let Ok((_, buf)) = self.reparse_point(&mut b) {
attr.reparse_tag = (*buf).ReparseTag; attr.reparse_tag = (*buf).ReparseTag;
} }
@ -463,7 +465,7 @@ impl File {
// avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`. // avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
fn reparse_point( fn reparse_point(
&self, &self,
space: &mut Align8<[u8]>, space: &mut Align8<[MaybeUninit<u8>]>,
) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> { ) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
unsafe { unsafe {
let mut bytes = 0; let mut bytes = 0;
@ -488,7 +490,7 @@ impl File {
} }
fn readlink(&self) -> io::Result<PathBuf> { 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)?; let (_bytes, buf) = self.reparse_point(&mut space)?;
unsafe { unsafe {
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag { let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
@ -658,12 +660,16 @@ impl File {
/// A buffer for holding directory entries. /// A buffer for holding directory entries.
struct DirBuff { struct DirBuff {
buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>, buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
} }
impl DirBuff { impl DirBuff {
const BUFFER_SIZE: usize = 1024; const BUFFER_SIZE: usize = 1024;
fn new() -> Self { 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 { fn capacity(&self) -> usize {
self.buffer.0.len() self.buffer.0.len()
@ -676,8 +682,8 @@ impl DirBuff {
DirBuffIter::new(self) DirBuffIter::new(self)
} }
} }
impl AsRef<[u8]> for DirBuff { impl AsRef<[MaybeUninit<u8>]> for DirBuff {
fn as_ref(&self) -> &[u8] { fn as_ref(&self) -> &[MaybeUninit<u8>] {
&self.buffer.0 &self.buffer.0
} }
} }
@ -686,7 +692,7 @@ impl AsRef<[u8]> for DirBuff {
/// ///
/// Currently only returns file names (UTF-16 encoded). /// Currently only returns file names (UTF-16 encoded).
struct DirBuffIter<'a> { struct DirBuffIter<'a> {
buffer: Option<&'a [u8]>, buffer: Option<&'a [MaybeUninit<u8>]>,
cursor: usize, cursor: usize,
} }
impl<'a> DirBuffIter<'a> { impl<'a> DirBuffIter<'a> {
@ -701,9 +707,13 @@ impl<'a> Iterator for DirBuffIter<'a> {
let buffer = &self.buffer?[self.cursor..]; let buffer = &self.buffer?[self.cursor..];
// Get the name and next entry from the buffer. // Get the name and next entry from the buffer.
// SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the // SAFETY:
// last field (the file name) is unsized. So an offset has to be // - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
// used to get the file name slice. // 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 (name, is_directory, next_entry) = unsafe {
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>(); let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
// Guaranteed to be aligned in documentation for // 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(); let h = f.as_inner().as_raw_handle();
unsafe { 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 data_ptr = data.0.as_mut_ptr();
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>(); let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>(); 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 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`. 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 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 which implement the `memory64` feature and if they do they're likely behind a
flag, for example: flag, for example:

View File

@ -1,6 +1,7 @@
// compile-flags: -Z unstable-options // compile-flags: -Z unstable-options
#![crate_type = "lib"] #![crate_type = "lib"]
#![feature(rustc_attrs)]
#![feature(rustc_private)] #![feature(rustc_private)]
#![deny(rustc::untranslatable_diagnostic)] #![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)] #![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 only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
//~^^ ERROR diagnostics should be created using translatable messages //~^^ 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 error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:36:14 --> $DIR/diagnostics.rs:37:14
| |
LL | sess.struct_err("untranslatable diagnostic") LL | sess.struct_err("untranslatable diagnostic")
| ^^^^^^^^^^ | ^^^^^^^^^^
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/diagnostics.rs:5:9 --> $DIR/diagnostics.rs:6:9
| |
LL | #![deny(rustc::untranslatable_diagnostic)] LL | #![deny(rustc::untranslatable_diagnostic)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:53:14 --> $DIR/diagnostics.rs:54:14
| |
LL | diag.note("untranslatable diagnostic"); LL | diag.note("untranslatable diagnostic");
| ^^^^ | ^^^^
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls 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); LL | let _diag = sess.struct_err(fluent::parser::expect_path);
| ^^^^^^^^^^ | ^^^^^^^^^^
| |
note: the lint level is defined here note: the lint level is defined here
--> $DIR/diagnostics.rs:6:9 --> $DIR/diagnostics.rs:7:9
| |
LL | #![deny(rustc::diagnostic_outside_of_impl)] LL | #![deny(rustc::diagnostic_outside_of_impl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls 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"); LL | let _diag = sess.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^ | ^^^^^^^^^^
error: diagnostics should be created using translatable messages 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"); LL | let _diag = sess.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^ | ^^^^^^^^^^

View File

@ -167,8 +167,8 @@ enum P {
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
enum Q { enum Q {
#[bar] #[bar]
//~^ ERROR `#[bar]` is not a valid attribute //~^ ERROR `#[bar]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope //~^^ ERROR cannot find attribute `bar` in this scope
A { A {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -179,8 +179,8 @@ enum Q {
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
enum R { enum R {
#[bar = "..."] #[bar = "..."]
//~^ ERROR `#[bar = ...]` is not a valid attribute //~^ ERROR `#[bar = ...]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope //~^^ ERROR cannot find attribute `bar` in this scope
A { A {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -191,8 +191,8 @@ enum R {
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
enum S { enum S {
#[bar = 4] #[bar = 4]
//~^ ERROR `#[bar = ...]` is not a valid attribute //~^ ERROR `#[bar = ...]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope //~^^ ERROR cannot find attribute `bar` in this scope
A { A {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -203,8 +203,8 @@ enum S {
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
enum T { enum T {
#[bar("...")] #[bar("...")]
//~^ ERROR `#[bar(...)]` is not a valid attribute //~^ ERROR `#[bar("...")]` is not a valid attribute
//~^^ ERROR cannot find attribute `bar` in this scope //~^^ ERROR cannot find attribute `bar` in this scope
A { A {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -215,7 +215,7 @@ enum T {
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
enum U { enum U {
#[label(code = "...")] #[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 { A {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -232,7 +232,7 @@ enum V {
var: String, var: String,
}, },
B { B {
//~^ ERROR subdiagnostic kind not specified //~^ ERROR subdiagnostic kind not specified
#[primary_span] #[primary_span]
span: Span, span: Span,
var: String, var: String,
@ -307,16 +307,6 @@ union AC {
b: u64 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)] #[derive(SessionSubdiagnostic)]
#[label(parser::add_paren, parser::add_paren)] #[label(parser::add_paren, parser::add_paren)]
//~^ ERROR `#[label(parser::add_paren)]` is not a valid attribute //~^ ERROR `#[label(parser::add_paren)]` is not a valid attribute
@ -329,16 +319,16 @@ struct AE {
#[label(parser::add_paren)] #[label(parser::add_paren)]
struct AF { struct AF {
#[primary_span] #[primary_span]
//~^ NOTE previously specified here //~^ NOTE previously specified here
span_a: Span, span_a: Span,
#[primary_span] #[primary_span]
//~^ ERROR specified multiple times //~^ ERROR specified multiple times
span_b: Span, span_b: Span,
} }
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
struct AG { struct AG {
//~^ ERROR subdiagnostic kind not specified //~^ ERROR subdiagnostic kind not specified
#[primary_span] #[primary_span]
span: Span, span: Span,
} }
@ -390,25 +380,27 @@ struct AK {
#[primary_span] #[primary_span]
span: Span, span: Span,
#[applicability] #[applicability]
//~^ NOTE previously specified here //~^ NOTE previously specified here
applicability_a: Applicability, applicability_a: Applicability,
#[applicability] #[applicability]
//~^ ERROR specified multiple times //~^ ERROR specified multiple times
applicability_b: Applicability, applicability_b: Applicability,
} }
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
#[suggestion(parser::add_paren, code = "...")] #[suggestion(parser::add_paren, code = "...")]
//~^ ERROR suggestion without `applicability`
struct AL { struct AL {
#[primary_span] #[primary_span]
span: Span, span: Span,
#[applicability] #[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, applicability: Span,
} }
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
#[suggestion(parser::add_paren, code = "...")] #[suggestion(parser::add_paren, code = "...")]
//~^ ERROR suggestion without `applicability`
struct AM { struct AM {
#[primary_span] #[primary_span]
span: Span, span: Span,
@ -444,7 +436,8 @@ struct AQ;
#[derive(SessionSubdiagnostic)] #[derive(SessionSubdiagnostic)]
#[suggestion(parser::add_paren, code = "...")] #[suggestion(parser::add_paren, code = "...")]
//~^ ERROR suggestion without `#[primary_span]` field //~^ ERROR suggestion without `applicability`
//~^^ ERROR suggestion without `#[primary_span]` field
struct AR { struct AR {
var: String, var: String,
} }
@ -514,120 +507,3 @@ struct AZ {
#[primary_span] #[primary_span]
span: 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 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 = "...")] LL | #[label(parser::add_paren, code = "...")]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `applicability` is not a valid nested attribute of a `label` attribute 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")] LL | #[label(parser::add_paren, applicability = "machine-applicable")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: unsupported type attribute for subdiagnostic enum error: unsupported type attribute for subdiagnostic enum
--> $DIR/subdiagnostic-derive.rs:155:1 --> $DIR/subdiagnostic-derive.rs:155:1
@ -100,11 +100,13 @@ error: `#[bar = ...]` is not a valid attribute
LL | #[bar = 4] LL | #[bar = 4]
| ^^^^^^^^^^ | ^^^^^^^^^^
error: `#[bar(...)]` is not a valid attribute error: `#[bar("...")]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:205:5 --> $DIR/subdiagnostic-derive.rs:205:11
| |
LL | #[bar("...")] LL | #[bar("...")]
| ^^^^^^^^^^^^^ | ^^^^^
|
= help: first argument of the attribute should be the diagnostic slug
error: diagnostic slug must be first argument of a `#[label(...)]` attribute error: diagnostic slug must be first argument of a `#[label(...)]` attribute
--> $DIR/subdiagnostic-derive.rs:217:5 --> $DIR/subdiagnostic-derive.rs:217:5
@ -161,8 +163,6 @@ error: `#[bar(...)]` is not a valid attribute
| |
LL | #[bar("...")] LL | #[bar("...")]
| ^^^^^^^^^^^^^ | ^^^^^^^^^^^^^
|
= help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
error: unexpected unsupported untagged union error: unexpected unsupported untagged union
--> $DIR/subdiagnostic-derive.rs:304:1 --> $DIR/subdiagnostic-derive.rs:304:1
@ -174,20 +174,8 @@ LL | | b: u64
LL | | } 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 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)] 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 = help: a diagnostic slug must be the first argument to the attribute
error: specified multiple times error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:334:5 --> $DIR/subdiagnostic-derive.rs:324:5
| |
LL | #[primary_span] LL | #[primary_span]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
| |
note: previously specified here note: previously specified here
--> $DIR/subdiagnostic-derive.rs:331:5 --> $DIR/subdiagnostic-derive.rs:321:5
| |
LL | #[primary_span] LL | #[primary_span]
| ^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^
error: subdiagnostic kind not specified error: subdiagnostic kind not specified
--> $DIR/subdiagnostic-derive.rs:340:8 --> $DIR/subdiagnostic-derive.rs:330:8
| |
LL | struct AG { LL | struct AG {
| ^^ | ^^
error: specified multiple times error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:377:47 --> $DIR/subdiagnostic-derive.rs:367:47
| |
LL | #[suggestion(parser::add_paren, code = "...", code = "...")] LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
| |
note: previously specified here note: previously specified here
--> $DIR/subdiagnostic-derive.rs:377:33 --> $DIR/subdiagnostic-derive.rs:367:33
| |
LL | #[suggestion(parser::add_paren, code = "...", code = "...")] LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
| ^^^^^^^^^^^^ | ^^^^^^^^^^^^
error: specified multiple times error: specified multiple times
--> $DIR/subdiagnostic-derive.rs:395:5 --> $DIR/subdiagnostic-derive.rs:385:5
| |
LL | #[applicability] LL | #[applicability]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
| |
note: previously specified here note: previously specified here
--> $DIR/subdiagnostic-derive.rs:392:5 --> $DIR/subdiagnostic-derive.rs:382:5
| |
LL | #[applicability] LL | #[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`
--> $DIR/subdiagnostic-derive.rs:405:5 --> $DIR/subdiagnostic-derive.rs:396:5
| |
LL | #[applicability] LL | #[applicability]
| ^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^
error: suggestion without `code = "..."` error: suggestion without `applicability`
--> $DIR/subdiagnostic-derive.rs:418:1 --> $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 error: invalid applicability
--> $DIR/subdiagnostic-derive.rs:428:46 --> $DIR/subdiagnostic-derive.rs:420:46
| |
LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")] LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^
error: suggestion without `#[primary_span]` field error: suggestion without `applicability`
--> $DIR/subdiagnostic-derive.rs:446:1 --> $DIR/subdiagnostic-derive.rs:438:1
| |
LL | / #[suggestion(parser::add_paren, code = "...")] LL | / #[suggestion(parser::add_paren, code = "...")]
LL | | 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 | | struct AR {
LL | | var: String, LL | | var: String,
LL | | } LL | | }
| |_^ | |_^
error: unsupported type attribute for subdiagnostic enum error: unsupported type attribute for subdiagnostic enum
--> $DIR/subdiagnostic-derive.rs:460:1 --> $DIR/subdiagnostic-derive.rs:453:1
| |
LL | #[label] LL | #[label]
| ^^^^^^^^ | ^^^^^^^^
error: `var` doesn't refer to a field on this type 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")] LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
| ^^^^^^^ | ^^^^^^^
error: `var` doesn't refer to a field on this type 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")] 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 error: cannot find attribute `foo` in this scope
--> $DIR/subdiagnostic-derive.rs:63:3 --> $DIR/subdiagnostic-derive.rs:63:3
| |
@ -475,6 +371,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
LL | #[label(slug)] LL | #[label(slug)]
| ^^^^ not found in `rustc_errors::fluent` | ^^^^ 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`. 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 --> $DIR/invalid-const-arg-for-type-param.rs:6:23
| |
LL | let _: u32 = 5i32.try_into::<32>().unwrap(); 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 note: associated function defined here, with 0 generic parameters
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL --> $SRC_DIR/core/src/convert/mod.rs:LL:COL
| |
LL | fn try_into(self) -> Result<T, Self::Error>; 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 error[E0599]: no method named `f` found for struct `S` in the current scope
--> $DIR/invalid-const-arg-for-type-param.rs:9:7 --> $DIR/invalid-const-arg-for-type-param.rs:9:7

View File

@ -22,6 +22,10 @@ enum D {
Unit, Unit,
} }
enum E {
TupleWithFields(()),
}
fn main() { fn main() {
// Only variants without fields are suggested (and others mentioned in a note) where an enum // Only variants without fields are suggested (and others mentioned in a note) where an enum
// is used rather than a variant. // is used rather than a variant.
@ -34,6 +38,8 @@ fn main() {
//~^ ERROR expected value, found enum `C` //~^ ERROR expected value, found enum `C`
D.foo(); D.foo();
//~^ ERROR expected value, found enum `D` //~^ 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. // 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` error[E0423]: expected value, found enum `A`
--> $DIR/issue-73427.rs:29:5 --> $DIR/issue-73427.rs:33:5
| |
LL | A.foo(); LL | A.foo();
| ^ | ^
@ -23,7 +23,7 @@ LL | (A::Tuple()).foo();
| ~~~~~~~~~~~~ | ~~~~~~~~~~~~
LL | A::Unit.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(); LL | (A::StructWithFields { /* fields */ }).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -31,7 +31,7 @@ LL | (A::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `B` error[E0423]: expected value, found enum `B`
--> $DIR/issue-73427.rs:31:5 --> $DIR/issue-73427.rs:35:5
| |
LL | B.foo(); LL | B.foo();
| ^ | ^
@ -52,7 +52,7 @@ LL | (B::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `C` error[E0423]: expected value, found enum `C`
--> $DIR/issue-73427.rs:33:5 --> $DIR/issue-73427.rs:37:5
| |
LL | C.foo(); LL | C.foo();
| ^ | ^
@ -70,7 +70,7 @@ help: you might have meant to use the following enum variant
| |
LL | C::Unit.foo(); 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(); LL | (C::StructWithFields { /* fields */ }).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -78,7 +78,7 @@ LL | (C::TupleWithFields(/* fields */)).foo();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0423]: expected value, found enum `D` error[E0423]: expected value, found enum `D`
--> $DIR/issue-73427.rs:35:5 --> $DIR/issue-73427.rs:39:5
| |
LL | D.foo(); LL | D.foo();
| ^ | ^
@ -95,13 +95,37 @@ help: you might have meant to use the following enum variant
| |
LL | D::Unit.foo(); LL | D::Unit.foo();
| ~~~~~~~ | ~~~~~~~
help: the following enum variant is available help: alternatively, the following enum variant is available
| |
LL | (D::TupleWithFields(/* fields */)).foo(); 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` 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); 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` 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 { } LL | if let A(3) = x { }
| ^ | ^
@ -150,7 +174,7 @@ LL | if let A::Tuple(3) = x { }
LL | if let A::TupleWithFields(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. Some errors have detailed explanations: E0423, E0532.
For more information about an error, try `rustc --explain E0423`. 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; 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 */)); LL | (m::Z::Fn(/* fields */));
| ~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~
@ -47,7 +47,7 @@ help: you might have meant to use the following enum variant
| |
LL | m::Z::Unit; 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 */)); 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; 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 */)); 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; 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 */)); 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; 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 */)); 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 RUST_BACKTRACE: 1
CARGO_TARGET_DIR: '${{ github.workspace }}/target' CARGO_TARGET_DIR: '${{ github.workspace }}/target'
NO_FMT_TEST: 1 NO_FMT_TEST: 1
CARGO_INCREMENTAL: 0
jobs: jobs:
base: base:

View File

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

View File

@ -6,11 +6,157 @@ document.
## Unreleased / In Rust Nightly ## 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 ## 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) [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_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_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_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 [`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_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 [`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_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_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_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_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 [`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 [`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_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`]: 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_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_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_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 [`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_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_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_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_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_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 [`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 [`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_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 [`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_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 [`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 [`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 [`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 [`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 [`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 [`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 [`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 [`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 [`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 [`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_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_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_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 [`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_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_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_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 [`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 [`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 [`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_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_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_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_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_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 [`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; 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(); let reference_file = fs::read(&reference_file_path).unwrap_or_default();
if test_output_file != reference_file { if test_output_file != reference_file {

View File

@ -46,7 +46,7 @@ pub fn run(check: bool, verbose: bool) {
// dependency // dependency
if fs::read_to_string(project_root.join("Cargo.toml")) if fs::read_to_string(project_root.join("Cargo.toml"))
.expect("Failed to read clippy 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); return Err(CliError::IntellijSetupActive);
} }
@ -193,10 +193,10 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
let args = &["--version"]; let args = &["--version"];
if context.verbose { 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() { if output.status.success() {
Ok(()) Ok(())
@ -207,7 +207,7 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
Err(CliError::RustfmtNotInstalled) Err(CliError::RustfmtNotInstalled)
} else { } else {
Err(CliError::CommandFailed( Err(CliError::CommandFailed(
format_command(&program, &dir, args), format_command(program, &dir, args),
std::str::from_utf8(&output.stderr).unwrap_or("").to_string(), 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('_') name.split('_')
.map(|s| { .map(|s| {
if s.is_empty() { if s.is_empty() {
String::from("") String::new()
} else { } else {
[&s[0..1].to_uppercase(), &s[1..]].concat() [&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"); .expect("failed to find `impl_lint_pass` terminator");
impl_lint_pass_end += impl_lint_pass_start; 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()); 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() { for c in content[lint_name_end..impl_lint_pass_end].chars() {
// Remove trailing whitespace // Remove trailing whitespace
@ -451,7 +451,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
} }
let mut content = 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!( eprintln!(
"warn: you will have to manually remove any code related to `{}` from `{}`", "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 /// ### Example
/// ```rust /// ```rust
/// fn foo(_x: &str) {}
///
/// let s = &String::new(); /// let s = &String::new();
/// ///
/// let a: &String = &* s; /// let a: &String = &* s;
/// foo(&*s);
/// ``` /// ```
/// ///
/// Use instead: /// Use instead:
/// ```rust /// ```rust
/// # fn foo(_x: &str) {}
/// # let s = &String::new(); /// # let s = &String::new();
/// let a: &String = s; /// let a: &String = s;
/// foo(&**s);
/// ``` /// ```
#[clippy::version = "1.59.0"] #[clippy::version = "1.63.0"]
pub BORROW_DEREF_REF, pub BORROW_DEREF_REF,
complexity, complexity,
"deref on an immutable reference returns the same type as itself" "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_abs_to_unsigned;
mod cast_enum_constructor; mod cast_enum_constructor;
mod cast_lossless; mod cast_lossless;
@ -8,6 +10,7 @@ mod cast_ptr_alignment;
mod cast_ref_to_mut; mod cast_ref_to_mut;
mod cast_sign_loss; mod cast_sign_loss;
mod cast_slice_different_sizes; mod cast_slice_different_sizes;
mod cast_slice_from_raw_parts;
mod char_lit_as_u8; mod char_lit_as_u8;
mod fn_to_numeric_cast; mod fn_to_numeric_cast;
mod fn_to_numeric_cast_any; mod fn_to_numeric_cast_any;
@ -16,7 +19,7 @@ mod ptr_as_ptr;
mod unnecessary_cast; mod unnecessary_cast;
mod utils; 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_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro; 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" "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 { pub struct Casts {
msrv: Option<RustcVersion>, msrv: Option<RustcVersion>,
} }
@ -534,7 +624,10 @@ impl_lint_pass!(Casts => [
PTR_AS_PTR, PTR_AS_PTR,
CAST_ENUM_TRUNCATION, CAST_ENUM_TRUNCATION,
CAST_ENUM_CONSTRUCTOR, 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 { impl<'tcx> LateLintPass<'tcx> for Casts {
@ -547,8 +640,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
return; return;
} }
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
if is_hir_ty_cfg_dependant(cx, cast_to) { if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
return; return;
} }
let (cast_from, cast_to) = ( 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) { if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
return; 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_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::check(cx, expr, cast_expr, cast_from, cast_to);
fn_to_numeric_cast_with_truncation::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_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
cast_enum_constructor::check(cx, expr, cast_expr, cast_from); 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); 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<'_>) { 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 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( span_lint_and_sugg(
cx, cx,
UNNECESSARY_CAST, UNNECESSARY_CAST,
expr.span, expr.span,
&format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to), &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
"try", "try",
format!("{}_{}", literal_str.trim_end_matches('.'), cast_to), format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }

View File

@ -1,24 +1,34 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren; 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::ty::{expr_sig, is_copy, 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::{
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_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::intravisit::{walk_ty, Visitor};
use rustc_hir::{ use rustc_hir::{
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId, self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
TraitItemKind, TyKind, UnOp, Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
}; };
use rustc_index::bit_set::BitSet;
use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; 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_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP}; 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! { declare_clippy_lint! {
/// ### What it does /// ### 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 /// been finished. Note we can't lint at the end of every body as they can be nested within each
/// other. /// other.
current_body: Option<BodyId>, current_body: Option<BodyId>,
/// The list of locals currently being checked by the lint. /// 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. /// 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 /// 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)` /// e.g. `m!(x) | Foo::Bar(ref x)`
ref_locals: FxIndexMap<HirId, Option<RefPat>>, 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 { struct StateData {
@ -170,6 +194,7 @@ struct StateData {
struct DerefedBorrow { struct DerefedBorrow {
count: usize, count: usize,
msg: &'static str, msg: &'static str,
snip_expr: Option<HirId>,
} }
enum State { enum State {
@ -250,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
match (self.state.take(), kind) { match (self.state.take(), kind) {
(None, kind) => { (None, kind) => {
let expr_ty = typeck.expr_ty(expr); 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 { match kind {
RefOp::Deref => { RefOp::Deref => {
@ -331,20 +356,23 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
let deref_msg = let deref_msg =
"this expression creates a reference which is immediately dereferenced by the compiler"; "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 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() { let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
(1, if deref_count == 1 { borrow_msg } else { deref_msg }) (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))) = } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
next_adjust.map(|a| &a.kind) next_adjust.map(|a| &a.kind)
{ {
if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable() if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
{ {
(3, deref_msg) (3, deref_msg, None)
} else { } else {
(2, deref_msg) (2, deref_msg, None)
} }
} else { } else {
(2, deref_msg) (2, deref_msg, None)
}; };
if deref_count >= required_refs { 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. // can't be removed without breaking the code. See earlier comment.
count: deref_count - required_refs, count: deref_count - required_refs,
msg, msg,
snip_expr,
}), }),
StateData { span: expr.span, hir_id: expr.hir_id, position }, StateData { span: expr.span, hir_id: expr.hir_id, position },
)); ));
@ -510,7 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
spans: vec![pat.span], spans: vec![pat.span],
app, app,
replacements: vec![(pat.span, snip.into())], 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; self.current_body = None;
} }
} }
extract_msrv_attr!(LateContext);
} }
fn try_parse_ref_op<'tcx>( 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` /// The method is defined on a reference type. e.g. `impl Foo for &T`
MethodReceiverRefImpl, MethodReceiverRefImpl,
Callee, Callee,
ImplArg(HirId),
FieldAccess(Symbol), FieldAccess(Symbol),
Postfix, Postfix,
Deref, Deref,
@ -630,7 +662,7 @@ impl Position {
| Self::Callee | Self::Callee
| Self::FieldAccess(_) | Self::FieldAccess(_)
| Self::Postfix => PREC_POSTFIX, | Self::Postfix => PREC_POSTFIX,
Self::Deref => PREC_PREFIX, Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p, 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 /// 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 /// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
/// locations as those follow different rules. /// locations as those follow different rules.
#[allow(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) { fn walk_parents<'tcx>(
cx: &LateContext<'tcx>,
e: &'tcx Expr<'_>,
msrv: Option<RustcVersion>,
) -> (Position, &'tcx [Adjustment<'tcx>]) {
let mut adjustments = [].as_slice(); let mut adjustments = [].as_slice();
let mut precedence = 0i8; let mut precedence = 0i8;
let ctxt = e.span.ctxt(); let ctxt = e.span.ctxt();
@ -745,13 +781,20 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
.iter() .iter()
.position(|arg| arg.hir_id == child_id) .position(|arg| arg.hir_id == child_id)
.zip(expr_sig(cx, func)) .zip(expr_sig(cx, func))
.and_then(|(i, sig)| sig.input_with_hir(i)) .and_then(|(i, sig)| {
.map(|(hir_ty, ty)| match hir_ty { 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 // Type inference for closures can depend on how they're called. Only go by the explicit
// types here. // types here.
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()), 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) None => {
.position_for_arg(), 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, _) => { ExprKind::MethodCall(_, args, _) => {
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(); 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..)) .and_then(|subs| subs.get(1..))
{ {
Some(subs) => cx.tcx.mk_substs(subs.iter().copied()), 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() { } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
// Trait methods taking `&self` // Trait methods taking `&self`
sub_ty sub_ty
@ -792,12 +835,17 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
Position::MethodReceiver Position::MethodReceiver
} }
} else { } else {
ty_auto_deref_stability( let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
cx, if let ty::Param(param_ty) = ty.kind() {
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)), needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
precedence, } else {
) ty_auto_deref_stability(
.position_for_arg() 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 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> { struct TyPosition<'tcx> {
position: Position, position: Position,
ty: Option<Ty<'tcx>>, ty: Option<Ty<'tcx>>,
@ -1086,7 +1333,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
}, },
State::DerefedBorrow(state) => { State::DerefedBorrow(state) => {
let mut app = Applicability::MachineApplicable; 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| { 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 calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
let sugg = if !snip_is_macro 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( tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| { params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate { 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, constness: BoundConstness::NotConst,
polarity: ImplPolarity::Positive, polarity: ImplPolarity::Positive,
}))) })))

View File

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

View File

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

View File

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

View File

@ -172,7 +172,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
expr.span, expr.span,
"logarithm for bases 2, 10 and e can be computed more accurately", "logarithm for bases 2, 10 and e can be computed more accurately",
"consider using", "consider using",
format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method), format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
@ -263,13 +263,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
( (
SUBOPTIMAL_FLOPS, SUBOPTIMAL_FLOPS,
"square-root of a number can be computed more efficiently and accurately", "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 { } else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
( (
IMPRECISE_FLOPS, IMPRECISE_FLOPS,
"cube-root of a number can be computed more accurately", "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) { } 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", "exponentiation with integer powers can be computed more efficiently",
format!( format!(
"{}.powi({})", "{}.powi({})",
Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..").maybe_par(),
numeric_literal::format(&exponent.to_string(), None, false) numeric_literal::format(&exponent.to_string(), None, false)
), ),
) )
@ -327,7 +327,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
"consider using", "consider using",
format!( format!(
"{}.mul_add({}, {})", "{}.mul_add({}, {})",
Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], "..").maybe_par(),
Sugg::hir(cx, &args[0], ".."), Sugg::hir(cx, &args[0], ".."),
Sugg::hir(cx, other_addend, ".."), Sugg::hir(cx, other_addend, ".."),
), ),
@ -418,7 +418,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
"consider using", "consider using",
format!( format!(
"{}.exp_m1()", "{}.exp_m1()",
Sugg::hir(cx, self_arg, "..") Sugg::hir(cx, self_arg, "..").maybe_par()
), ),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
@ -550,11 +550,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
then { then {
let positive_abs_sugg = ( let positive_abs_sugg = (
"manual implementation of `abs` method", "manual implementation of `abs` method",
format!("{}.abs()", Sugg::hir(cx, body, "..")), format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
); );
let negative_abs_sugg = ( let negative_abs_sugg = (
"manual implementation of negation of `abs` method", "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) { let sugg = if is_testing_positive(cx, cond, body) {
if if_expr_positive { if if_expr_positive {
@ -621,7 +621,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
expr.span, expr.span,
"log base can be expressed more clearly", "log base can be expressed more clearly",
"consider using", "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, Applicability::MachineApplicable,
); );
} }
@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
(F32(180_f32) == lvalue || F64(180_f64) == lvalue) (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_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind; if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node; 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(180_f32) == rvalue || F64(180_f64) == rvalue) &&
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue) (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_chain! {
if let ExprKind::Lit(ref literal) = mul_lhs.kind; if let ExprKind::Lit(ref literal) = mul_lhs.kind;
if let ast::LitKind::Float(ref value, float_type) = literal.node; 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::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; 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 clippy_utils::sugg::Sugg;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -56,29 +56,27 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
}; };
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
if format_args.value_args.is_empty() { if format_args.args.is_empty() {
match *format_args.format_string_parts { match *format_args.format_string.parts {
[] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), [] => 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 }.
// Simulate macro expansion, converting {{ and }} to { and }. let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
let s_expand = s_src.replace("{{", "{").replace("}}", "}"); let sugg = format!("{}.to_string()", s_expand);
let sugg = format!("{}.to_string()", s_expand); span_useless_format(cx, call_site, sugg, applicability);
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_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() { if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()), ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
ty::Str => true, ty::Str => true,
_ => false, _ => false,
}; };
if let Some(args) = format_args.args(); if !arg.format.has_string_formatting();
if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
then { then {
let is_new_string = match value.kind { let is_new_string = match value.kind {
ExprKind::Binary(..) => true, ExprKind::Binary(..) => true,

View File

@ -1,11 +1,12 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::is_diag_trait_item; 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::source::snippet_opt;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use if_chain::if_chain; use if_chain::if_chain;
use itertools::Itertools;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind, HirId};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::adjustment::{Adjust, Adjustment};
use rustc_middle::ty::Ty; 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 let Some(macro_def_id) = outermost_expn_data.macro_def_id;
if is_format_macro(cx, macro_def_id); if is_format_macro(cx, macro_def_id);
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind; if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
if let Some(args) = format_args.args();
then { then {
for (i, arg) in args.iter().enumerate() { for arg in &format_args.args {
if arg.format_trait != sym::Display { if arg.format.has_string_formatting() {
continue; continue;
} }
if arg.has_string_formatting() { if is_aliased(&format_args, arg.param.value.hir_id) {
continue; continue;
} }
if is_aliased(&args, i) { check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
continue; check_to_string_in_format_args(cx, name, arg.param.value);
}
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
check_to_string_in_format_args(cx, name, arg.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); if is_diag_trait_item(cx, method_def_id, sym::ToString);
let receiver_ty = cx.typeck_results().expr_ty(receiver); let receiver_ty = cx.typeck_results().expr_ty(receiver);
if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display); 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); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
then { then {
let (n_needed_derefs, target) = count_needed_derefs( let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
receiver_ty, if n_needed_derefs == 0 && !needs_ref {
cx.typeck_results().expr_adjustments(receiver).iter(), span_lint_and_sugg(
); cx,
if implements_trait(cx, target, display_trait_id, &[]) { TO_STRING_IN_FORMAT_ARGS,
if n_needed_derefs == 0 { value.span.with_lo(receiver.span.hi()),
span_lint_and_sugg( &format!(
cx, "`to_string` applied to a type that implements `Display` in `{}!` args",
TO_STRING_IN_FORMAT_ARGS, name
value.span.with_lo(receiver.span.hi()), ),
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), "remove this",
"remove this", String::new(),
String::new(), Applicability::MachineApplicable,
Applicability::MachineApplicable, );
); } else {
} else { span_lint_and_sugg(
span_lint_and_sugg( cx,
cx, TO_STRING_IN_FORMAT_ARGS,
TO_STRING_IN_FORMAT_ARGS, value.span,
value.span, &format!(
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name), "`to_string` applied to a type that implements `Display` in `{}!` args",
"use this", name
format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs), ),
Applicability::MachineApplicable, "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. // Returns true if `hir_id` is referred to by multiple format params
fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool { fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
let value = args[i].value; args.params()
args.iter() .filter(|param| param.value.hir_id == hir_id)
.enumerate() .at_most_one()
.any(|(j, arg)| i != j && std::ptr::eq(value, arg.value)) .is_err()
} }
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>) 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::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 clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Applicability; 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 macro_def_id = outer_macro.def_id;
if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn); if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
if is_format_macro(cx, macro_def_id); if is_format_macro(cx, macro_def_id);
if let Some(args) = format_args.args();
then { then {
for arg in args { for arg in format_args.args {
if arg.format_trait != impl_trait.name { if arg.format.r#trait != impl_trait.name {
continue; continue;
} }
check_format_arg_self(cx, expr, &arg, impl_trait); 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 multiple dereferencing of references e.g. &&self
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl) // 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 // 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(); let map = cx.tcx.hir();
// Is the reference self? // Is the reference self?
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) { if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {

View File

@ -1,6 +1,6 @@
mod must_use; mod must_use;
mod not_unsafe_ptr_arg_deref; mod not_unsafe_ptr_arg_deref;
mod result_unit_err; mod result;
mod too_many_arguments; mod too_many_arguments;
mod too_many_lines; mod too_many_lines;
@ -217,17 +217,62 @@ declare_clippy_lint! {
"public function returning `Result` with an `Err` type of `()`" "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)] #[derive(Copy, Clone)]
pub struct Functions { pub struct Functions {
too_many_arguments_threshold: u64, too_many_arguments_threshold: u64,
too_many_lines_threshold: u64, too_many_lines_threshold: u64,
large_error_threshold: u64,
} }
impl Functions { 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 { Self {
too_many_arguments_threshold, too_many_arguments_threshold,
too_many_lines_threshold, too_many_lines_threshold,
large_error_threshold,
} }
} }
} }
@ -240,6 +285,7 @@ impl_lint_pass!(Functions => [
DOUBLE_MUST_USE, DOUBLE_MUST_USE,
MUST_USE_CANDIDATE, MUST_USE_CANDIDATE,
RESULT_UNIT_ERR, RESULT_UNIT_ERR,
RESULT_LARGE_ERR,
]); ]);
impl<'tcx> LateLintPass<'tcx> for Functions { 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<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
must_use::check_item(cx, 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<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
must_use::check_impl_item(cx, item); 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<'_>) { 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); too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
not_unsafe_ptr_arg_deref::check_trait_item(cx, item); not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
must_use::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::higher;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::SpanlessEq; use clippy_utils::SpanlessEq;
use if_chain::if_chain; use if_chain::if_chain;
use rustc_errors::Diagnostic;
use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::intravisit::{self as visit, Visitor};
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
impl<'tcx> LateLintPass<'tcx> for IfLetMutex { impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let mut arm_visit = ArmVisitor { let mut arm_visit = ArmVisitor { found_mutex: None, cx };
mutex_lock_called: false, let mut op_visit = OppVisitor { found_mutex: None, cx };
found_mutex: None,
cx,
};
let mut op_visit = OppVisitor {
mutex_lock_called: false,
found_mutex: None,
cx,
};
if let Some(higher::IfLet { if let Some(higher::IfLet {
let_expr, let_expr,
if_then, if_then,
@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
}) = higher::IfLet::hir(cx, expr) }) = higher::IfLet::hir(cx, expr)
{ {
op_visit.visit_expr(let_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_then);
arm_visit.visit_expr(if_else); arm_visit.visit_expr(if_else);
if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) { if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
span_lint_and_help( 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, cx,
IF_LET_MUTEX, IF_LET_MUTEX,
expr.span, expr.span,
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock", "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
None, diag,
"move the lock call outside of the `if let ...` expression",
); );
} }
} }
@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
/// Checks if `Mutex::lock` is called in the `if let` expr. /// Checks if `Mutex::lock` is called in the `if let` expr.
pub struct OppVisitor<'a, 'tcx> { pub struct OppVisitor<'a, 'tcx> {
mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>, found_mutex: Option<&'tcx Expr<'tcx>>,
cx: &'a LateContext<'tcx>, cx: &'a LateContext<'tcx>,
} }
@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
self.found_mutex = Some(mutex); self.found_mutex = Some(mutex);
self.mutex_lock_called = true;
return; return;
} }
visit::walk_expr(self, expr); 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. /// Checks if `Mutex::lock` is called in any of the branches.
pub struct ArmVisitor<'a, 'tcx> { pub struct ArmVisitor<'a, 'tcx> {
mutex_lock_called: bool,
found_mutex: Option<&'tcx Expr<'tcx>>, found_mutex: Option<&'tcx Expr<'tcx>>,
cx: &'a LateContext<'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>) { fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) { if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
self.found_mutex = Some(mutex); self.found_mutex = Some(mutex);
self.mutex_lock_called = true;
return; return;
} }
visit::walk_expr(self, expr); visit::walk_expr(self, expr);
@ -119,9 +118,12 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
} }
impl<'tcx, 'l> ArmVisitor<'tcx, 'l> { impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool { fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
self.found_mutex self.found_mutex.and_then(|arm_mutex| {
.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, 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_chain! {
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind; if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
if path.ident.as_str() == "lock"; 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); if is_type_diagnostic_item(cx, ty, sym::Mutex);
then { then {
Some(self_arg) Some(self_arg)

View File

@ -1,7 +1,7 @@
use clippy_utils::diagnostics::span_lint_and_help; 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::source::snippet_with_macro_callsite;
use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks}; 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::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### 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? /// ### 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 /// ### Example
/// ```rust /// ```rust
@ -39,7 +41,7 @@ declare_clippy_lint! {
#[clippy::version = "1.53.0"] #[clippy::version = "1.53.0"]
pub IF_THEN_SOME_ELSE_NONE, pub IF_THEN_SOME_ELSE_NONE,
restriction, 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 { pub struct IfThenSomeElseNone {
@ -56,7 +58,7 @@ impl IfThenSomeElseNone {
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { 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) { if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
return; return;
} }
@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
return; return;
} }
if_chain! { if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr); && let ExprKind::Block(then_block, _) = then.kind
if let ExprKind::Block(then_block, _) = then.kind; && let Some(then_expr) = then_block.expr
if let Some(then_expr) = then_block.expr; && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind; && let ExprKind::Path(ref then_call_qpath) = then_call.kind
if let ExprKind::Path(ref then_call_qpath) = then_call.kind; && is_lang_ctor(cx, then_call_qpath, OptionSome)
if is_lang_ctor(cx, then_call_qpath, OptionSome); && let ExprKind::Path(ref qpath) = peel_blocks(els).kind
if let ExprKind::Path(ref qpath) = peel_blocks(els).kind; && is_lang_ctor(cx, qpath, OptionNone)
if is_lang_ctor(cx, qpath, OptionNone); && !stmts_contains_early_return(then_block.stmts)
if !stmts_contains_early_return(then_block.stmts); {
then { let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]"); let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) { format!("({})", cond_snip)
format!("({})", cond_snip) } else {
} else { cond_snip.into_owned()
cond_snip.into_owned() };
}; let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, ""); let mut method_body = if then_block.stmts.is_empty() {
let closure_body = if then_block.stmts.is_empty() { arg_snip.into_owned()
arg_snip.into_owned() } else {
} else { format!("{{ /* snippet */ {} }}", arg_snip)
format!("{{ /* snippet */ {} }}", arg_snip) };
}; let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
let help = format!( "then_some"
"consider using `bool::then` like: `{}.then(|| {})`", } else {
cond_snip, method_body.insert_str(0, "|| ");
closure_body, "then"
); };
span_lint_and_help(
cx, let help = format!(
IF_THEN_SOME_ELSE_NONE, "consider using `bool::{}` like: `{}.{}({})`",
expr.span, method_name, cond_snip, method_name, method_body,
"this could be simplified with `bool::then`", );
None, span_lint_and_help(
&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::NONMINIMAL_BOOL),
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR), LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF), 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_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION), LintId::of(casts::CAST_ENUM_TRUNCATION),
LintId::of(casts::CAST_REF_TO_MUT), LintId::of(casts::CAST_REF_TO_MUT),
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES), 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::CHAR_LIT_AS_U8),
LintId::of(casts::FN_TO_NUMERIC_CAST), LintId::of(casts::FN_TO_NUMERIC_CAST),
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION), 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::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF), LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
LintId::of(functions::RESULT_LARGE_ERR),
LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(functions::TOO_MANY_ARGUMENTS), LintId::of(functions::TOO_MANY_ARGUMENTS),
LintId::of(get_first::GET_FIRST),
LintId::of(if_let_mutex::IF_LET_MUTEX), LintId::of(if_let_mutex::IF_LET_MUTEX),
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING), LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
LintId::of(infinite_iter::INFINITE_ITER), 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_rem_euclid::MANUAL_REM_EUCLID),
LintId::of(manual_retain::MANUAL_RETAIN), LintId::of(manual_retain::MANUAL_RETAIN),
LintId::of(manual_strip::MANUAL_STRIP), 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::OPTION_MAP_UNIT_FN),
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN), LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
LintId::of(match_result_ok::MATCH_RESULT_OK), 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_DEFAULT),
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT), LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::BIND_INSTEAD_OF_MAP), LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::BYTES_NTH), LintId::of(methods::BYTES_NTH),
LintId::of(methods::CHARS_LAST_CMP), LintId::of(methods::CHARS_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::CLONE_ON_COPY), LintId::of(methods::CLONE_ON_COPY),
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::ERR_EXPECT), LintId::of(methods::ERR_EXPECT),
LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT), LintId::of(methods::FILTER_NEXT),
LintId::of(methods::FLAT_MAP_IDENTITY), LintId::of(methods::FLAT_MAP_IDENTITY),
LintId::of(methods::GET_FIRST),
LintId::of(methods::GET_LAST_WITH_LEN), LintId::of(methods::GET_LAST_WITH_LEN),
LintId::of(methods::INSPECT_FOR_EACH), LintId::of(methods::INSPECT_FOR_EACH),
LintId::of(methods::INTO_ITER_ON_REF), 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_SATURATING_ARITHMETIC),
LintId::of(methods::MANUAL_SPLIT_ONCE), LintId::of(methods::MANUAL_SPLIT_ONCE),
LintId::of(methods::MANUAL_STR_REPEAT), LintId::of(methods::MANUAL_STR_REPEAT),
LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MAP_FLATTEN), LintId::of(methods::MAP_FLATTEN),
LintId::of(methods::MAP_IDENTITY), LintId::of(methods::MAP_IDENTITY),
LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF), LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
LintId::of(methods::NEEDLESS_OPTION_TAKE), LintId::of(methods::NEEDLESS_OPTION_TAKE),
LintId::of(methods::NEEDLESS_SPLITN), LintId::of(methods::NEEDLESS_SPLITN),
LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::OBFUSCATED_IF_ELSE), LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT), 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::OPTION_MAP_OR_NONE),
LintId::of(methods::OR_FUN_CALL), LintId::of(methods::OR_FUN_CALL),
LintId::of(methods::OR_THEN_UNWRAP), 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::RESULT_MAP_OR_INTO_OPTION),
LintId::of(methods::SEARCH_IS_SOME), LintId::of(methods::SEARCH_IS_SOME),
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT), 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::STRING_EXTEND_CHARS),
LintId::of(methods::SUSPICIOUS_MAP), LintId::of(methods::SUSPICIOUS_MAP),
LintId::of(methods::SUSPICIOUS_SPLITN), LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::SUSPICIOUS_TO_OWNED),
LintId::of(methods::UNINIT_ASSUMED_INIT), LintId::of(methods::UNINIT_ASSUMED_INIT),
LintId::of(methods::UNIT_HASH),
LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP), LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_FOLD), LintId::of(methods::UNNECESSARY_FOLD),
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS), LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(methods::UNNECESSARY_TO_OWNED),
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT), LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
LintId::of(methods::USELESS_ASREF), LintId::of(methods::USELESS_ASREF),
LintId::of(methods::VEC_RESIZE_TO_ZERO),
LintId::of(methods::WRONG_SELF_CONVENTION), LintId::of(methods::WRONG_SELF_CONVENTION),
LintId::of(methods::ZST_OFFSET), LintId::of(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX), 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::UNNEEDED_WILDCARD_PATTERN),
LintId::of(misc_early::ZERO_PREFIXED_LITERAL), LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION), 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_key::MUTABLE_KEY_TYPE),
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
LintId::of(needless_bool::BOOL_COMPARISON), 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_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
LintId::of(octal_escapes::OCTAL_ESCAPES), 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::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::ASSIGN_OP_PATTERN), LintId::of(operators::ASSIGN_OP_PATTERN),
LintId::of(operators::BAD_BIT_MASK), 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(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
LintId::of(question_mark::QUESTION_MARK), LintId::of(question_mark::QUESTION_MARK),
LintId::of(ranges::MANUAL_RANGE_CONTAINS), LintId::of(ranges::MANUAL_RANGE_CONTAINS),
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
LintId::of(ranges::REVERSED_EMPTY_RANGES), LintId::of(ranges::REVERSED_EMPTY_RANGES),
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT), LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC), 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(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(reference::DEREF_ADDROF), LintId::of(reference::DEREF_ADDROF),
LintId::of(regex::INVALID_REGEX), LintId::of(regex::INVALID_REGEX),
LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(returns::LET_AND_RETURN), LintId::of(returns::LET_AND_RETURN),
LintId::of(returns::NEEDLESS_RETURN), LintId::of(returns::NEEDLESS_RETURN),
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS), 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_INT_TO_FLOAT),
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES), LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
LintId::of(transmute::TRANSMUTE_PTR_TO_REF), LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::USELESS_TRANSMUTE), LintId::of(transmute::USELESS_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(types::BORROWED_BOX), LintId::of(types::BORROWED_BOX),
LintId::of(types::BOX_COLLECTION), LintId::of(types::BOX_COLLECTION),
LintId::of(types::REDUNDANT_ALLOCATION), 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(types::VEC_BOX),
LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC), 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_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::LET_UNIT_VALUE), LintId::of(unit_types::LET_UNIT_VALUE),
LintId::of(unit_types::UNIT_ARG), 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::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS), 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(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(unused_peekable::UNUSED_PEEKABLE),
LintId::of(unused_unit::UNUSED_UNIT), LintId::of(unused_unit::UNUSED_UNIT),
LintId::of(unwrap::PANICKING_UNWRAP), LintId::of(unwrap::PANICKING_UNWRAP),
LintId::of(unwrap::UNNECESSARY_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(useless_conversion::USELESS_CONVERSION),
LintId::of(vec::USELESS_VEC), LintId::of(vec::USELESS_VEC),
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH), 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::PRINTLN_EMPTY_STRING),
LintId::of(write::PRINT_LITERAL), LintId::of(write::PRINT_LITERAL),
LintId::of(write::PRINT_WITH_NEWLINE), 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(attrs::DEPRECATED_CFG_ATTR),
LintId::of(booleans::NONMINIMAL_BOOL), LintId::of(booleans::NONMINIMAL_BOOL),
LintId::of(borrow_deref_ref::BORROW_DEREF_REF), 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::CHAR_LIT_AS_U8),
LintId::of(casts::UNNECESSARY_CAST), LintId::of(casts::UNNECESSARY_CAST),
LintId::of(dereference::EXPLICIT_AUTO_DEREF), 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::NEEDLESS_MATCH),
LintId::of(matches::WILDCARD_IN_OR_PATTERNS), LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
LintId::of(methods::BIND_INSTEAD_OF_MAP), LintId::of(methods::BIND_INSTEAD_OF_MAP),
LintId::of(methods::BYTES_COUNT_TO_LEN),
LintId::of(methods::CLONE_ON_COPY), LintId::of(methods::CLONE_ON_COPY),
LintId::of(methods::FILTER_MAP_IDENTITY), LintId::of(methods::FILTER_MAP_IDENTITY),
LintId::of(methods::FILTER_NEXT), 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_AS_REF_DEREF),
LintId::of(methods::OPTION_FILTER_MAP), LintId::of(methods::OPTION_FILTER_MAP),
LintId::of(methods::OR_THEN_UNWRAP), 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::SEARCH_IS_SOME),
LintId::of(methods::SKIP_WHILE_NEXT), LintId::of(methods::SKIP_WHILE_NEXT),
LintId::of(methods::UNNECESSARY_FILTER_MAP), LintId::of(methods::UNNECESSARY_FILTER_MAP),
LintId::of(methods::UNNECESSARY_FIND_MAP), LintId::of(methods::UNNECESSARY_FIND_MAP),
LintId::of(methods::UNNECESSARY_SORT_BY),
LintId::of(methods::USELESS_ASREF), LintId::of(methods::USELESS_ASREF),
LintId::of(misc::SHORT_CIRCUIT_STATEMENT), LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN), 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(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
LintId::of(no_effect::NO_EFFECT), LintId::of(no_effect::NO_EFFECT),
LintId::of(no_effect::UNNECESSARY_OPERATION), LintId::of(no_effect::UNNECESSARY_OPERATION),
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
LintId::of(operators::DOUBLE_COMPARISONS), LintId::of(operators::DOUBLE_COMPARISONS),
LintId::of(operators::DURATION_SUBSEC), LintId::of(operators::DURATION_SUBSEC),
LintId::of(operators::IDENTITY_OP), 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(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
LintId::of(precedence::PRECEDENCE), LintId::of(precedence::PRECEDENCE),
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST), 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_closure_call::REDUNDANT_CLOSURE_CALL),
LintId::of(redundant_slicing::REDUNDANT_SLICING), LintId::of(redundant_slicing::REDUNDANT_SLICING),
LintId::of(reference::DEREF_ADDROF), LintId::of(reference::DEREF_ADDROF),
LintId::of(repeat_once::REPEAT_ONCE),
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES), LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS), LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
LintId::of(swap::MANUAL_SWAP), 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::TYPE_COMPLEXITY),
LintId::of(types::VEC_BOX), LintId::of(types::VEC_BOX),
LintId::of(unit_types::UNIT_ARG), LintId::of(unit_types::UNIT_ARG),
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
LintId::of(unwrap::UNNECESSARY_UNWRAP), LintId::of(unwrap::UNNECESSARY_UNWRAP),
LintId::of(useless_conversion::USELESS_CONVERSION), LintId::of(useless_conversion::USELESS_CONVERSION),
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO), 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(mem_replace::MEM_REPLACE_WITH_UNINIT),
LintId::of(methods::CLONE_DOUBLE_REF), LintId::of(methods::CLONE_DOUBLE_REF),
LintId::of(methods::ITERATOR_STEP_BY_ZERO), LintId::of(methods::ITERATOR_STEP_BY_ZERO),
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
LintId::of(methods::SUSPICIOUS_SPLITN), LintId::of(methods::SUSPICIOUS_SPLITN),
LintId::of(methods::UNINIT_ASSUMED_INIT), 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(methods::ZST_OFFSET),
LintId::of(minmax::MIN_MAX), LintId::of(minmax::MIN_MAX),
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS), 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::ABSURD_EXTREME_COMPARISONS),
LintId::of(operators::BAD_BIT_MASK), LintId::of(operators::BAD_BIT_MASK),
LintId::of(operators::CMP_NAN), 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(serde_api::SERDE_API_MISUSE),
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT), LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
LintId::of(swap::ALMOST_SWAPPED), LintId::of(swap::ALMOST_SWAPPED),
LintId::of(transmute::TRANSMUTING_NULL),
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE), LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
LintId::of(transmute::WRONG_TRANSMUTE), LintId::of(transmute::WRONG_TRANSMUTE),
LintId::of(transmuting_null::TRANSMUTING_NULL),
LintId::of(unicode::INVISIBLE_CHARACTERS), LintId::of(unicode::INVISIBLE_CHARACTERS),
LintId::of(uninit_vec::UNINIT_VEC), 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_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(unit_types::UNIT_CMP), LintId::of(unit_types::UNIT_CMP),
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS), LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS), LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT), LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
LintId::of(unwrap::PANICKING_UNWRAP), 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, almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
approx_const::APPROX_CONSTANT, approx_const::APPROX_CONSTANT,
as_conversions::AS_CONVERSIONS, as_conversions::AS_CONVERSIONS,
as_underscore::AS_UNDERSCORE,
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX, asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX, asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
assertions_on_constants::ASSERTIONS_ON_CONSTANTS, assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
@ -59,16 +58,14 @@ store.register_lints(&[
bool_assert_comparison::BOOL_ASSERT_COMPARISON, bool_assert_comparison::BOOL_ASSERT_COMPARISON,
booleans::NONMINIMAL_BOOL, booleans::NONMINIMAL_BOOL,
booleans::OVERLY_COMPLEX_BOOL_EXPR, booleans::OVERLY_COMPLEX_BOOL_EXPR,
borrow_as_ptr::BORROW_AS_PTR,
borrow_deref_ref::BORROW_DEREF_REF, borrow_deref_ref::BORROW_DEREF_REF,
bytecount::NAIVE_BYTECOUNT,
bytes_count_to_len::BYTES_COUNT_TO_LEN,
cargo::CARGO_COMMON_METADATA, cargo::CARGO_COMMON_METADATA,
cargo::MULTIPLE_CRATE_VERSIONS, cargo::MULTIPLE_CRATE_VERSIONS,
cargo::NEGATIVE_FEATURE_NAMES, cargo::NEGATIVE_FEATURE_NAMES,
cargo::REDUNDANT_FEATURE_NAMES, cargo::REDUNDANT_FEATURE_NAMES,
cargo::WILDCARD_DEPENDENCIES, 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_ABS_TO_UNSIGNED,
casts::CAST_ENUM_CONSTRUCTOR, casts::CAST_ENUM_CONSTRUCTOR,
casts::CAST_ENUM_TRUNCATION, casts::CAST_ENUM_TRUNCATION,
@ -80,6 +77,7 @@ store.register_lints(&[
casts::CAST_REF_TO_MUT, casts::CAST_REF_TO_MUT,
casts::CAST_SIGN_LOSS, casts::CAST_SIGN_LOSS,
casts::CAST_SLICE_DIFFERENT_SIZES, casts::CAST_SLICE_DIFFERENT_SIZES,
casts::CAST_SLICE_FROM_RAW_PARTS,
casts::CHAR_LIT_AS_U8, casts::CHAR_LIT_AS_U8,
casts::FN_TO_NUMERIC_CAST, casts::FN_TO_NUMERIC_CAST,
casts::FN_TO_NUMERIC_CAST_ANY, casts::FN_TO_NUMERIC_CAST_ANY,
@ -173,11 +171,11 @@ store.register_lints(&[
functions::MUST_USE_CANDIDATE, functions::MUST_USE_CANDIDATE,
functions::MUST_USE_UNIT, functions::MUST_USE_UNIT,
functions::NOT_UNSAFE_PTR_ARG_DEREF, functions::NOT_UNSAFE_PTR_ARG_DEREF,
functions::RESULT_LARGE_ERR,
functions::RESULT_UNIT_ERR, functions::RESULT_UNIT_ERR,
functions::TOO_MANY_ARGUMENTS, functions::TOO_MANY_ARGUMENTS,
functions::TOO_MANY_LINES, functions::TOO_MANY_LINES,
future_not_send::FUTURE_NOT_SEND, future_not_send::FUTURE_NOT_SEND,
get_first::GET_FIRST,
if_let_mutex::IF_LET_MUTEX, if_let_mutex::IF_LET_MUTEX,
if_not_else::IF_NOT_ELSE, if_not_else::IF_NOT_ELSE,
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE, if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
@ -246,12 +244,10 @@ store.register_lints(&[
manual_bits::MANUAL_BITS, manual_bits::MANUAL_BITS,
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED, manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
manual_ok_or::MANUAL_OK_OR,
manual_rem_euclid::MANUAL_REM_EUCLID, manual_rem_euclid::MANUAL_REM_EUCLID,
manual_retain::MANUAL_RETAIN, manual_retain::MANUAL_RETAIN,
manual_string_new::MANUAL_STRING_NEW,
manual_strip::MANUAL_STRIP, manual_strip::MANUAL_STRIP,
map_clone::MAP_CLONE,
map_err_ignore::MAP_ERR_IGNORE,
map_unit_fn::OPTION_MAP_UNIT_FN, map_unit_fn::OPTION_MAP_UNIT_FN,
map_unit_fn::RESULT_MAP_UNIT_FN, map_unit_fn::RESULT_MAP_UNIT_FN,
match_result_ok::MATCH_RESULT_OK, match_result_ok::MATCH_RESULT_OK,
@ -284,13 +280,16 @@ store.register_lints(&[
mem_replace::MEM_REPLACE_WITH_DEFAULT, mem_replace::MEM_REPLACE_WITH_DEFAULT,
mem_replace::MEM_REPLACE_WITH_UNINIT, mem_replace::MEM_REPLACE_WITH_UNINIT,
methods::BIND_INSTEAD_OF_MAP, methods::BIND_INSTEAD_OF_MAP,
methods::BYTES_COUNT_TO_LEN,
methods::BYTES_NTH, methods::BYTES_NTH,
methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
methods::CHARS_LAST_CMP, methods::CHARS_LAST_CMP,
methods::CHARS_NEXT_CMP, methods::CHARS_NEXT_CMP,
methods::CLONED_INSTEAD_OF_COPIED, methods::CLONED_INSTEAD_OF_COPIED,
methods::CLONE_DOUBLE_REF, methods::CLONE_DOUBLE_REF,
methods::CLONE_ON_COPY, methods::CLONE_ON_COPY,
methods::CLONE_ON_REF_PTR, methods::CLONE_ON_REF_PTR,
methods::COLLAPSIBLE_STR_REPLACE,
methods::ERR_EXPECT, methods::ERR_EXPECT,
methods::EXPECT_FUN_CALL, methods::EXPECT_FUN_CALL,
methods::EXPECT_USED, methods::EXPECT_USED,
@ -302,6 +301,7 @@ store.register_lints(&[
methods::FLAT_MAP_IDENTITY, methods::FLAT_MAP_IDENTITY,
methods::FLAT_MAP_OPTION, methods::FLAT_MAP_OPTION,
methods::FROM_ITER_INSTEAD_OF_COLLECT, methods::FROM_ITER_INSTEAD_OF_COLLECT,
methods::GET_FIRST,
methods::GET_LAST_WITH_LEN, methods::GET_LAST_WITH_LEN,
methods::GET_UNWRAP, methods::GET_UNWRAP,
methods::IMPLICIT_CLONE, methods::IMPLICIT_CLONE,
@ -315,22 +315,30 @@ store.register_lints(&[
methods::ITER_NEXT_SLICE, methods::ITER_NEXT_SLICE,
methods::ITER_NTH, methods::ITER_NTH,
methods::ITER_NTH_ZERO, methods::ITER_NTH_ZERO,
methods::ITER_ON_EMPTY_COLLECTIONS,
methods::ITER_ON_SINGLE_ITEMS,
methods::ITER_OVEREAGER_CLONED, methods::ITER_OVEREAGER_CLONED,
methods::ITER_SKIP_NEXT, methods::ITER_SKIP_NEXT,
methods::ITER_WITH_DRAIN, methods::ITER_WITH_DRAIN,
methods::MANUAL_FILTER_MAP, methods::MANUAL_FILTER_MAP,
methods::MANUAL_FIND_MAP, methods::MANUAL_FIND_MAP,
methods::MANUAL_OK_OR,
methods::MANUAL_SATURATING_ARITHMETIC, methods::MANUAL_SATURATING_ARITHMETIC,
methods::MANUAL_SPLIT_ONCE, methods::MANUAL_SPLIT_ONCE,
methods::MANUAL_STR_REPEAT, methods::MANUAL_STR_REPEAT,
methods::MAP_CLONE,
methods::MAP_COLLECT_RESULT_UNIT, methods::MAP_COLLECT_RESULT_UNIT,
methods::MAP_ERR_IGNORE,
methods::MAP_FLATTEN, methods::MAP_FLATTEN,
methods::MAP_IDENTITY, methods::MAP_IDENTITY,
methods::MAP_UNWRAP_OR, methods::MAP_UNWRAP_OR,
methods::MUT_MUTEX_LOCK,
methods::NAIVE_BYTECOUNT,
methods::NEEDLESS_OPTION_AS_DEREF, methods::NEEDLESS_OPTION_AS_DEREF,
methods::NEEDLESS_OPTION_TAKE, methods::NEEDLESS_OPTION_TAKE,
methods::NEEDLESS_SPLITN, methods::NEEDLESS_SPLITN,
methods::NEW_RET_NO_SELF, methods::NEW_RET_NO_SELF,
methods::NONSENSICAL_OPEN_OPTIONS,
methods::NO_EFFECT_REPLACE, methods::NO_EFFECT_REPLACE,
methods::OBFUSCATED_IF_ELSE, methods::OBFUSCATED_IF_ELSE,
methods::OK_EXPECT, methods::OK_EXPECT,
@ -339,25 +347,34 @@ store.register_lints(&[
methods::OPTION_MAP_OR_NONE, methods::OPTION_MAP_OR_NONE,
methods::OR_FUN_CALL, methods::OR_FUN_CALL,
methods::OR_THEN_UNWRAP, methods::OR_THEN_UNWRAP,
methods::PATH_BUF_PUSH_OVERWRITE,
methods::RANGE_ZIP_WITH_LEN,
methods::REPEAT_ONCE,
methods::RESULT_MAP_OR_INTO_OPTION, methods::RESULT_MAP_OR_INTO_OPTION,
methods::SEARCH_IS_SOME, methods::SEARCH_IS_SOME,
methods::SHOULD_IMPLEMENT_TRAIT, methods::SHOULD_IMPLEMENT_TRAIT,
methods::SINGLE_CHAR_ADD_STR, methods::SINGLE_CHAR_ADD_STR,
methods::SINGLE_CHAR_PATTERN, methods::SINGLE_CHAR_PATTERN,
methods::SKIP_WHILE_NEXT, methods::SKIP_WHILE_NEXT,
methods::STABLE_SORT_PRIMITIVE,
methods::STRING_EXTEND_CHARS, methods::STRING_EXTEND_CHARS,
methods::SUSPICIOUS_MAP, methods::SUSPICIOUS_MAP,
methods::SUSPICIOUS_SPLITN, methods::SUSPICIOUS_SPLITN,
methods::SUSPICIOUS_TO_OWNED,
methods::UNINIT_ASSUMED_INIT, methods::UNINIT_ASSUMED_INIT,
methods::UNIT_HASH,
methods::UNNECESSARY_FILTER_MAP, methods::UNNECESSARY_FILTER_MAP,
methods::UNNECESSARY_FIND_MAP, methods::UNNECESSARY_FIND_MAP,
methods::UNNECESSARY_FOLD, methods::UNNECESSARY_FOLD,
methods::UNNECESSARY_JOIN, methods::UNNECESSARY_JOIN,
methods::UNNECESSARY_LAZY_EVALUATIONS, methods::UNNECESSARY_LAZY_EVALUATIONS,
methods::UNNECESSARY_SORT_BY,
methods::UNNECESSARY_TO_OWNED, methods::UNNECESSARY_TO_OWNED,
methods::UNWRAP_OR_ELSE_DEFAULT, methods::UNWRAP_OR_ELSE_DEFAULT,
methods::UNWRAP_USED, methods::UNWRAP_USED,
methods::USELESS_ASREF, methods::USELESS_ASREF,
methods::VEC_RESIZE_TO_ZERO,
methods::VERBOSE_FILE_READS,
methods::WRONG_SELF_CONVENTION, methods::WRONG_SELF_CONVENTION,
methods::ZST_OFFSET, methods::ZST_OFFSET,
minmax::MIN_MAX, minmax::MIN_MAX,
@ -384,9 +401,9 @@ store.register_lints(&[
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION, mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
module_style::MOD_MODULE_FILES, module_style::MOD_MODULE_FILES,
module_style::SELF_NAMED_MODULE_FILES, module_style::SELF_NAMED_MODULE_FILES,
multi_assignments::MULTI_ASSIGNMENTS,
mut_key::MUTABLE_KEY_TYPE, mut_key::MUTABLE_KEY_TYPE,
mut_mut::MUT_MUT, mut_mut::MUT_MUT,
mut_mutex_lock::MUT_MUTEX_LOCK,
mut_reference::UNNECESSARY_MUT_PASSED, mut_reference::UNNECESSARY_MUT_PASSED,
mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL, mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
mutex_atomic::MUTEX_ATOMIC, mutex_atomic::MUTEX_ATOMIC,
@ -418,7 +435,6 @@ store.register_lints(&[
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES, nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
octal_escapes::OCTAL_ESCAPES, octal_escapes::OCTAL_ESCAPES,
only_used_in_recursion::ONLY_USED_IN_RECURSION, only_used_in_recursion::ONLY_USED_IN_RECURSION,
open_options::NONSENSICAL_OPEN_OPTIONS,
operators::ABSURD_EXTREME_COMPARISONS, operators::ABSURD_EXTREME_COMPARISONS,
operators::ARITHMETIC, operators::ARITHMETIC,
operators::ASSIGN_OP_PATTERN, operators::ASSIGN_OP_PATTERN,
@ -457,7 +473,6 @@ store.register_lints(&[
partialeq_to_none::PARTIALEQ_TO_NONE, partialeq_to_none::PARTIALEQ_TO_NONE,
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE, pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF, pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
pattern_type_mismatch::PATTERN_TYPE_MISMATCH, pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
precedence::PRECEDENCE, precedence::PRECEDENCE,
ptr::CMP_NULL, ptr::CMP_NULL,
@ -470,7 +485,6 @@ store.register_lints(&[
ranges::MANUAL_RANGE_CONTAINS, ranges::MANUAL_RANGE_CONTAINS,
ranges::RANGE_MINUS_ONE, ranges::RANGE_MINUS_ONE,
ranges::RANGE_PLUS_ONE, ranges::RANGE_PLUS_ONE,
ranges::RANGE_ZIP_WITH_LEN,
ranges::REVERSED_EMPTY_RANGES, ranges::REVERSED_EMPTY_RANGES,
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT, rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
read_zero_byte_vec::READ_ZERO_BYTE_VEC, read_zero_byte_vec::READ_ZERO_BYTE_VEC,
@ -486,7 +500,6 @@ store.register_lints(&[
reference::DEREF_ADDROF, reference::DEREF_ADDROF,
regex::INVALID_REGEX, regex::INVALID_REGEX,
regex::TRIVIAL_REGEX, regex::TRIVIAL_REGEX,
repeat_once::REPEAT_ONCE,
return_self_not_must_use::RETURN_SELF_NOT_MUST_USE, return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
returns::LET_AND_RETURN, returns::LET_AND_RETURN,
returns::NEEDLESS_RETURN, returns::NEEDLESS_RETURN,
@ -501,7 +514,6 @@ store.register_lints(&[
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
stable_sort_primitive::STABLE_SORT_PRIMITIVE,
std_instead_of_core::ALLOC_INSTEAD_OF_CORE, std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
std_instead_of_core::STD_INSTEAD_OF_ALLOC, std_instead_of_core::STD_INSTEAD_OF_ALLOC,
std_instead_of_core::STD_INSTEAD_OF_CORE, std_instead_of_core::STD_INSTEAD_OF_CORE,
@ -537,10 +549,10 @@ store.register_lints(&[
transmute::TRANSMUTE_PTR_TO_PTR, transmute::TRANSMUTE_PTR_TO_PTR,
transmute::TRANSMUTE_PTR_TO_REF, transmute::TRANSMUTE_PTR_TO_REF,
transmute::TRANSMUTE_UNDEFINED_REPR, transmute::TRANSMUTE_UNDEFINED_REPR,
transmute::TRANSMUTING_NULL,
transmute::UNSOUND_COLLECTION_TRANSMUTE, transmute::UNSOUND_COLLECTION_TRANSMUTE,
transmute::USELESS_TRANSMUTE, transmute::USELESS_TRANSMUTE,
transmute::WRONG_TRANSMUTE, transmute::WRONG_TRANSMUTE,
transmuting_null::TRANSMUTING_NULL,
types::BORROWED_BOX, types::BORROWED_BOX,
types::BOX_COLLECTION, types::BOX_COLLECTION,
types::LINKEDLIST, types::LINKEDLIST,
@ -555,7 +567,6 @@ store.register_lints(&[
unicode::NON_ASCII_LITERAL, unicode::NON_ASCII_LITERAL,
unicode::UNICODE_NOT_NFC, unicode::UNICODE_NOT_NFC,
uninit_vec::UNINIT_VEC, uninit_vec::UNINIT_VEC,
unit_hash::UNIT_HASH,
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD, unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
unit_types::LET_UNIT_VALUE, unit_types::LET_UNIT_VALUE,
unit_types::UNIT_ARG, unit_types::UNIT_ARG,
@ -564,12 +575,12 @@ store.register_lints(&[
unnamed_address::VTABLE_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS,
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS, unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
unnecessary_sort_by::UNNECESSARY_SORT_BY,
unnecessary_wraps::UNNECESSARY_WRAPS, unnecessary_wraps::UNNECESSARY_WRAPS,
unnested_or_patterns::UNNESTED_OR_PATTERNS, unnested_or_patterns::UNNESTED_OR_PATTERNS,
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
unused_async::UNUSED_ASYNC, unused_async::UNUSED_ASYNC,
unused_io_amount::UNUSED_IO_AMOUNT, unused_io_amount::UNUSED_IO_AMOUNT,
unused_peekable::UNUSED_PEEKABLE,
unused_rounding::UNUSED_ROUNDING, unused_rounding::UNUSED_ROUNDING,
unused_self::UNUSED_SELF, unused_self::UNUSED_SELF,
unused_unit::UNUSED_UNIT, unused_unit::UNUSED_UNIT,
@ -581,10 +592,9 @@ store.register_lints(&[
useless_conversion::USELESS_CONVERSION, useless_conversion::USELESS_CONVERSION,
vec::USELESS_VEC, vec::USELESS_VEC,
vec_init_then_push::VEC_INIT_THEN_PUSH, 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::ENUM_GLOB_USE,
wildcard_imports::WILDCARD_IMPORTS, wildcard_imports::WILDCARD_IMPORTS,
write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
write::PRINTLN_EMPTY_STRING, write::PRINTLN_EMPTY_STRING,
write::PRINT_LITERAL, write::PRINT_LITERAL,
write::PRINT_STDERR, 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(index_refutable_slice::INDEX_REFUTABLE_SLICE),
LintId::of(let_if_seq::USELESS_LET_IF_SEQ), LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE), 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::ITER_WITH_DRAIN),
LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(mutex_atomic::MUTEX_ATOMIC),
LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(mutex_atomic::MUTEX_INTEGER),
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), 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(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(redundant_pub_crate::REDUNDANT_PUB_CRATE),
LintId::of(regex::TRIVIAL_REGEX), LintId::of(regex::TRIVIAL_REGEX),
LintId::of(strings::STRING_LIT_AS_BYTES), LintId::of(strings::STRING_LIT_AS_BYTES),

View File

@ -4,9 +4,7 @@
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(attrs::INLINE_ALWAYS), LintId::of(attrs::INLINE_ALWAYS),
LintId::of(borrow_as_ptr::BORROW_AS_PTR), LintId::of(casts::BORROW_AS_PTR),
LintId::of(bytecount::NAIVE_BYTECOUNT),
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(casts::CAST_LOSSLESS), LintId::of(casts::CAST_LOSSLESS),
LintId::of(casts::CAST_POSSIBLE_TRUNCATION), LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
LintId::of(casts::CAST_POSSIBLE_WRAP), 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(macro_use::MACRO_USE_IMPORTS),
LintId::of(manual_assert::MANUAL_ASSERT), LintId::of(manual_assert::MANUAL_ASSERT),
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED), 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_BOOL),
LintId::of(matches::MATCH_ON_VEC_ITEMS), LintId::of(matches::MATCH_ON_VEC_ITEMS),
LintId::of(matches::MATCH_SAME_ARMS), LintId::of(matches::MATCH_SAME_ARMS),
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS), LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
LintId::of(matches::MATCH_WILD_ERR_ARM), LintId::of(matches::MATCH_WILD_ERR_ARM),
LintId::of(matches::SINGLE_MATCH_ELSE), LintId::of(matches::SINGLE_MATCH_ELSE),
LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
LintId::of(methods::CLONED_INSTEAD_OF_COPIED), LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
LintId::of(methods::FILTER_MAP_NEXT), LintId::of(methods::FILTER_MAP_NEXT),
LintId::of(methods::FLAT_MAP_OPTION), LintId::of(methods::FLAT_MAP_OPTION),
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT), LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
LintId::of(methods::IMPLICIT_CLONE), LintId::of(methods::IMPLICIT_CLONE),
LintId::of(methods::INEFFICIENT_TO_STRING), LintId::of(methods::INEFFICIENT_TO_STRING),
LintId::of(methods::MANUAL_OK_OR),
LintId::of(methods::MAP_UNWRAP_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(methods::UNNECESSARY_JOIN),
LintId::of(misc::USED_UNDERSCORE_BINDING), LintId::of(misc::USED_UNDERSCORE_BINDING),
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER), 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(ref_option_ref::REF_OPTION_REF),
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE), LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED), 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(strings::STRING_ADD_ASSIGN),
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS), LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(trait_bounds::TYPE_REPETITION_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(escape::BOXED_LOCAL),
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS), LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
LintId::of(format_args::TO_STRING_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_const_arrays::LARGE_CONST_ARRAYS),
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT), LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
LintId::of(loops::MANUAL_MEMCPY), LintId::of(loops::MANUAL_MEMCPY),
LintId::of(loops::MISSING_SPIN_LOOP), LintId::of(loops::MISSING_SPIN_LOOP),
LintId::of(loops::NEEDLESS_COLLECT), LintId::of(loops::NEEDLESS_COLLECT),
LintId::of(manual_retain::MANUAL_RETAIN), LintId::of(manual_retain::MANUAL_RETAIN),
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
LintId::of(methods::EXPECT_FUN_CALL), LintId::of(methods::EXPECT_FUN_CALL),
LintId::of(methods::EXTEND_WITH_DRAIN), LintId::of(methods::EXTEND_WITH_DRAIN),
LintId::of(methods::ITER_NTH), LintId::of(methods::ITER_NTH),

View File

@ -4,11 +4,11 @@
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(as_conversions::AS_CONVERSIONS), 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_ATT_SYNTAX),
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX), LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES), LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON), LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
LintId::of(casts::AS_UNDERSCORE),
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY), LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
LintId::of(create_dir::CREATE_DIR), LintId::of(create_dir::CREATE_DIR),
LintId::of(dbg_macro::DBG_MACRO), 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(large_include_file::LARGE_INCLUDE_FILE),
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE), LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION), 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::REST_PAT_IN_FULLY_BOUND_STRUCTS),
LintId::of(matches::TRY_ERR), LintId::of(matches::TRY_ERR),
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM), 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::EXPECT_USED),
LintId::of(methods::FILETYPE_IS_FILE), LintId::of(methods::FILETYPE_IS_FILE),
LintId::of(methods::GET_UNWRAP), LintId::of(methods::GET_UNWRAP),
LintId::of(methods::MAP_ERR_IGNORE),
LintId::of(methods::UNWRAP_USED), LintId::of(methods::UNWRAP_USED),
LintId::of(methods::VERBOSE_FILE_READS),
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX), LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN), LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX), 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(unicode::NON_ASCII_LITERAL),
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS), LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT), 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_STDERR),
LintId::of(write::PRINT_STDOUT), LintId::of(write::PRINT_STDOUT),
LintId::of(write::USE_DEBUG), 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::DOUBLE_MUST_USE),
LintId::of(functions::MUST_USE_UNIT), LintId::of(functions::MUST_USE_UNIT),
LintId::of(functions::RESULT_UNIT_ERR), LintId::of(functions::RESULT_UNIT_ERR),
LintId::of(get_first::GET_FIRST),
LintId::of(inherent_to_string::INHERENT_TO_STRING), LintId::of(inherent_to_string::INHERENT_TO_STRING),
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS), LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
LintId::of(len_zero::COMPARISON_TO_EMPTY), 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_async_fn::MANUAL_ASYNC_FN),
LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_bits::MANUAL_BITS),
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(map_clone::MAP_CLONE),
LintId::of(match_result_ok::MATCH_RESULT_OK), LintId::of(match_result_ok::MATCH_RESULT_OK),
LintId::of(matches::COLLAPSIBLE_MATCH), LintId::of(matches::COLLAPSIBLE_MATCH),
LintId::of(matches::INFALLIBLE_DESTRUCTURING_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_LAST_CMP),
LintId::of(methods::CHARS_NEXT_CMP), LintId::of(methods::CHARS_NEXT_CMP),
LintId::of(methods::ERR_EXPECT), LintId::of(methods::ERR_EXPECT),
LintId::of(methods::GET_FIRST),
LintId::of(methods::INTO_ITER_ON_REF), LintId::of(methods::INTO_ITER_ON_REF),
LintId::of(methods::IS_DIGIT_ASCII_RADIX), LintId::of(methods::IS_DIGIT_ASCII_RADIX),
LintId::of(methods::ITER_CLONED_COLLECT), 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_NTH_ZERO),
LintId::of(methods::ITER_SKIP_NEXT), LintId::of(methods::ITER_SKIP_NEXT),
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC), LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
LintId::of(methods::MAP_CLONE),
LintId::of(methods::MAP_COLLECT_RESULT_UNIT), LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
LintId::of(methods::MUT_MUTEX_LOCK),
LintId::of(methods::NEW_RET_NO_SELF), LintId::of(methods::NEW_RET_NO_SELF),
LintId::of(methods::OBFUSCATED_IF_ELSE), LintId::of(methods::OBFUSCATED_IF_ELSE),
LintId::of(methods::OK_EXPECT), 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::DUPLICATE_UNDERSCORE_ARGUMENT),
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS), LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
LintId::of(misc_early::REDUNDANT_PATTERN), LintId::of(misc_early::REDUNDANT_PATTERN),
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(needless_late_init::NEEDLESS_LATE_INIT), LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS), 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_ABS_TO_UNSIGNED),
LintId::of(casts::CAST_ENUM_CONSTRUCTOR), LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
LintId::of(casts::CAST_ENUM_TRUNCATION), 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(crate_in_macro_def::CRATE_IN_MACRO_DEF),
LintId::of(drop_forget_ref::DROP_NON_DROP), LintId::of(drop_forget_ref::DROP_NON_DROP),
LintId::of(drop_forget_ref::FORGET_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(loops::MUT_RANGE_BOUND),
LintId::of(methods::NO_EFFECT_REPLACE), LintId::of(methods::NO_EFFECT_REPLACE),
LintId::of(methods::SUSPICIOUS_MAP), 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(mut_key::MUTABLE_KEY_TYPE),
LintId::of(octal_escapes::OCTAL_ESCAPES), LintId::of(octal_escapes::OCTAL_ESCAPES),
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS), 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_ARITHMETIC_IMPL),
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL), LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF), 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 almost_complete_letter_range;
mod approx_const; mod approx_const;
mod as_conversions; mod as_conversions;
mod as_underscore;
mod asm_syntax; mod asm_syntax;
mod assertions_on_constants; mod assertions_on_constants;
mod assertions_on_result_states; mod assertions_on_result_states;
@ -181,12 +180,8 @@ mod await_holding_invalid;
mod blocks_in_if_conditions; mod blocks_in_if_conditions;
mod bool_assert_comparison; mod bool_assert_comparison;
mod booleans; mod booleans;
mod borrow_as_ptr;
mod borrow_deref_ref; mod borrow_deref_ref;
mod bytecount;
mod bytes_count_to_len;
mod cargo; mod cargo;
mod case_sensitive_file_extension_comparisons;
mod casts; mod casts;
mod checked_conversions; mod checked_conversions;
mod cognitive_complexity; mod cognitive_complexity;
@ -239,7 +234,6 @@ mod from_over_into;
mod from_str_radix_10; mod from_str_radix_10;
mod functions; mod functions;
mod future_not_send; mod future_not_send;
mod get_first;
mod if_let_mutex; mod if_let_mutex;
mod if_not_else; mod if_not_else;
mod if_then_some_else_none; mod if_then_some_else_none;
@ -276,12 +270,10 @@ mod manual_async_fn;
mod manual_bits; mod manual_bits;
mod manual_instant_elapsed; mod manual_instant_elapsed;
mod manual_non_exhaustive; mod manual_non_exhaustive;
mod manual_ok_or;
mod manual_rem_euclid; mod manual_rem_euclid;
mod manual_retain; mod manual_retain;
mod manual_string_new;
mod manual_strip; mod manual_strip;
mod map_clone;
mod map_err_ignore;
mod map_unit_fn; mod map_unit_fn;
mod match_result_ok; mod match_result_ok;
mod matches; mod matches;
@ -298,9 +290,9 @@ mod missing_enforced_import_rename;
mod missing_inline; mod missing_inline;
mod mixed_read_write_in_expression; mod mixed_read_write_in_expression;
mod module_style; mod module_style;
mod multi_assignments;
mod mut_key; mod mut_key;
mod mut_mut; mod mut_mut;
mod mut_mutex_lock;
mod mut_reference; mod mut_reference;
mod mutable_debug_assertion; mod mutable_debug_assertion;
mod mutex_atomic; mod mutex_atomic;
@ -325,7 +317,6 @@ mod non_send_fields_in_send_ty;
mod nonstandard_macro_braces; mod nonstandard_macro_braces;
mod octal_escapes; mod octal_escapes;
mod only_used_in_recursion; mod only_used_in_recursion;
mod open_options;
mod operators; mod operators;
mod option_env_unwrap; mod option_env_unwrap;
mod option_if_let_else; mod option_if_let_else;
@ -335,7 +326,6 @@ mod panic_unimplemented;
mod partialeq_ne_impl; mod partialeq_ne_impl;
mod partialeq_to_none; mod partialeq_to_none;
mod pass_by_ref_or_value; mod pass_by_ref_or_value;
mod path_buf_push_overwrite;
mod pattern_type_mismatch; mod pattern_type_mismatch;
mod precedence; mod precedence;
mod ptr; mod ptr;
@ -355,7 +345,6 @@ mod redundant_static_lifetimes;
mod ref_option_ref; mod ref_option_ref;
mod reference; mod reference;
mod regex; mod regex;
mod repeat_once;
mod return_self_not_must_use; mod return_self_not_must_use;
mod returns; mod returns;
mod same_name_method; mod same_name_method;
@ -367,7 +356,6 @@ mod single_char_lifetime_names;
mod single_component_path_imports; mod single_component_path_imports;
mod size_of_in_element_count; mod size_of_in_element_count;
mod slow_vector_initialization; mod slow_vector_initialization;
mod stable_sort_primitive;
mod std_instead_of_core; mod std_instead_of_core;
mod strings; mod strings;
mod strlen_on_c_strings; mod strlen_on_c_strings;
@ -381,23 +369,21 @@ mod to_digit_is_some;
mod trailing_empty_array; mod trailing_empty_array;
mod trait_bounds; mod trait_bounds;
mod transmute; mod transmute;
mod transmuting_null;
mod types; mod types;
mod undocumented_unsafe_blocks; mod undocumented_unsafe_blocks;
mod unicode; mod unicode;
mod uninit_vec; mod uninit_vec;
mod unit_hash;
mod unit_return_expecting_ord; mod unit_return_expecting_ord;
mod unit_types; mod unit_types;
mod unnamed_address; mod unnamed_address;
mod unnecessary_owned_empty_strings; mod unnecessary_owned_empty_strings;
mod unnecessary_self_imports; mod unnecessary_self_imports;
mod unnecessary_sort_by;
mod unnecessary_wraps; mod unnecessary_wraps;
mod unnested_or_patterns; mod unnested_or_patterns;
mod unsafe_removed_from_name; mod unsafe_removed_from_name;
mod unused_async; mod unused_async;
mod unused_io_amount; mod unused_io_amount;
mod unused_peekable;
mod unused_rounding; mod unused_rounding;
mod unused_self; mod unused_self;
mod unused_unit; mod unused_unit;
@ -408,8 +394,6 @@ mod use_self;
mod useless_conversion; mod useless_conversion;
mod vec; mod vec;
mod vec_init_then_push; mod vec_init_then_push;
mod vec_resize_to_zero;
mod verbose_file_reads;
mod wildcard_imports; mod wildcard_imports;
mod write; mod write;
mod zero_div_zero; 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(blocks_in_if_conditions::BlocksInIfConditions));
store.register_late_pass(|| Box::new(unicode::Unicode)); store.register_late_pass(|| Box::new(unicode::Unicode));
store.register_late_pass(|| Box::new(uninit_vec::UninitVec)); 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(unit_return_expecting_ord::UnitReturnExpectingOrd));
store.register_late_pass(|| Box::new(strings::StringAdd)); store.register_late_pass(|| Box::new(strings::StringAdd));
store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn)); 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(needless_question_mark::NeedlessQuestionMark));
store.register_late_pass(move || Box::new(casts::Casts::new(msrv))); 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_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(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod)); store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length; 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, 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(shadow::Shadow::default()));
store.register_late_pass(|| Box::new(unit_types::UnitTypes)); store.register_late_pass(|| Box::new(unit_types::UnitTypes));
store.register_late_pass(|| Box::new(loops::Loops)); 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(lifetimes::Lifetimes));
store.register_late_pass(|| Box::new(entry::HashMapPass)); store.register_late_pass(|| Box::new(entry::HashMapPass));
store.register_late_pass(|| Box::new(minmax::MinMaxPass)); 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(zero_div_zero::ZeroDiv));
store.register_late_pass(|| Box::new(mutex_atomic::Mutex)); store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate)); 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()))); 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_arguments_threshold = conf.too_many_arguments_threshold;
let too_many_lines_threshold = conf.too_many_lines_threshold; let too_many_lines_threshold = conf.too_many_lines_threshold;
let large_error_threshold = conf.large_error_threshold;
store.register_late_pass(move || { store.register_late_pass(move || {
Box::new(functions::Functions::new( Box::new(functions::Functions::new(
too_many_arguments_threshold, too_many_arguments_threshold,
too_many_lines_threshold, too_many_lines_threshold,
large_error_threshold,
)) ))
}); });
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>(); 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(move || Box::new(pass_by_ref_or_value));
store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef)); 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(infinite_iter::InfiniteIter));
store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody)); store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default())); 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(ptr_offset_with_cast::PtrOffsetWithCast));
store.register_late_pass(|| Box::new(redundant_clone::RedundantClone)); 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(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(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_constants::AssertionsOnConstants));
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates)); 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)); store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
let max_trait_bounds = conf.max_trait_bounds; let max_trait_bounds = conf.max_trait_bounds;
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(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)); store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports; 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(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(redundant_pub_crate::RedundantPubCrate::default()));
store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress)); 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(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|| Box::new(future_not_send::FutureNotSend)); 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_let_mutex::IfLetMutex));
store.register_late_pass(|| Box::new(if_not_else::IfNotElse)); 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(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(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)); store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold; let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
store.register_early_pass(move || { 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_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(macro_use::MacroUseImports::default()));
store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch)); 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(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(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync)); store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
let disallowed_methods = conf.disallowed_methods.clone(); 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(strings::StringToString));
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues)); 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(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(redundant_slicing::RedundantSlicing));
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10)); 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))); 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(return_self_not_must_use::ReturnSelfNotMustUse));
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); 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(move || Box::new(manual_bits::ManualBits::new(msrv)));
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes)); 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; 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))); store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish; 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_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
store.register_early_pass(|| Box::new(pub_use::PubUse)); 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(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; 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(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(strings::TrimSplitWhitespace));
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default())); 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(|| Box::new(unused_rounding::UnusedRounding));
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); 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(swap_ptr_to_ref::SwapPtrToRef));
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch)); 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(read_zero_byte_vec::ReadZeroByteVec));
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); 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))); 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(std_instead_of_core::StdReexports::default()));
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed)); 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(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` // 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 super::NEEDLESS_COLLECT;
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::higher;
use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> {
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> { impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
fn visit_block(&mut self, block: &'tcx Block<'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) { 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); self.visit_block_expr(expr, hir_id);
} }
if let Some(expr) = block.expr { 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> { impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) { fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
self.current_statement_hir_id = hir_id; 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 { match output.kind {
TyKind::Tup(tys) if tys.is_empty() => { TyKind::Tup(tys) if tys.is_empty() => {
let sugg = "remove the return type"; let sugg = "remove the return type";
Some((sugg, "".into())) Some((sugg, String::new()))
}, },
_ => { _ => {
let sugg = "return the output of the future directly"; 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::diagnostics::span_lint_and_sugg;
use clippy_utils::is_wild; use clippy_utils::is_wild;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::span_contains_comment;
use rustc_ast::{Attribute, LitKind}; use rustc_ast::{Attribute, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat}; use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
use rustc_lint::LateContext; use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty; use rustc_middle::ty;
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
@ -76,6 +77,7 @@ where
>, >,
{ {
if_chain! { if_chain! {
if !span_contains_comment(cx.sess().source_map(), expr.span);
if iter.len() >= 2; if iter.len() >= 2;
if cx.typeck_results().expr_ty(expr).is_bool(); if cx.typeck_results().expr_ty(expr).is_bool();
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back(); 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_errors::Applicability;
use rustc_hir::LangItem::OptionNone; 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_lint::LateContext;
use rustc_span::sym; use rustc_span::sym;
use rustc_typeck::hir_ty_to_ty; 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 { fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
for arm in arms { for arm in arms {
let arm_expr = peel_blocks_with_stmt(arm.body); 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 { 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) { } else if !pat_same_as_expr(arm.pat, arm_expr) {
return false; 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; use super::EXPECT_USED;
/// lint use of `expect()` for `Option`s and `Result`s /// 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<'_>, allow_expect_in_tests: bool) { 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 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", "")) Some((EXPECT_USED, "an Option", "None", ""))
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { } 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 { } else {
None None
}; };
let method = if is_err { "expect_err" } else { "expect" };
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) { if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
return; return;
} }
@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
cx, cx,
lint, lint,
expr.span, expr.span,
&format!("used `expect()` on `{kind}` value"), &format!("used `{method}()` on `{kind}` value"),
None, None,
&format!("if this value is {none_prefix}`{none_value}`, it will panic"), &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