mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
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:
commit
db00199d99
@ -727,6 +727,7 @@ version = "0.1.65"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"if_chain",
|
||||
"itertools",
|
||||
"rustc-semver",
|
||||
]
|
||||
|
||||
|
@ -555,7 +555,7 @@ impl TokenStreamBuilder {
|
||||
|
||||
// Get the first stream, which will become the result stream.
|
||||
// If it's `None`, create an empty stream.
|
||||
let mut iter = streams.drain(..);
|
||||
let mut iter = streams.into_iter();
|
||||
let mut res_stream_lrc = iter.next().unwrap().0;
|
||||
|
||||
// Append the subsequent elements to the result stream, after
|
||||
|
@ -164,7 +164,7 @@ impl<K: Ord, V> SortedMap<K, V> {
|
||||
/// It is up to the caller to make sure that the elements are sorted by key
|
||||
/// and that there are no duplicates.
|
||||
#[inline]
|
||||
pub fn insert_presorted(&mut self, mut elements: Vec<(K, V)>) {
|
||||
pub fn insert_presorted(&mut self, elements: Vec<(K, V)>) {
|
||||
if elements.is_empty() {
|
||||
return;
|
||||
}
|
||||
@ -173,28 +173,28 @@ impl<K: Ord, V> SortedMap<K, V> {
|
||||
|
||||
let start_index = self.lookup_index_for(&elements[0].0);
|
||||
|
||||
let drain = match start_index {
|
||||
let elements = match start_index {
|
||||
Ok(index) => {
|
||||
let mut drain = elements.drain(..);
|
||||
self.data[index] = drain.next().unwrap();
|
||||
drain
|
||||
let mut elements = elements.into_iter();
|
||||
self.data[index] = elements.next().unwrap();
|
||||
elements
|
||||
}
|
||||
Err(index) => {
|
||||
if index == self.data.len() || elements.last().unwrap().0 < self.data[index].0 {
|
||||
// We can copy the whole range without having to mix with
|
||||
// existing elements.
|
||||
self.data.splice(index..index, elements.drain(..));
|
||||
self.data.splice(index..index, elements.into_iter());
|
||||
return;
|
||||
}
|
||||
|
||||
let mut drain = elements.drain(..);
|
||||
self.data.insert(index, drain.next().unwrap());
|
||||
drain
|
||||
let mut elements = elements.into_iter();
|
||||
self.data.insert(index, elements.next().unwrap());
|
||||
elements
|
||||
}
|
||||
};
|
||||
|
||||
// Insert the rest
|
||||
for (k, v) in drain {
|
||||
for (k, v) in elements {
|
||||
self.insert(k, v);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! The various pretty-printing routines.
|
||||
|
||||
use crate::session_diagnostics::UnprettyDumpFail;
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
@ -357,12 +358,15 @@ fn get_source(input: &Input, sess: &Session) -> (String, FileName) {
|
||||
(src, src_name)
|
||||
}
|
||||
|
||||
fn write_or_print(out: &str, ofile: Option<&Path>) {
|
||||
fn write_or_print(out: &str, ofile: Option<&Path>, sess: &Session) {
|
||||
match ofile {
|
||||
None => print!("{}", out),
|
||||
Some(p) => {
|
||||
if let Err(e) = std::fs::write(p, out) {
|
||||
panic!("print-print failed to write {} due to {}", p.display(), e);
|
||||
sess.emit_fatal(UnprettyDumpFail {
|
||||
path: p.display().to_string(),
|
||||
err: e.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -402,7 +406,7 @@ pub fn print_after_parsing(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write_or_print(&out, ofile);
|
||||
write_or_print(&out, ofile, sess);
|
||||
}
|
||||
|
||||
pub fn print_after_hir_lowering<'tcx>(
|
||||
@ -468,7 +472,7 @@ pub fn print_after_hir_lowering<'tcx>(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write_or_print(&out, ofile);
|
||||
write_or_print(&out, ofile, tcx.sess);
|
||||
}
|
||||
|
||||
// In an ideal world, this would be a public function called by the driver after
|
||||
@ -512,7 +516,7 @@ fn print_with_analysis(
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write_or_print(&out, ofile);
|
||||
write_or_print(&out, ofile, tcx.sess);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -31,3 +31,10 @@ pub(crate) struct RLinkRustcVersionMismatch<'a> {
|
||||
#[derive(SessionDiagnostic)]
|
||||
#[diag(driver::rlink_no_a_file)]
|
||||
pub(crate) struct RlinkNotAFile;
|
||||
|
||||
#[derive(SessionDiagnostic)]
|
||||
#[diag(driver::unpretty_dump_fail)]
|
||||
pub(crate) struct UnprettyDumpFail {
|
||||
pub path: String,
|
||||
pub err: String,
|
||||
}
|
||||
|
@ -9,3 +9,5 @@ driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding
|
||||
driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
|
||||
|
||||
driver_rlink_no_a_file = rlink must be a file
|
||||
|
||||
driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
|
||||
|
25
compiler/rustc_error_messages/locales/en-US/query_system.ftl
Normal file
25
compiler/rustc_error_messages/locales/en-US/query_system.ftl
Normal 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!
|
@ -50,6 +50,7 @@ fluent_messages! {
|
||||
passes => "../locales/en-US/passes.ftl",
|
||||
plugin_impl => "../locales/en-US/plugin_impl.ftl",
|
||||
privacy => "../locales/en-US/privacy.ftl",
|
||||
query_system => "../locales/en-US/query_system.ftl",
|
||||
save_analysis => "../locales/en-US/save_analysis.ftl",
|
||||
ty_utils => "../locales/en-US/ty_utils.ftl",
|
||||
typeck => "../locales/en-US/typeck.ftl",
|
||||
|
@ -974,12 +974,12 @@ impl Diagnostic {
|
||||
fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
|
||||
&mut self,
|
||||
level: Level,
|
||||
mut message: Vec<(M, Style)>,
|
||||
message: Vec<(M, Style)>,
|
||||
span: MultiSpan,
|
||||
render_span: Option<MultiSpan>,
|
||||
) {
|
||||
let message = message
|
||||
.drain(..)
|
||||
.into_iter()
|
||||
.map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
|
||||
.collect();
|
||||
let sub = SubDiagnostic { level, message, span, render_span };
|
||||
|
@ -21,7 +21,7 @@ pub trait Translate {
|
||||
/// Typically performed once for each diagnostic at the start of `emit_diagnostic` and then
|
||||
/// passed around as a reference thereafter.
|
||||
fn to_fluent_args<'arg>(&self, args: &[DiagnosticArg<'arg>]) -> FluentArgs<'arg> {
|
||||
FromIterator::from_iter(args.to_vec().drain(..))
|
||||
FromIterator::from_iter(args.iter().cloned())
|
||||
}
|
||||
|
||||
/// Convert `DiagnosticMessage`s to a string, performing translation if necessary.
|
||||
|
@ -393,8 +393,14 @@ impl LateLintPass<'_> for Diagnostics {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut found_parent_with_attr = false;
|
||||
let mut found_impl = false;
|
||||
for (_, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
||||
for (hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
|
||||
if let Some(owner_did) = hir_id.as_owner() {
|
||||
found_parent_with_attr = found_parent_with_attr
|
||||
|| cx.tcx.has_attr(owner_did.to_def_id(), sym::rustc_lint_diagnostics);
|
||||
}
|
||||
|
||||
debug!(?parent);
|
||||
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent &&
|
||||
let Impl { of_trait: Some(of_trait), .. } = impl_ &&
|
||||
@ -407,7 +413,7 @@ impl LateLintPass<'_> for Diagnostics {
|
||||
}
|
||||
}
|
||||
debug!(?found_impl);
|
||||
if !found_impl {
|
||||
if !found_parent_with_attr && !found_impl {
|
||||
cx.struct_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, |lint| {
|
||||
lint.build(fluent::lint::diag_out_of_impl).emit();
|
||||
})
|
||||
@ -425,7 +431,7 @@ impl LateLintPass<'_> for Diagnostics {
|
||||
}
|
||||
}
|
||||
debug!(?found_diagnostic_message);
|
||||
if !found_diagnostic_message {
|
||||
if !found_parent_with_attr && !found_diagnostic_message {
|
||||
cx.struct_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, |lint| {
|
||||
lint.build(fluent::lint::untranslatable_diag).emit();
|
||||
})
|
||||
|
@ -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 {
|
||||
|
@ -12,7 +12,7 @@ use quote::{format_ident, quote};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path};
|
||||
use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue, NestedMeta, Path};
|
||||
use synstructure::{BindingInfo, Structure, VariantInfo};
|
||||
|
||||
/// Which kind of suggestion is being created?
|
||||
@ -28,41 +28,8 @@ enum SubdiagnosticSuggestionKind {
|
||||
Verbose,
|
||||
}
|
||||
|
||||
impl FromStr for SubdiagnosticSuggestionKind {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"" => Ok(SubdiagnosticSuggestionKind::Normal),
|
||||
"_short" => Ok(SubdiagnosticSuggestionKind::Short),
|
||||
"_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden),
|
||||
"_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SubdiagnosticSuggestionKind {
|
||||
pub fn to_suggestion_style(&self) -> TokenStream {
|
||||
match self {
|
||||
SubdiagnosticSuggestionKind::Normal => {
|
||||
quote! { rustc_errors::SuggestionStyle::ShowCode }
|
||||
}
|
||||
SubdiagnosticSuggestionKind::Short => {
|
||||
quote! { rustc_errors::SuggestionStyle::HideCodeInline }
|
||||
}
|
||||
SubdiagnosticSuggestionKind::Hidden => {
|
||||
quote! { rustc_errors::SuggestionStyle::HideCodeAlways }
|
||||
}
|
||||
SubdiagnosticSuggestionKind::Verbose => {
|
||||
quote! { rustc_errors::SuggestionStyle::ShowAlways }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Which kind of subdiagnostic is being created from a variant?
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Copy)]
|
||||
enum SubdiagnosticKind {
|
||||
/// `#[label(...)]`
|
||||
Label,
|
||||
@ -73,9 +40,31 @@ enum SubdiagnosticKind {
|
||||
/// `#[warning(...)]`
|
||||
Warn,
|
||||
/// `#[suggestion{,_short,_hidden,_verbose}]`
|
||||
Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream },
|
||||
/// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
|
||||
MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind },
|
||||
Suggestion(SubdiagnosticSuggestionKind),
|
||||
}
|
||||
|
||||
impl FromStr for SubdiagnosticKind {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"label" => Ok(SubdiagnosticKind::Label),
|
||||
"note" => Ok(SubdiagnosticKind::Note),
|
||||
"help" => Ok(SubdiagnosticKind::Help),
|
||||
"warning" => Ok(SubdiagnosticKind::Warn),
|
||||
"suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
|
||||
"suggestion_short" => {
|
||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
|
||||
}
|
||||
"suggestion_hidden" => {
|
||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden))
|
||||
}
|
||||
"suggestion_verbose" => {
|
||||
Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose))
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl quote::IdentFragment for SubdiagnosticKind {
|
||||
@ -85,9 +74,17 @@ impl quote::IdentFragment for SubdiagnosticKind {
|
||||
SubdiagnosticKind::Note => write!(f, "note"),
|
||||
SubdiagnosticKind::Help => write!(f, "help"),
|
||||
SubdiagnosticKind::Warn => write!(f, "warn"),
|
||||
SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"),
|
||||
SubdiagnosticKind::MultipartSuggestion { .. } => {
|
||||
write!(f, "multipart_suggestion_with_style")
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
|
||||
write!(f, "suggestion")
|
||||
}
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => {
|
||||
write!(f, "suggestion_short")
|
||||
}
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => {
|
||||
write!(f, "suggestion_hidden")
|
||||
}
|
||||
SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => {
|
||||
write!(f, "suggestion_verbose")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -151,9 +148,11 @@ impl<'a> SessionSubdiagnosticDerive<'a> {
|
||||
variant,
|
||||
span,
|
||||
fields: fields_map,
|
||||
kinds: Vec::new(),
|
||||
slugs: Vec::new(),
|
||||
code: None,
|
||||
span_field: None,
|
||||
applicability: None,
|
||||
has_suggestion_parts: false,
|
||||
};
|
||||
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
|
||||
});
|
||||
@ -194,15 +193,21 @@ struct SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
/// derive builder.
|
||||
fields: HashMap<String, TokenStream>,
|
||||
|
||||
/// Subdiagnostic kind of the type/variant.
|
||||
kinds: Vec<(SubdiagnosticKind, proc_macro::Span)>,
|
||||
|
||||
/// Slugs of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
|
||||
/// `#[kind(slug)]` attribute on the type or variant.
|
||||
slugs: Vec<(Path, proc_macro::Span)>,
|
||||
/// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
|
||||
/// attribute on the type or variant.
|
||||
code: Option<(TokenStream, proc_macro::Span)>,
|
||||
|
||||
/// Identifier for the binding to the `#[primary_span]` field.
|
||||
span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
|
||||
/// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
|
||||
/// `rustc_errors::Applicability::*` variant directly.
|
||||
applicability: Option<(TokenStream, proc_macro::Span)>,
|
||||
|
||||
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
|
||||
/// during finalization if still `false`.
|
||||
has_suggestion_parts: bool,
|
||||
}
|
||||
|
||||
impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
@ -212,133 +217,124 @@ impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
}
|
||||
|
||||
impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
fn identify_kind(
|
||||
&mut self,
|
||||
) -> Result<Option<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
|
||||
let mut kind_slug = None;
|
||||
|
||||
for attr in self.variant.ast().attrs {
|
||||
fn identify_kind(&mut self) -> Result<(), DiagnosticDeriveError> {
|
||||
for (i, attr) in self.variant.ast().attrs.iter().enumerate() {
|
||||
let span = attr.span().unwrap();
|
||||
|
||||
let name = attr.path.segments.last().unwrap().ident.to_string();
|
||||
let name = name.as_str();
|
||||
|
||||
let meta = attr.parse_meta()?;
|
||||
let Meta::List(MetaList { ref nested, .. }) = meta else {
|
||||
throw_invalid_attr!(attr, &meta);
|
||||
};
|
||||
|
||||
let mut kind = match name {
|
||||
"label" => SubdiagnosticKind::Label,
|
||||
"note" => SubdiagnosticKind::Note,
|
||||
"help" => SubdiagnosticKind::Help,
|
||||
"warning" => SubdiagnosticKind::Warn,
|
||||
_ => {
|
||||
if let Some(suggestion_kind) =
|
||||
name.strip_prefix("suggestion").and_then(|s| s.parse().ok())
|
||||
{
|
||||
SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() }
|
||||
} else if let Some(suggestion_kind) =
|
||||
name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok())
|
||||
{
|
||||
SubdiagnosticKind::MultipartSuggestion { suggestion_kind }
|
||||
} else {
|
||||
throw_invalid_attr!(attr, &meta);
|
||||
let kind = match meta {
|
||||
Meta::List(MetaList { ref nested, .. }) => {
|
||||
let mut nested_iter = nested.into_iter();
|
||||
if let Some(nested_attr) = nested_iter.next() {
|
||||
match nested_attr {
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
self.slugs.push((path.clone(), span));
|
||||
}
|
||||
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
||||
if matches!(
|
||||
meta.path().segments.last().unwrap().ident.to_string().as_str(),
|
||||
"code" | "applicability"
|
||||
) =>
|
||||
{
|
||||
// don't error for valid follow-up attributes
|
||||
}
|
||||
nested_attr => {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help(
|
||||
"first argument of the attribute should be the diagnostic \
|
||||
slug",
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut slug = None;
|
||||
let mut code = None;
|
||||
for nested_attr in nested_iter {
|
||||
let meta = match nested_attr {
|
||||
NestedMeta::Meta(ref meta) => meta,
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr),
|
||||
};
|
||||
|
||||
let mut nested_iter = nested.into_iter();
|
||||
if let Some(nested_attr) = nested_iter.next() {
|
||||
match nested_attr {
|
||||
NestedMeta::Meta(Meta::Path(path)) => {
|
||||
slug.set_once((path.clone(), span));
|
||||
}
|
||||
NestedMeta::Meta(meta @ Meta::NameValue(_))
|
||||
if matches!(
|
||||
meta.path().segments.last().unwrap().ident.to_string().as_str(),
|
||||
"code" | "applicability"
|
||||
) =>
|
||||
{
|
||||
// Don't error for valid follow-up attributes.
|
||||
}
|
||||
nested_attr => {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help(
|
||||
"first argument of the attribute should be the diagnostic \
|
||||
slug",
|
||||
)
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
let span = meta.span().unwrap();
|
||||
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
|
||||
let nested_name = nested_name.as_str();
|
||||
|
||||
for nested_attr in nested_iter {
|
||||
let meta = match nested_attr {
|
||||
NestedMeta::Meta(ref meta) => meta,
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr),
|
||||
};
|
||||
|
||||
let span = meta.span().unwrap();
|
||||
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
|
||||
let nested_name = nested_name.as_str();
|
||||
|
||||
let value = match meta {
|
||||
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value,
|
||||
Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help("a diagnostic slug must be the first argument to the attribute")
|
||||
}),
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr),
|
||||
};
|
||||
|
||||
match nested_name {
|
||||
"code" => {
|
||||
if matches!(kind, SubdiagnosticKind::Suggestion { .. }) {
|
||||
let formatted_str = self.build_format(&value.value(), value.span());
|
||||
code.set_once((formatted_str, span));
|
||||
} else {
|
||||
span_err(
|
||||
span,
|
||||
&format!(
|
||||
"`code` is not a valid nested attribute of a `{}` attribute",
|
||||
name
|
||||
),
|
||||
)
|
||||
.emit();
|
||||
match meta {
|
||||
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
|
||||
match nested_name {
|
||||
"code" => {
|
||||
let formatted_str = self.build_format(&s.value(), s.span());
|
||||
self.code.set_once((formatted_str, span));
|
||||
}
|
||||
"applicability" => {
|
||||
let value = match Applicability::from_str(&s.value()) {
|
||||
Ok(v) => v,
|
||||
Err(()) => {
|
||||
span_err(span, "invalid applicability").emit();
|
||||
Applicability::Unspecified
|
||||
}
|
||||
};
|
||||
self.applicability.set_once((quote! { #value }, span));
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help(
|
||||
"only `code` and `applicability` are valid nested \
|
||||
attributes",
|
||||
)
|
||||
}),
|
||||
}
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
if matches!(meta, Meta::Path(_)) {
|
||||
diag.help(
|
||||
"a diagnostic slug must be the first argument to the \
|
||||
attribute",
|
||||
)
|
||||
} else {
|
||||
diag
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
"applicability" => {
|
||||
if matches!(
|
||||
kind,
|
||||
SubdiagnosticKind::Suggestion { .. }
|
||||
| SubdiagnosticKind::MultipartSuggestion { .. }
|
||||
) {
|
||||
let value =
|
||||
Applicability::from_str(&value.value()).unwrap_or_else(|()| {
|
||||
span_err(span, "invalid applicability").emit();
|
||||
Applicability::Unspecified
|
||||
});
|
||||
self.applicability.set_once((quote! { #value }, span));
|
||||
} else {
|
||||
span_err(
|
||||
span,
|
||||
&format!(
|
||||
"`applicability` is not a valid nested attribute of a `{}` attribute",
|
||||
name
|
||||
)
|
||||
).emit();
|
||||
}
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help("only `code` and `applicability` are valid nested attributes")
|
||||
}),
|
||||
|
||||
let Ok(kind) = SubdiagnosticKind::from_str(name) else {
|
||||
throw_invalid_attr!(attr, &meta)
|
||||
};
|
||||
|
||||
kind
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &meta),
|
||||
};
|
||||
|
||||
if matches!(
|
||||
kind,
|
||||
SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
|
||||
) && self.code.is_some()
|
||||
{
|
||||
throw_span_err!(
|
||||
span,
|
||||
&format!("`code` is not a valid nested attribute of a `{}` attribute", name)
|
||||
);
|
||||
}
|
||||
|
||||
let Some((slug, _)) = slug else {
|
||||
if matches!(
|
||||
kind,
|
||||
SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
|
||||
) && self.applicability.is_some()
|
||||
{
|
||||
throw_span_err!(
|
||||
span,
|
||||
&format!(
|
||||
"`applicability` is not a valid nested attribute of a `{}` attribute",
|
||||
name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if self.slugs.len() != i + 1 {
|
||||
throw_span_err!(
|
||||
span,
|
||||
&format!(
|
||||
@ -346,338 +342,146 @@ impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
|
||||
name
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
match kind {
|
||||
SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => {
|
||||
let Some((code, _)) = code else {
|
||||
throw_span_err!(span, "suggestion without `code = \"...\"`");
|
||||
};
|
||||
*code_field = code;
|
||||
}
|
||||
SubdiagnosticKind::Label
|
||||
| SubdiagnosticKind::Note
|
||||
| SubdiagnosticKind::Help
|
||||
| SubdiagnosticKind::Warn
|
||||
| SubdiagnosticKind::MultipartSuggestion { .. } => {}
|
||||
}
|
||||
|
||||
kind_slug.set_once(((kind, slug), span))
|
||||
self.kinds.push((kind, span));
|
||||
}
|
||||
|
||||
Ok(kind_slug.map(|(kind_slug, _)| kind_slug))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generates the code for a field with no attributes.
|
||||
fn generate_field_set_arg(&mut self, binding: &BindingInfo<'_>) -> TokenStream {
|
||||
fn generate_field_code(
|
||||
&mut self,
|
||||
binding: &BindingInfo<'_>,
|
||||
have_suggestion: bool,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let ast = binding.ast();
|
||||
assert_eq!(ast.attrs.len(), 0, "field with attribute used as diagnostic arg");
|
||||
|
||||
let inner_ty = FieldInnerTy::from_type(&ast.ty);
|
||||
let info = FieldInfo {
|
||||
binding: binding,
|
||||
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
|
||||
span: &ast.span(),
|
||||
};
|
||||
|
||||
for attr in &ast.attrs {
|
||||
let name = attr.path.segments.last().unwrap().ident.to_string();
|
||||
let name = name.as_str();
|
||||
let span = attr.span().unwrap();
|
||||
|
||||
let meta = attr.parse_meta()?;
|
||||
match meta {
|
||||
Meta::Path(_) => match name {
|
||||
"primary_span" => {
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
self.span_field.set_once((binding.binding.clone(), span));
|
||||
return Ok(quote! {});
|
||||
}
|
||||
"applicability" if have_suggestion => {
|
||||
report_error_if_not_applied_to_applicability(attr, &info)?;
|
||||
let binding = binding.binding.clone();
|
||||
self.applicability.set_once((quote! { #binding }, span));
|
||||
return Ok(quote! {});
|
||||
}
|
||||
"applicability" => {
|
||||
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
||||
return Ok(quote! {});
|
||||
}
|
||||
"skip_arg" => {
|
||||
return Ok(quote! {});
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &meta, |diag| {
|
||||
diag.help(
|
||||
"only `primary_span`, `applicability` and `skip_arg` are valid field \
|
||||
attributes",
|
||||
)
|
||||
}),
|
||||
},
|
||||
_ => throw_invalid_attr!(attr, &meta),
|
||||
}
|
||||
}
|
||||
|
||||
let ident = ast.ident.as_ref().unwrap();
|
||||
|
||||
let diag = &self.diag;
|
||||
let ident = ast.ident.as_ref().unwrap();
|
||||
quote! {
|
||||
let generated = quote! {
|
||||
#diag.set_arg(
|
||||
stringify!(#ident),
|
||||
#binding
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
Ok(inner_ty.with(binding, generated))
|
||||
}
|
||||
|
||||
/// Generates the necessary code for all attributes on a field.
|
||||
fn generate_field_attr_code(
|
||||
&mut self,
|
||||
binding: &BindingInfo<'_>,
|
||||
kind: &SubdiagnosticKind,
|
||||
) -> TokenStream {
|
||||
let ast = binding.ast();
|
||||
assert!(ast.attrs.len() > 0, "field without attributes generating attr code");
|
||||
|
||||
// Abstract over `Vec<T>` and `Option<T>` fields using `FieldInnerTy`, which will
|
||||
// apply the generated code on each element in the `Vec` or `Option`.
|
||||
let inner_ty = FieldInnerTy::from_type(&ast.ty);
|
||||
ast.attrs
|
||||
.iter()
|
||||
.map(|attr| {
|
||||
let info = FieldInfo {
|
||||
binding,
|
||||
ty: inner_ty.inner_type().unwrap_or(&ast.ty),
|
||||
span: &ast.span(),
|
||||
};
|
||||
|
||||
let generated = self
|
||||
.generate_field_code_inner(kind, attr, info)
|
||||
.unwrap_or_else(|v| v.to_compile_error());
|
||||
|
||||
inner_ty.with(binding, generated)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn generate_field_code_inner(
|
||||
&mut self,
|
||||
kind: &SubdiagnosticKind,
|
||||
attr: &Attribute,
|
||||
info: FieldInfo<'_>,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let meta = attr.parse_meta()?;
|
||||
match meta {
|
||||
Meta::Path(path) => self.generate_field_code_inner_path(kind, attr, info, path),
|
||||
Meta::List(list @ MetaList { .. }) => {
|
||||
self.generate_field_code_inner_list(kind, attr, info, list)
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &meta),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the code for a `[Meta::Path]`-like attribute on a field (e.g. `#[primary_span]`).
|
||||
fn generate_field_code_inner_path(
|
||||
&mut self,
|
||||
kind: &SubdiagnosticKind,
|
||||
attr: &Attribute,
|
||||
info: FieldInfo<'_>,
|
||||
path: Path,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let span = attr.span().unwrap();
|
||||
let ident = &path.segments.last().unwrap().ident;
|
||||
let name = ident.to_string();
|
||||
let name = name.as_str();
|
||||
|
||||
match name {
|
||||
"skip_arg" => Ok(quote! {}),
|
||||
"primary_span" => {
|
||||
if matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
|
||||
throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
|
||||
diag.help(
|
||||
"multipart suggestions use one or more `#[suggestion_part]`s rather \
|
||||
than one `#[primary_span]`",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
|
||||
let binding = info.binding.binding.clone();
|
||||
self.span_field.set_once((binding, span));
|
||||
|
||||
Ok(quote! {})
|
||||
}
|
||||
"suggestion_part" => {
|
||||
self.has_suggestion_parts = true;
|
||||
|
||||
match kind {
|
||||
SubdiagnosticKind::MultipartSuggestion { .. } => {
|
||||
span_err(
|
||||
span,
|
||||
"`#[suggestion_part(...)]` attribute without `code = \"...\"`",
|
||||
)
|
||||
.emit();
|
||||
Ok(quote! {})
|
||||
}
|
||||
SubdiagnosticKind::Label
|
||||
| SubdiagnosticKind::Note
|
||||
| SubdiagnosticKind::Help
|
||||
| SubdiagnosticKind::Warn
|
||||
| SubdiagnosticKind::Suggestion { .. } => {
|
||||
throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
|
||||
diag.help(
|
||||
"`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead",
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
"applicability" => {
|
||||
if let SubdiagnosticKind::Suggestion { .. }
|
||||
| SubdiagnosticKind::MultipartSuggestion { .. } = kind
|
||||
{
|
||||
report_error_if_not_applied_to_applicability(attr, &info)?;
|
||||
|
||||
let binding = info.binding.binding.clone();
|
||||
self.applicability.set_once((quote! { #binding }, span));
|
||||
} else {
|
||||
span_err(span, "`#[applicability]` is only valid on suggestions").emit();
|
||||
}
|
||||
|
||||
Ok(quote! {})
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| {
|
||||
let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
|
||||
"suggestion_part"
|
||||
} else {
|
||||
"primary_span"
|
||||
};
|
||||
diag.help(format!(
|
||||
"only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the code for a `[Meta::List]`-like attribute on a field (e.g.
|
||||
/// `#[suggestion_part(code = "...")]`).
|
||||
fn generate_field_code_inner_list(
|
||||
&mut self,
|
||||
kind: &SubdiagnosticKind,
|
||||
attr: &Attribute,
|
||||
info: FieldInfo<'_>,
|
||||
list: MetaList,
|
||||
) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let span = attr.span().unwrap();
|
||||
let ident = &list.path.segments.last().unwrap().ident;
|
||||
let name = ident.to_string();
|
||||
let name = name.as_str();
|
||||
|
||||
match name {
|
||||
"suggestion_part" => {
|
||||
if !matches!(kind, SubdiagnosticKind::MultipartSuggestion { .. }) {
|
||||
throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
||||
diag.help(
|
||||
"`#[suggestion_part(...)]` is only valid in multipart suggestions",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
self.has_suggestion_parts = true;
|
||||
|
||||
report_error_if_not_applied_to_span(attr, &info)?;
|
||||
|
||||
let mut code = None;
|
||||
for nested_attr in list.nested.iter() {
|
||||
let NestedMeta::Meta(ref meta) = nested_attr else {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr);
|
||||
};
|
||||
|
||||
let span = meta.span().unwrap();
|
||||
let nested_name = meta.path().segments.last().unwrap().ident.to_string();
|
||||
let nested_name = nested_name.as_str();
|
||||
|
||||
let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) = meta else {
|
||||
throw_invalid_nested_attr!(attr, &nested_attr);
|
||||
};
|
||||
|
||||
match nested_name {
|
||||
"code" => {
|
||||
let formatted_str = self.build_format(&value.value(), value.span());
|
||||
code.set_once((formatted_str, span));
|
||||
}
|
||||
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
|
||||
diag.help("`code` is the only valid nested attribute")
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
let Some((code, _)) = code else {
|
||||
span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`")
|
||||
.emit();
|
||||
return Ok(quote! {});
|
||||
};
|
||||
let binding = info.binding;
|
||||
|
||||
Ok(quote! { suggestions.push((#binding, #code)); })
|
||||
}
|
||||
_ => throw_invalid_attr!(attr, &Meta::List(list), |diag| {
|
||||
let span_attr = if let SubdiagnosticKind::MultipartSuggestion { .. } = kind {
|
||||
"suggestion_part"
|
||||
} else {
|
||||
"primary_span"
|
||||
};
|
||||
diag.help(format!(
|
||||
"only `{span_attr}`, `applicability` and `skip_arg` are valid field attributes",
|
||||
))
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
let Some((kind, slug)) = self.identify_kind()? else {
|
||||
fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
|
||||
self.identify_kind()?;
|
||||
if self.kinds.is_empty() {
|
||||
throw_span_err!(
|
||||
self.variant.ast().ident.span().unwrap(),
|
||||
"subdiagnostic kind not specified"
|
||||
);
|
||||
};
|
||||
let have_suggestion =
|
||||
self.kinds.iter().any(|(k, _)| matches!(k, SubdiagnosticKind::Suggestion(_)));
|
||||
let mut args = TokenStream::new();
|
||||
for binding in self.variant.bindings() {
|
||||
let arg = self
|
||||
.generate_field_code(binding, have_suggestion)
|
||||
.unwrap_or_else(|v| v.to_compile_error());
|
||||
args.extend(arg);
|
||||
}
|
||||
let mut tokens = TokenStream::new();
|
||||
for ((kind, _), (slug, _)) in self.kinds.iter().zip(&self.slugs) {
|
||||
let code = match self.code.as_ref() {
|
||||
Some((code, _)) => Some(quote! { #code }),
|
||||
None if have_suggestion => {
|
||||
span_err(self.span, "suggestion without `code = \"...\"`").emit();
|
||||
Some(quote! { /* macro error */ "..." })
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let init = match &kind {
|
||||
SubdiagnosticKind::Label
|
||||
| SubdiagnosticKind::Note
|
||||
| SubdiagnosticKind::Help
|
||||
| SubdiagnosticKind::Warn
|
||||
| SubdiagnosticKind::Suggestion { .. } => quote! {},
|
||||
SubdiagnosticKind::MultipartSuggestion { .. } => {
|
||||
quote! { let mut suggestions = Vec::new(); }
|
||||
}
|
||||
};
|
||||
let span_field = self.span_field.as_ref().map(|(span, _)| span);
|
||||
let applicability = match self.applicability.clone() {
|
||||
Some((applicability, _)) => Some(applicability),
|
||||
None if have_suggestion => {
|
||||
span_err(self.span, "suggestion without `applicability`").emit();
|
||||
Some(quote! { rustc_errors::Applicability::Unspecified })
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let attr_args: TokenStream = self
|
||||
.variant
|
||||
.bindings()
|
||||
.iter()
|
||||
.filter(|binding| !binding.ast().attrs.is_empty())
|
||||
.map(|binding| self.generate_field_attr_code(binding, &kind))
|
||||
.collect();
|
||||
|
||||
let span_field = self.span_field.as_ref().map(|(span, _)| span);
|
||||
let applicability = self.applicability.take().map_or_else(
|
||||
|| quote! { rustc_errors::Applicability::Unspecified },
|
||||
|(applicability, _)| applicability,
|
||||
);
|
||||
|
||||
let diag = &self.diag;
|
||||
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
|
||||
let message = quote! { rustc_errors::fluent::#slug };
|
||||
let call = match kind {
|
||||
SubdiagnosticKind::Suggestion { suggestion_kind, code } => {
|
||||
let diag = &self.diag;
|
||||
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
|
||||
let message = quote! { rustc_errors::fluent::#slug };
|
||||
let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
|
||||
if let Some(span) = span_field {
|
||||
let style = suggestion_kind.to_suggestion_style();
|
||||
|
||||
quote! { #diag.#name(#span, #message, #code, #applicability, #style); }
|
||||
quote! { #diag.#name(#span, #message, #code, #applicability); }
|
||||
} else {
|
||||
span_err(self.span, "suggestion without `#[primary_span]` field").emit();
|
||||
quote! { unreachable!(); }
|
||||
}
|
||||
}
|
||||
SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => {
|
||||
if !self.has_suggestion_parts {
|
||||
span_err(
|
||||
self.span,
|
||||
"multipart suggestion without any `#[suggestion_part(...)]` fields",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
|
||||
let style = suggestion_kind.to_suggestion_style();
|
||||
|
||||
quote! { #diag.#name(#message, suggestions, #applicability, #style); }
|
||||
}
|
||||
SubdiagnosticKind::Label => {
|
||||
} else if matches!(kind, SubdiagnosticKind::Label) {
|
||||
if let Some(span) = span_field {
|
||||
quote! { #diag.#name(#span, #message); }
|
||||
} else {
|
||||
span_err(self.span, "label without `#[primary_span]` field").emit();
|
||||
quote! { unreachable!(); }
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
} else {
|
||||
if let Some(span) = span_field {
|
||||
quote! { #diag.#name(#span, #message); }
|
||||
} else {
|
||||
quote! { #diag.#name(#message); }
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
tokens.extend(quote! {
|
||||
#call
|
||||
#args
|
||||
});
|
||||
}
|
||||
|
||||
let plain_args: TokenStream = self
|
||||
.variant
|
||||
.bindings()
|
||||
.iter()
|
||||
.filter(|binding| binding.ast().attrs.is_empty())
|
||||
.map(|binding| self.generate_field_set_arg(binding))
|
||||
.collect();
|
||||
|
||||
Ok(quote! {
|
||||
#init
|
||||
#attr_args
|
||||
#call
|
||||
#plain_args
|
||||
})
|
||||
Ok(tokens)
|
||||
}
|
||||
}
|
||||
|
73
compiler/rustc_query_system/src/error.rs
Normal file
73
compiler/rustc_query_system/src/error.rs
Normal 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;
|
@ -5,6 +5,8 @@
|
||||
#![feature(min_specialization)]
|
||||
#![feature(extern_types)]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
// #![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
@ -15,5 +17,6 @@ extern crate rustc_macros;
|
||||
|
||||
pub mod cache;
|
||||
pub mod dep_graph;
|
||||
mod error;
|
||||
pub mod ich;
|
||||
pub mod query;
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::error::CycleStack;
|
||||
use crate::query::plumbing::CycleError;
|
||||
use crate::query::{QueryContext, QueryStackFrame};
|
||||
use rustc_hir::def::DefKind;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::{
|
||||
struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level,
|
||||
};
|
||||
use rustc_session::Session;
|
||||
use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed, Handler, Level};
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_session::{Session, SessionDiagnostic};
|
||||
use rustc_span::Span;
|
||||
|
||||
use std::hash::Hash;
|
||||
@ -536,46 +535,44 @@ pub(crate) fn report_cycle<'a>(
|
||||
assert!(!stack.is_empty());
|
||||
|
||||
let span = stack[0].query.default_span(stack[1 % stack.len()].span);
|
||||
let mut err =
|
||||
struct_span_err!(sess, span, E0391, "cycle detected when {}", stack[0].query.description);
|
||||
|
||||
let mut cycle_stack = Vec::new();
|
||||
|
||||
use crate::error::StackCount;
|
||||
let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple };
|
||||
|
||||
for i in 1..stack.len() {
|
||||
let query = &stack[i].query;
|
||||
let span = query.default_span(stack[(i + 1) % stack.len()].span);
|
||||
err.span_note(span, &format!("...which requires {}...", query.description));
|
||||
}
|
||||
|
||||
if stack.len() == 1 {
|
||||
err.note(&format!("...which immediately requires {} again", stack[0].query.description));
|
||||
} else {
|
||||
err.note(&format!(
|
||||
"...which again requires {}, completing the cycle",
|
||||
stack[0].query.description
|
||||
));
|
||||
}
|
||||
|
||||
if stack.iter().all(|entry| {
|
||||
entry
|
||||
.query
|
||||
.def_kind
|
||||
.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias | DefKind::TraitAlias))
|
||||
}) {
|
||||
if stack.iter().all(|entry| {
|
||||
entry.query.def_kind.map_or(false, |def_kind| matches!(def_kind, DefKind::TyAlias))
|
||||
}) {
|
||||
err.note("type aliases cannot be recursive");
|
||||
err.help("consider using a struct, enum, or union instead to break the cycle");
|
||||
err.help("see <https://doc.rust-lang.org/reference/types.html#recursive-types> for more information");
|
||||
} else {
|
||||
err.note("trait aliases cannot be recursive");
|
||||
}
|
||||
cycle_stack.push(CycleStack { span, desc: query.description.to_owned() });
|
||||
}
|
||||
|
||||
let mut cycle_usage = None;
|
||||
if let Some((span, query)) = usage {
|
||||
err.span_note(query.default_span(span), &format!("cycle used when {}", query.description));
|
||||
cycle_usage = Some(crate::error::CycleUsage {
|
||||
span: query.default_span(span),
|
||||
usage: query.description,
|
||||
});
|
||||
}
|
||||
|
||||
err
|
||||
let alias = if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TyAlias)) {
|
||||
Some(crate::error::Alias::Ty)
|
||||
} else if stack.iter().all(|entry| entry.query.def_kind == Some(DefKind::TraitAlias)) {
|
||||
Some(crate::error::Alias::Trait)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let cycle_diag = crate::error::Cycle {
|
||||
span,
|
||||
cycle_stack,
|
||||
stack_bottom: stack[0].query.description.to_owned(),
|
||||
alias,
|
||||
cycle_usage: cycle_usage,
|
||||
stack_count,
|
||||
};
|
||||
|
||||
cycle_diag.into_diagnostic(&sess.parse_sess)
|
||||
}
|
||||
|
||||
pub fn print_query_stack<CTX: QueryContext>(
|
||||
|
@ -125,6 +125,6 @@ pub trait QueryContext: HasDepContext {
|
||||
) -> R;
|
||||
|
||||
fn depth_limit_error(&self) {
|
||||
self.dep_context().sess().fatal("queries overflow the depth limit!");
|
||||
self.dep_context().sess().emit_fatal(crate::error::QueryOverflow);
|
||||
}
|
||||
}
|
||||
|
@ -618,16 +618,12 @@ fn incremental_verify_ich_cold(sess: &Session, dep_node: DebugArg<'_>, result: D
|
||||
let old_in_panic = INSIDE_VERIFY_PANIC.with(|in_panic| in_panic.replace(true));
|
||||
|
||||
if old_in_panic {
|
||||
sess.struct_err(
|
||||
"internal compiler error: re-entrant incremental verify failure, suppressing message",
|
||||
)
|
||||
.emit();
|
||||
sess.emit_err(crate::error::Reentrant);
|
||||
} else {
|
||||
sess.struct_err(&format!("internal compiler error: encountered incremental compilation error with {:?}", dep_node))
|
||||
.help(&format!("This is a known issue with the compiler. Run {} to allow your project to compile", run_cmd))
|
||||
.note("Please follow the instructions below to create a bug report with the provided information")
|
||||
.note("See <https://github.com/rust-lang/rust/issues/84970> for more information")
|
||||
.emit();
|
||||
sess.emit_err(crate::error::IncrementCompilation {
|
||||
run_cmd,
|
||||
dep_node: format!("{:?}", dep_node),
|
||||
});
|
||||
panic!("Found unstable fingerprints for {:?}: {:?}", dep_node, result);
|
||||
}
|
||||
|
||||
|
@ -1792,7 +1792,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut suggestable_variants = variants
|
||||
let suggestable_variants = variants
|
||||
.iter()
|
||||
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
|
||||
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
|
||||
@ -1802,8 +1802,9 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
CtorKind::Fictive => format!("({} {{}})", variant),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let no_suggestable_variant = suggestable_variants.is_empty();
|
||||
|
||||
if !suggestable_variants.is_empty() {
|
||||
if !no_suggestable_variant {
|
||||
let msg = if suggestable_variants.len() == 1 {
|
||||
"you might have meant to use the following enum variant"
|
||||
} else {
|
||||
@ -1813,7 +1814,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
err.span_suggestions(
|
||||
span,
|
||||
msg,
|
||||
suggestable_variants.drain(..),
|
||||
suggestable_variants.into_iter(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
@ -1830,15 +1831,15 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if !suggestable_variants_with_placeholders.is_empty() {
|
||||
let msg = match (
|
||||
suggestable_variants.is_empty(),
|
||||
suggestable_variants_with_placeholders.len(),
|
||||
) {
|
||||
(true, 1) => "the following enum variant is available",
|
||||
(true, _) => "the following enum variants are available",
|
||||
(false, 1) => "alternatively, the following enum variant is available",
|
||||
(false, _) => "alternatively, the following enum variants are also available",
|
||||
};
|
||||
let msg =
|
||||
match (no_suggestable_variant, suggestable_variants_with_placeholders.len()) {
|
||||
(true, 1) => "the following enum variant is available",
|
||||
(true, _) => "the following enum variants are available",
|
||||
(false, 1) => "alternatively, the following enum variant is available",
|
||||
(false, _) => {
|
||||
"alternatively, the following enum variants are also available"
|
||||
}
|
||||
};
|
||||
|
||||
err.span_suggestions(
|
||||
span,
|
||||
|
@ -501,7 +501,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||
|
||||
if !errors_buffer.is_empty() {
|
||||
errors_buffer.sort_by_key(|diag| diag.span.primary_span());
|
||||
for mut diag in errors_buffer.drain(..) {
|
||||
for mut diag in errors_buffer {
|
||||
self.tcx().sess.diagnostic().emit_diagnostic(&mut diag);
|
||||
}
|
||||
}
|
||||
|
@ -523,6 +523,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||
if self.not_enough_args_provided() {
|
||||
self.suggest_adding_args(err);
|
||||
} else if self.too_many_args_provided() {
|
||||
self.suggest_moving_args_from_assoc_fn_to_trait(err);
|
||||
self.suggest_removing_args_or_generics(err);
|
||||
} else {
|
||||
unreachable!();
|
||||
@ -653,6 +654,123 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggests moving redundant argument(s) of an associate function to the
|
||||
/// trait it belongs to.
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
|
||||
/// ```
|
||||
fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
|
||||
let trait_ = match self.tcx.trait_of_item(self.def_id) {
|
||||
Some(def_id) => def_id,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// Skip suggestion when the associated function is itself generic, it is unclear
|
||||
// how to split the provided parameters between those to suggest to the trait and
|
||||
// those to remain on the associated type.
|
||||
let num_assoc_fn_expected_args =
|
||||
self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
|
||||
if num_assoc_fn_expected_args > 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let num_assoc_fn_excess_args =
|
||||
self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
|
||||
|
||||
let trait_generics = self.tcx.generics_of(trait_);
|
||||
let num_trait_generics_except_self =
|
||||
trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
|
||||
|
||||
let msg = format!(
|
||||
"consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
|
||||
these = pluralize!("this", num_assoc_fn_excess_args),
|
||||
s = pluralize!(num_assoc_fn_excess_args),
|
||||
name = self.tcx.item_name(trait_),
|
||||
num = num_trait_generics_except_self,
|
||||
);
|
||||
|
||||
if let Some(hir_id) = self.path_segment.hir_id
|
||||
&& let Some(parent_node) = self.tcx.hir().find_parent_node(hir_id)
|
||||
&& let Some(parent_node) = self.tcx.hir().find(parent_node)
|
||||
&& let hir::Node::Expr(expr) = parent_node {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Path(ref qpath) => {
|
||||
self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
|
||||
err,
|
||||
qpath,
|
||||
msg,
|
||||
num_assoc_fn_excess_args,
|
||||
num_trait_generics_except_self
|
||||
)
|
||||
},
|
||||
hir::ExprKind::MethodCall(..) => {
|
||||
self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
|
||||
err,
|
||||
trait_,
|
||||
expr,
|
||||
msg,
|
||||
num_assoc_fn_excess_args,
|
||||
num_trait_generics_except_self
|
||||
)
|
||||
},
|
||||
_ => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
qpath: &'tcx hir::QPath<'tcx>,
|
||||
msg: String,
|
||||
num_assoc_fn_excess_args: usize,
|
||||
num_trait_generics_except_self: usize,
|
||||
) {
|
||||
if let hir::QPath::Resolved(_, path) = qpath
|
||||
&& let Some(trait_path_segment) = path.segments.get(0) {
|
||||
let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
|
||||
|
||||
if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
|
||||
if let Some(span) = self.gen_args.span_ext()
|
||||
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
|
||||
let sugg = vec![
|
||||
(self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
|
||||
(span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
|
||||
];
|
||||
|
||||
err.multipart_suggestion(
|
||||
msg,
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
trait_: DefId,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
msg: String,
|
||||
num_assoc_fn_excess_args: usize,
|
||||
num_trait_generics_except_self: usize,
|
||||
) {
|
||||
if let hir::ExprKind::MethodCall(_, args, _) = expr.kind {
|
||||
assert_eq!(args.len(), 1);
|
||||
if num_assoc_fn_excess_args == num_trait_generics_except_self {
|
||||
if let Some(gen_args) = self.gen_args.span_ext()
|
||||
&& let Ok(gen_args) = self.tcx.sess.source_map().span_to_snippet(gen_args)
|
||||
&& let Ok(args) = self.tcx.sess.source_map().span_to_snippet(args[0].span) {
|
||||
let sugg = format!("{}::{}::{}({})", self.tcx.item_name(trait_), gen_args, self.tcx.item_name(self.def_id), args);
|
||||
err.span_suggestion(expr.span, msg, sugg, Applicability::MaybeIncorrect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Suggests to remove redundant argument(s):
|
||||
///
|
||||
/// ```text
|
||||
|
@ -350,10 +350,12 @@ macro_rules! matches {
|
||||
|
||||
/// Unwraps a result or propagates its error.
|
||||
///
|
||||
/// The `?` operator was added to replace `try!` and should be used instead.
|
||||
/// Furthermore, `try` is a reserved word in Rust 2018, so if you must use
|
||||
/// it, you will need to use the [raw-identifier syntax][ris]: `r#try`.
|
||||
/// The [`?` operator][propagating-errors] was added to replace `try!`
|
||||
/// and should be used instead. Furthermore, `try` is a reserved word
|
||||
/// in Rust 2018, so if you must use it, you will need to use the
|
||||
/// [raw-identifier syntax][ris]: `r#try`.
|
||||
///
|
||||
/// [propagating-errors]: https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator
|
||||
/// [ris]: https://doc.rust-lang.org/nightly/rust-by-example/compatibility/raw_identifiers.html
|
||||
///
|
||||
/// `try!` matches the given [`Result`]. In case of the `Ok` variant, the
|
||||
|
@ -3,7 +3,7 @@ use crate::os::windows::prelude::*;
|
||||
use crate::ffi::OsString;
|
||||
use crate::fmt;
|
||||
use crate::io::{self, BorrowedCursor, Error, IoSlice, IoSliceMut, SeekFrom};
|
||||
use crate::mem;
|
||||
use crate::mem::{self, MaybeUninit};
|
||||
use crate::os::windows::io::{AsHandle, BorrowedHandle};
|
||||
use crate::path::{Path, PathBuf};
|
||||
use crate::ptr;
|
||||
@ -326,7 +326,8 @@ impl File {
|
||||
cvt(c::GetFileInformationByHandle(self.handle.as_raw_handle(), &mut info))?;
|
||||
let mut reparse_tag = 0;
|
||||
if info.dwFileAttributes & c::FILE_ATTRIBUTE_REPARSE_POINT != 0 {
|
||||
let mut b = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let mut b =
|
||||
Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
if let Ok((_, buf)) = self.reparse_point(&mut b) {
|
||||
reparse_tag = (*buf).ReparseTag;
|
||||
}
|
||||
@ -389,7 +390,8 @@ impl File {
|
||||
attr.file_size = info.AllocationSize as u64;
|
||||
attr.number_of_links = Some(info.NumberOfLinks);
|
||||
if attr.file_type().is_reparse_point() {
|
||||
let mut b = Align8([0; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let mut b =
|
||||
Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
if let Ok((_, buf)) = self.reparse_point(&mut b) {
|
||||
attr.reparse_tag = (*buf).ReparseTag;
|
||||
}
|
||||
@ -463,7 +465,7 @@ impl File {
|
||||
// avoid narrowing provenance to the actual `REPARSE_DATA_BUFFER`.
|
||||
fn reparse_point(
|
||||
&self,
|
||||
space: &mut Align8<[u8]>,
|
||||
space: &mut Align8<[MaybeUninit<u8>]>,
|
||||
) -> io::Result<(c::DWORD, *const c::REPARSE_DATA_BUFFER)> {
|
||||
unsafe {
|
||||
let mut bytes = 0;
|
||||
@ -488,7 +490,7 @@ impl File {
|
||||
}
|
||||
|
||||
fn readlink(&self) -> io::Result<PathBuf> {
|
||||
let mut space = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let mut space = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let (_bytes, buf) = self.reparse_point(&mut space)?;
|
||||
unsafe {
|
||||
let (path_buffer, subst_off, subst_len, relative) = match (*buf).ReparseTag {
|
||||
@ -658,12 +660,16 @@ impl File {
|
||||
|
||||
/// A buffer for holding directory entries.
|
||||
struct DirBuff {
|
||||
buffer: Box<Align8<[u8; Self::BUFFER_SIZE]>>,
|
||||
buffer: Box<Align8<[MaybeUninit<u8>; Self::BUFFER_SIZE]>>,
|
||||
}
|
||||
impl DirBuff {
|
||||
const BUFFER_SIZE: usize = 1024;
|
||||
fn new() -> Self {
|
||||
Self { buffer: Box::new(Align8([0u8; Self::BUFFER_SIZE])) }
|
||||
Self {
|
||||
// Safety: `Align8<[MaybeUninit<u8>; N]>` does not need
|
||||
// initialization.
|
||||
buffer: unsafe { Box::new_uninit().assume_init() },
|
||||
}
|
||||
}
|
||||
fn capacity(&self) -> usize {
|
||||
self.buffer.0.len()
|
||||
@ -676,8 +682,8 @@ impl DirBuff {
|
||||
DirBuffIter::new(self)
|
||||
}
|
||||
}
|
||||
impl AsRef<[u8]> for DirBuff {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
impl AsRef<[MaybeUninit<u8>]> for DirBuff {
|
||||
fn as_ref(&self) -> &[MaybeUninit<u8>] {
|
||||
&self.buffer.0
|
||||
}
|
||||
}
|
||||
@ -686,7 +692,7 @@ impl AsRef<[u8]> for DirBuff {
|
||||
///
|
||||
/// Currently only returns file names (UTF-16 encoded).
|
||||
struct DirBuffIter<'a> {
|
||||
buffer: Option<&'a [u8]>,
|
||||
buffer: Option<&'a [MaybeUninit<u8>]>,
|
||||
cursor: usize,
|
||||
}
|
||||
impl<'a> DirBuffIter<'a> {
|
||||
@ -701,9 +707,13 @@ impl<'a> Iterator for DirBuffIter<'a> {
|
||||
let buffer = &self.buffer?[self.cursor..];
|
||||
|
||||
// Get the name and next entry from the buffer.
|
||||
// SAFETY: The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the
|
||||
// last field (the file name) is unsized. So an offset has to be
|
||||
// used to get the file name slice.
|
||||
// SAFETY:
|
||||
// - The buffer contains a `FILE_ID_BOTH_DIR_INFO` struct but the last
|
||||
// field (the file name) is unsized. So an offset has to be used to
|
||||
// get the file name slice.
|
||||
// - The OS has guaranteed initialization of the fields of
|
||||
// `FILE_ID_BOTH_DIR_INFO` and the trailing filename (for at least
|
||||
// `FileNameLength` bytes)
|
||||
let (name, is_directory, next_entry) = unsafe {
|
||||
let info = buffer.as_ptr().cast::<c::FILE_ID_BOTH_DIR_INFO>();
|
||||
// Guaranteed to be aligned in documentation for
|
||||
@ -1349,7 +1359,7 @@ fn symlink_junction_inner(original: &Path, junction: &Path) -> io::Result<()> {
|
||||
let h = f.as_inner().as_raw_handle();
|
||||
|
||||
unsafe {
|
||||
let mut data = Align8([0u8; c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let mut data = Align8([MaybeUninit::<u8>::uninit(); c::MAXIMUM_REPARSE_DATA_BUFFER_SIZE]);
|
||||
let data_ptr = data.0.as_mut_ptr();
|
||||
let db = data_ptr.cast::<c::REPARSE_MOUNTPOINT_DATA_BUFFER>();
|
||||
let buf = ptr::addr_of_mut!((*db).ReparseTarget).cast::<c::WCHAR>();
|
||||
|
@ -30,7 +30,7 @@ is 8-bytes large as well as pointers. The tradeoff, though, is that the maximum
|
||||
memory size is now the full 64-bit address space instead of the 4GB as limited
|
||||
by the 32-bit address space for `wasm32-unknown-unknown`.
|
||||
|
||||
This target is not a stable target. The [memory64] WebAssembly proposal is stil
|
||||
This target is not a stable target. The [memory64] WebAssembly proposal is still
|
||||
in-progress and not standardized. This means that there are not many engines
|
||||
which implement the `memory64` feature and if they do they're likely behind a
|
||||
flag, for example:
|
||||
|
@ -1,6 +1,7 @@
|
||||
// compile-flags: -Z unstable-options
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(rustc_private)]
|
||||
#![deny(rustc::untranslatable_diagnostic)]
|
||||
#![deny(rustc::diagnostic_outside_of_impl)]
|
||||
@ -71,3 +72,10 @@ pub fn make_diagnostics<'a>(sess: &'a ParseSess) {
|
||||
//~^ ERROR diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
|
||||
//~^^ ERROR diagnostics should be created using translatable messages
|
||||
}
|
||||
|
||||
// Check that `rustc_lint_diagnostics`-annotated functions aren't themselves linted.
|
||||
|
||||
#[rustc_lint_diagnostics]
|
||||
pub fn skipped_because_of_annotation<'a>(sess: &'a ParseSess) {
|
||||
let _diag = sess.struct_err("untranslatable diagnostic"); // okay!
|
||||
}
|
||||
|
@ -1,41 +1,41 @@
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:36:14
|
||||
--> $DIR/diagnostics.rs:37:14
|
||||
|
|
||||
LL | sess.struct_err("untranslatable diagnostic")
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/diagnostics.rs:5:9
|
||||
--> $DIR/diagnostics.rs:6:9
|
||||
|
|
||||
LL | #![deny(rustc::untranslatable_diagnostic)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:53:14
|
||||
--> $DIR/diagnostics.rs:54:14
|
||||
|
|
||||
LL | diag.note("untranslatable diagnostic");
|
||||
| ^^^^
|
||||
|
||||
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
|
||||
--> $DIR/diagnostics.rs:67:22
|
||||
--> $DIR/diagnostics.rs:68:22
|
||||
|
|
||||
LL | let _diag = sess.struct_err(fluent::parser::expect_path);
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/diagnostics.rs:6:9
|
||||
--> $DIR/diagnostics.rs:7:9
|
||||
|
|
||||
LL | #![deny(rustc::diagnostic_outside_of_impl)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: diagnostics should only be created in `SessionDiagnostic`/`AddSubdiagnostic` impls
|
||||
--> $DIR/diagnostics.rs:70:22
|
||||
--> $DIR/diagnostics.rs:71:22
|
||||
|
|
||||
LL | let _diag = sess.struct_err("untranslatable diagnostic");
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: diagnostics should be created using translatable messages
|
||||
--> $DIR/diagnostics.rs:70:22
|
||||
--> $DIR/diagnostics.rs:71:22
|
||||
|
|
||||
LL | let _diag = sess.struct_err("untranslatable diagnostic");
|
||||
| ^^^^^^^^^^
|
||||
|
@ -167,8 +167,8 @@ enum P {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum Q {
|
||||
#[bar]
|
||||
//~^ ERROR `#[bar]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -179,8 +179,8 @@ enum Q {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum R {
|
||||
#[bar = "..."]
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -191,8 +191,8 @@ enum R {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum S {
|
||||
#[bar = 4]
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar = ...]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -203,8 +203,8 @@ enum S {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum T {
|
||||
#[bar("...")]
|
||||
//~^ ERROR `#[bar(...)]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
//~^ ERROR `#[bar("...")]` is not a valid attribute
|
||||
//~^^ ERROR cannot find attribute `bar` in this scope
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -215,7 +215,7 @@ enum T {
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
enum U {
|
||||
#[label(code = "...")]
|
||||
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
|
||||
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
|
||||
A {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -232,7 +232,7 @@ enum V {
|
||||
var: String,
|
||||
},
|
||||
B {
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
var: String,
|
||||
@ -307,16 +307,6 @@ union AC {
|
||||
b: u64
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[label(parser::add_paren)]
|
||||
//~^ NOTE previously specified here
|
||||
#[label(parser::add_paren)]
|
||||
//~^ ERROR specified multiple times
|
||||
struct AD {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[label(parser::add_paren, parser::add_paren)]
|
||||
//~^ ERROR `#[label(parser::add_paren)]` is not a valid attribute
|
||||
@ -329,16 +319,16 @@ struct AE {
|
||||
#[label(parser::add_paren)]
|
||||
struct AF {
|
||||
#[primary_span]
|
||||
//~^ NOTE previously specified here
|
||||
//~^ NOTE previously specified here
|
||||
span_a: Span,
|
||||
#[primary_span]
|
||||
//~^ ERROR specified multiple times
|
||||
//~^ ERROR specified multiple times
|
||||
span_b: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
struct AG {
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
//~^ ERROR subdiagnostic kind not specified
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
}
|
||||
@ -390,25 +380,27 @@ struct AK {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[applicability]
|
||||
//~^ NOTE previously specified here
|
||||
//~^ NOTE previously specified here
|
||||
applicability_a: Applicability,
|
||||
#[applicability]
|
||||
//~^ ERROR specified multiple times
|
||||
//~^ ERROR specified multiple times
|
||||
applicability_b: Applicability,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `applicability`
|
||||
struct AL {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
#[applicability]
|
||||
//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
|
||||
//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
|
||||
applicability: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `applicability`
|
||||
struct AM {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
@ -444,7 +436,8 @@ struct AQ;
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `#[primary_span]` field
|
||||
//~^ ERROR suggestion without `applicability`
|
||||
//~^^ ERROR suggestion without `#[primary_span]` field
|
||||
struct AR {
|
||||
var: String,
|
||||
}
|
||||
@ -514,120 +507,3 @@ struct AZ {
|
||||
#[primary_span]
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[suggestion(parser::add_paren, code = "...")]
|
||||
//~^ ERROR suggestion without `#[primary_span]` field
|
||||
struct BA {
|
||||
#[suggestion_part]
|
||||
//~^ ERROR `#[suggestion_part]` is not a valid attribute
|
||||
span: Span,
|
||||
#[suggestion_part(code = "...")]
|
||||
//~^ ERROR `#[suggestion_part(...)]` is not a valid attribute
|
||||
span2: Span,
|
||||
#[applicability]
|
||||
applicability: Applicability,
|
||||
var: String,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
|
||||
//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute
|
||||
struct BBa {
|
||||
var: String,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BBb {
|
||||
#[suggestion_part]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span1: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BBc {
|
||||
#[suggestion_part()]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span1: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren)]
|
||||
//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
struct BC {
|
||||
#[primary_span]
|
||||
//~^ ERROR `#[primary_span]` is not a valid attribute
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren)]
|
||||
struct BD {
|
||||
#[suggestion_part]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span1: Span,
|
||||
#[suggestion_part()]
|
||||
//~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
span2: Span,
|
||||
#[suggestion_part(foo = "bar")]
|
||||
//~^ ERROR `#[suggestion_part(foo = ...)]` is not a valid attribute
|
||||
span4: Span,
|
||||
#[suggestion_part(code = "...")]
|
||||
//~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
s1: String,
|
||||
#[suggestion_part()]
|
||||
//~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
s2: String,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BE {
|
||||
#[suggestion_part(code = "...", code = ",,,")]
|
||||
//~^ ERROR specified multiple times
|
||||
//~| NOTE previously specified here
|
||||
span: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BF {
|
||||
#[suggestion_part(code = "(")]
|
||||
first: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
second: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren)]
|
||||
struct BG {
|
||||
#[applicability]
|
||||
appl: Applicability,
|
||||
#[suggestion_part(code = "(")]
|
||||
first: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
second: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
//~^ NOTE previously specified here
|
||||
struct BH {
|
||||
#[applicability]
|
||||
//~^ ERROR specified multiple times
|
||||
appl: Applicability,
|
||||
#[suggestion_part(code = "(")]
|
||||
first: Span,
|
||||
#[suggestion_part(code = ")")]
|
||||
second: Span,
|
||||
}
|
||||
|
||||
#[derive(SessionSubdiagnostic)]
|
||||
#[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
struct BI {
|
||||
#[suggestion_part(code = "")]
|
||||
spans: Vec<Span>,
|
||||
}
|
||||
|
@ -65,16 +65,16 @@ LL | #[label()]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `code` is not a valid nested attribute of a `label` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:137:28
|
||||
--> $DIR/subdiagnostic-derive.rs:137:1
|
||||
|
|
||||
LL | #[label(parser::add_paren, code = "...")]
|
||||
| ^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `applicability` is not a valid nested attribute of a `label` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:146:28
|
||||
--> $DIR/subdiagnostic-derive.rs:146:1
|
||||
|
|
||||
LL | #[label(parser::add_paren, applicability = "machine-applicable")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unsupported type attribute for subdiagnostic enum
|
||||
--> $DIR/subdiagnostic-derive.rs:155:1
|
||||
@ -100,11 +100,13 @@ error: `#[bar = ...]` is not a valid attribute
|
||||
LL | #[bar = 4]
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: `#[bar(...)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:205:5
|
||||
error: `#[bar("...")]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:205:11
|
||||
|
|
||||
LL | #[bar("...")]
|
||||
| ^^^^^^^^^^^^^
|
||||
| ^^^^^
|
||||
|
|
||||
= help: first argument of the attribute should be the diagnostic slug
|
||||
|
||||
error: diagnostic slug must be first argument of a `#[label(...)]` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:217:5
|
||||
@ -161,8 +163,6 @@ error: `#[bar(...)]` is not a valid attribute
|
||||
|
|
||||
LL | #[bar("...")]
|
||||
| ^^^^^^^^^^^^^
|
||||
|
|
||||
= help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
|
||||
|
||||
error: unexpected unsupported untagged union
|
||||
--> $DIR/subdiagnostic-derive.rs:304:1
|
||||
@ -174,20 +174,8 @@ LL | | b: u64
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:313:1
|
||||
|
|
||||
LL | #[label(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:311:1
|
||||
|
|
||||
LL | #[label(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[label(parser::add_paren)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:321:28
|
||||
--> $DIR/subdiagnostic-derive.rs:311:28
|
||||
|
|
||||
LL | #[label(parser::add_paren, parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
@ -195,226 +183,134 @@ LL | #[label(parser::add_paren, parser::add_paren)]
|
||||
= help: a diagnostic slug must be the first argument to the attribute
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:334:5
|
||||
--> $DIR/subdiagnostic-derive.rs:324:5
|
||||
|
|
||||
LL | #[primary_span]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:331:5
|
||||
--> $DIR/subdiagnostic-derive.rs:321:5
|
||||
|
|
||||
LL | #[primary_span]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: subdiagnostic kind not specified
|
||||
--> $DIR/subdiagnostic-derive.rs:340:8
|
||||
--> $DIR/subdiagnostic-derive.rs:330:8
|
||||
|
|
||||
LL | struct AG {
|
||||
| ^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:377:47
|
||||
--> $DIR/subdiagnostic-derive.rs:367:47
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:377:33
|
||||
--> $DIR/subdiagnostic-derive.rs:367:33
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code = "...", code = "...")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:395:5
|
||||
--> $DIR/subdiagnostic-derive.rs:385:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:392:5
|
||||
--> $DIR/subdiagnostic-derive.rs:382:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:405:5
|
||||
--> $DIR/subdiagnostic-derive.rs:396:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: suggestion without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:418:1
|
||||
error: suggestion without `applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:391:1
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | | struct AL {
|
||||
LL | | #[primary_span]
|
||||
... |
|
||||
LL | | applicability: Span,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: suggestion without `applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:402:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | | struct AM {
|
||||
LL | | #[primary_span]
|
||||
LL | | span: Span,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: suggestion without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:410:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren)]
|
||||
LL | |
|
||||
LL | | struct AN {
|
||||
LL | | #[primary_span]
|
||||
... |
|
||||
LL | | applicability: Applicability,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: invalid applicability
|
||||
--> $DIR/subdiagnostic-derive.rs:428:46
|
||||
--> $DIR/subdiagnostic-derive.rs:420:46
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: suggestion without `#[primary_span]` field
|
||||
--> $DIR/subdiagnostic-derive.rs:446:1
|
||||
error: suggestion without `applicability`
|
||||
--> $DIR/subdiagnostic-derive.rs:438:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | struct AR {
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: suggestion without `#[primary_span]` field
|
||||
--> $DIR/subdiagnostic-derive.rs:438:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | struct AR {
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: unsupported type attribute for subdiagnostic enum
|
||||
--> $DIR/subdiagnostic-derive.rs:460:1
|
||||
--> $DIR/subdiagnostic-derive.rs:453:1
|
||||
|
|
||||
LL | #[label]
|
||||
| ^^^^^^^^
|
||||
|
||||
error: `var` doesn't refer to a field on this type
|
||||
--> $DIR/subdiagnostic-derive.rs:480:39
|
||||
--> $DIR/subdiagnostic-derive.rs:473:39
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `var` doesn't refer to a field on this type
|
||||
--> $DIR/subdiagnostic-derive.rs:499:43
|
||||
--> $DIR/subdiagnostic-derive.rs:492:43
|
||||
|
|
||||
LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")]
|
||||
| ^^^^^^^
|
||||
|
||||
error: `#[suggestion_part]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:522:5
|
||||
|
|
||||
LL | #[suggestion_part]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead
|
||||
|
||||
error: `#[suggestion_part(...)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:525:5
|
||||
|
|
||||
LL | #[suggestion_part(code = "...")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: `#[suggestion_part(...)]` is only valid in multipart suggestions
|
||||
|
||||
error: suggestion without `#[primary_span]` field
|
||||
--> $DIR/subdiagnostic-derive.rs:519:1
|
||||
|
|
||||
LL | / #[suggestion(parser::add_paren, code = "...")]
|
||||
LL | |
|
||||
LL | | struct BA {
|
||||
LL | | #[suggestion_part]
|
||||
... |
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:534:43
|
||||
|
|
||||
LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
--> $DIR/subdiagnostic-derive.rs:534:1
|
||||
|
|
||||
LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")]
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | struct BBa {
|
||||
LL | | var: String,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:544:5
|
||||
|
|
||||
LL | #[suggestion_part]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:552:5
|
||||
|
|
||||
LL | #[suggestion_part()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[primary_span]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:561:5
|
||||
|
|
||||
LL | #[primary_span]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
|
||||
= help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]`
|
||||
|
||||
error: multipart suggestion without any `#[suggestion_part(...)]` fields
|
||||
--> $DIR/subdiagnostic-derive.rs:558:1
|
||||
|
|
||||
LL | / #[multipart_suggestion(parser::add_paren)]
|
||||
LL | |
|
||||
LL | | struct BC {
|
||||
LL | | #[primary_span]
|
||||
LL | |
|
||||
LL | | span: Span,
|
||||
LL | | }
|
||||
| |_^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:569:5
|
||||
|
|
||||
LL | #[suggestion_part]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[suggestion_part(...)]` attribute without `code = "..."`
|
||||
--> $DIR/subdiagnostic-derive.rs:572:5
|
||||
|
|
||||
LL | #[suggestion_part()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: `#[suggestion_part(foo = ...)]` is not a valid attribute
|
||||
--> $DIR/subdiagnostic-derive.rs:575:23
|
||||
|
|
||||
LL | #[suggestion_part(foo = "bar")]
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: `code` is the only valid nested attribute
|
||||
|
||||
error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
--> $DIR/subdiagnostic-derive.rs:578:5
|
||||
|
|
||||
LL | #[suggestion_part(code = "...")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
|
||||
--> $DIR/subdiagnostic-derive.rs:581:5
|
||||
|
|
||||
LL | #[suggestion_part()]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:589:37
|
||||
|
|
||||
LL | #[suggestion_part(code = "...", code = ",,,")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:589:23
|
||||
|
|
||||
LL | #[suggestion_part(code = "...", code = ",,,")]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: specified multiple times
|
||||
--> $DIR/subdiagnostic-derive.rs:619:5
|
||||
|
|
||||
LL | #[applicability]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: previously specified here
|
||||
--> $DIR/subdiagnostic-derive.rs:616:43
|
||||
|
|
||||
LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: cannot find attribute `foo` in this scope
|
||||
--> $DIR/subdiagnostic-derive.rs:63:3
|
||||
|
|
||||
@ -475,6 +371,6 @@ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent`
|
||||
LL | #[label(slug)]
|
||||
| ^^^^ not found in `rustc_errors::fluent`
|
||||
|
||||
error: aborting due to 64 previous errors
|
||||
error: aborting due to 50 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0425`.
|
||||
|
@ -2,15 +2,22 @@ error[E0107]: this associated function takes 0 generic arguments but 1 generic a
|
||||
--> $DIR/invalid-const-arg-for-type-param.rs:6:23
|
||||
|
|
||||
LL | let _: u32 = 5i32.try_into::<32>().unwrap();
|
||||
| ^^^^^^^^------ help: remove these generics
|
||||
| |
|
||||
| expected 0 generic arguments
|
||||
| ^^^^^^^^ expected 0 generic arguments
|
||||
|
|
||||
note: associated function defined here, with 0 generic parameters
|
||||
--> $SRC_DIR/core/src/convert/mod.rs:LL:COL
|
||||
|
|
||||
LL | fn try_into(self) -> Result<T, Self::Error>;
|
||||
| ^^^^^^^^
|
||||
help: consider moving this generic argument to the `TryInto` trait, which takes up to 1 argument
|
||||
|
|
||||
LL | let _: u32 = TryInto::<32>::try_into(5i32).unwrap();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: remove these generics
|
||||
|
|
||||
LL - let _: u32 = 5i32.try_into::<32>().unwrap();
|
||||
LL + let _: u32 = 5i32.try_into().unwrap();
|
||||
|
|
||||
|
||||
error[E0599]: no method named `f` found for struct `S` in the current scope
|
||||
--> $DIR/invalid-const-arg-for-type-param.rs:9:7
|
||||
|
@ -22,6 +22,10 @@ enum D {
|
||||
Unit,
|
||||
}
|
||||
|
||||
enum E {
|
||||
TupleWithFields(()),
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Only variants without fields are suggested (and others mentioned in a note) where an enum
|
||||
// is used rather than a variant.
|
||||
@ -34,6 +38,8 @@ fn main() {
|
||||
//~^ ERROR expected value, found enum `C`
|
||||
D.foo();
|
||||
//~^ ERROR expected value, found enum `D`
|
||||
E.foo();
|
||||
//~^ ERROR expected value, found enum `E`
|
||||
|
||||
// Only tuple variants are suggested in calls or tuple struct pattern matching.
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
error[E0423]: expected value, found enum `A`
|
||||
--> $DIR/issue-73427.rs:29:5
|
||||
--> $DIR/issue-73427.rs:33:5
|
||||
|
|
||||
LL | A.foo();
|
||||
| ^
|
||||
@ -23,7 +23,7 @@ LL | (A::Tuple()).foo();
|
||||
| ~~~~~~~~~~~~
|
||||
LL | A::Unit.foo();
|
||||
| ~~~~~~~
|
||||
help: the following enum variants are available
|
||||
help: alternatively, the following enum variants are also available
|
||||
|
|
||||
LL | (A::StructWithFields { /* fields */ }).foo();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -31,7 +31,7 @@ LL | (A::TupleWithFields(/* fields */)).foo();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0423]: expected value, found enum `B`
|
||||
--> $DIR/issue-73427.rs:31:5
|
||||
--> $DIR/issue-73427.rs:35:5
|
||||
|
|
||||
LL | B.foo();
|
||||
| ^
|
||||
@ -52,7 +52,7 @@ LL | (B::TupleWithFields(/* fields */)).foo();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0423]: expected value, found enum `C`
|
||||
--> $DIR/issue-73427.rs:33:5
|
||||
--> $DIR/issue-73427.rs:37:5
|
||||
|
|
||||
LL | C.foo();
|
||||
| ^
|
||||
@ -70,7 +70,7 @@ help: you might have meant to use the following enum variant
|
||||
|
|
||||
LL | C::Unit.foo();
|
||||
| ~~~~~~~
|
||||
help: the following enum variants are available
|
||||
help: alternatively, the following enum variants are also available
|
||||
|
|
||||
LL | (C::StructWithFields { /* fields */ }).foo();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -78,7 +78,7 @@ LL | (C::TupleWithFields(/* fields */)).foo();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0423]: expected value, found enum `D`
|
||||
--> $DIR/issue-73427.rs:35:5
|
||||
--> $DIR/issue-73427.rs:39:5
|
||||
|
|
||||
LL | D.foo();
|
||||
| ^
|
||||
@ -95,13 +95,37 @@ help: you might have meant to use the following enum variant
|
||||
|
|
||||
LL | D::Unit.foo();
|
||||
| ~~~~~~~
|
||||
help: the following enum variant is available
|
||||
help: alternatively, the following enum variant is available
|
||||
|
|
||||
LL | (D::TupleWithFields(/* fields */)).foo();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0423]: expected value, found enum `E`
|
||||
--> $DIR/issue-73427.rs:41:5
|
||||
|
|
||||
LL | E.foo();
|
||||
| ^
|
||||
|
|
||||
note: the enum is defined here
|
||||
--> $DIR/issue-73427.rs:25:1
|
||||
|
|
||||
LL | / enum E {
|
||||
LL | | TupleWithFields(()),
|
||||
LL | | }
|
||||
| |_^
|
||||
help: the following enum variant is available
|
||||
|
|
||||
LL | (E::TupleWithFields(/* fields */)).foo();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
help: consider importing one of these items instead
|
||||
|
|
||||
LL | use std::f32::consts::E;
|
||||
|
|
||||
LL | use std::f64::consts::E;
|
||||
|
|
||||
|
||||
error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
|
||||
--> $DIR/issue-73427.rs:40:13
|
||||
--> $DIR/issue-73427.rs:46:13
|
||||
|
|
||||
LL | let x = A(3);
|
||||
| ^
|
||||
@ -126,7 +150,7 @@ LL | let x = A::TupleWithFields(3);
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0532]: expected tuple struct or tuple variant, found enum `A`
|
||||
--> $DIR/issue-73427.rs:42:12
|
||||
--> $DIR/issue-73427.rs:48:12
|
||||
|
|
||||
LL | if let A(3) = x { }
|
||||
| ^
|
||||
@ -150,7 +174,7 @@ LL | if let A::Tuple(3) = x { }
|
||||
LL | if let A::TupleWithFields(3) = x { }
|
||||
| ~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0423, E0532.
|
||||
For more information about an error, try `rustc --explain E0423`.
|
||||
|
@ -19,7 +19,7 @@ help: you might have meant to use the following enum variant
|
||||
|
|
||||
LL | m::Z::Unit;
|
||||
| ~~~~~~~~~~
|
||||
help: the following enum variants are available
|
||||
help: alternatively, the following enum variants are also available
|
||||
|
|
||||
LL | (m::Z::Fn(/* fields */));
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -47,7 +47,7 @@ help: you might have meant to use the following enum variant
|
||||
|
|
||||
LL | m::Z::Unit;
|
||||
| ~~~~~~~~~~
|
||||
help: the following enum variants are available
|
||||
help: alternatively, the following enum variants are also available
|
||||
|
|
||||
LL | (m::Z::Fn(/* fields */));
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -89,7 +89,7 @@ help: you might have meant to use the following enum variant
|
||||
|
|
||||
LL | let _: E = E::Unit;
|
||||
| ~~~~~~~
|
||||
help: the following enum variants are available
|
||||
help: alternatively, the following enum variants are also available
|
||||
|
|
||||
LL | let _: E = (E::Fn(/* fields */));
|
||||
| ~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -143,7 +143,7 @@ help: you might have meant to use the following enum variant
|
||||
|
|
||||
LL | let _: E = E::Unit;
|
||||
| ~~~~~~~
|
||||
help: the following enum variants are available
|
||||
help: alternatively, the following enum variants are also available
|
||||
|
|
||||
LL | let _: E = (E::Fn(/* fields */));
|
||||
| ~~~~~~~~~~~~~~~~~~~~~
|
||||
@ -203,7 +203,7 @@ help: you might have meant to use the following enum variant
|
||||
|
|
||||
LL | let _: Z = m::Z::Unit;
|
||||
| ~~~~~~~~~~
|
||||
help: the following enum variants are available
|
||||
help: alternatively, the following enum variants are also available
|
||||
|
|
||||
LL | let _: Z = (m::Z::Fn(/* fields */));
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
35
src/test/ui/suggestions/issue-89064.rs
Normal file
35
src/test/ui/suggestions/issue-89064.rs
Normal 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
|
||||
}
|
82
src/test/ui/suggestions/issue-89064.stderr
Normal file
82
src/test/ui/suggestions/issue-89064.stderr
Normal 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`.
|
4
src/test/ui/unpretty/avoid-crash.rs
Normal file
4
src/test/ui/unpretty/avoid-crash.rs
Normal file
@ -0,0 +1,4 @@
|
||||
// normalize-stderr-test "error `.*`" -> "$$ERROR_MESSAGE"
|
||||
// compile-flags: -o/tmp/ -Zunpretty=ast-tree
|
||||
|
||||
fn main() {}
|
4
src/test/ui/unpretty/avoid-crash.stderr
Normal file
4
src/test/ui/unpretty/avoid-crash.stderr
Normal file
@ -0,0 +1,4 @@
|
||||
error: pretty-print failed to write `/tmp/` due to $ERROR_MESSAGE
|
||||
|
||||
error: aborting due to previous error
|
||||
|
@ -24,6 +24,7 @@ env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
NO_FMT_TEST: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
||||
jobs:
|
||||
base:
|
||||
|
@ -10,6 +10,7 @@ env:
|
||||
RUST_BACKTRACE: 1
|
||||
CARGO_TARGET_DIR: '${{ github.workspace }}/target'
|
||||
NO_FMT_TEST: 1
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
||||
defaults:
|
||||
run:
|
||||
|
@ -6,11 +6,157 @@ document.
|
||||
|
||||
## Unreleased / In Rust Nightly
|
||||
|
||||
[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
|
||||
[d7b5cbf0...master](https://github.com/rust-lang/rust-clippy/compare/d7b5cbf0...master)
|
||||
|
||||
## Rust 1.63
|
||||
|
||||
Current stable, released 2022-08-11
|
||||
|
||||
[7c21f91b...d7b5cbf0](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...d7b5cbf0)
|
||||
|
||||
### New Lints
|
||||
|
||||
* [`borrow_deref_ref`]
|
||||
[#7930](https://github.com/rust-lang/rust-clippy/pull/7930)
|
||||
* [`doc_link_with_quotes`]
|
||||
[#8385](https://github.com/rust-lang/rust-clippy/pull/8385)
|
||||
* [`no_effect_replace`]
|
||||
[#8754](https://github.com/rust-lang/rust-clippy/pull/8754)
|
||||
* [`rc_clone_in_vec_init`]
|
||||
[#8769](https://github.com/rust-lang/rust-clippy/pull/8769)
|
||||
* [`derive_partial_eq_without_eq`]
|
||||
[#8796](https://github.com/rust-lang/rust-clippy/pull/8796)
|
||||
* [`mismatching_type_param_order`]
|
||||
[#8831](https://github.com/rust-lang/rust-clippy/pull/8831)
|
||||
* [`duplicate_mod`] [#8832](https://github.com/rust-lang/rust-clippy/pull/8832)
|
||||
* [`unused_rounding`]
|
||||
[#8866](https://github.com/rust-lang/rust-clippy/pull/8866)
|
||||
* [`get_first`] [#8882](https://github.com/rust-lang/rust-clippy/pull/8882)
|
||||
* [`swap_ptr_to_ref`]
|
||||
[#8916](https://github.com/rust-lang/rust-clippy/pull/8916)
|
||||
* [`almost_complete_letter_range`]
|
||||
[#8918](https://github.com/rust-lang/rust-clippy/pull/8918)
|
||||
* [`needless_parens_on_range_literals`]
|
||||
[#8933](https://github.com/rust-lang/rust-clippy/pull/8933)
|
||||
* [`as_underscore`] [#8934](https://github.com/rust-lang/rust-clippy/pull/8934)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Rename `eval_order_dependence` to [`mixed_read_write_in_expression`], move to
|
||||
`nursery` [#8621](https://github.com/rust-lang/rust-clippy/pull/8621)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`undocumented_unsafe_blocks`]: Now also lints on unsafe trait implementations
|
||||
[#8761](https://github.com/rust-lang/rust-clippy/pull/8761)
|
||||
* [`empty_line_after_outer_attr`]: Now also lints on argumentless macros
|
||||
[#8790](https://github.com/rust-lang/rust-clippy/pull/8790)
|
||||
* [`expect_used`]: Now can be disabled in tests with the `allow-expect-in-tests`
|
||||
option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
|
||||
* [`unwrap_used`]: Now can be disabled in tests with the `allow-unwrap-in-tests`
|
||||
option [#8802](https://github.com/rust-lang/rust-clippy/pull/8802)
|
||||
* [`disallowed_methods`]: Now also lints indirect usages
|
||||
[#8852](https://github.com/rust-lang/rust-clippy/pull/8852)
|
||||
* [`get_last_with_len`]: Now also lints `VecDeque` and any deref to slice
|
||||
[#8862](https://github.com/rust-lang/rust-clippy/pull/8862)
|
||||
* [`manual_range_contains`]: Now also lints on chains of `&&` and `||`
|
||||
[#8884](https://github.com/rust-lang/rust-clippy/pull/8884)
|
||||
* [`rc_clone_in_vec_init`]: Now also lints on `Weak`
|
||||
[#8885](https://github.com/rust-lang/rust-clippy/pull/8885)
|
||||
* [`dbg_macro`]: Introduce `allow-dbg-in-tests` config option
|
||||
[#8897](https://github.com/rust-lang/rust-clippy/pull/8897)
|
||||
* [`use_self`]: Now also lints on `TupleStruct` and `Struct` patterns
|
||||
[#8899](https://github.com/rust-lang/rust-clippy/pull/8899)
|
||||
* [`manual_find_map`] and [`manual_filter_map`]: Now also lints on more complex
|
||||
method chains inside `map`
|
||||
[#8930](https://github.com/rust-lang/rust-clippy/pull/8930)
|
||||
* [`needless_return`]: Now also lints on macro expressions in return statements
|
||||
[#8932](https://github.com/rust-lang/rust-clippy/pull/8932)
|
||||
* [`doc_markdown`]: Users can now indicate, that the `doc-valid-idents` config
|
||||
should extend the default and not replace it
|
||||
[#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
|
||||
* [`disallowed_names`]: Users can now indicate, that the `disallowed-names`
|
||||
config should extend the default and not replace it
|
||||
[#8944](https://github.com/rust-lang/rust-clippy/pull/8944)
|
||||
* [`never_loop`]: Now checks for `continue` in struct expression
|
||||
[#9002](https://github.com/rust-lang/rust-clippy/pull/9002)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`useless_transmute`]: No longer lints on types with erased regions
|
||||
[#8564](https://github.com/rust-lang/rust-clippy/pull/8564)
|
||||
* [`vec_init_then_push`]: No longer lints when further extended
|
||||
[#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
|
||||
* [`cmp_owned`]: No longer lints on `From::from` for `Copy` types
|
||||
[#8807](https://github.com/rust-lang/rust-clippy/pull/8807)
|
||||
* [`redundant_allocation`]: No longer lints on fat pointers that would become
|
||||
thin pointers [#8813](https://github.com/rust-lang/rust-clippy/pull/8813)
|
||||
* [`derive_partial_eq_without_eq`]:
|
||||
* Handle differing predicates applied by `#[derive(PartialEq)]` and
|
||||
`#[derive(Eq)]`
|
||||
[#8869](https://github.com/rust-lang/rust-clippy/pull/8869)
|
||||
* No longer lints on non-public types and better handles generics
|
||||
[#8950](https://github.com/rust-lang/rust-clippy/pull/8950)
|
||||
* [`empty_line_after_outer_attr`]: No longer lints empty lines in inner
|
||||
string values [#8892](https://github.com/rust-lang/rust-clippy/pull/8892)
|
||||
* [`branches_sharing_code`]: No longer lints when using different binding names
|
||||
[#8901](https://github.com/rust-lang/rust-clippy/pull/8901)
|
||||
* [`significant_drop_in_scrutinee`]: No longer lints on Try `?` and `await`
|
||||
desugared expressions [#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
|
||||
* [`checked_conversions`]: No longer lints in `const` contexts
|
||||
[#8907](https://github.com/rust-lang/rust-clippy/pull/8907)
|
||||
* [`iter_overeager_cloned`]: No longer lints on `.cloned().flatten()` when
|
||||
`T::Item` doesn't implement `IntoIterator`
|
||||
[#8960](https://github.com/rust-lang/rust-clippy/pull/8960)
|
||||
|
||||
### Suggestion Fixes/Improvements
|
||||
|
||||
* [`vec_init_then_push`]: Suggest to remove `mut` binding when possible
|
||||
[#8699](https://github.com/rust-lang/rust-clippy/pull/8699)
|
||||
* [`manual_range_contains`]: Fix suggestion for integers with different signs
|
||||
[#8763](https://github.com/rust-lang/rust-clippy/pull/8763)
|
||||
* [`identity_op`]: Add parenthesis to suggestions where required
|
||||
[#8786](https://github.com/rust-lang/rust-clippy/pull/8786)
|
||||
* [`cast_lossless`]: No longer gives wrong suggestion on `usize`/`isize`->`f64`
|
||||
[#8778](https://github.com/rust-lang/rust-clippy/pull/8778)
|
||||
* [`rc_clone_in_vec_init`]: Add suggestion
|
||||
[#8814](https://github.com/rust-lang/rust-clippy/pull/8814)
|
||||
* The "unknown field" error messages for config files now wraps the field names
|
||||
[#8823](https://github.com/rust-lang/rust-clippy/pull/8823)
|
||||
* [`cast_abs_to_unsigned`]: Do not remove cast if it's required
|
||||
[#8876](https://github.com/rust-lang/rust-clippy/pull/8876)
|
||||
* [`significant_drop_in_scrutinee`]: Improve lint message for types that are not
|
||||
references and not trivially clone-able
|
||||
[#8902](https://github.com/rust-lang/rust-clippy/pull/8902)
|
||||
* [`for_loops_over_fallibles`]: Now suggests the correct variant of `iter()`,
|
||||
`iter_mut()` or `into_iter()`
|
||||
[#8941](https://github.com/rust-lang/rust-clippy/pull/8941)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* Fix ICE in [`let_unit_value`] when calling a `static`/`const` callable type
|
||||
[#8835](https://github.com/rust-lang/rust-clippy/pull/8835)
|
||||
* Fix ICEs on callable `static`/`const`s
|
||||
[#8896](https://github.com/rust-lang/rust-clippy/pull/8896)
|
||||
* [`needless_late_init`]
|
||||
[#8912](https://github.com/rust-lang/rust-clippy/pull/8912)
|
||||
* Fix ICE in shadow lints
|
||||
[#8913](https://github.com/rust-lang/rust-clippy/pull/8913)
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
* Clippy has a [Book](https://doc.rust-lang.org/nightly/clippy/) now!
|
||||
[#7359](https://github.com/rust-lang/rust-clippy/pull/7359)
|
||||
* Add a *copy lint name*-button to Clippy's lint list
|
||||
[#8839](https://github.com/rust-lang/rust-clippy/pull/8839)
|
||||
* Display past names of renamed lints on Clippy's lint list
|
||||
[#8843](https://github.com/rust-lang/rust-clippy/pull/8843)
|
||||
* Add the ability to show the lint output in the lint list
|
||||
[#8947](https://github.com/rust-lang/rust-clippy/pull/8947)
|
||||
|
||||
## Rust 1.62
|
||||
|
||||
Current stable, released 2022-06-30
|
||||
Released 2022-06-30
|
||||
|
||||
[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
|
||||
|
||||
@ -3481,6 +3627,7 @@ Released 2018-09-13
|
||||
[`cast_ref_to_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_ref_to_mut
|
||||
[`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
|
||||
[`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
|
||||
[`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
|
||||
[`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
|
||||
[`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
|
||||
[`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
|
||||
@ -3496,6 +3643,7 @@ Released 2018-09-13
|
||||
[`collapsible_else_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_else_if
|
||||
[`collapsible_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_if
|
||||
[`collapsible_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_match
|
||||
[`collapsible_str_replace`]: https://rust-lang.github.io/rust-clippy/master/index.html#collapsible_str_replace
|
||||
[`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
|
||||
[`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
|
||||
[`const_static_lifetime`]: https://rust-lang.github.io/rust-clippy/master/index.html#const_static_lifetime
|
||||
@ -3656,6 +3804,8 @@ Released 2018-09-13
|
||||
[`iter_not_returning_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_not_returning_iterator
|
||||
[`iter_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth
|
||||
[`iter_nth_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_nth_zero
|
||||
[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
|
||||
[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
|
||||
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
|
||||
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
|
||||
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
|
||||
@ -3697,6 +3847,7 @@ Released 2018-09-13
|
||||
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
|
||||
[`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
|
||||
[`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
|
||||
[`manual_string_new`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_string_new
|
||||
[`manual_strip`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_strip
|
||||
[`manual_swap`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_swap
|
||||
[`manual_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or
|
||||
@ -3747,6 +3898,7 @@ Released 2018-09-13
|
||||
[`module_name_repetitions`]: https://rust-lang.github.io/rust-clippy/master/index.html#module_name_repetitions
|
||||
[`modulo_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic
|
||||
[`modulo_one`]: https://rust-lang.github.io/rust-clippy/master/index.html#modulo_one
|
||||
[`multi_assignments`]: https://rust-lang.github.io/rust-clippy/master/index.html#multi_assignments
|
||||
[`multiple_crate_versions`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_crate_versions
|
||||
[`multiple_inherent_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#multiple_inherent_impl
|
||||
[`must_use_candidate`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_candidate
|
||||
@ -3827,6 +3979,7 @@ Released 2018-09-13
|
||||
[`partialeq_to_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_to_none
|
||||
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
|
||||
[`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch
|
||||
[`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters
|
||||
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
|
||||
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
|
||||
[`print_in_format_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_in_format_impl
|
||||
@ -3872,6 +4025,7 @@ Released 2018-09-13
|
||||
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
|
||||
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
|
||||
[`result_expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_expect_used
|
||||
[`result_large_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err
|
||||
[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option
|
||||
[`result_map_unit_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unit_fn
|
||||
[`result_map_unwrap_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_unwrap_or_else
|
||||
@ -3930,6 +4084,7 @@ Released 2018-09-13
|
||||
[`suspicious_op_assign_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_op_assign_impl
|
||||
[`suspicious_operation_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_operation_groupings
|
||||
[`suspicious_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_splitn
|
||||
[`suspicious_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_to_owned
|
||||
[`suspicious_unary_op_formatting`]: https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_unary_op_formatting
|
||||
[`swap_ptr_to_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#swap_ptr_to_ref
|
||||
[`tabs_in_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#tabs_in_doc_comments
|
||||
@ -4002,6 +4157,7 @@ Released 2018-09-13
|
||||
[`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect
|
||||
[`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
|
||||
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
|
||||
[`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
|
||||
[`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
|
||||
[`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
|
||||
[`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
|
||||
|
@ -37,7 +37,7 @@ fn update_reference_file(test_output_entry: &DirEntry, ignore_timestamp: bool) {
|
||||
return;
|
||||
}
|
||||
|
||||
let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
|
||||
let test_output_file = fs::read(test_output_path).expect("Unable to read test output file");
|
||||
let reference_file = fs::read(&reference_file_path).unwrap_or_default();
|
||||
|
||||
if test_output_file != reference_file {
|
||||
|
@ -46,7 +46,7 @@ pub fn run(check: bool, verbose: bool) {
|
||||
// dependency
|
||||
if fs::read_to_string(project_root.join("Cargo.toml"))
|
||||
.expect("Failed to read clippy Cargo.toml")
|
||||
.contains(&"[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||
.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
|
||||
{
|
||||
return Err(CliError::IntellijSetupActive);
|
||||
}
|
||||
@ -193,10 +193,10 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
|
||||
let args = &["--version"];
|
||||
|
||||
if context.verbose {
|
||||
println!("{}", format_command(&program, &dir, args));
|
||||
println!("{}", format_command(program, &dir, args));
|
||||
}
|
||||
|
||||
let output = Command::new(&program).current_dir(&dir).args(args.iter()).output()?;
|
||||
let output = Command::new(program).current_dir(&dir).args(args.iter()).output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(())
|
||||
@ -207,7 +207,7 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
|
||||
Err(CliError::RustfmtNotInstalled)
|
||||
} else {
|
||||
Err(CliError::CommandFailed(
|
||||
format_command(&program, &dir, args),
|
||||
format_command(program, &dir, args),
|
||||
std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
|
||||
))
|
||||
}
|
||||
|
@ -155,7 +155,7 @@ fn to_camel_case(name: &str) -> String {
|
||||
name.split('_')
|
||||
.map(|s| {
|
||||
if s.is_empty() {
|
||||
String::from("")
|
||||
String::new()
|
||||
} else {
|
||||
[&s[0..1].to_uppercase(), &s[1..]].concat()
|
||||
}
|
||||
|
@ -418,7 +418,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
|
||||
.expect("failed to find `impl_lint_pass` terminator");
|
||||
|
||||
impl_lint_pass_end += impl_lint_pass_start;
|
||||
if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
|
||||
if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(lint_name_upper) {
|
||||
let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
|
||||
for c in content[lint_name_end..impl_lint_pass_end].chars() {
|
||||
// Remove trailing whitespace
|
||||
@ -451,7 +451,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
|
||||
}
|
||||
|
||||
let mut content =
|
||||
fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
fs::read_to_string(path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
|
||||
|
||||
eprintln!(
|
||||
"warn: you will have to manually remove any code related to `{}` from `{}`",
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -29,22 +29,17 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(_x: &str) {}
|
||||
///
|
||||
/// let s = &String::new();
|
||||
///
|
||||
/// let a: &String = &* s;
|
||||
/// foo(&*s);
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn foo(_x: &str) {}
|
||||
/// # let s = &String::new();
|
||||
/// let a: &String = s;
|
||||
/// foo(&**s);
|
||||
/// ```
|
||||
#[clippy::version = "1.59.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub BORROW_DEREF_REF,
|
||||
complexity,
|
||||
"deref on an immutable reference returns the same type as itself"
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
25
src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
Normal file
25
src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
37
src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
Normal file
37
src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,3 +1,5 @@
|
||||
mod as_underscore;
|
||||
mod borrow_as_ptr;
|
||||
mod cast_abs_to_unsigned;
|
||||
mod cast_enum_constructor;
|
||||
mod cast_lossless;
|
||||
@ -8,6 +10,7 @@ mod cast_ptr_alignment;
|
||||
mod cast_ref_to_mut;
|
||||
mod cast_sign_loss;
|
||||
mod cast_slice_different_sizes;
|
||||
mod cast_slice_from_raw_parts;
|
||||
mod char_lit_as_u8;
|
||||
mod fn_to_numeric_cast;
|
||||
mod fn_to_numeric_cast_any;
|
||||
@ -16,7 +19,7 @@ mod ptr_as_ptr;
|
||||
mod unnecessary_cast;
|
||||
mod utils;
|
||||
|
||||
use clippy_utils::is_hir_ty_cfg_dependant;
|
||||
use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
@ -506,6 +509,93 @@ declare_clippy_lint! {
|
||||
"casting the result of `abs()` to an unsigned integer can panic"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Check for the usage of `as _` conversion using inferred type.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// The conversion might include lossy conversion and dangerous cast that might go
|
||||
/// undetected due to the type being inferred.
|
||||
///
|
||||
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as _);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// fn foo(n: usize) {}
|
||||
/// let n: u16 = 256;
|
||||
/// foo(n as usize);
|
||||
/// ```
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub AS_UNDERSCORE,
|
||||
restriction,
|
||||
"detects `as _` conversion"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for the usage of `&expr as *const T` or
|
||||
/// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or
|
||||
/// `ptr::addr_of_mut` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This would improve readability and avoid creating a reference
|
||||
/// that points to an uninitialized value or unaligned place.
|
||||
/// Read the `ptr::addr_of` docs for more information.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = &val as *const i32;
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = &mut val_mut as *mut i32;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// let val = 1;
|
||||
/// let p = std::ptr::addr_of!(val);
|
||||
///
|
||||
/// let mut val_mut = 1;
|
||||
/// let p_mut = std::ptr::addr_of_mut!(val_mut);
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
pub BORROW_AS_PTR,
|
||||
pedantic,
|
||||
"borrowing just to cast to a raw pointer"
|
||||
}
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for a raw slice being cast to a slice pointer
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// This can result in multiple `&mut` references to the same location when only a pointer is
|
||||
/// required.
|
||||
/// `ptr::slice_from_raw_parts` is a safe alternative that doesn't require
|
||||
/// the same [safety requirements] to be upheld.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::slice::from_raw_parts(ptr, len) as *const _;
|
||||
/// let _: *mut [u8] = std::slice::from_raw_parts_mut(ptr, len) as *mut _;
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust,ignore
|
||||
/// let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, len);
|
||||
/// let _: *mut [u8] = std::ptr::slice_from_raw_parts_mut(ptr, len);
|
||||
/// ```
|
||||
/// [safety requirements]: https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub CAST_SLICE_FROM_RAW_PARTS,
|
||||
suspicious,
|
||||
"casting a slice created from a pointer and length to a slice pointer"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
@ -534,7 +624,10 @@ impl_lint_pass!(Casts => [
|
||||
PTR_AS_PTR,
|
||||
CAST_ENUM_TRUNCATION,
|
||||
CAST_ENUM_CONSTRUCTOR,
|
||||
CAST_ABS_TO_UNSIGNED
|
||||
CAST_ABS_TO_UNSIGNED,
|
||||
AS_UNDERSCORE,
|
||||
BORROW_AS_PTR,
|
||||
CAST_SLICE_FROM_RAW_PARTS
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
@ -547,8 +640,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
return;
|
||||
}
|
||||
|
||||
if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to) {
|
||||
if let ExprKind::Cast(cast_expr, cast_to_hir) = expr.kind {
|
||||
if is_hir_ty_cfg_dependant(cx, cast_to_hir) {
|
||||
return;
|
||||
}
|
||||
let (cast_from, cast_to) = (
|
||||
@ -559,7 +652,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) {
|
||||
return;
|
||||
}
|
||||
|
||||
cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv);
|
||||
fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
@ -575,6 +668,12 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv);
|
||||
cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
|
||||
}
|
||||
|
||||
as_underscore::check(cx, expr, cast_to_hir);
|
||||
|
||||
if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) {
|
||||
borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
|
||||
}
|
||||
}
|
||||
|
||||
cast_ref_to_mut::check(cx, expr);
|
||||
|
@ -90,13 +90,20 @@ pub(super) fn check<'tcx>(
|
||||
|
||||
fn lint_unnecessary_cast(cx: &LateContext<'_>, expr: &Expr<'_>, literal_str: &str, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
let literal_kind_name = if cast_from.is_integral() { "integer" } else { "float" };
|
||||
let replaced_literal;
|
||||
let matchless = if literal_str.contains(['(', ')']) {
|
||||
replaced_literal = literal_str.replace(['(', ')'], "");
|
||||
&replaced_literal
|
||||
} else {
|
||||
literal_str
|
||||
};
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
UNNECESSARY_CAST,
|
||||
expr.span,
|
||||
&format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
|
||||
"try",
|
||||
format!("{}_{}", literal_str.trim_end_matches('.'), cast_to),
|
||||
format!("{}_{}", matchless.trim_end_matches('.'), cast_to),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
|
@ -1,24 +1,34 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, ty_sig, variant_of_res};
|
||||
use clippy_utils::{get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage};
|
||||
use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res};
|
||||
use clippy_utils::{
|
||||
fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local,
|
||||
walk_to_expr_usage,
|
||||
};
|
||||
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_ty, Visitor};
|
||||
use rustc_hir::{
|
||||
self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
|
||||
ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
|
||||
TraitItemKind, TyKind, UnOp,
|
||||
self as hir, def_id::DefId, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy,
|
||||
GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind,
|
||||
Path, QPath, TraitItem, TraitItemKind, TyKind, UnOp,
|
||||
};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, Binder, BoundVariableKind, List, Ty, TyCtxt, TypeVisitable, TypeckResults};
|
||||
use rustc_middle::ty::{
|
||||
self, subst::Subst, Binder, BoundVariableKind, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind,
|
||||
ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults,
|
||||
};
|
||||
use rustc_semver::RustcVersion;
|
||||
use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
use rustc_span::{symbol::sym, Span, Symbol, DUMMY_SP};
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
@ -151,6 +161,7 @@ pub struct Dereferencing {
|
||||
/// been finished. Note we can't lint at the end of every body as they can be nested within each
|
||||
/// other.
|
||||
current_body: Option<BodyId>,
|
||||
|
||||
/// The list of locals currently being checked by the lint.
|
||||
/// If the value is `None`, then the binding has been seen as a ref pattern, but is not linted.
|
||||
/// This is needed for or patterns where one of the branches can be linted, but another can not
|
||||
@ -158,6 +169,19 @@ pub struct Dereferencing {
|
||||
///
|
||||
/// e.g. `m!(x) | Foo::Bar(ref x)`
|
||||
ref_locals: FxIndexMap<HirId, Option<RefPat>>,
|
||||
|
||||
// `IntoIterator` for arrays requires Rust 1.53.
|
||||
msrv: Option<RustcVersion>,
|
||||
}
|
||||
|
||||
impl Dereferencing {
|
||||
#[must_use]
|
||||
pub fn new(msrv: Option<RustcVersion>) -> Self {
|
||||
Self {
|
||||
msrv,
|
||||
..Dereferencing::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StateData {
|
||||
@ -170,6 +194,7 @@ struct StateData {
|
||||
struct DerefedBorrow {
|
||||
count: usize,
|
||||
msg: &'static str,
|
||||
snip_expr: Option<HirId>,
|
||||
}
|
||||
|
||||
enum State {
|
||||
@ -250,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
match (self.state.take(), kind) {
|
||||
(None, kind) => {
|
||||
let expr_ty = typeck.expr_ty(expr);
|
||||
let (position, adjustments) = walk_parents(cx, expr);
|
||||
let (position, adjustments) = walk_parents(cx, expr, self.msrv);
|
||||
|
||||
match kind {
|
||||
RefOp::Deref => {
|
||||
@ -331,20 +356,23 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
let deref_msg =
|
||||
"this expression creates a reference which is immediately dereferenced by the compiler";
|
||||
let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
|
||||
let impl_msg = "the borrowed expression implements the required traits";
|
||||
|
||||
let (required_refs, msg) = if position.can_auto_borrow() {
|
||||
(1, if deref_count == 1 { borrow_msg } else { deref_msg })
|
||||
let (required_refs, msg, snip_expr) = if position.can_auto_borrow() {
|
||||
(1, if deref_count == 1 { borrow_msg } else { deref_msg }, None)
|
||||
} else if let Position::ImplArg(hir_id) = position {
|
||||
(0, impl_msg, Some(hir_id))
|
||||
} else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
|
||||
next_adjust.map(|a| &a.kind)
|
||||
{
|
||||
if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
|
||||
{
|
||||
(3, deref_msg)
|
||||
(3, deref_msg, None)
|
||||
} else {
|
||||
(2, deref_msg)
|
||||
(2, deref_msg, None)
|
||||
}
|
||||
} else {
|
||||
(2, deref_msg)
|
||||
(2, deref_msg, None)
|
||||
};
|
||||
|
||||
if deref_count >= required_refs {
|
||||
@ -354,6 +382,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
// can't be removed without breaking the code. See earlier comment.
|
||||
count: deref_count - required_refs,
|
||||
msg,
|
||||
snip_expr,
|
||||
}),
|
||||
StateData { span: expr.span, hir_id: expr.hir_id, position },
|
||||
));
|
||||
@ -510,7 +539,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
spans: vec![pat.span],
|
||||
app,
|
||||
replacements: vec![(pat.span, snip.into())],
|
||||
hir_id: pat.hir_id
|
||||
hir_id: pat.hir_id,
|
||||
}),
|
||||
);
|
||||
}
|
||||
@ -542,6 +571,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
|
||||
self.current_body = None;
|
||||
}
|
||||
}
|
||||
|
||||
extract_msrv_attr!(LateContext);
|
||||
}
|
||||
|
||||
fn try_parse_ref_op<'tcx>(
|
||||
@ -594,6 +625,7 @@ enum Position {
|
||||
/// The method is defined on a reference type. e.g. `impl Foo for &T`
|
||||
MethodReceiverRefImpl,
|
||||
Callee,
|
||||
ImplArg(HirId),
|
||||
FieldAccess(Symbol),
|
||||
Postfix,
|
||||
Deref,
|
||||
@ -630,7 +662,7 @@ impl Position {
|
||||
| Self::Callee
|
||||
| Self::FieldAccess(_)
|
||||
| Self::Postfix => PREC_POSTFIX,
|
||||
Self::Deref => PREC_PREFIX,
|
||||
Self::ImplArg(_) | Self::Deref => PREC_PREFIX,
|
||||
Self::DerefStable(p, _) | Self::ReborrowStable(p) | Self::Other(p) => p,
|
||||
}
|
||||
}
|
||||
@ -639,8 +671,12 @@ impl Position {
|
||||
/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
|
||||
/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
|
||||
/// locations as those follow different rules.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
|
||||
#[expect(clippy::too_many_lines)]
|
||||
fn walk_parents<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
msrv: Option<RustcVersion>,
|
||||
) -> (Position, &'tcx [Adjustment<'tcx>]) {
|
||||
let mut adjustments = [].as_slice();
|
||||
let mut precedence = 0i8;
|
||||
let ctxt = e.span.ctxt();
|
||||
@ -745,13 +781,20 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
.iter()
|
||||
.position(|arg| arg.hir_id == child_id)
|
||||
.zip(expr_sig(cx, func))
|
||||
.and_then(|(i, sig)| sig.input_with_hir(i))
|
||||
.map(|(hir_ty, ty)| match hir_ty {
|
||||
// Type inference for closures can depend on how they're called. Only go by the explicit
|
||||
// types here.
|
||||
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
|
||||
None => ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
|
||||
.position_for_arg(),
|
||||
.and_then(|(i, sig)| {
|
||||
sig.input_with_hir(i).map(|(hir_ty, ty)| match hir_ty {
|
||||
// Type inference for closures can depend on how they're called. Only go by the explicit
|
||||
// types here.
|
||||
Some(hir_ty) => binding_ty_auto_deref_stability(cx, hir_ty, precedence, ty.bound_vars()),
|
||||
None => {
|
||||
if let ty::Param(param_ty) = ty.skip_binder().kind() {
|
||||
needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
|
||||
} else {
|
||||
ty_auto_deref_stability(cx, cx.tcx.erase_late_bound_regions(ty), precedence)
|
||||
.position_for_arg()
|
||||
}
|
||||
},
|
||||
})
|
||||
}),
|
||||
ExprKind::MethodCall(_, args, _) => {
|
||||
let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
|
||||
@ -773,7 +816,7 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
.and_then(|subs| subs.get(1..))
|
||||
{
|
||||
Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
|
||||
None => cx.tcx.mk_substs([].iter()),
|
||||
None => cx.tcx.mk_substs(std::iter::empty::<ty::subst::GenericArg<'_>>()),
|
||||
} && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
|
||||
// Trait methods taking `&self`
|
||||
sub_ty
|
||||
@ -792,12 +835,17 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
|
||||
Position::MethodReceiver
|
||||
}
|
||||
} else {
|
||||
ty_auto_deref_stability(
|
||||
cx,
|
||||
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
|
||||
precedence,
|
||||
)
|
||||
.position_for_arg()
|
||||
let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
|
||||
if let ty::Param(param_ty) = ty.kind() {
|
||||
needless_borrow_impl_arg_position(cx, parent, i, *param_ty, e, precedence, msrv)
|
||||
} else {
|
||||
ty_auto_deref_stability(
|
||||
cx,
|
||||
cx.tcx.erase_late_bound_regions(cx.tcx.fn_sig(id).input(i)),
|
||||
precedence,
|
||||
)
|
||||
.position_for_arg()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
@ -948,6 +996,205 @@ fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
|
||||
v.0
|
||||
}
|
||||
|
||||
// Checks whether:
|
||||
// * child is an expression of the form `&e` in an argument position requiring an `impl Trait`
|
||||
// * `e`'s type implements `Trait` and is copyable
|
||||
// If the conditions are met, returns `Some(Position::ImplArg(..))`; otherwise, returns `None`.
|
||||
// The "is copyable" condition is to avoid the case where removing the `&` means `e` would have to
|
||||
// be moved, but it cannot be.
|
||||
fn needless_borrow_impl_arg_position<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
parent: &Expr<'tcx>,
|
||||
arg_index: usize,
|
||||
param_ty: ParamTy,
|
||||
mut expr: &Expr<'tcx>,
|
||||
precedence: i8,
|
||||
msrv: Option<RustcVersion>,
|
||||
) -> Position {
|
||||
let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait();
|
||||
let sized_trait_def_id = cx.tcx.lang_items().sized_trait();
|
||||
|
||||
let Some(callee_def_id) = fn_def_id(cx, parent) else { return Position::Other(precedence) };
|
||||
let fn_sig = cx.tcx.fn_sig(callee_def_id).skip_binder();
|
||||
let substs_with_expr_ty = cx
|
||||
.typeck_results()
|
||||
.node_substs(if let ExprKind::Call(callee, _) = parent.kind {
|
||||
callee.hir_id
|
||||
} else {
|
||||
parent.hir_id
|
||||
});
|
||||
|
||||
let predicates = cx.tcx.param_env(callee_def_id).caller_bounds();
|
||||
let projection_predicates = predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
|
||||
Some(projection_predicate)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut trait_with_ref_mut_self_method = false;
|
||||
|
||||
// If no traits were found, or only the `Destruct`, `Sized`, or `Any` traits were found, return.
|
||||
if predicates
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& trait_predicate.trait_ref.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
{
|
||||
Some(trait_predicate.trait_ref.def_id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.inspect(|trait_def_id| {
|
||||
trait_with_ref_mut_self_method |= has_ref_mut_self_method(cx, *trait_def_id);
|
||||
})
|
||||
.all(|trait_def_id| {
|
||||
Some(trait_def_id) == destruct_trait_def_id
|
||||
|| Some(trait_def_id) == sized_trait_def_id
|
||||
|| cx.tcx.is_diagnostic_item(sym::Any, trait_def_id)
|
||||
})
|
||||
{
|
||||
return Position::Other(precedence);
|
||||
}
|
||||
|
||||
// `substs_with_referent_ty` can be constructed outside of `check_referent` because the same
|
||||
// elements are modified each time `check_referent` is called.
|
||||
let mut substs_with_referent_ty = substs_with_expr_ty.to_vec();
|
||||
|
||||
let mut check_referent = |referent| {
|
||||
let referent_ty = cx.typeck_results().expr_ty(referent);
|
||||
|
||||
if !is_copy(cx, referent_ty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/rust-lang/rust-clippy/pull/9136#pullrequestreview-1037379321
|
||||
if trait_with_ref_mut_self_method && !matches!(referent_ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !replace_types(
|
||||
cx,
|
||||
param_ty,
|
||||
referent_ty,
|
||||
fn_sig,
|
||||
arg_index,
|
||||
&projection_predicates,
|
||||
&mut substs_with_referent_ty,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
predicates.iter().all(|predicate| {
|
||||
if let PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder()
|
||||
&& cx.tcx.is_diagnostic_item(sym::IntoIterator, trait_predicate.trait_ref.def_id)
|
||||
&& let ty::Param(param_ty) = trait_predicate.self_ty().kind()
|
||||
&& let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack()
|
||||
&& ty.is_array()
|
||||
&& !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
let predicate = EarlyBinder(predicate).subst(cx.tcx, &substs_with_referent_ty);
|
||||
let obligation = Obligation::new(ObligationCause::dummy(), cx.param_env, predicate);
|
||||
cx.tcx
|
||||
.infer_ctxt()
|
||||
.enter(|infcx| infcx.predicate_must_hold_modulo_regions(&obligation))
|
||||
})
|
||||
};
|
||||
|
||||
let mut needless_borrow = false;
|
||||
while let ExprKind::AddrOf(_, _, referent) = expr.kind {
|
||||
if !check_referent(referent) {
|
||||
break;
|
||||
}
|
||||
expr = referent;
|
||||
needless_borrow = true;
|
||||
}
|
||||
|
||||
if needless_borrow {
|
||||
Position::ImplArg(expr.hir_id)
|
||||
} else {
|
||||
Position::Other(precedence)
|
||||
}
|
||||
}
|
||||
|
||||
fn has_ref_mut_self_method(cx: &LateContext<'_>, trait_def_id: DefId) -> bool {
|
||||
cx.tcx
|
||||
.associated_items(trait_def_id)
|
||||
.in_definition_order()
|
||||
.any(|assoc_item| {
|
||||
if assoc_item.fn_has_self_parameter {
|
||||
let self_ty = cx.tcx.fn_sig(assoc_item.def_id).skip_binder().inputs()[0];
|
||||
matches!(self_ty.kind(), ty::Ref(_, _, Mutability::Mut))
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Iteratively replaces `param_ty` with `new_ty` in `substs`, and similarly for each resulting
|
||||
// projected type that is a type parameter. Returns `false` if replacing the types would have an
|
||||
// effect on the function signature beyond substituting `new_ty` for `param_ty`.
|
||||
// See: https://github.com/rust-lang/rust-clippy/pull/9136#discussion_r927212757
|
||||
fn replace_types<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
param_ty: ParamTy,
|
||||
new_ty: Ty<'tcx>,
|
||||
fn_sig: FnSig<'tcx>,
|
||||
arg_index: usize,
|
||||
projection_predicates: &[ProjectionPredicate<'tcx>],
|
||||
substs: &mut [ty::GenericArg<'tcx>],
|
||||
) -> bool {
|
||||
let mut replaced = BitSet::new_empty(substs.len());
|
||||
|
||||
let mut deque = VecDeque::with_capacity(substs.len());
|
||||
deque.push_back((param_ty, new_ty));
|
||||
|
||||
while let Some((param_ty, new_ty)) = deque.pop_front() {
|
||||
// If `replaced.is_empty()`, then `param_ty` and `new_ty` are those initially passed in.
|
||||
if !fn_sig
|
||||
.inputs_and_output
|
||||
.iter()
|
||||
.enumerate()
|
||||
.all(|(i, ty)| (replaced.is_empty() && i == arg_index) || !ty.contains(param_ty.to_ty(cx.tcx)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
substs[param_ty.index as usize] = ty::GenericArg::from(new_ty);
|
||||
|
||||
// The `replaced.insert(...)` check provides some protection against infinite loops.
|
||||
if replaced.insert(param_ty.index) {
|
||||
for projection_predicate in projection_predicates {
|
||||
if projection_predicate.projection_ty.self_ty() == param_ty.to_ty(cx.tcx)
|
||||
&& let ty::Term::Ty(term_ty) = projection_predicate.term
|
||||
&& let ty::Param(term_param_ty) = term_ty.kind()
|
||||
{
|
||||
let item_def_id = projection_predicate.projection_ty.item_def_id;
|
||||
let assoc_item = cx.tcx.associated_item(item_def_id);
|
||||
let projection = cx.tcx
|
||||
.mk_projection(assoc_item.def_id, cx.tcx.mk_substs_trait(new_ty, &[]));
|
||||
|
||||
if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
|
||||
&& substs[term_param_ty.index as usize] != ty::GenericArg::from(projected_ty)
|
||||
{
|
||||
deque.push_back((*term_param_ty, projected_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
struct TyPosition<'tcx> {
|
||||
position: Position,
|
||||
ty: Option<Ty<'tcx>>,
|
||||
@ -1086,7 +1333,8 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
|
||||
},
|
||||
State::DerefedBorrow(state) => {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
|
||||
let snip_expr = state.snip_expr.map_or(expr, |hir_id| cx.tcx.hir().expect_expr(hir_id));
|
||||
let (snip, snip_is_macro) = snippet_with_context(cx, snip_expr.span, data.span.ctxt(), "..", &mut app);
|
||||
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
|
||||
let calls_field = matches!(expr.kind, ExprKind::Field(..)) && matches!(data.position, Position::Callee);
|
||||
let sugg = if !snip_is_macro
|
||||
|
@ -516,7 +516,10 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
|
||||
tcx.mk_predicates(ty_predicates.iter().map(|&(p, _)| p).chain(
|
||||
params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
|
||||
tcx.mk_predicate(Binder::dummy(PredicateKind::Trait(TraitPredicate {
|
||||
trait_ref: TraitRef::new(eq_trait_id, tcx.mk_substs([tcx.mk_param_from_def(param)].into_iter())),
|
||||
trait_ref: TraitRef::new(
|
||||
eq_trait_id,
|
||||
tcx.mk_substs(std::iter::once(tcx.mk_param_from_def(param))),
|
||||
),
|
||||
constness: BoundConstness::NotConst,
|
||||
polarity: ImplPolarity::Positive,
|
||||
})))
|
||||
|
@ -21,7 +21,7 @@ declare_clippy_lint! {
|
||||
/// /// See also: [`foo`]
|
||||
/// fn bar() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.60.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DOC_LINK_WITH_QUOTES,
|
||||
pedantic,
|
||||
"possible typo for an intra-doc link"
|
||||
|
@ -39,7 +39,7 @@ declare_clippy_lint! {
|
||||
/// // a.rs
|
||||
/// use crate::b;
|
||||
/// ```
|
||||
#[clippy::version = "1.62.0"]
|
||||
#[clippy::version = "1.63.0"]
|
||||
pub DUPLICATE_MOD,
|
||||
suspicious,
|
||||
"file loaded as module multiple times"
|
||||
|
@ -1,5 +1,4 @@
|
||||
use clippy_utils::diagnostics::span_lint_hir;
|
||||
use clippy_utils::ty::contains_ty;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
@ -30,18 +29,12 @@ declare_clippy_lint! {
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// let x = Box::new(1);
|
||||
/// foo(*x);
|
||||
/// println!("{}", *x);
|
||||
/// fn foo(x: Box<u32>) {}
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// # fn foo(bar: usize) {}
|
||||
/// let x = 1;
|
||||
/// foo(x);
|
||||
/// println!("{}", x);
|
||||
/// fn foo(x: u32) {}
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub BOXED_LOCAL,
|
||||
@ -172,7 +165,7 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
|
||||
// skip if there is a `self` parameter binding to a type
|
||||
// that contains `Self` (i.e.: `self: Box<Self>`), see #4804
|
||||
if let Some(trait_self_ty) = self.trait_self_ty {
|
||||
if map.name(cmt.hir_id) == kw::SelfLower && contains_ty(cmt.place.ty(), trait_self_ty) {
|
||||
if map.name(cmt.hir_id) == kw::SelfLower && cmt.place.ty().contains(trait_self_ty) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ fn check_log_base(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
expr.span,
|
||||
"logarithm for bases 2, 10 and e can be computed more accurately",
|
||||
"consider using",
|
||||
format!("{}.{}()", Sugg::hir(cx, &args[0], ".."), method),
|
||||
format!("{}.{}()", Sugg::hir(cx, &args[0], "..").maybe_par(), method),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
@ -263,13 +263,13 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
(
|
||||
SUBOPTIMAL_FLOPS,
|
||||
"square-root of a number can be computed more efficiently and accurately",
|
||||
format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..")),
|
||||
format!("{}.sqrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
|
||||
)
|
||||
} else if F32(1.0 / 3.0) == value || F64(1.0 / 3.0) == value {
|
||||
(
|
||||
IMPRECISE_FLOPS,
|
||||
"cube-root of a number can be computed more accurately",
|
||||
format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..")),
|
||||
format!("{}.cbrt()", Sugg::hir(cx, &args[0], "..").maybe_par()),
|
||||
)
|
||||
} else if let Some(exponent) = get_integer_from_float_constant(&value) {
|
||||
(
|
||||
@ -277,7 +277,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
"exponentiation with integer powers can be computed more efficiently",
|
||||
format!(
|
||||
"{}.powi({})",
|
||||
Sugg::hir(cx, &args[0], ".."),
|
||||
Sugg::hir(cx, &args[0], "..").maybe_par(),
|
||||
numeric_literal::format(&exponent.to_string(), None, false)
|
||||
),
|
||||
)
|
||||
@ -327,7 +327,7 @@ fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, args: &[Expr<'_>]) {
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.mul_add({}, {})",
|
||||
Sugg::hir(cx, &args[0], ".."),
|
||||
Sugg::hir(cx, &args[0], "..").maybe_par(),
|
||||
Sugg::hir(cx, &args[0], ".."),
|
||||
Sugg::hir(cx, other_addend, ".."),
|
||||
),
|
||||
@ -418,7 +418,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
"consider using",
|
||||
format!(
|
||||
"{}.exp_m1()",
|
||||
Sugg::hir(cx, self_arg, "..")
|
||||
Sugg::hir(cx, self_arg, "..").maybe_par()
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
@ -550,11 +550,11 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
then {
|
||||
let positive_abs_sugg = (
|
||||
"manual implementation of `abs` method",
|
||||
format!("{}.abs()", Sugg::hir(cx, body, "..")),
|
||||
format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
|
||||
);
|
||||
let negative_abs_sugg = (
|
||||
"manual implementation of negation of `abs` method",
|
||||
format!("-{}.abs()", Sugg::hir(cx, body, "..")),
|
||||
format!("-{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
|
||||
);
|
||||
let sugg = if is_testing_positive(cx, cond, body) {
|
||||
if if_expr_positive {
|
||||
@ -621,7 +621,7 @@ fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
expr.span,
|
||||
"log base can be expressed more clearly",
|
||||
"consider using",
|
||||
format!("{}.log({})", Sugg::hir(cx, largs_self, ".."), Sugg::hir(cx, rargs_self, ".."),),
|
||||
format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
@ -651,7 +651,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) &&
|
||||
(F32(180_f32) == lvalue || F64(180_f64) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
|
||||
if let ast::LitKind::Float(ref value, float_type) = literal.node;
|
||||
@ -677,7 +677,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) &&
|
||||
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
|
||||
{
|
||||
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
|
||||
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
|
||||
if_chain! {
|
||||
if let ExprKind::Lit(ref literal) = mul_lhs.kind;
|
||||
if let ast::LitKind::Float(ref value, float_type) = literal.node;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn};
|
||||
use clippy_utils::source::{snippet_opt, snippet_with_applicability};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -56,29 +56,27 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
|
||||
};
|
||||
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
if format_args.value_args.is_empty() {
|
||||
match *format_args.format_string_parts {
|
||||
if format_args.args.is_empty() {
|
||||
match *format_args.format_string.parts {
|
||||
[] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
|
||||
[_] => {
|
||||
if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) {
|
||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||
let s_expand = s_src.replace("{{", "{").replace("}}", "}");
|
||||
let sugg = format!("{}.to_string()", s_expand);
|
||||
span_useless_format(cx, call_site, sugg, applicability);
|
||||
}
|
||||
// Simulate macro expansion, converting {{ and }} to { and }.
|
||||
let s_expand = format_args.format_string.snippet.replace("{{", "{").replace("}}", "}");
|
||||
let sugg = format!("{}.to_string()", s_expand);
|
||||
span_useless_format(cx, call_site, sugg, applicability);
|
||||
},
|
||||
[..] => {},
|
||||
}
|
||||
} else if let [value] = *format_args.value_args {
|
||||
} else if let [arg] = &*format_args.args {
|
||||
let value = arg.param.value;
|
||||
if_chain! {
|
||||
if format_args.format_string_parts == [kw::Empty];
|
||||
if format_args.format_string.parts == [kw::Empty];
|
||||
if match cx.typeck_results().expr_ty(value).peel_refs().kind() {
|
||||
ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did()),
|
||||
ty::Str => true,
|
||||
_ => false,
|
||||
};
|
||||
if let Some(args) = format_args.args();
|
||||
if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting());
|
||||
if !arg.format.has_string_formatting();
|
||||
then {
|
||||
let is_new_string = match value.kind {
|
||||
ExprKind::Binary(..) => true,
|
||||
|
@ -1,11 +1,12 @@
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::is_diag_trait_item;
|
||||
use clippy_utils::macros::{is_format_macro, FormatArgsArg, FormatArgsExpn};
|
||||
use clippy_utils::macros::{is_format_macro, FormatArgsExpn};
|
||||
use clippy_utils::source::snippet_opt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use if_chain::if_chain;
|
||||
use itertools::Itertools;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_hir::{Expr, ExprKind, HirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||
use rustc_middle::ty::Ty;
|
||||
@ -74,20 +75,16 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
|
||||
if let Some(macro_def_id) = outermost_expn_data.macro_def_id;
|
||||
if is_format_macro(cx, macro_def_id);
|
||||
if let ExpnKind::Macro(_, name) = outermost_expn_data.kind;
|
||||
if let Some(args) = format_args.args();
|
||||
then {
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if arg.format_trait != sym::Display {
|
||||
for arg in &format_args.args {
|
||||
if arg.format.has_string_formatting() {
|
||||
continue;
|
||||
}
|
||||
if arg.has_string_formatting() {
|
||||
if is_aliased(&format_args, arg.param.value.hir_id) {
|
||||
continue;
|
||||
}
|
||||
if is_aliased(&args, i) {
|
||||
continue;
|
||||
}
|
||||
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value);
|
||||
check_to_string_in_format_args(cx, name, arg.value);
|
||||
check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value);
|
||||
check_to_string_in_format_args(cx, name, arg.param.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,45 +131,56 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
|
||||
if is_diag_trait_item(cx, method_def_id, sym::ToString);
|
||||
let receiver_ty = cx.typeck_results().expr_ty(receiver);
|
||||
if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display);
|
||||
let (n_needed_derefs, target) =
|
||||
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter());
|
||||
if implements_trait(cx, target, display_trait_id, &[]);
|
||||
if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait();
|
||||
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span);
|
||||
then {
|
||||
let (n_needed_derefs, target) = count_needed_derefs(
|
||||
receiver_ty,
|
||||
cx.typeck_results().expr_adjustments(receiver).iter(),
|
||||
);
|
||||
if implements_trait(cx, target, display_trait_id, &[]) {
|
||||
if n_needed_derefs == 0 {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span.with_lo(receiver.span.hi()),
|
||||
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
|
||||
"remove this",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span,
|
||||
&format!("`to_string` applied to a type that implements `Display` in `{}!` args", name),
|
||||
"use this",
|
||||
format!("{:*>width$}{}", "", receiver_snippet, width = n_needed_derefs),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
|
||||
if n_needed_derefs == 0 && !needs_ref {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span.with_lo(receiver.span.hi()),
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{}!` args",
|
||||
name
|
||||
),
|
||||
"remove this",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
TO_STRING_IN_FORMAT_ARGS,
|
||||
value.span,
|
||||
&format!(
|
||||
"`to_string` applied to a type that implements `Display` in `{}!` args",
|
||||
name
|
||||
),
|
||||
"use this",
|
||||
format!(
|
||||
"{}{:*>width$}{}",
|
||||
if needs_ref { "&" } else { "" },
|
||||
"",
|
||||
receiver_snippet,
|
||||
width = n_needed_derefs
|
||||
),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if `args[i]` "refers to" or "is referred to by" another argument.
|
||||
fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
|
||||
let value = args[i].value;
|
||||
args.iter()
|
||||
.enumerate()
|
||||
.any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
|
||||
// Returns true if `hir_id` is referred to by multiple format params
|
||||
fn is_aliased(args: &FormatArgsExpn<'_>, hir_id: HirId) -> bool {
|
||||
args.params()
|
||||
.filter(|param| param.value.hir_id == hir_id)
|
||||
.at_most_one()
|
||||
.is_err()
|
||||
}
|
||||
|
||||
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArgsArg, FormatArgsExpn};
|
||||
use clippy_utils::macros::{is_format_macro, root_macro_call_first_node, FormatArg, FormatArgsExpn};
|
||||
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
@ -168,10 +168,9 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
|
||||
if let macro_def_id = outer_macro.def_id;
|
||||
if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, outer_macro.expn);
|
||||
if is_format_macro(cx, macro_def_id);
|
||||
if let Some(args) = format_args.args();
|
||||
then {
|
||||
for arg in args {
|
||||
if arg.format_trait != impl_trait.name {
|
||||
for arg in format_args.args {
|
||||
if arg.format.r#trait != impl_trait.name {
|
||||
continue;
|
||||
}
|
||||
check_format_arg_self(cx, expr, &arg, impl_trait);
|
||||
@ -180,11 +179,11 @@ fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
|
||||
}
|
||||
}
|
||||
|
||||
fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArgsArg<'_>, impl_trait: FormatTrait) {
|
||||
fn check_format_arg_self(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &FormatArg<'_>, impl_trait: FormatTrait) {
|
||||
// Handle multiple dereferencing of references e.g. &&self
|
||||
// Handle dereference of &self -> self that is equivalent (i.e. via *self in fmt() impl)
|
||||
// Since the argument to fmt is itself a reference: &self
|
||||
let reference = peel_ref_operators(cx, arg.value);
|
||||
let reference = peel_ref_operators(cx, arg.param.value);
|
||||
let map = cx.tcx.hir();
|
||||
// Is the reference self?
|
||||
if path_to_local(reference).map(|x| map.name(x)) == Some(kw::SelfLower) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
mod must_use;
|
||||
mod not_unsafe_ptr_arg_deref;
|
||||
mod result_unit_err;
|
||||
mod result;
|
||||
mod too_many_arguments;
|
||||
mod too_many_lines;
|
||||
|
||||
@ -217,17 +217,62 @@ declare_clippy_lint! {
|
||||
"public function returning `Result` with an `Err` type of `()`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for functions that return `Result` with an unusually large
|
||||
/// `Err`-variant.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// A `Result` is at least as large as the `Err`-variant. While we
|
||||
/// expect that variant to be seldomly used, the compiler needs to reserve
|
||||
/// and move that much memory every single time.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// The size determined by Clippy is platform-dependent.
|
||||
///
|
||||
/// ### Examples
|
||||
/// ```rust
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes([u8; 512]),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` has at least 512 bytes, even in the `Ok`-case
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
/// should be
|
||||
/// ```
|
||||
/// pub enum ParseError {
|
||||
/// UnparsedBytes(Box<[u8; 512]>),
|
||||
/// UnexpectedEof,
|
||||
/// }
|
||||
///
|
||||
/// // The `Result` is slightly larger than a pointer
|
||||
/// pub fn parse() -> Result<(), ParseError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.64.0"]
|
||||
pub RESULT_LARGE_ERR,
|
||||
perf,
|
||||
"function returning `Result` with large `Err` type"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Functions {
|
||||
too_many_arguments_threshold: u64,
|
||||
too_many_lines_threshold: u64,
|
||||
large_error_threshold: u64,
|
||||
}
|
||||
|
||||
impl Functions {
|
||||
pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64) -> Self {
|
||||
pub fn new(too_many_arguments_threshold: u64, too_many_lines_threshold: u64, large_error_threshold: u64) -> Self {
|
||||
Self {
|
||||
too_many_arguments_threshold,
|
||||
too_many_lines_threshold,
|
||||
large_error_threshold,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,6 +285,7 @@ impl_lint_pass!(Functions => [
|
||||
DOUBLE_MUST_USE,
|
||||
MUST_USE_CANDIDATE,
|
||||
RESULT_UNIT_ERR,
|
||||
RESULT_LARGE_ERR,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
@ -259,18 +305,18 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
|
||||
|
||||
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
|
||||
must_use::check_item(cx, item);
|
||||
result_unit_err::check_item(cx, item);
|
||||
result::check_item(cx, item, self.large_error_threshold);
|
||||
}
|
||||
|
||||
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) {
|
||||
must_use::check_impl_item(cx, item);
|
||||
result_unit_err::check_impl_item(cx, item);
|
||||
result::check_impl_item(cx, item, self.large_error_threshold);
|
||||
}
|
||||
|
||||
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
|
||||
too_many_arguments::check_trait_item(cx, item, self.too_many_arguments_threshold);
|
||||
not_unsafe_ptr_arg_deref::check_trait_item(cx, item);
|
||||
must_use::check_trait_item(cx, item);
|
||||
result_unit_err::check_trait_item(cx, item);
|
||||
result::check_trait_item(cx, item, self.large_error_threshold);
|
||||
}
|
||||
}
|
||||
|
100
src/tools/clippy/clippy_lints/src/functions/result.rs
Normal file
100
src/tools/clippy/clippy_lints/src/functions/result.rs
Normal 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}>`"));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::SpanlessEq;
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_hir::intravisit::{self as visit, Visitor};
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -45,16 +46,8 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
let mut arm_visit = ArmVisitor {
|
||||
mutex_lock_called: false,
|
||||
found_mutex: None,
|
||||
cx,
|
||||
};
|
||||
let mut op_visit = OppVisitor {
|
||||
mutex_lock_called: false,
|
||||
found_mutex: None,
|
||||
cx,
|
||||
};
|
||||
let mut arm_visit = ArmVisitor { found_mutex: None, cx };
|
||||
let mut op_visit = OppVisitor { found_mutex: None, cx };
|
||||
if let Some(higher::IfLet {
|
||||
let_expr,
|
||||
if_then,
|
||||
@ -63,18 +56,28 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
||||
}) = higher::IfLet::hir(cx, expr)
|
||||
{
|
||||
op_visit.visit_expr(let_expr);
|
||||
if op_visit.mutex_lock_called {
|
||||
if let Some(op_mutex) = op_visit.found_mutex {
|
||||
arm_visit.visit_expr(if_then);
|
||||
arm_visit.visit_expr(if_else);
|
||||
|
||||
if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
|
||||
span_lint_and_help(
|
||||
if let Some(arm_mutex) = arm_visit.found_mutex_if_same_as(op_mutex) {
|
||||
let diag = |diag: &mut Diagnostic| {
|
||||
diag.span_label(
|
||||
op_mutex.span,
|
||||
"this Mutex will remain locked for the entire `if let`-block...",
|
||||
);
|
||||
diag.span_label(
|
||||
arm_mutex.span,
|
||||
"... and is tried to lock again here, which will always deadlock.",
|
||||
);
|
||||
diag.help("move the lock call outside of the `if let ...` expression");
|
||||
};
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
IF_LET_MUTEX,
|
||||
expr.span,
|
||||
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
|
||||
None,
|
||||
"move the lock call outside of the `if let ...` expression",
|
||||
diag,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -84,7 +87,6 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
|
||||
|
||||
/// Checks if `Mutex::lock` is called in the `if let` expr.
|
||||
pub struct OppVisitor<'a, 'tcx> {
|
||||
mutex_lock_called: bool,
|
||||
found_mutex: Option<&'tcx Expr<'tcx>>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
@ -93,7 +95,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
|
||||
self.found_mutex = Some(mutex);
|
||||
self.mutex_lock_called = true;
|
||||
return;
|
||||
}
|
||||
visit::walk_expr(self, expr);
|
||||
@ -102,7 +103,6 @@ impl<'tcx> Visitor<'tcx> for OppVisitor<'_, 'tcx> {
|
||||
|
||||
/// Checks if `Mutex::lock` is called in any of the branches.
|
||||
pub struct ArmVisitor<'a, 'tcx> {
|
||||
mutex_lock_called: bool,
|
||||
found_mutex: Option<&'tcx Expr<'tcx>>,
|
||||
cx: &'a LateContext<'tcx>,
|
||||
}
|
||||
@ -111,7 +111,6 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||
if let Some(mutex) = is_mutex_lock_call(self.cx, expr) {
|
||||
self.found_mutex = Some(mutex);
|
||||
self.mutex_lock_called = true;
|
||||
return;
|
||||
}
|
||||
visit::walk_expr(self, expr);
|
||||
@ -119,9 +118,12 @@ impl<'tcx> Visitor<'tcx> for ArmVisitor<'_, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
|
||||
fn same_mutex(&self, cx: &LateContext<'_>, op_mutex: &Expr<'_>) -> bool {
|
||||
self.found_mutex
|
||||
.map_or(false, |arm_mutex| SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex))
|
||||
fn found_mutex_if_same_as(&self, op_mutex: &Expr<'_>) -> Option<&Expr<'_>> {
|
||||
self.found_mutex.and_then(|arm_mutex| {
|
||||
SpanlessEq::new(self.cx)
|
||||
.eq_expr(op_mutex, arm_mutex)
|
||||
.then_some(arm_mutex)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,7 +131,7 @@ fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Opt
|
||||
if_chain! {
|
||||
if let ExprKind::MethodCall(path, [self_arg, ..], _) = &expr.kind;
|
||||
if path.ident.as_str() == "lock";
|
||||
let ty = cx.typeck_results().expr_ty(self_arg);
|
||||
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs();
|
||||
if is_type_diagnostic_item(cx, ty, sym::Mutex);
|
||||
then {
|
||||
Some(self_arg)
|
||||
|
@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::source::snippet_with_macro_callsite;
|
||||
use clippy_utils::{contains_return, higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs, peel_blocks};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::LangItem::{OptionNone, OptionSome};
|
||||
use rustc_hir::{Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
@ -11,10 +11,12 @@ use rustc_session::{declare_tool_lint, impl_lint_pass};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for if-else that could be written to `bool::then`.
|
||||
/// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Looks a little redundant. Using `bool::then` helps it have less lines of code.
|
||||
/// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
|
||||
/// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
|
||||
/// in comparison to `bool::then`.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust
|
||||
@ -39,7 +41,7 @@ declare_clippy_lint! {
|
||||
#[clippy::version = "1.53.0"]
|
||||
pub IF_THEN_SOME_ELSE_NONE,
|
||||
restriction,
|
||||
"Finds if-else that could be written using `bool::then`"
|
||||
"Finds if-else that could be written using either `bool::then` or `bool::then_some`"
|
||||
}
|
||||
|
||||
pub struct IfThenSomeElseNone {
|
||||
@ -56,7 +58,7 @@ impl IfThenSomeElseNone {
|
||||
impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
|
||||
if !meets_msrv(self.msrv, msrvs::BOOL_THEN) {
|
||||
return;
|
||||
}
|
||||
@ -70,43 +72,47 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
|
||||
return;
|
||||
}
|
||||
|
||||
if_chain! {
|
||||
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
|
||||
if let ExprKind::Block(then_block, _) = then.kind;
|
||||
if let Some(then_expr) = then_block.expr;
|
||||
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
|
||||
if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
|
||||
if is_lang_ctor(cx, then_call_qpath, OptionSome);
|
||||
if let ExprKind::Path(ref qpath) = peel_blocks(els).kind;
|
||||
if is_lang_ctor(cx, qpath, OptionNone);
|
||||
if !stmts_contains_early_return(then_block.stmts);
|
||||
then {
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
|
||||
format!("({})", cond_snip)
|
||||
} else {
|
||||
cond_snip.into_owned()
|
||||
};
|
||||
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
|
||||
let closure_body = if then_block.stmts.is_empty() {
|
||||
arg_snip.into_owned()
|
||||
} else {
|
||||
format!("{{ /* snippet */ {} }}", arg_snip)
|
||||
};
|
||||
let help = format!(
|
||||
"consider using `bool::then` like: `{}.then(|| {})`",
|
||||
cond_snip,
|
||||
closure_body,
|
||||
);
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
IF_THEN_SOME_ELSE_NONE,
|
||||
expr.span,
|
||||
"this could be simplified with `bool::then`",
|
||||
None,
|
||||
&help,
|
||||
);
|
||||
}
|
||||
if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr)
|
||||
&& let ExprKind::Block(then_block, _) = then.kind
|
||||
&& let Some(then_expr) = then_block.expr
|
||||
&& let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
|
||||
&& let ExprKind::Path(ref then_call_qpath) = then_call.kind
|
||||
&& is_lang_ctor(cx, then_call_qpath, OptionSome)
|
||||
&& let ExprKind::Path(ref qpath) = peel_blocks(els).kind
|
||||
&& is_lang_ctor(cx, qpath, OptionNone)
|
||||
&& !stmts_contains_early_return(then_block.stmts)
|
||||
{
|
||||
let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
|
||||
let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
|
||||
format!("({})", cond_snip)
|
||||
} else {
|
||||
cond_snip.into_owned()
|
||||
};
|
||||
let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
|
||||
let mut method_body = if then_block.stmts.is_empty() {
|
||||
arg_snip.into_owned()
|
||||
} else {
|
||||
format!("{{ /* snippet */ {} }}", arg_snip)
|
||||
};
|
||||
let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
|
||||
"then_some"
|
||||
} else {
|
||||
method_body.insert_str(0, "|| ");
|
||||
"then"
|
||||
};
|
||||
|
||||
let help = format!(
|
||||
"consider using `bool::{}` like: `{}.{}({})`",
|
||||
method_name, cond_snip, method_name, method_body,
|
||||
);
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
IF_THEN_SOME_ELSE_NONE,
|
||||
expr.span,
|
||||
&format!("this could be simplified with `bool::{}`", method_name),
|
||||
None,
|
||||
&help,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,12 +20,12 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(booleans::OVERLY_COMPLEX_BOOL_EXPR),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
LintId::of(casts::CAST_ENUM_TRUNCATION),
|
||||
LintId::of(casts::CAST_REF_TO_MUT),
|
||||
LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
|
||||
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION),
|
||||
@ -80,9 +80,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(functions::DOUBLE_MUST_USE),
|
||||
LintId::of(functions::MUST_USE_UNIT),
|
||||
LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
|
||||
LintId::of(functions::RESULT_LARGE_ERR),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(functions::TOO_MANY_ARGUMENTS),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(if_let_mutex::IF_LET_MUTEX),
|
||||
LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
|
||||
LintId::of(infinite_iter::INFINITE_ITER),
|
||||
@ -128,7 +128,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
|
||||
LintId::of(manual_retain::MANUAL_RETAIN),
|
||||
LintId::of(manual_strip::MANUAL_STRIP),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
|
||||
LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
@ -150,17 +149,20 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_DEFAULT),
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::BIND_INSTEAD_OF_MAP),
|
||||
LintId::of(methods::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(methods::BYTES_NTH),
|
||||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
LintId::of(methods::CLONE_DOUBLE_REF),
|
||||
LintId::of(methods::CLONE_ON_COPY),
|
||||
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
|
||||
LintId::of(methods::ERR_EXPECT),
|
||||
LintId::of(methods::EXPECT_FUN_CALL),
|
||||
LintId::of(methods::EXTEND_WITH_DRAIN),
|
||||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_IDENTITY),
|
||||
LintId::of(methods::GET_FIRST),
|
||||
LintId::of(methods::GET_LAST_WITH_LEN),
|
||||
LintId::of(methods::INSPECT_FOR_EACH),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
@ -178,13 +180,16 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MANUAL_SPLIT_ONCE),
|
||||
LintId::of(methods::MANUAL_STR_REPEAT),
|
||||
LintId::of(methods::MAP_CLONE),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MAP_FLATTEN),
|
||||
LintId::of(methods::MAP_IDENTITY),
|
||||
LintId::of(methods::MUT_MUTEX_LOCK),
|
||||
LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
|
||||
LintId::of(methods::NEEDLESS_OPTION_TAKE),
|
||||
LintId::of(methods::NEEDLESS_SPLITN),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
@ -193,6 +198,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::OPTION_MAP_OR_NONE),
|
||||
LintId::of(methods::OR_FUN_CALL),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(methods::REPEAT_ONCE),
|
||||
LintId::of(methods::RESULT_MAP_OR_INTO_OPTION),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SHOULD_IMPLEMENT_TRAIT),
|
||||
@ -202,14 +209,18 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(methods::STRING_EXTEND_CHARS),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(methods::SUSPICIOUS_SPLITN),
|
||||
LintId::of(methods::SUSPICIOUS_TO_OWNED),
|
||||
LintId::of(methods::UNINIT_ASSUMED_INIT),
|
||||
LintId::of(methods::UNIT_HASH),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FIND_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FOLD),
|
||||
LintId::of(methods::UNNECESSARY_LAZY_EVALUATIONS),
|
||||
LintId::of(methods::UNNECESSARY_SORT_BY),
|
||||
LintId::of(methods::UNNECESSARY_TO_OWNED),
|
||||
LintId::of(methods::UNWRAP_OR_ELSE_DEFAULT),
|
||||
LintId::of(methods::USELESS_ASREF),
|
||||
LintId::of(methods::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(methods::WRONG_SELF_CONVENTION),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
@ -224,8 +235,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
|
||||
LintId::of(misc_early::ZERO_PREFIXED_LITERAL),
|
||||
LintId::of(mixed_read_write_in_expression::DIVERGING_SUB_EXPRESSION),
|
||||
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
|
||||
LintId::of(needless_bool::BOOL_COMPARISON),
|
||||
@ -245,7 +256,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::ASSIGN_OP_PATTERN),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
@ -275,7 +286,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(question_mark::QUESTION_MARK),
|
||||
LintId::of(ranges::MANUAL_RANGE_CONTAINS),
|
||||
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(ranges::REVERSED_EMPTY_RANGES),
|
||||
LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
|
||||
LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
|
||||
@ -286,7 +296,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
|
||||
LintId::of(reference::DEREF_ADDROF),
|
||||
LintId::of(regex::INVALID_REGEX),
|
||||
LintId::of(repeat_once::REPEAT_ONCE),
|
||||
LintId::of(returns::LET_AND_RETURN),
|
||||
LintId::of(returns::NEEDLESS_RETURN),
|
||||
LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
|
||||
@ -314,10 +323,10 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(transmute::TRANSMUTE_INT_TO_FLOAT),
|
||||
LintId::of(transmute::TRANSMUTE_NUM_TO_BYTES),
|
||||
LintId::of(transmute::TRANSMUTE_PTR_TO_REF),
|
||||
LintId::of(transmute::TRANSMUTING_NULL),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::USELESS_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(types::BORROWED_BOX),
|
||||
LintId::of(types::BOX_COLLECTION),
|
||||
LintId::of(types::REDUNDANT_ALLOCATION),
|
||||
@ -325,7 +334,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(types::VEC_BOX),
|
||||
LintId::of(unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(uninit_vec::UNINIT_VEC),
|
||||
LintId::of(unit_hash::UNIT_HASH),
|
||||
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(unit_types::LET_UNIT_VALUE),
|
||||
LintId::of(unit_types::UNIT_ARG),
|
||||
@ -333,9 +341,9 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS),
|
||||
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME),
|
||||
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(unused_peekable::UNUSED_PEEKABLE),
|
||||
LintId::of(unused_unit::UNUSED_UNIT),
|
||||
LintId::of(unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(unwrap::UNNECESSARY_UNWRAP),
|
||||
@ -343,7 +351,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
|
||||
LintId::of(useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(vec::USELESS_VEC),
|
||||
LintId::of(vec_init_then_push::VEC_INIT_THEN_PUSH),
|
||||
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
|
||||
LintId::of(write::PRINTLN_EMPTY_STRING),
|
||||
LintId::of(write::PRINT_LITERAL),
|
||||
LintId::of(write::PRINT_WITH_NEWLINE),
|
||||
|
@ -6,7 +6,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(attrs::DEPRECATED_CFG_ATTR),
|
||||
LintId::of(booleans::NONMINIMAL_BOOL),
|
||||
LintId::of(borrow_deref_ref::BORROW_DEREF_REF),
|
||||
LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(casts::CHAR_LIT_AS_U8),
|
||||
LintId::of(casts::UNNECESSARY_CAST),
|
||||
LintId::of(dereference::EXPLICIT_AUTO_DEREF),
|
||||
@ -33,6 +32,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(matches::NEEDLESS_MATCH),
|
||||
LintId::of(matches::WILDCARD_IN_OR_PATTERNS),
|
||||
LintId::of(methods::BIND_INSTEAD_OF_MAP),
|
||||
LintId::of(methods::BYTES_COUNT_TO_LEN),
|
||||
LintId::of(methods::CLONE_ON_COPY),
|
||||
LintId::of(methods::FILTER_MAP_IDENTITY),
|
||||
LintId::of(methods::FILTER_NEXT),
|
||||
@ -51,10 +51,13 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(methods::OPTION_AS_REF_DEREF),
|
||||
LintId::of(methods::OPTION_FILTER_MAP),
|
||||
LintId::of(methods::OR_THEN_UNWRAP),
|
||||
LintId::of(methods::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(methods::REPEAT_ONCE),
|
||||
LintId::of(methods::SEARCH_IS_SOME),
|
||||
LintId::of(methods::SKIP_WHILE_NEXT),
|
||||
LintId::of(methods::UNNECESSARY_FILTER_MAP),
|
||||
LintId::of(methods::UNNECESSARY_FIND_MAP),
|
||||
LintId::of(methods::UNNECESSARY_SORT_BY),
|
||||
LintId::of(methods::USELESS_ASREF),
|
||||
LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
|
||||
LintId::of(misc_early::UNNEEDED_WILDCARD_PATTERN),
|
||||
@ -69,6 +72,7 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
|
||||
LintId::of(no_effect::NO_EFFECT),
|
||||
LintId::of(no_effect::UNNECESSARY_OPERATION),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(operators::DOUBLE_COMPARISONS),
|
||||
LintId::of(operators::DURATION_SUBSEC),
|
||||
LintId::of(operators::IDENTITY_OP),
|
||||
@ -76,11 +80,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
|
||||
LintId::of(precedence::PRECEDENCE),
|
||||
LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
|
||||
LintId::of(ranges::RANGE_ZIP_WITH_LEN),
|
||||
LintId::of(redundant_closure_call::REDUNDANT_CLOSURE_CALL),
|
||||
LintId::of(redundant_slicing::REDUNDANT_SLICING),
|
||||
LintId::of(reference::DEREF_ADDROF),
|
||||
LintId::of(repeat_once::REPEAT_ONCE),
|
||||
LintId::of(strings::STRING_FROM_UTF8_AS_BYTES),
|
||||
LintId::of(strlen_on_c_strings::STRLEN_ON_C_STRINGS),
|
||||
LintId::of(swap::MANUAL_SWAP),
|
||||
@ -99,7 +101,6 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
|
||||
LintId::of(types::TYPE_COMPLEXITY),
|
||||
LintId::of(types::VEC_BOX),
|
||||
LintId::of(unit_types::UNIT_ARG),
|
||||
LintId::of(unnecessary_sort_by::UNNECESSARY_SORT_BY),
|
||||
LintId::of(unwrap::UNNECESSARY_UNWRAP),
|
||||
LintId::of(useless_conversion::USELESS_CONVERSION),
|
||||
LintId::of(zero_div_zero::ZERO_DIVIDED_BY_ZERO),
|
||||
|
@ -39,12 +39,14 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
||||
LintId::of(mem_replace::MEM_REPLACE_WITH_UNINIT),
|
||||
LintId::of(methods::CLONE_DOUBLE_REF),
|
||||
LintId::of(methods::ITERATOR_STEP_BY_ZERO),
|
||||
LintId::of(methods::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(methods::SUSPICIOUS_SPLITN),
|
||||
LintId::of(methods::UNINIT_ASSUMED_INIT),
|
||||
LintId::of(methods::UNIT_HASH),
|
||||
LintId::of(methods::VEC_RESIZE_TO_ZERO),
|
||||
LintId::of(methods::ZST_OFFSET),
|
||||
LintId::of(minmax::MIN_MAX),
|
||||
LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
|
||||
LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
|
||||
LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
|
||||
LintId::of(operators::BAD_BIT_MASK),
|
||||
LintId::of(operators::CMP_NAN),
|
||||
@ -62,17 +64,15 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
|
||||
LintId::of(serde_api::SERDE_API_MISUSE),
|
||||
LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
|
||||
LintId::of(swap::ALMOST_SWAPPED),
|
||||
LintId::of(transmute::TRANSMUTING_NULL),
|
||||
LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
|
||||
LintId::of(transmute::WRONG_TRANSMUTE),
|
||||
LintId::of(transmuting_null::TRANSMUTING_NULL),
|
||||
LintId::of(unicode::INVISIBLE_CHARACTERS),
|
||||
LintId::of(uninit_vec::UNINIT_VEC),
|
||||
LintId::of(unit_hash::UNIT_HASH),
|
||||
LintId::of(unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
|
||||
LintId::of(unit_types::UNIT_CMP),
|
||||
LintId::of(unnamed_address::FN_ADDRESS_COMPARISONS),
|
||||
LintId::of(unnamed_address::VTABLE_ADDRESS_COMPARISONS),
|
||||
LintId::of(unused_io_amount::UNUSED_IO_AMOUNT),
|
||||
LintId::of(unwrap::PANICKING_UNWRAP),
|
||||
LintId::of(vec_resize_to_zero::VEC_RESIZE_TO_ZERO),
|
||||
])
|
||||
|
@ -38,7 +38,6 @@ store.register_lints(&[
|
||||
almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
|
||||
approx_const::APPROX_CONSTANT,
|
||||
as_conversions::AS_CONVERSIONS,
|
||||
as_underscore::AS_UNDERSCORE,
|
||||
asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
|
||||
asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
|
||||
assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
|
||||
@ -59,16 +58,14 @@ store.register_lints(&[
|
||||
bool_assert_comparison::BOOL_ASSERT_COMPARISON,
|
||||
booleans::NONMINIMAL_BOOL,
|
||||
booleans::OVERLY_COMPLEX_BOOL_EXPR,
|
||||
borrow_as_ptr::BORROW_AS_PTR,
|
||||
borrow_deref_ref::BORROW_DEREF_REF,
|
||||
bytecount::NAIVE_BYTECOUNT,
|
||||
bytes_count_to_len::BYTES_COUNT_TO_LEN,
|
||||
cargo::CARGO_COMMON_METADATA,
|
||||
cargo::MULTIPLE_CRATE_VERSIONS,
|
||||
cargo::NEGATIVE_FEATURE_NAMES,
|
||||
cargo::REDUNDANT_FEATURE_NAMES,
|
||||
cargo::WILDCARD_DEPENDENCIES,
|
||||
case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
casts::AS_UNDERSCORE,
|
||||
casts::BORROW_AS_PTR,
|
||||
casts::CAST_ABS_TO_UNSIGNED,
|
||||
casts::CAST_ENUM_CONSTRUCTOR,
|
||||
casts::CAST_ENUM_TRUNCATION,
|
||||
@ -80,6 +77,7 @@ store.register_lints(&[
|
||||
casts::CAST_REF_TO_MUT,
|
||||
casts::CAST_SIGN_LOSS,
|
||||
casts::CAST_SLICE_DIFFERENT_SIZES,
|
||||
casts::CAST_SLICE_FROM_RAW_PARTS,
|
||||
casts::CHAR_LIT_AS_U8,
|
||||
casts::FN_TO_NUMERIC_CAST,
|
||||
casts::FN_TO_NUMERIC_CAST_ANY,
|
||||
@ -173,11 +171,11 @@ store.register_lints(&[
|
||||
functions::MUST_USE_CANDIDATE,
|
||||
functions::MUST_USE_UNIT,
|
||||
functions::NOT_UNSAFE_PTR_ARG_DEREF,
|
||||
functions::RESULT_LARGE_ERR,
|
||||
functions::RESULT_UNIT_ERR,
|
||||
functions::TOO_MANY_ARGUMENTS,
|
||||
functions::TOO_MANY_LINES,
|
||||
future_not_send::FUTURE_NOT_SEND,
|
||||
get_first::GET_FIRST,
|
||||
if_let_mutex::IF_LET_MUTEX,
|
||||
if_not_else::IF_NOT_ELSE,
|
||||
if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
|
||||
@ -246,12 +244,10 @@ store.register_lints(&[
|
||||
manual_bits::MANUAL_BITS,
|
||||
manual_instant_elapsed::MANUAL_INSTANT_ELAPSED,
|
||||
manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
|
||||
manual_ok_or::MANUAL_OK_OR,
|
||||
manual_rem_euclid::MANUAL_REM_EUCLID,
|
||||
manual_retain::MANUAL_RETAIN,
|
||||
manual_string_new::MANUAL_STRING_NEW,
|
||||
manual_strip::MANUAL_STRIP,
|
||||
map_clone::MAP_CLONE,
|
||||
map_err_ignore::MAP_ERR_IGNORE,
|
||||
map_unit_fn::OPTION_MAP_UNIT_FN,
|
||||
map_unit_fn::RESULT_MAP_UNIT_FN,
|
||||
match_result_ok::MATCH_RESULT_OK,
|
||||
@ -284,13 +280,16 @@ store.register_lints(&[
|
||||
mem_replace::MEM_REPLACE_WITH_DEFAULT,
|
||||
mem_replace::MEM_REPLACE_WITH_UNINIT,
|
||||
methods::BIND_INSTEAD_OF_MAP,
|
||||
methods::BYTES_COUNT_TO_LEN,
|
||||
methods::BYTES_NTH,
|
||||
methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
|
||||
methods::CHARS_LAST_CMP,
|
||||
methods::CHARS_NEXT_CMP,
|
||||
methods::CLONED_INSTEAD_OF_COPIED,
|
||||
methods::CLONE_DOUBLE_REF,
|
||||
methods::CLONE_ON_COPY,
|
||||
methods::CLONE_ON_REF_PTR,
|
||||
methods::COLLAPSIBLE_STR_REPLACE,
|
||||
methods::ERR_EXPECT,
|
||||
methods::EXPECT_FUN_CALL,
|
||||
methods::EXPECT_USED,
|
||||
@ -302,6 +301,7 @@ store.register_lints(&[
|
||||
methods::FLAT_MAP_IDENTITY,
|
||||
methods::FLAT_MAP_OPTION,
|
||||
methods::FROM_ITER_INSTEAD_OF_COLLECT,
|
||||
methods::GET_FIRST,
|
||||
methods::GET_LAST_WITH_LEN,
|
||||
methods::GET_UNWRAP,
|
||||
methods::IMPLICIT_CLONE,
|
||||
@ -315,22 +315,30 @@ store.register_lints(&[
|
||||
methods::ITER_NEXT_SLICE,
|
||||
methods::ITER_NTH,
|
||||
methods::ITER_NTH_ZERO,
|
||||
methods::ITER_ON_EMPTY_COLLECTIONS,
|
||||
methods::ITER_ON_SINGLE_ITEMS,
|
||||
methods::ITER_OVEREAGER_CLONED,
|
||||
methods::ITER_SKIP_NEXT,
|
||||
methods::ITER_WITH_DRAIN,
|
||||
methods::MANUAL_FILTER_MAP,
|
||||
methods::MANUAL_FIND_MAP,
|
||||
methods::MANUAL_OK_OR,
|
||||
methods::MANUAL_SATURATING_ARITHMETIC,
|
||||
methods::MANUAL_SPLIT_ONCE,
|
||||
methods::MANUAL_STR_REPEAT,
|
||||
methods::MAP_CLONE,
|
||||
methods::MAP_COLLECT_RESULT_UNIT,
|
||||
methods::MAP_ERR_IGNORE,
|
||||
methods::MAP_FLATTEN,
|
||||
methods::MAP_IDENTITY,
|
||||
methods::MAP_UNWRAP_OR,
|
||||
methods::MUT_MUTEX_LOCK,
|
||||
methods::NAIVE_BYTECOUNT,
|
||||
methods::NEEDLESS_OPTION_AS_DEREF,
|
||||
methods::NEEDLESS_OPTION_TAKE,
|
||||
methods::NEEDLESS_SPLITN,
|
||||
methods::NEW_RET_NO_SELF,
|
||||
methods::NONSENSICAL_OPEN_OPTIONS,
|
||||
methods::NO_EFFECT_REPLACE,
|
||||
methods::OBFUSCATED_IF_ELSE,
|
||||
methods::OK_EXPECT,
|
||||
@ -339,25 +347,34 @@ store.register_lints(&[
|
||||
methods::OPTION_MAP_OR_NONE,
|
||||
methods::OR_FUN_CALL,
|
||||
methods::OR_THEN_UNWRAP,
|
||||
methods::PATH_BUF_PUSH_OVERWRITE,
|
||||
methods::RANGE_ZIP_WITH_LEN,
|
||||
methods::REPEAT_ONCE,
|
||||
methods::RESULT_MAP_OR_INTO_OPTION,
|
||||
methods::SEARCH_IS_SOME,
|
||||
methods::SHOULD_IMPLEMENT_TRAIT,
|
||||
methods::SINGLE_CHAR_ADD_STR,
|
||||
methods::SINGLE_CHAR_PATTERN,
|
||||
methods::SKIP_WHILE_NEXT,
|
||||
methods::STABLE_SORT_PRIMITIVE,
|
||||
methods::STRING_EXTEND_CHARS,
|
||||
methods::SUSPICIOUS_MAP,
|
||||
methods::SUSPICIOUS_SPLITN,
|
||||
methods::SUSPICIOUS_TO_OWNED,
|
||||
methods::UNINIT_ASSUMED_INIT,
|
||||
methods::UNIT_HASH,
|
||||
methods::UNNECESSARY_FILTER_MAP,
|
||||
methods::UNNECESSARY_FIND_MAP,
|
||||
methods::UNNECESSARY_FOLD,
|
||||
methods::UNNECESSARY_JOIN,
|
||||
methods::UNNECESSARY_LAZY_EVALUATIONS,
|
||||
methods::UNNECESSARY_SORT_BY,
|
||||
methods::UNNECESSARY_TO_OWNED,
|
||||
methods::UNWRAP_OR_ELSE_DEFAULT,
|
||||
methods::UNWRAP_USED,
|
||||
methods::USELESS_ASREF,
|
||||
methods::VEC_RESIZE_TO_ZERO,
|
||||
methods::VERBOSE_FILE_READS,
|
||||
methods::WRONG_SELF_CONVENTION,
|
||||
methods::ZST_OFFSET,
|
||||
minmax::MIN_MAX,
|
||||
@ -384,9 +401,9 @@ store.register_lints(&[
|
||||
mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
|
||||
module_style::MOD_MODULE_FILES,
|
||||
module_style::SELF_NAMED_MODULE_FILES,
|
||||
multi_assignments::MULTI_ASSIGNMENTS,
|
||||
mut_key::MUTABLE_KEY_TYPE,
|
||||
mut_mut::MUT_MUT,
|
||||
mut_mutex_lock::MUT_MUTEX_LOCK,
|
||||
mut_reference::UNNECESSARY_MUT_PASSED,
|
||||
mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
mutex_atomic::MUTEX_ATOMIC,
|
||||
@ -418,7 +435,6 @@ store.register_lints(&[
|
||||
nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
|
||||
octal_escapes::OCTAL_ESCAPES,
|
||||
only_used_in_recursion::ONLY_USED_IN_RECURSION,
|
||||
open_options::NONSENSICAL_OPEN_OPTIONS,
|
||||
operators::ABSURD_EXTREME_COMPARISONS,
|
||||
operators::ARITHMETIC,
|
||||
operators::ASSIGN_OP_PATTERN,
|
||||
@ -457,7 +473,6 @@ store.register_lints(&[
|
||||
partialeq_to_none::PARTIALEQ_TO_NONE,
|
||||
pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE,
|
||||
pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF,
|
||||
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
|
||||
pattern_type_mismatch::PATTERN_TYPE_MISMATCH,
|
||||
precedence::PRECEDENCE,
|
||||
ptr::CMP_NULL,
|
||||
@ -470,7 +485,6 @@ store.register_lints(&[
|
||||
ranges::MANUAL_RANGE_CONTAINS,
|
||||
ranges::RANGE_MINUS_ONE,
|
||||
ranges::RANGE_PLUS_ONE,
|
||||
ranges::RANGE_ZIP_WITH_LEN,
|
||||
ranges::REVERSED_EMPTY_RANGES,
|
||||
rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT,
|
||||
read_zero_byte_vec::READ_ZERO_BYTE_VEC,
|
||||
@ -486,7 +500,6 @@ store.register_lints(&[
|
||||
reference::DEREF_ADDROF,
|
||||
regex::INVALID_REGEX,
|
||||
regex::TRIVIAL_REGEX,
|
||||
repeat_once::REPEAT_ONCE,
|
||||
return_self_not_must_use::RETURN_SELF_NOT_MUST_USE,
|
||||
returns::LET_AND_RETURN,
|
||||
returns::NEEDLESS_RETURN,
|
||||
@ -501,7 +514,6 @@ store.register_lints(&[
|
||||
single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS,
|
||||
size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
|
||||
slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
|
||||
stable_sort_primitive::STABLE_SORT_PRIMITIVE,
|
||||
std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
|
||||
std_instead_of_core::STD_INSTEAD_OF_ALLOC,
|
||||
std_instead_of_core::STD_INSTEAD_OF_CORE,
|
||||
@ -537,10 +549,10 @@ store.register_lints(&[
|
||||
transmute::TRANSMUTE_PTR_TO_PTR,
|
||||
transmute::TRANSMUTE_PTR_TO_REF,
|
||||
transmute::TRANSMUTE_UNDEFINED_REPR,
|
||||
transmute::TRANSMUTING_NULL,
|
||||
transmute::UNSOUND_COLLECTION_TRANSMUTE,
|
||||
transmute::USELESS_TRANSMUTE,
|
||||
transmute::WRONG_TRANSMUTE,
|
||||
transmuting_null::TRANSMUTING_NULL,
|
||||
types::BORROWED_BOX,
|
||||
types::BOX_COLLECTION,
|
||||
types::LINKEDLIST,
|
||||
@ -555,7 +567,6 @@ store.register_lints(&[
|
||||
unicode::NON_ASCII_LITERAL,
|
||||
unicode::UNICODE_NOT_NFC,
|
||||
uninit_vec::UNINIT_VEC,
|
||||
unit_hash::UNIT_HASH,
|
||||
unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
|
||||
unit_types::LET_UNIT_VALUE,
|
||||
unit_types::UNIT_ARG,
|
||||
@ -564,12 +575,12 @@ store.register_lints(&[
|
||||
unnamed_address::VTABLE_ADDRESS_COMPARISONS,
|
||||
unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS,
|
||||
unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS,
|
||||
unnecessary_sort_by::UNNECESSARY_SORT_BY,
|
||||
unnecessary_wraps::UNNECESSARY_WRAPS,
|
||||
unnested_or_patterns::UNNESTED_OR_PATTERNS,
|
||||
unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME,
|
||||
unused_async::UNUSED_ASYNC,
|
||||
unused_io_amount::UNUSED_IO_AMOUNT,
|
||||
unused_peekable::UNUSED_PEEKABLE,
|
||||
unused_rounding::UNUSED_ROUNDING,
|
||||
unused_self::UNUSED_SELF,
|
||||
unused_unit::UNUSED_UNIT,
|
||||
@ -581,10 +592,9 @@ store.register_lints(&[
|
||||
useless_conversion::USELESS_CONVERSION,
|
||||
vec::USELESS_VEC,
|
||||
vec_init_then_push::VEC_INIT_THEN_PUSH,
|
||||
vec_resize_to_zero::VEC_RESIZE_TO_ZERO,
|
||||
verbose_file_reads::VERBOSE_FILE_READS,
|
||||
wildcard_imports::ENUM_GLOB_USE,
|
||||
wildcard_imports::WILDCARD_IMPORTS,
|
||||
write::POSITIONAL_NAMED_FORMAT_PARAMETERS,
|
||||
write::PRINTLN_EMPTY_STRING,
|
||||
write::PRINT_LITERAL,
|
||||
write::PRINT_STDERR,
|
||||
|
@ -14,16 +14,17 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![
|
||||
LintId::of(index_refutable_slice::INDEX_REFUTABLE_SLICE),
|
||||
LintId::of(let_if_seq::USELESS_LET_IF_SEQ),
|
||||
LintId::of(matches::SIGNIFICANT_DROP_IN_SCRUTINEE),
|
||||
LintId::of(methods::ITER_ON_EMPTY_COLLECTIONS),
|
||||
LintId::of(methods::ITER_ON_SINGLE_ITEMS),
|
||||
LintId::of(methods::ITER_WITH_DRAIN),
|
||||
LintId::of(methods::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN),
|
||||
LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL),
|
||||
LintId::of(mutex_atomic::MUTEX_ATOMIC),
|
||||
LintId::of(mutex_atomic::MUTEX_INTEGER),
|
||||
LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY),
|
||||
LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES),
|
||||
LintId::of(only_used_in_recursion::ONLY_USED_IN_RECURSION),
|
||||
LintId::of(option_if_let_else::OPTION_IF_LET_ELSE),
|
||||
LintId::of(path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE),
|
||||
LintId::of(redundant_pub_crate::REDUNDANT_PUB_CRATE),
|
||||
LintId::of(regex::TRIVIAL_REGEX),
|
||||
LintId::of(strings::STRING_LIT_AS_BYTES),
|
||||
|
@ -4,9 +4,7 @@
|
||||
|
||||
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(attrs::INLINE_ALWAYS),
|
||||
LintId::of(borrow_as_ptr::BORROW_AS_PTR),
|
||||
LintId::of(bytecount::NAIVE_BYTECOUNT),
|
||||
LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(casts::BORROW_AS_PTR),
|
||||
LintId::of(casts::CAST_LOSSLESS),
|
||||
LintId::of(casts::CAST_POSSIBLE_TRUNCATION),
|
||||
LintId::of(casts::CAST_POSSIBLE_WRAP),
|
||||
@ -50,20 +48,24 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(macro_use::MACRO_USE_IMPORTS),
|
||||
LintId::of(manual_assert::MANUAL_ASSERT),
|
||||
LintId::of(manual_instant_elapsed::MANUAL_INSTANT_ELAPSED),
|
||||
LintId::of(manual_ok_or::MANUAL_OK_OR),
|
||||
LintId::of(manual_string_new::MANUAL_STRING_NEW),
|
||||
LintId::of(matches::MATCH_BOOL),
|
||||
LintId::of(matches::MATCH_ON_VEC_ITEMS),
|
||||
LintId::of(matches::MATCH_SAME_ARMS),
|
||||
LintId::of(matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS),
|
||||
LintId::of(matches::MATCH_WILD_ERR_ARM),
|
||||
LintId::of(matches::SINGLE_MATCH_ELSE),
|
||||
LintId::of(methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
|
||||
LintId::of(methods::CLONED_INSTEAD_OF_COPIED),
|
||||
LintId::of(methods::FILTER_MAP_NEXT),
|
||||
LintId::of(methods::FLAT_MAP_OPTION),
|
||||
LintId::of(methods::FROM_ITER_INSTEAD_OF_COLLECT),
|
||||
LintId::of(methods::IMPLICIT_CLONE),
|
||||
LintId::of(methods::INEFFICIENT_TO_STRING),
|
||||
LintId::of(methods::MANUAL_OK_OR),
|
||||
LintId::of(methods::MAP_UNWRAP_OR),
|
||||
LintId::of(methods::NAIVE_BYTECOUNT),
|
||||
LintId::of(methods::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(methods::UNNECESSARY_JOIN),
|
||||
LintId::of(misc::USED_UNDERSCORE_BINDING),
|
||||
LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
|
||||
@ -85,7 +87,6 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
|
||||
LintId::of(ref_option_ref::REF_OPTION_REF),
|
||||
LintId::of(return_self_not_must_use::RETURN_SELF_NOT_MUST_USE),
|
||||
LintId::of(semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED),
|
||||
LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE),
|
||||
LintId::of(strings::STRING_ADD_ASSIGN),
|
||||
LintId::of(trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
|
||||
LintId::of(trait_bounds::TYPE_REPETITION_IN_BOUNDS),
|
||||
|
@ -7,12 +7,14 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
|
||||
LintId::of(escape::BOXED_LOCAL),
|
||||
LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
|
||||
LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
|
||||
LintId::of(functions::RESULT_LARGE_ERR),
|
||||
LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
|
||||
LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
|
||||
LintId::of(loops::MANUAL_MEMCPY),
|
||||
LintId::of(loops::MISSING_SPIN_LOOP),
|
||||
LintId::of(loops::NEEDLESS_COLLECT),
|
||||
LintId::of(manual_retain::MANUAL_RETAIN),
|
||||
LintId::of(methods::COLLAPSIBLE_STR_REPLACE),
|
||||
LintId::of(methods::EXPECT_FUN_CALL),
|
||||
LintId::of(methods::EXTEND_WITH_DRAIN),
|
||||
LintId::of(methods::ITER_NTH),
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
|
||||
LintId::of(as_conversions::AS_CONVERSIONS),
|
||||
LintId::of(as_underscore::AS_UNDERSCORE),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_ATT_SYNTAX),
|
||||
LintId::of(asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX),
|
||||
LintId::of(assertions_on_result_states::ASSERTIONS_ON_RESULT_STATES),
|
||||
LintId::of(attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON),
|
||||
LintId::of(casts::AS_UNDERSCORE),
|
||||
LintId::of(casts::FN_TO_NUMERIC_CAST_ANY),
|
||||
LintId::of(create_dir::CREATE_DIR),
|
||||
LintId::of(dbg_macro::DBG_MACRO),
|
||||
@ -30,7 +30,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(large_include_file::LARGE_INCLUDE_FILE),
|
||||
LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
|
||||
LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
|
||||
LintId::of(map_err_ignore::MAP_ERR_IGNORE),
|
||||
LintId::of(matches::REST_PAT_IN_FULLY_BOUND_STRUCTS),
|
||||
LintId::of(matches::TRY_ERR),
|
||||
LintId::of(matches::WILDCARD_ENUM_MATCH_ARM),
|
||||
@ -39,7 +38,9 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(methods::EXPECT_USED),
|
||||
LintId::of(methods::FILETYPE_IS_FILE),
|
||||
LintId::of(methods::GET_UNWRAP),
|
||||
LintId::of(methods::MAP_ERR_IGNORE),
|
||||
LintId::of(methods::UNWRAP_USED),
|
||||
LintId::of(methods::VERBOSE_FILE_READS),
|
||||
LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
|
||||
LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
|
||||
LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
|
||||
@ -81,7 +82,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
|
||||
LintId::of(unicode::NON_ASCII_LITERAL),
|
||||
LintId::of(unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS),
|
||||
LintId::of(unwrap_in_result::UNWRAP_IN_RESULT),
|
||||
LintId::of(verbose_file_reads::VERBOSE_FILE_READS),
|
||||
LintId::of(write::PRINT_STDERR),
|
||||
LintId::of(write::PRINT_STDOUT),
|
||||
LintId::of(write::USE_DEBUG),
|
||||
|
@ -29,7 +29,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(functions::DOUBLE_MUST_USE),
|
||||
LintId::of(functions::MUST_USE_UNIT),
|
||||
LintId::of(functions::RESULT_UNIT_ERR),
|
||||
LintId::of(get_first::GET_FIRST),
|
||||
LintId::of(inherent_to_string::INHERENT_TO_STRING),
|
||||
LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
|
||||
LintId::of(len_zero::COMPARISON_TO_EMPTY),
|
||||
@ -45,7 +44,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
|
||||
LintId::of(manual_bits::MANUAL_BITS),
|
||||
LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
|
||||
LintId::of(map_clone::MAP_CLONE),
|
||||
LintId::of(match_result_ok::MATCH_RESULT_OK),
|
||||
LintId::of(matches::COLLAPSIBLE_MATCH),
|
||||
LintId::of(matches::INFALLIBLE_DESTRUCTURING_MATCH),
|
||||
@ -61,6 +59,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(methods::CHARS_LAST_CMP),
|
||||
LintId::of(methods::CHARS_NEXT_CMP),
|
||||
LintId::of(methods::ERR_EXPECT),
|
||||
LintId::of(methods::GET_FIRST),
|
||||
LintId::of(methods::INTO_ITER_ON_REF),
|
||||
LintId::of(methods::IS_DIGIT_ASCII_RADIX),
|
||||
LintId::of(methods::ITER_CLONED_COLLECT),
|
||||
@ -68,7 +67,9 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(methods::ITER_NTH_ZERO),
|
||||
LintId::of(methods::ITER_SKIP_NEXT),
|
||||
LintId::of(methods::MANUAL_SATURATING_ARITHMETIC),
|
||||
LintId::of(methods::MAP_CLONE),
|
||||
LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
|
||||
LintId::of(methods::MUT_MUTEX_LOCK),
|
||||
LintId::of(methods::NEW_RET_NO_SELF),
|
||||
LintId::of(methods::OBFUSCATED_IF_ELSE),
|
||||
LintId::of(methods::OK_EXPECT),
|
||||
@ -88,7 +89,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
|
||||
LintId::of(misc_early::DUPLICATE_UNDERSCORE_ARGUMENT),
|
||||
LintId::of(misc_early::MIXED_CASE_HEX_LITERALS),
|
||||
LintId::of(misc_early::REDUNDANT_PATTERN),
|
||||
LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK),
|
||||
LintId::of(mut_reference::UNNECESSARY_MUT_PASSED),
|
||||
LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
|
||||
LintId::of(needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS),
|
||||
|
@ -11,6 +11,7 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||
LintId::of(casts::CAST_ABS_TO_UNSIGNED),
|
||||
LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
|
||||
LintId::of(casts::CAST_ENUM_TRUNCATION),
|
||||
LintId::of(casts::CAST_SLICE_FROM_RAW_PARTS),
|
||||
LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
|
||||
LintId::of(drop_forget_ref::DROP_NON_DROP),
|
||||
LintId::of(drop_forget_ref::FORGET_NON_DROP),
|
||||
@ -24,6 +25,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||
LintId::of(loops::MUT_RANGE_BOUND),
|
||||
LintId::of(methods::NO_EFFECT_REPLACE),
|
||||
LintId::of(methods::SUSPICIOUS_MAP),
|
||||
LintId::of(methods::SUSPICIOUS_TO_OWNED),
|
||||
LintId::of(multi_assignments::MULTI_ASSIGNMENTS),
|
||||
LintId::of(mut_key::MUTABLE_KEY_TYPE),
|
||||
LintId::of(octal_escapes::OCTAL_ESCAPES),
|
||||
LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
|
||||
@ -32,4 +35,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
|
||||
LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
|
||||
LintId::of(swap_ptr_to_ref::SWAP_PTR_TO_REF),
|
||||
LintId::of(unused_peekable::UNUSED_PEEKABLE),
|
||||
LintId::of(write::POSITIONAL_NAMED_FORMAT_PARAMETERS),
|
||||
])
|
||||
|
@ -171,7 +171,6 @@ mod renamed_lints;
|
||||
mod almost_complete_letter_range;
|
||||
mod approx_const;
|
||||
mod as_conversions;
|
||||
mod as_underscore;
|
||||
mod asm_syntax;
|
||||
mod assertions_on_constants;
|
||||
mod assertions_on_result_states;
|
||||
@ -181,12 +180,8 @@ mod await_holding_invalid;
|
||||
mod blocks_in_if_conditions;
|
||||
mod bool_assert_comparison;
|
||||
mod booleans;
|
||||
mod borrow_as_ptr;
|
||||
mod borrow_deref_ref;
|
||||
mod bytecount;
|
||||
mod bytes_count_to_len;
|
||||
mod cargo;
|
||||
mod case_sensitive_file_extension_comparisons;
|
||||
mod casts;
|
||||
mod checked_conversions;
|
||||
mod cognitive_complexity;
|
||||
@ -239,7 +234,6 @@ mod from_over_into;
|
||||
mod from_str_radix_10;
|
||||
mod functions;
|
||||
mod future_not_send;
|
||||
mod get_first;
|
||||
mod if_let_mutex;
|
||||
mod if_not_else;
|
||||
mod if_then_some_else_none;
|
||||
@ -276,12 +270,10 @@ mod manual_async_fn;
|
||||
mod manual_bits;
|
||||
mod manual_instant_elapsed;
|
||||
mod manual_non_exhaustive;
|
||||
mod manual_ok_or;
|
||||
mod manual_rem_euclid;
|
||||
mod manual_retain;
|
||||
mod manual_string_new;
|
||||
mod manual_strip;
|
||||
mod map_clone;
|
||||
mod map_err_ignore;
|
||||
mod map_unit_fn;
|
||||
mod match_result_ok;
|
||||
mod matches;
|
||||
@ -298,9 +290,9 @@ mod missing_enforced_import_rename;
|
||||
mod missing_inline;
|
||||
mod mixed_read_write_in_expression;
|
||||
mod module_style;
|
||||
mod multi_assignments;
|
||||
mod mut_key;
|
||||
mod mut_mut;
|
||||
mod mut_mutex_lock;
|
||||
mod mut_reference;
|
||||
mod mutable_debug_assertion;
|
||||
mod mutex_atomic;
|
||||
@ -325,7 +317,6 @@ mod non_send_fields_in_send_ty;
|
||||
mod nonstandard_macro_braces;
|
||||
mod octal_escapes;
|
||||
mod only_used_in_recursion;
|
||||
mod open_options;
|
||||
mod operators;
|
||||
mod option_env_unwrap;
|
||||
mod option_if_let_else;
|
||||
@ -335,7 +326,6 @@ mod panic_unimplemented;
|
||||
mod partialeq_ne_impl;
|
||||
mod partialeq_to_none;
|
||||
mod pass_by_ref_or_value;
|
||||
mod path_buf_push_overwrite;
|
||||
mod pattern_type_mismatch;
|
||||
mod precedence;
|
||||
mod ptr;
|
||||
@ -355,7 +345,6 @@ mod redundant_static_lifetimes;
|
||||
mod ref_option_ref;
|
||||
mod reference;
|
||||
mod regex;
|
||||
mod repeat_once;
|
||||
mod return_self_not_must_use;
|
||||
mod returns;
|
||||
mod same_name_method;
|
||||
@ -367,7 +356,6 @@ mod single_char_lifetime_names;
|
||||
mod single_component_path_imports;
|
||||
mod size_of_in_element_count;
|
||||
mod slow_vector_initialization;
|
||||
mod stable_sort_primitive;
|
||||
mod std_instead_of_core;
|
||||
mod strings;
|
||||
mod strlen_on_c_strings;
|
||||
@ -381,23 +369,21 @@ mod to_digit_is_some;
|
||||
mod trailing_empty_array;
|
||||
mod trait_bounds;
|
||||
mod transmute;
|
||||
mod transmuting_null;
|
||||
mod types;
|
||||
mod undocumented_unsafe_blocks;
|
||||
mod unicode;
|
||||
mod uninit_vec;
|
||||
mod unit_hash;
|
||||
mod unit_return_expecting_ord;
|
||||
mod unit_types;
|
||||
mod unnamed_address;
|
||||
mod unnecessary_owned_empty_strings;
|
||||
mod unnecessary_self_imports;
|
||||
mod unnecessary_sort_by;
|
||||
mod unnecessary_wraps;
|
||||
mod unnested_or_patterns;
|
||||
mod unsafe_removed_from_name;
|
||||
mod unused_async;
|
||||
mod unused_io_amount;
|
||||
mod unused_peekable;
|
||||
mod unused_rounding;
|
||||
mod unused_self;
|
||||
mod unused_unit;
|
||||
@ -408,8 +394,6 @@ mod use_self;
|
||||
mod useless_conversion;
|
||||
mod vec;
|
||||
mod vec_init_then_push;
|
||||
mod vec_resize_to_zero;
|
||||
mod verbose_file_reads;
|
||||
mod wildcard_imports;
|
||||
mod write;
|
||||
mod zero_div_zero;
|
||||
@ -597,7 +581,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(blocks_in_if_conditions::BlocksInIfConditions));
|
||||
store.register_late_pass(|| Box::new(unicode::Unicode));
|
||||
store.register_late_pass(|| Box::new(uninit_vec::UninitVec));
|
||||
store.register_late_pass(|| Box::new(unit_hash::UnitHash));
|
||||
store.register_late_pass(|| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd));
|
||||
store.register_late_pass(|| Box::new(strings::StringAdd));
|
||||
store.register_late_pass(|| Box::new(implicit_return::ImplicitReturn));
|
||||
@ -635,8 +618,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(needless_question_mark::NeedlessQuestionMark));
|
||||
store.register_late_pass(move || Box::new(casts::Casts::new(msrv)));
|
||||
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(map_clone::MapClone::new(msrv)));
|
||||
|
||||
store.register_late_pass(|| Box::new(size_of_in_element_count::SizeOfInElementCount));
|
||||
store.register_late_pass(|| Box::new(same_name_method::SameNameMethod));
|
||||
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
|
||||
@ -646,7 +627,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
msrv,
|
||||
))
|
||||
});
|
||||
store.register_late_pass(|| Box::new(map_err_ignore::MapErrIgnore));
|
||||
store.register_late_pass(|| Box::new(shadow::Shadow::default()));
|
||||
store.register_late_pass(|| Box::new(unit_types::UnitTypes));
|
||||
store.register_late_pass(|| Box::new(loops::Loops));
|
||||
@ -654,7 +634,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(lifetimes::Lifetimes));
|
||||
store.register_late_pass(|| Box::new(entry::HashMapPass));
|
||||
store.register_late_pass(|| Box::new(minmax::MinMaxPass));
|
||||
store.register_late_pass(|| Box::new(open_options::OpenOptions));
|
||||
store.register_late_pass(|| Box::new(zero_div_zero::ZeroDiv));
|
||||
store.register_late_pass(|| Box::new(mutex_atomic::Mutex));
|
||||
store.register_late_pass(|| Box::new(needless_update::NeedlessUpdate));
|
||||
@ -690,10 +669,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(move || Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
|
||||
let too_many_arguments_threshold = conf.too_many_arguments_threshold;
|
||||
let too_many_lines_threshold = conf.too_many_lines_threshold;
|
||||
let large_error_threshold = conf.large_error_threshold;
|
||||
store.register_late_pass(move || {
|
||||
Box::new(functions::Functions::new(
|
||||
too_many_arguments_threshold,
|
||||
too_many_lines_threshold,
|
||||
large_error_threshold,
|
||||
))
|
||||
});
|
||||
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>();
|
||||
@ -720,7 +701,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
);
|
||||
store.register_late_pass(move || Box::new(pass_by_ref_or_value));
|
||||
store.register_late_pass(|| Box::new(ref_option_ref::RefOptionRef));
|
||||
store.register_late_pass(|| Box::new(bytecount::ByteCount));
|
||||
store.register_late_pass(|| Box::new(infinite_iter::InfiniteIter));
|
||||
store.register_late_pass(|| Box::new(inline_fn_without_body::InlineFnWithoutBody));
|
||||
store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
|
||||
@ -738,12 +718,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
|
||||
store.register_late_pass(|| Box::new(redundant_clone::RedundantClone));
|
||||
store.register_late_pass(|| Box::new(slow_vector_initialization::SlowVectorInit));
|
||||
store.register_late_pass(|| Box::new(unnecessary_sort_by::UnnecessarySortBy));
|
||||
store.register_late_pass(move || Box::new(unnecessary_wraps::UnnecessaryWraps::new(avoid_breaking_exported_api)));
|
||||
store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
|
||||
store.register_late_pass(|| Box::new(assertions_on_result_states::AssertionsOnResultStates));
|
||||
store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
|
||||
store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
|
||||
store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
|
||||
let max_trait_bounds = conf.max_trait_bounds;
|
||||
store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
|
||||
@ -819,18 +796,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
|
||||
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
|
||||
store.register_late_pass(move || Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
|
||||
store.register_late_pass(|| Box::new(verbose_file_reads::VerboseFileReads));
|
||||
store.register_late_pass(|| Box::new(redundant_pub_crate::RedundantPubCrate::default()));
|
||||
store.register_late_pass(|| Box::new(unnamed_address::UnnamedAddress));
|
||||
store.register_late_pass(|| Box::new(dereference::Dereferencing::default()));
|
||||
store.register_late_pass(move || Box::new(dereference::Dereferencing::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(option_if_let_else::OptionIfLetElse));
|
||||
store.register_late_pass(|| Box::new(future_not_send::FutureNotSend));
|
||||
store.register_late_pass(|| Box::new(if_let_mutex::IfLetMutex));
|
||||
store.register_late_pass(|| Box::new(if_not_else::IfNotElse));
|
||||
store.register_late_pass(|| Box::new(equatable_if_let::PatternEquality));
|
||||
store.register_late_pass(|| Box::new(mut_mutex_lock::MutMutexLock));
|
||||
store.register_late_pass(|| Box::new(manual_async_fn::ManualAsyncFn));
|
||||
store.register_late_pass(|| Box::new(vec_resize_to_zero::VecResizeToZero));
|
||||
store.register_late_pass(|| Box::new(panic_in_result_fn::PanicInResultFn));
|
||||
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
|
||||
store.register_early_pass(move || {
|
||||
@ -842,10 +816,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(¯o_matcher)));
|
||||
store.register_late_pass(|| Box::new(macro_use::MacroUseImports::default()));
|
||||
store.register_late_pass(|| Box::new(pattern_type_mismatch::PatternTypeMismatch));
|
||||
store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
|
||||
store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
|
||||
store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
|
||||
store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
|
||||
store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
|
||||
store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
|
||||
let disallowed_methods = conf.disallowed_methods.clone();
|
||||
@ -857,9 +828,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(strings::StringToString));
|
||||
store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
|
||||
store.register_late_pass(|| Box::new(vec_init_then_push::VecInitThenPush::default()));
|
||||
store.register_late_pass(|| {
|
||||
Box::new(case_sensitive_file_extension_comparisons::CaseSensitiveFileExtensionComparisons)
|
||||
});
|
||||
store.register_late_pass(|| Box::new(redundant_slicing::RedundantSlicing));
|
||||
store.register_late_pass(|| Box::new(from_str_radix_10::FromStrRadix10));
|
||||
store.register_late_pass(move || Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv)));
|
||||
@ -894,11 +862,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse));
|
||||
store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields));
|
||||
store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames));
|
||||
store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv)));
|
||||
store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(default_union_representation::DefaultUnionRepresentation));
|
||||
store.register_early_pass(|| Box::new(doc_link_with_quotes::DocLinkWithQuotes));
|
||||
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion));
|
||||
store.register_late_pass(|| Box::new(only_used_in_recursion::OnlyUsedInRecursion::default()));
|
||||
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
|
||||
store.register_late_pass(move || Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
|
||||
let cargo_ignore_publish = conf.cargo_ignore_publish;
|
||||
@ -912,18 +879,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
|
||||
store.register_early_pass(|| Box::new(pub_use::PubUse));
|
||||
store.register_late_pass(|| Box::new(format_push_string::FormatPushString));
|
||||
store.register_late_pass(|| Box::new(bytes_count_to_len::BytesCountToLen));
|
||||
let max_include_file_size = conf.max_include_file_size;
|
||||
store.register_late_pass(move || Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
|
||||
store.register_late_pass(|| Box::new(strings::TrimSplitWhitespace));
|
||||
store.register_late_pass(|| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
|
||||
store.register_early_pass(|| Box::new(duplicate_mod::DuplicateMod::default()));
|
||||
store.register_late_pass(|| Box::new(get_first::GetFirst));
|
||||
store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding));
|
||||
store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv)));
|
||||
store.register_late_pass(|| Box::new(swap_ptr_to_ref::SwapPtrToRef));
|
||||
store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
|
||||
store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
|
||||
store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
|
||||
store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
|
||||
store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
|
||||
@ -934,6 +898,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
|
||||
store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports::default()));
|
||||
store.register_late_pass(|| Box::new(manual_instant_elapsed::ManualInstantElapsed));
|
||||
store.register_late_pass(|| Box::new(partialeq_to_none::PartialeqToNone));
|
||||
store.register_late_pass(|| Box::new(manual_string_new::ManualStringNew));
|
||||
store.register_late_pass(|| Box::new(unused_peekable::UnusedPeekable));
|
||||
store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::NEEDLESS_COLLECT;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
|
||||
use clippy_utils::higher;
|
||||
use clippy_utils::source::{snippet, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
@ -184,10 +185,19 @@ struct IterFunctionVisitor<'a, 'tcx> {
|
||||
impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
||||
fn visit_block(&mut self, block: &'tcx Block<'tcx>) {
|
||||
for (expr, hir_id) in block.stmts.iter().filter_map(get_expr_and_hir_id_from_stmt) {
|
||||
if check_loop_kind(expr).is_some() {
|
||||
continue;
|
||||
}
|
||||
self.visit_block_expr(expr, hir_id);
|
||||
}
|
||||
if let Some(expr) = block.expr {
|
||||
self.visit_block_expr(expr, None);
|
||||
if let Some(loop_kind) = check_loop_kind(expr) {
|
||||
if let LoopKind::Conditional(block_expr) = loop_kind {
|
||||
self.visit_block_expr(block_expr, None);
|
||||
}
|
||||
} else {
|
||||
self.visit_block_expr(expr, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,6 +274,28 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
enum LoopKind<'tcx> {
|
||||
Conditional(&'tcx Expr<'tcx>),
|
||||
Loop,
|
||||
}
|
||||
|
||||
fn check_loop_kind<'tcx>(expr: &Expr<'tcx>) -> Option<LoopKind<'tcx>> {
|
||||
if let Some(higher::WhileLet { let_expr, .. }) = higher::WhileLet::hir(expr) {
|
||||
return Some(LoopKind::Conditional(let_expr));
|
||||
}
|
||||
if let Some(higher::While { condition, .. }) = higher::While::hir(expr) {
|
||||
return Some(LoopKind::Conditional(condition));
|
||||
}
|
||||
if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr) {
|
||||
return Some(LoopKind::Conditional(arg));
|
||||
}
|
||||
if let ExprKind::Loop { .. } = expr.kind {
|
||||
return Some(LoopKind::Loop);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
impl<'tcx> IterFunctionVisitor<'_, 'tcx> {
|
||||
fn visit_block_expr(&mut self, expr: &'tcx Expr<'tcx>, hir_id: Option<HirId>) {
|
||||
self.current_statement_hir_id = hir_id;
|
||||
|
@ -192,7 +192,7 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
|
||||
match output.kind {
|
||||
TyKind::Tup(tys) if tys.is_empty() => {
|
||||
let sugg = "remove the return type";
|
||||
Some((sugg, "".into()))
|
||||
Some((sugg, String::new()))
|
||||
},
|
||||
_ => {
|
||||
let sugg = "return the output of the future directly";
|
||||
|
@ -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 }
|
||||
}
|
||||
}
|
140
src/tools/clippy/clippy_lints/src/manual_string_new.rs
Normal file
140
src/tools/clippy/clippy_lints/src/manual_string_new.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
@ -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| ...`)",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::is_wild;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::span_contains_comment;
|
||||
use rustc_ast::{Attribute, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::source_map::Spanned;
|
||||
|
||||
@ -76,6 +77,7 @@ where
|
||||
>,
|
||||
{
|
||||
if_chain! {
|
||||
if !span_contains_comment(cx.sess().source_map(), expr.span);
|
||||
if iter.len() >= 2;
|
||||
if cx.typeck_results().expr_ty(expr).is_bool();
|
||||
if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
|
||||
|
@ -8,7 +8,7 @@ use clippy_utils::{
|
||||
};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::LangItem::OptionNone;
|
||||
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
|
||||
use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Guard, Node, Pat, PatKind, Path, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::sym;
|
||||
use rustc_typeck::hir_ty_to_ty;
|
||||
@ -65,8 +65,26 @@ pub(crate) fn check_if_let<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'_>, if_let:
|
||||
fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
|
||||
for arm in arms {
|
||||
let arm_expr = peel_blocks_with_stmt(arm.body);
|
||||
|
||||
if let Some(guard_expr) = &arm.guard {
|
||||
match guard_expr {
|
||||
// gives up if `pat if expr` can have side effects
|
||||
Guard::If(if_cond) => {
|
||||
if if_cond.can_have_side_effects() {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
// gives up `pat if let ...` arm
|
||||
Guard::IfLet(_) => {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if let PatKind::Wild = arm.pat.kind {
|
||||
return eq_expr_value(cx, match_expr, strip_return(arm_expr));
|
||||
if !eq_expr_value(cx, match_expr, strip_return(arm_expr)) {
|
||||
return false;
|
||||
}
|
||||
} else if !pat_same_as_expr(arm.pat, arm_expr) {
|
||||
return false;
|
||||
}
|
||||
|
70
src/tools/clippy/clippy_lints/src/methods/bytecount.rs
Normal file
70
src/tools/clippy/clippy_lints/src/methods/bytecount.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
@ -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
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
@ -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",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
@ -7,18 +7,26 @@ use rustc_span::sym;
|
||||
|
||||
use super::EXPECT_USED;
|
||||
|
||||
/// lint use of `expect()` for `Option`s and `Result`s
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>, allow_expect_in_tests: bool) {
|
||||
/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`.
|
||||
pub(super) fn check(
|
||||
cx: &LateContext<'_>,
|
||||
expr: &hir::Expr<'_>,
|
||||
recv: &hir::Expr<'_>,
|
||||
is_err: bool,
|
||||
allow_expect_in_tests: bool,
|
||||
) {
|
||||
let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs();
|
||||
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) {
|
||||
let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err {
|
||||
Some((EXPECT_USED, "an Option", "None", ""))
|
||||
} else if is_type_diagnostic_item(cx, obj_ty, sym::Result) {
|
||||
Some((EXPECT_USED, "a Result", "Err", "an "))
|
||||
Some((EXPECT_USED, "a Result", if is_err { "Ok" } else { "Err" }, "an "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let method = if is_err { "expect_err" } else { "expect" };
|
||||
|
||||
if allow_expect_in_tests && is_in_test_function(cx.tcx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
@ -28,7 +36,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
|
||||
cx,
|
||||
lint,
|
||||
expr.span,
|
||||
&format!("used `expect()` on `{kind}` value"),
|
||||
&format!("used `{method}()` on `{kind}` value"),
|
||||
None,
|
||||
&format!("if this value is {none_prefix}`{none_value}`, it will panic"),
|
||||
);
|
||||
|
39
src/tools/clippy/clippy_lints/src/methods/get_first.rs
Normal file
39
src/tools/clippy/clippy_lints/src/methods/get_first.rs
Normal 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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
64
src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
Normal file
64
src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs
Normal 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 }
|
||||
}
|
||||
}
|
122
src/tools/clippy/clippy_lints/src/methods/map_clone.rs
Normal file
122
src/tools/clippy/clippy_lints/src/methods/map_clone.rs
Normal 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,
|
||||
);
|
||||
}
|
34
src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
Normal file
34
src/tools/clippy/clippy_lints/src/methods/map_err_ignore.rs
Normal 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
30
src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
Normal file
30
src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
Normal 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
Loading…
Reference in New Issue
Block a user