Rollup merge of #78545 - jackh726:anonymous, r=oli-obk
Make anonymous binders start at 0
A few changes to some test outputs, but these actually look *more* correct to me.
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index a3f0469..2698110 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -148,7 +148,7 @@
is_collecting_in_band_lifetimes: bool,
/// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
- /// When `is_collectin_in_band_lifetimes` is true, each lifetime is checked
+ /// When `is_collecting_in_band_lifetimes` is true, each lifetime is checked
/// against this list to see if it is already in-scope, or if a definition
/// needs to be created for it.
///
@@ -257,7 +257,7 @@
/// Disallowed in `let` / `const` / `static` bindings.
Binding,
- /// All other posiitons.
+ /// All other positions.
Other,
}
@@ -363,7 +363,7 @@
/// elided bounds follow special rules. Note that this only covers
/// cases where *nothing* is written; the `'_` in `Box<dyn Foo +
/// '_>` is a case of "modern" elision.
-/// - **Deprecated** -- this coverse cases like `Ref<T>`, where the lifetime
+/// - **Deprecated** -- this covers cases like `Ref<T>`, where the lifetime
/// parameter to ref is completely elided. `Ref<'_, T>` would be the modern,
/// non-deprecated equivalent.
///
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index b1c8e0e..d6585bc 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -516,7 +516,7 @@
self.session.source_map().guess_head_span(self.extern_mod.unwrap().span)
}
- /// An `fn` in `extern { ... }` cannot have qualfiers, e.g. `async fn`.
+ /// An `fn` in `extern { ... }` cannot have qualifiers, e.g. `async fn`.
fn check_foreign_fn_headerless(&self, ident: Ident, span: Span, header: FnHeader) {
if header.has_qualifiers() {
self.err_handler()
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 029a6cb..8be3441 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -156,24 +156,13 @@
}
}
match tt {
- TokenTree::Token(token) => match token.kind {
- token::Comma => false,
- _ => true,
- },
- TokenTree::Delimited(_, DelimToken::Paren, _) => match prev {
- TokenTree::Token(token) => match token.kind {
- token::Ident(_, _) => false,
- _ => true,
- },
- _ => true,
- },
- TokenTree::Delimited(_, DelimToken::Bracket, _) => match prev {
- TokenTree::Token(token) => match token.kind {
- token::Pound => false,
- _ => true,
- },
- _ => true,
- },
+ TokenTree::Token(token) => token.kind != token::Comma,
+ TokenTree::Delimited(_, DelimToken::Paren, _) => {
+ !matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }))
+ }
+ TokenTree::Delimited(_, DelimToken::Bracket, _) => {
+ !matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }))
+ }
TokenTree::Delimited(..) => true,
}
}
diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs
index d22f3ad..26706cd 100644
--- a/compiler/rustc_data_structures/src/sync.rs
+++ b/compiler/rustc_data_structures/src/sync.rs
@@ -512,7 +512,7 @@
}
}
-#[derive(Debug)]
+#[derive(Debug, Default)]
pub struct RwLock<T>(InnerRwLock<T>);
impl<T> RwLock<T> {
diff --git a/compiler/rustc_data_structures/src/tagged_ptr.rs b/compiler/rustc_data_structures/src/tagged_ptr.rs
index e3839d1..cd1e12c 100644
--- a/compiler/rustc_data_structures/src/tagged_ptr.rs
+++ b/compiler/rustc_data_structures/src/tagged_ptr.rs
@@ -24,7 +24,7 @@
pub use copy::CopyTaggedPtr;
pub use drop::TaggedPtr;
-/// This describes the pointer type encaspulated by TaggedPtr.
+/// This describes the pointer type encapsulated by TaggedPtr.
///
/// # Safety
///
diff --git a/compiler/rustc_data_structures/src/transitive_relation.rs b/compiler/rustc_data_structures/src/transitive_relation.rs
index fe60a99..2e1512b 100644
--- a/compiler/rustc_data_structures/src/transitive_relation.rs
+++ b/compiler/rustc_data_structures/src/transitive_relation.rs
@@ -18,7 +18,7 @@
edges: Vec<Edge>,
// This is a cached transitive closure derived from the edges.
- // Currently, we build it lazilly and just throw out any existing
+ // Currently, we build it lazily and just throw out any existing
// copy whenever a new edge is added. (The Lock is to permit
// the lazy computation.) This is kind of silly, except for the
// fact its size is tied to `self.elements.len()`, so I wanted to
@@ -255,7 +255,7 @@
// argument is that, after step 2, we know that no element
// can reach its successors (in the vector, not the graph).
// After step 3, we know that no element can reach any of
- // its predecesssors (because of step 2) nor successors
+ // its predecessors (because of step 2) nor successors
// (because we just called `pare_down`)
//
// This same algorithm is used in `parents` below.
diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs
index b5155f8..08e9bdf 100644
--- a/compiler/rustc_errors/src/emitter.rs
+++ b/compiler/rustc_errors/src/emitter.rs
@@ -513,7 +513,7 @@
/// Maximum number of lines we will print for a multiline suggestion; arbitrary.
///
/// This should be replaced with a more involved mechanism to output multiline suggestions that
-/// more closely mimmics the regular diagnostic output, where irrelevant code lines are elided.
+/// more closely mimics the regular diagnostic output, where irrelevant code lines are elided.
pub const MAX_SUGGESTION_HIGHLIGHT_LINES: usize = 6;
/// Maximum number of suggestions to be shown
///
@@ -887,7 +887,7 @@
// or the next are vertical line placeholders.
|| (annotation.takes_space() // If either this or the next annotation is
&& next.has_label()) // multiline start/end, move it to a new line
- || (annotation.has_label() // so as not to overlap the orizontal lines.
+ || (annotation.has_label() // so as not to overlap the horizontal lines.
&& next.takes_space())
|| (annotation.takes_space() && next.takes_space())
|| (overlaps(next, annotation, l)
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index c899084..2ecdff1 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -35,6 +35,8 @@
#![feature(never_type)]
#![feature(nll)]
#![feature(or_patterns)]
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
#![recursion_limit = "256"]
#[macro_use]
diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs
index b502bd7..bd02503 100644
--- a/compiler/rustc_lint/src/types.rs
+++ b/compiler/rustc_lint/src/types.rs
@@ -439,7 +439,7 @@
cx.struct_span_lint(OVERFLOWING_LITERALS, e.span, |lint| {
lint.build(&format!("literal out of range for `{}`", t.name_str()))
.note(&format!(
- "the literal `{}` does not fit into the type `{}` and will be converted to `std::{}::INFINITY`",
+ "the literal `{}` does not fit into the type `{}` and will be converted to `{}::INFINITY`",
cx.sess()
.source_map()
.span_to_snippet(lit.span)
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 2409069..17f0d56 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -250,13 +250,13 @@
has_emitted
}
ty::Array(ty, len) => match len.try_eval_usize(cx.tcx, cx.param_env) {
+ // If the array is empty we don't lint, to avoid false positives
+ Some(0) | None => false,
// If the array is definitely non-empty, we can do `#[must_use]` checking.
- Some(n) if n != 0 => {
+ Some(n) => {
let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,);
check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, n as usize + 1)
}
- // Otherwise, we don't lint, to avoid false positives.
- _ => false,
},
ty::Closure(..) => {
cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
index 3beb328..869aaa5 100644
--- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
+++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
@@ -766,7 +766,7 @@
LLVMMetadataRef File, unsigned LineNo, LLVMMetadataRef Scope) {
return wrap(Builder->createTypedef(
unwrap<DIType>(Type), StringRef(Name, NameLen), unwrap<DIFile>(File),
- LineNo, unwrap<DIScope>(Scope)));
+ LineNo, unwrapDIPtr<DIScope>(Scope)));
}
extern "C" LLVMMetadataRef LLVMRustDIBuilderCreatePointerType(
diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs
index 7e33a47..2a81737 100644
--- a/compiler/rustc_metadata/src/rmeta/encoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/encoder.rs
@@ -2042,6 +2042,10 @@
encoder.emit_raw_bytes(&[0, 0, 0, 0]);
let source_map_files = tcx.sess.source_map().files();
+ let source_file_cache = (source_map_files[0].clone(), 0);
+ let required_source_files = Some(GrowableBitSet::with_capacity(source_map_files.len()));
+ drop(source_map_files);
+
let hygiene_ctxt = HygieneEncodeContext::default();
let mut ecx = EncodeContext {
@@ -2052,13 +2056,12 @@
lazy_state: LazyState::NoNode,
type_shorthands: Default::default(),
predicate_shorthands: Default::default(),
- source_file_cache: (source_map_files[0].clone(), 0),
+ source_file_cache,
interpret_allocs: Default::default(),
- required_source_files: Some(GrowableBitSet::with_capacity(source_map_files.len())),
+ required_source_files,
is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro),
hygiene_ctxt: &hygiene_ctxt,
};
- drop(source_map_files);
// Encode the rustc version string in a predictable location.
rustc_version().encode(&mut ecx).unwrap();
diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs
index fa885ce..5ccadb7 100644
--- a/compiler/rustc_middle/src/lib.rs
+++ b/compiler/rustc_middle/src/lib.rs
@@ -47,6 +47,8 @@
#![feature(associated_type_bounds)]
#![feature(rustc_attrs)]
#![feature(int_error_matching)]
+#![feature(half_open_range_patterns)]
+#![feature(exclusive_range_pattern)]
#![recursion_limit = "512"]
#[macro_use]
diff --git a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
index bf1f5b8..2f7707b 100644
--- a/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
+++ b/compiler/rustc_middle/src/ty/inhabitedness/mod.rs
@@ -201,13 +201,13 @@
),
Array(ty, len) => match len.try_eval_usize(tcx, param_env) {
+ Some(0) | None => DefIdForest::empty(),
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
- Some(n) if n != 0 => ty.uninhabited_from(tcx, param_env),
- _ => DefIdForest::empty(),
+ Some(1..) => ty.uninhabited_from(tcx, param_env),
},
- // References to uninitialised memory is valid for any type, including
+ // References to uninitialised memory are valid for any type, including
// uninhabited types, in unsafe code, so we treat all references as
// inhabited.
// The precise semantics of inhabitedness with respect to references is currently
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 0fd48d0..384d08f 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -388,9 +388,19 @@
self.split().parent_substs
}
+ /// Returns an iterator over the list of types of captured paths by the closure.
+ /// In case there was a type error in figuring out the types of the captured path, an
+ /// empty iterator is returned.
#[inline]
pub fn upvar_tys(self) -> impl Iterator<Item = Ty<'tcx>> + 'tcx {
- self.tupled_upvars_ty().tuple_fields()
+ match self.tupled_upvars_ty().kind() {
+ TyKind::Error(_) => None,
+ TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()),
+ TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
+ ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
+ }
+ .into_iter()
+ .flatten()
}
/// Returns the tuple type representing the upvars for this closure.
@@ -515,9 +525,19 @@
self.split().witness.expect_ty()
}
+ /// Returns an iterator over the list of types of captured paths by the generator.
+ /// In case there was a type error in figuring out the types of the captured path, an
+ /// empty iterator is returned.
#[inline]
pub fn upvar_tys(self) -> impl Iterator<Item = Ty<'tcx>> + 'tcx {
- self.tupled_upvars_ty().tuple_fields()
+ match self.tupled_upvars_ty().kind() {
+ TyKind::Error(_) => None,
+ TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()),
+ TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
+ ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
+ }
+ .into_iter()
+ .flatten()
}
/// Returns the tuple type representing the upvars for this generator.
@@ -660,13 +680,24 @@
}
impl<'tcx> UpvarSubsts<'tcx> {
+ /// Returns an iterator over the list of types of captured paths by the closure/generator.
+ /// In case there was a type error in figuring out the types of the captured path, an
+ /// empty iterator is returned.
#[inline]
pub fn upvar_tys(self) -> impl Iterator<Item = Ty<'tcx>> + 'tcx {
- let tupled_upvars_ty = match self {
- UpvarSubsts::Closure(substs) => substs.as_closure().split().tupled_upvars_ty,
- UpvarSubsts::Generator(substs) => substs.as_generator().split().tupled_upvars_ty,
+ let tupled_tys = match self {
+ UpvarSubsts::Closure(substs) => substs.as_closure().tupled_upvars_ty(),
+ UpvarSubsts::Generator(substs) => substs.as_generator().tupled_upvars_ty(),
};
- tupled_upvars_ty.expect_ty().tuple_fields()
+
+ match tupled_tys.kind() {
+ TyKind::Error(_) => None,
+ TyKind::Tuple(..) => Some(self.tupled_upvars_ty().tuple_fields()),
+ TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
+ ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
+ }
+ .into_iter()
+ .flatten()
}
#[inline]
@@ -1834,10 +1865,10 @@
}
ty::Array(ty, len) => {
match len.try_eval_usize(tcx, ParamEnv::empty()) {
+ Some(0) | None => false,
// If the array is definitely non-empty, it's uninhabited if
// the type of its elements is uninhabited.
- Some(n) if n != 0 => ty.conservative_is_privately_uninhabited(tcx),
- _ => false,
+ Some(1..) => ty.conservative_is_privately_uninhabited(tcx),
}
}
ty::Ref(..) => {
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 39d4875..26492d9 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1666,19 +1666,10 @@
req_name: ReqName,
ret_allow_plus: AllowPlus,
) -> PResult<'a, P<FnDecl>> {
- let inputs = self.parse_fn_params(req_name)?;
- let output = self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes)?;
-
- if let ast::FnRetTy::Ty(ty) = &output {
- if let TyKind::Path(_, Path { segments, .. }) = &ty.kind {
- if let [.., last] = &segments[..] {
- // Detect and recover `fn foo() -> Vec<i32>> {}`
- self.check_trailing_angle_brackets(last, &[&token::OpenDelim(token::Brace)]);
- }
- }
- }
-
- Ok(P(FnDecl { inputs, output }))
+ Ok(P(FnDecl {
+ inputs: self.parse_fn_params(req_name)?,
+ output: self.parse_ret_ty(ret_allow_plus, RecoverQPath::Yes)?,
+ }))
}
/// Parses the parameter list of a function, including the `(` and `)` delimiters.
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index b12e516..902ee5f 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -1725,10 +1725,9 @@
Scope::MacroRules(binding.parent_macro_rules_scope)
}
MacroRulesScope::Invocation(invoc_id) => Scope::MacroRules(
- self.output_macro_rules_scopes
- .get(&invoc_id)
- .cloned()
- .unwrap_or(self.invocation_parent_scopes[&invoc_id].macro_rules),
+ self.output_macro_rules_scopes.get(&invoc_id).cloned().unwrap_or_else(
+ || self.invocation_parent_scopes[&invoc_id].macro_rules,
+ ),
),
MacroRulesScope::Empty => Scope::Module(module),
},
diff --git a/compiler/rustc_session/src/filesearch.rs b/compiler/rustc_session/src/filesearch.rs
index 12a268d..55ee4e5 100644
--- a/compiler/rustc_session/src/filesearch.rs
+++ b/compiler/rustc_session/src/filesearch.rs
@@ -153,14 +153,14 @@
const SECONDARY_LIB_DIR: &str = "lib";
match option_env!("CFG_LIBDIR_RELATIVE") {
- Some(libdir) if libdir != "lib" => libdir.into(),
- _ => {
+ None | Some("lib") => {
if sysroot.join(PRIMARY_LIB_DIR).join(RUST_LIB_DIR).exists() {
PRIMARY_LIB_DIR.into()
} else {
SECONDARY_LIB_DIR.into()
}
}
+ Some(libdir) => libdir.into(),
}
}
diff --git a/compiler/rustc_span/src/caching_source_map_view.rs b/compiler/rustc_span/src/caching_source_map_view.rs
index 68b0bd1..15dd00f 100644
--- a/compiler/rustc_span/src/caching_source_map_view.rs
+++ b/compiler/rustc_span/src/caching_source_map_view.rs
@@ -1,13 +1,25 @@
use crate::source_map::SourceMap;
use crate::{BytePos, SourceFile};
use rustc_data_structures::sync::Lrc;
+use std::ops::Range;
#[derive(Clone)]
struct CacheEntry {
time_stamp: usize,
line_number: usize,
- line_start: BytePos,
- line_end: BytePos,
+ // The line's byte position range in the `SourceMap`. This range will fail to contain a valid
+ // position in certain edge cases. Spans often start/end one past something, and when that
+ // something is the last character of a file (this can happen when a file doesn't end in a
+ // newline, for example), we'd still like for the position to be considered within the last
+ // line. However, it isn't according to the exclusive upper bound of this range. We cannot
+ // change the upper bound to be inclusive, because for most lines, the upper bound is the same
+ // as the lower bound of the next line, so there would be an ambiguity.
+ //
+ // Since the containment aspect of this range is only used to see whether or not the cache
+ // entry contains a position, the only ramification of the above is that we will get cache
+ // misses for these rare positions. A line lookup for the position via `SourceMap::lookup_line`
+ // after a cache miss will produce the last line number, as desired.
+ line: Range<BytePos>,
file: Lrc<SourceFile>,
file_index: usize,
}
@@ -26,8 +38,7 @@
let entry = CacheEntry {
time_stamp: 0,
line_number: 0,
- line_start: BytePos(0),
- line_end: BytePos(0),
+ line: BytePos(0)..BytePos(0),
file: first_file,
file_index: 0,
};
@@ -47,13 +58,13 @@
// Check if the position is in one of the cached lines
for cache_entry in self.line_cache.iter_mut() {
- if pos >= cache_entry.line_start && pos < cache_entry.line_end {
+ if cache_entry.line.contains(&pos) {
cache_entry.time_stamp = self.time_stamp;
return Some((
cache_entry.file.clone(),
cache_entry.line_number,
- pos - cache_entry.line_start,
+ pos - cache_entry.line.start,
));
}
}
@@ -69,13 +80,13 @@
let cache_entry = &mut self.line_cache[oldest];
// If the entry doesn't point to the correct file, fix it up
- if pos < cache_entry.file.start_pos || pos >= cache_entry.file.end_pos {
+ if !file_contains(&cache_entry.file, pos) {
let file_valid;
if self.source_map.files().len() > 0 {
let file_index = self.source_map.lookup_source_file_idx(pos);
let file = self.source_map.files()[file_index].clone();
- if pos >= file.start_pos && pos < file.end_pos {
+ if file_contains(&file, pos) {
cache_entry.file = file;
cache_entry.file_index = file_index;
file_valid = true;
@@ -95,10 +106,19 @@
let line_bounds = cache_entry.file.line_bounds(line_index);
cache_entry.line_number = line_index + 1;
- cache_entry.line_start = line_bounds.0;
- cache_entry.line_end = line_bounds.1;
+ cache_entry.line = line_bounds;
cache_entry.time_stamp = self.time_stamp;
- Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line_start))
+ Some((cache_entry.file.clone(), cache_entry.line_number, pos - cache_entry.line.start))
}
}
+
+#[inline]
+fn file_contains(file: &SourceFile, pos: BytePos) -> bool {
+ // `SourceMap::lookup_source_file_idx` and `SourceFile::contains` both consider the position
+ // one past the end of a file to belong to it. Normally, that's what we want. But for the
+ // purposes of converting a byte position to a line and column number, we can't come up with a
+ // line and column number if the file is empty, because an empty file doesn't contain any
+ // lines. So for our purposes, we don't consider empty files to contain any byte position.
+ file.contains(pos) && !file.is_empty()
+}
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index 79363c3..0e30272 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -52,7 +52,7 @@
use std::cmp::{self, Ordering};
use std::fmt;
use std::hash::Hash;
-use std::ops::{Add, Sub};
+use std::ops::{Add, Range, Sub};
use std::path::{Path, PathBuf};
use std::str::FromStr;
@@ -1426,24 +1426,33 @@
if line_index >= 0 { Some(line_index as usize) } else { None }
}
- pub fn line_bounds(&self, line_index: usize) -> (BytePos, BytePos) {
- if self.start_pos == self.end_pos {
- return (self.start_pos, self.end_pos);
+ pub fn line_bounds(&self, line_index: usize) -> Range<BytePos> {
+ if self.is_empty() {
+ return self.start_pos..self.end_pos;
}
assert!(line_index < self.lines.len());
if line_index == (self.lines.len() - 1) {
- (self.lines[line_index], self.end_pos)
+ self.lines[line_index]..self.end_pos
} else {
- (self.lines[line_index], self.lines[line_index + 1])
+ self.lines[line_index]..self.lines[line_index + 1]
}
}
+ /// Returns whether or not the file contains the given `SourceMap` byte
+ /// position. The position one past the end of the file is considered to be
+ /// contained by the file. This implies that files for which `is_empty`
+ /// returns true still contain one byte position according to this function.
#[inline]
pub fn contains(&self, byte_pos: BytePos) -> bool {
byte_pos >= self.start_pos && byte_pos <= self.end_pos
}
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.start_pos == self.end_pos
+ }
+
/// Calculates the original byte position relative to the start of the file
/// based on the given byte position.
pub fn original_relative_byte_pos(&self, pos: BytePos) -> BytePos {
diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs
index 3b929c4..f067cdb 100644
--- a/compiler/rustc_span/src/source_map.rs
+++ b/compiler/rustc_span/src/source_map.rs
@@ -12,7 +12,7 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::StableHasher;
-use rustc_data_structures::sync::{AtomicU32, Lock, LockGuard, Lrc, MappedLockGuard};
+use rustc_data_structures::sync::{AtomicU32, Lrc, MappedReadGuard, ReadGuard, RwLock};
use std::cmp;
use std::convert::TryFrom;
use std::hash::Hash;
@@ -168,7 +168,7 @@
/// The address space below this value is currently used by the files in the source map.
used_address_space: AtomicU32,
- files: Lock<SourceMapFiles>,
+ files: RwLock<SourceMapFiles>,
file_loader: Box<dyn FileLoader + Sync + Send>,
// This is used to apply the file path remapping as specified via
// `--remap-path-prefix` to all `SourceFile`s allocated within this `SourceMap`.
@@ -236,8 +236,8 @@
// By returning a `MonotonicVec`, we ensure that consumers cannot invalidate
// any existing indices pointing into `files`.
- pub fn files(&self) -> MappedLockGuard<'_, monotonic::MonotonicVec<Lrc<SourceFile>>> {
- LockGuard::map(self.files.borrow(), |files| &mut files.source_files)
+ pub fn files(&self) -> MappedReadGuard<'_, monotonic::MonotonicVec<Lrc<SourceFile>>> {
+ ReadGuard::map(self.files.borrow(), |files| &files.source_files)
}
pub fn source_file_by_stable_id(
diff --git a/compiler/rustc_trait_selection/src/traits/object_safety.rs b/compiler/rustc_trait_selection/src/traits/object_safety.rs
index d2ac24b..c52fd0b 100644
--- a/compiler/rustc_trait_selection/src/traits/object_safety.rs
+++ b/compiler/rustc_trait_selection/src/traits/object_safety.rs
@@ -461,10 +461,17 @@
let param_env = tcx.param_env(method.def_id);
- let abi_of_ty = |ty: Ty<'tcx>| -> &Abi {
+ let abi_of_ty = |ty: Ty<'tcx>| -> Option<&Abi> {
match tcx.layout_of(param_env.and(ty)) {
- Ok(layout) => &layout.abi,
- Err(err) => bug!("error: {}\n while computing layout for type {:?}", err, ty),
+ Ok(layout) => Some(&layout.abi),
+ Err(err) => {
+ // #78372
+ tcx.sess.delay_span_bug(
+ tcx.def_span(method.def_id),
+ &format!("error: {}\n while computing layout for type {:?}", err, ty),
+ );
+ None
+ }
}
};
@@ -473,7 +480,7 @@
receiver_for_self_ty(tcx, receiver_ty, tcx.mk_unit(), method.def_id);
match abi_of_ty(unit_receiver_ty) {
- &Abi::Scalar(..) => (),
+ Some(Abi::Scalar(..)) => (),
abi => {
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
@@ -493,13 +500,12 @@
receiver_for_self_ty(tcx, receiver_ty, trait_object_ty, method.def_id);
match abi_of_ty(trait_object_receiver) {
- &Abi::ScalarPair(..) => (),
+ Some(Abi::ScalarPair(..)) => (),
abi => {
tcx.sess.delay_span_bug(
tcx.def_span(method.def_id),
&format!(
- "receiver when `Self = {}` should have a ScalarPair ABI; \
- found {:?}",
+ "receiver when `Self = {}` should have a ScalarPair ABI; found {:?}",
trait_object_ty, abi
),
);
diff --git a/library/alloc/benches/vec.rs b/library/alloc/benches/vec.rs
index 789ae3a..89893b6 100644
--- a/library/alloc/benches/vec.rs
+++ b/library/alloc/benches/vec.rs
@@ -570,6 +570,8 @@
})
}
+const LEN: usize = 16384;
+
#[bench]
fn bench_chain_collect(b: &mut Bencher) {
let data = black_box([0; LEN]);
@@ -613,8 +615,6 @@
result
}
-const LEN: usize = 16384;
-
#[bench]
fn bench_range_map_collect(b: &mut Bencher) {
b.iter(|| (0..LEN).map(|_| u32::default()).collect::<Vec<_>>());
diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs
index 72e5e0a..6dcd0c6 100644
--- a/library/alloc/src/rc.rs
+++ b/library/alloc/src/rc.rs
@@ -11,7 +11,7 @@
//! is no exception: you cannot generally obtain a mutable reference to
//! something inside an [`Rc`]. If you need mutability, put a [`Cell`]
//! or [`RefCell`] inside the [`Rc`]; see [an example of mutability
-//! inside an Rc][mutability].
+//! inside an `Rc`][mutability].
//!
//! [`Rc`] uses non-atomic reference counting. This means that overhead is very
//! low, but an [`Rc`] cannot be sent between threads, and consequently [`Rc`]
@@ -35,15 +35,29 @@
//! `Rc<T>` automatically dereferences to `T` (via the [`Deref`] trait),
//! so you can call `T`'s methods on a value of type [`Rc<T>`][`Rc`]. To avoid name
//! clashes with `T`'s methods, the methods of [`Rc<T>`][`Rc`] itself are associated
-//! functions, called using function-like syntax:
+//! functions, called using [fully qualified syntax]:
//!
//! ```
//! use std::rc::Rc;
-//! let my_rc = Rc::new(());
//!
+//! let my_rc = Rc::new(());
//! Rc::downgrade(&my_rc);
//! ```
//!
+//! `Rc<T>`'s implementations of traits like `Clone` may also be called using
+//! fully qualified syntax. Some people prefer to use fully qualified syntax,
+//! while others prefer using method-call syntax.
+//!
+//! ```
+//! use std::rc::Rc;
+//!
+//! let rc = Rc::new(());
+//! // Method-call syntax
+//! let rc2 = rc.clone();
+//! // Fully qualified syntax
+//! let rc3 = Rc::clone(&rc);
+//! ```
+//!
//! [`Weak<T>`][`Weak`] does not auto-dereference to `T`, because the inner value may have
//! already been dropped.
//!
@@ -54,6 +68,7 @@
//!
//! ```
//! use std::rc::Rc;
+//!
//! let foo = Rc::new(vec![1.0, 2.0, 3.0]);
//! // The two syntaxes below are equivalent.
//! let a = foo.clone();
@@ -218,7 +233,7 @@
//! [`Cell`]: core::cell::Cell
//! [`RefCell`]: core::cell::RefCell
//! [send]: core::marker::Send
-//! [arc]: ../../std/sync/struct.Arc.html
+//! [arc]: crate::sync::Arc
//! [`Deref`]: core::ops::Deref
//! [downgrade]: Rc::downgrade
//! [upgrade]: Weak::upgrade
@@ -272,10 +287,9 @@
///
/// The inherent methods of `Rc` are all associated functions, which means
/// that you have to call them as e.g., [`Rc::get_mut(&mut value)`][get_mut] instead of
-/// `value.get_mut()`. This avoids conflicts with methods of the inner
-/// type `T`.
+/// `value.get_mut()`. This avoids conflicts with methods of the inner type `T`.
///
-/// [get_mut]: #method.get_mut
+/// [get_mut]: Rc::get_mut
#[cfg_attr(not(test), rustc_diagnostic_item = "Rc")]
#[stable(feature = "rust1", since = "1.0.0")]
pub struct Rc<T: ?Sized> {
diff --git a/library/alloc/src/string.rs b/library/alloc/src/string.rs
index 72ed036..ce216e5 100644
--- a/library/alloc/src/string.rs
+++ b/library/alloc/src/string.rs
@@ -1235,6 +1235,10 @@
let mut del_bytes = 0;
let mut idx = 0;
+ unsafe {
+ self.vec.set_len(0);
+ }
+
while idx < len {
let ch = unsafe { self.get_unchecked(idx..len).chars().next().unwrap() };
let ch_len = ch.len_utf8();
@@ -1255,10 +1259,8 @@
idx += ch_len;
}
- if del_bytes > 0 {
- unsafe {
- self.vec.set_len(len - del_bytes);
- }
+ unsafe {
+ self.vec.set_len(len - del_bytes);
}
}
diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs
index 73ff795..5ab930a 100644
--- a/library/alloc/src/sync.rs
+++ b/library/alloc/src/sync.rs
@@ -129,15 +129,29 @@
/// `Arc<T>` automatically dereferences to `T` (via the [`Deref`][deref] trait),
/// so you can call `T`'s methods on a value of type `Arc<T>`. To avoid name
/// clashes with `T`'s methods, the methods of `Arc<T>` itself are associated
-/// functions, called using function-like syntax:
+/// functions, called using [fully qualified syntax]:
///
/// ```
/// use std::sync::Arc;
-/// let my_arc = Arc::new(());
///
+/// let my_arc = Arc::new(());
/// Arc::downgrade(&my_arc);
/// ```
///
+/// `Arc<T>`'s implementations of traits like `Clone` may also be called using
+/// fully qualified syntax. Some people prefer to use fully qualified syntax,
+/// while others prefer using method-call syntax.
+///
+/// ```
+/// use std::sync::Arc;
+///
+/// let arc = Arc::new(());
+/// // Method-call syntax
+/// let arc2 = arc.clone();
+/// // Fully qualified syntax
+/// let arc3 = Arc::clone(&arc);
+/// ```
+///
/// [`Weak<T>`][Weak] does not auto-dereference to `T`, because the inner value may have
/// already been dropped.
///
@@ -154,6 +168,7 @@
/// [`RefCell<T>`]: core::cell::RefCell
/// [`std::sync`]: ../../std/sync/index.html
/// [`Arc::clone(&from)`]: Arc::clone
+/// [fully qualified syntax]: https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#fully-qualified-syntax-for-disambiguation-calling-methods-with-the-same-name
///
/// # Examples
///
diff --git a/library/alloc/tests/string.rs b/library/alloc/tests/string.rs
index a6e41b2..b286941 100644
--- a/library/alloc/tests/string.rs
+++ b/library/alloc/tests/string.rs
@@ -1,6 +1,7 @@
use std::borrow::Cow;
use std::collections::TryReserveError::*;
use std::ops::Bound::*;
+use std::panic;
pub trait IntoCow<'a, B: ?Sized>
where
@@ -378,6 +379,20 @@
s.retain(|_| false);
assert_eq!(s, "");
+
+ let mut s = String::from("0è0");
+ let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+ let mut count = 0;
+ s.retain(|_| {
+ count += 1;
+ match count {
+ 1 => false,
+ 2 => true,
+ _ => panic!(),
+ }
+ });
+ }));
+ assert!(std::str::from_utf8(s.as_bytes()).is_ok());
}
#[test]
diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs
index f514a90..79ae1d5 100644
--- a/library/core/src/slice/mod.rs
+++ b/library/core/src/slice/mod.rs
@@ -1768,8 +1768,10 @@
/// Returns a subslice with the prefix removed.
///
- /// This method returns [`None`] if slice does not start with `prefix`.
- /// Also it returns the original slice if `prefix` is an empty slice.
+ /// If the slice starts with `prefix`, returns the subslice after the prefix, wrapped in `Some`.
+ /// If `prefix` is empty, simply returns the original slice.
+ ///
+ /// If the slice does not start with `prefix`, returns `None`.
///
/// # Examples
///
@@ -1799,8 +1801,10 @@
/// Returns a subslice with the suffix removed.
///
- /// This method returns [`None`] if slice does not end with `suffix`.
- /// Also it returns the original slice if `suffix` is an empty slice
+ /// If the slice ends with `suffix`, returns the subslice before the suffix, wrapped in `Some`.
+ /// If `suffix` is empty, simply returns the original slice.
+ ///
+ /// If the slice does not end with `suffix`, returns `None`.
///
/// # Examples
///
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index ee9c09f..b36d9f5 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -1965,11 +1965,10 @@
/// Returns a string slice with the prefix removed.
///
- /// If the string starts with the pattern `prefix`, `Some` is returned with the substring where
- /// the prefix is removed. Unlike `trim_start_matches`, this method removes the prefix exactly
- /// once.
+ /// If the string starts with the pattern `prefix`, returns substring after the prefix, wrapped
+ /// in `Some`. Unlike `trim_start_matches`, this method removes the prefix exactly once.
///
- /// If the string does not start with `prefix`, `None` is returned.
+ /// If the string does not start with `prefix`, returns `None`.
///
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
@@ -1993,11 +1992,10 @@
/// Returns a string slice with the suffix removed.
///
- /// If the string ends with the pattern `suffix`, `Some` is returned with the substring where
- /// the suffix is removed. Unlike `trim_end_matches`, this method removes the suffix exactly
- /// once.
+ /// If the string ends with the pattern `suffix`, returns the substring before the suffix,
+ /// wrapped in `Some`. Unlike `trim_end_matches`, this method removes the suffix exactly once.
///
- /// If the string does not end with `suffix`, `None` is returned.
+ /// If the string does not end with `suffix`, returns `None`.
///
/// The [pattern] can be a `&str`, [`char`], a slice of [`char`]s, or a
/// function or closure that determines if a character matches.
diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs
index bd094bd..2ecb0f4 100644
--- a/library/std/src/f64.rs
+++ b/library/std/src/f64.rs
@@ -920,22 +920,20 @@
fn log_wrapper<F: Fn(f64) -> f64>(self, log_fn: F) -> f64 {
if !cfg!(any(target_os = "solaris", target_os = "illumos")) {
log_fn(self)
- } else {
- if self.is_finite() {
- if self > 0.0 {
- log_fn(self)
- } else if self == 0.0 {
- Self::NEG_INFINITY // log(0) = -Inf
- } else {
- Self::NAN // log(-n) = NaN
- }
- } else if self.is_nan() {
- self // log(NaN) = NaN
- } else if self > 0.0 {
- self // log(Inf) = Inf
+ } else if self.is_finite() {
+ if self > 0.0 {
+ log_fn(self)
+ } else if self == 0.0 {
+ Self::NEG_INFINITY // log(0) = -Inf
} else {
- Self::NAN // log(-Inf) = NaN
+ Self::NAN // log(-n) = NaN
}
+ } else if self.is_nan() {
+ self // log(NaN) = NaN
+ } else if self > 0.0 {
+ self // log(Inf) = Inf
+ } else {
+ Self::NAN // log(-Inf) = NaN
}
}
}
diff --git a/src/test/ui/auxiliary/rustc-rust-log-aux.rs b/src/test/ui/auxiliary/rustc-rust-log-aux.rs
new file mode 100644
index 0000000..daa8e9f
--- /dev/null
+++ b/src/test/ui/auxiliary/rustc-rust-log-aux.rs
@@ -0,0 +1 @@
+// rustc-env:RUSTC_LOG=debug
diff --git a/src/test/ui/issues/issue-77993-1.rs b/src/test/ui/issues/issue-77993-1.rs
new file mode 100644
index 0000000..515b3bc
--- /dev/null
+++ b/src/test/ui/issues/issue-77993-1.rs
@@ -0,0 +1,12 @@
+#[derive(Clone)]
+struct InGroup<F> {
+ it: It,
+ //~^ ERROR cannot find type `It` in this scope
+ f: F,
+}
+fn dates_in_year() -> impl Clone {
+ InGroup { f: |d| d }
+ //~^ ERROR missing field `it` in initializer of `InGroup<_>`
+}
+
+fn main() {}
diff --git a/src/test/ui/issues/issue-77993-1.stderr b/src/test/ui/issues/issue-77993-1.stderr
new file mode 100644
index 0000000..3dc78ba
--- /dev/null
+++ b/src/test/ui/issues/issue-77993-1.stderr
@@ -0,0 +1,16 @@
+error[E0412]: cannot find type `It` in this scope
+ --> $DIR/issue-77993-1.rs:3:9
+ |
+LL | it: It,
+ | ^^ not found in this scope
+
+error[E0063]: missing field `it` in initializer of `InGroup<_>`
+ --> $DIR/issue-77993-1.rs:8:5
+ |
+LL | InGroup { f: |d| d }
+ | ^^^^^^^ missing `it`
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0063, E0412.
+For more information about an error, try `rustc --explain E0063`.
diff --git a/src/test/ui/issues/issue-77993-2.rs b/src/test/ui/issues/issue-77993-2.rs
new file mode 100644
index 0000000..4d554a0
--- /dev/null
+++ b/src/test/ui/issues/issue-77993-2.rs
@@ -0,0 +1,9 @@
+// edition:2018
+
+async fn test() -> Result<(), Box<dyn std::error::Error>> {
+ macro!();
+ //~^ ERROR expected identifier, found `!`
+ Ok(())
+}
+
+fn main() {}
diff --git a/src/test/ui/issues/issue-77993-2.stderr b/src/test/ui/issues/issue-77993-2.stderr
new file mode 100644
index 0000000..64b378f
--- /dev/null
+++ b/src/test/ui/issues/issue-77993-2.stderr
@@ -0,0 +1,8 @@
+error: expected identifier, found `!`
+ --> $DIR/issue-77993-2.rs:4:10
+ |
+LL | macro!();
+ | ^ expected identifier
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/issues/issue-78372.rs b/src/test/ui/issues/issue-78372.rs
new file mode 100644
index 0000000..77a8c92
--- /dev/null
+++ b/src/test/ui/issues/issue-78372.rs
@@ -0,0 +1,14 @@
+use std::ops::DispatchFromDyn; //~ ERROR use of unstable library feature 'dispatch_from_dyn'
+struct Smaht<T, MISC>(PhantomData); //~ ERROR cannot find type `PhantomData` in this scope
+impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {} //~ ERROR cannot find type `U` in this scope
+//~^ ERROR cannot find type `MISC` in this scope
+//~| ERROR use of unstable library feature 'dispatch_from_dyn'
+//~| ERROR the trait `DispatchFromDyn` may only be implemented for a coercion between structures
+//~| ERROR type parameter `T` must be covered by another type when it appears before the first
+trait Foo: X<u32> {}
+trait X<T> {
+ fn foo(self: Smaht<Self, T>);
+}
+trait Marker {}
+impl Marker for dyn Foo {}
+fn main() {}
diff --git a/src/test/ui/issues/issue-78372.stderr b/src/test/ui/issues/issue-78372.stderr
new file mode 100644
index 0000000..9267e83
--- /dev/null
+++ b/src/test/ui/issues/issue-78372.stderr
@@ -0,0 +1,62 @@
+error[E0412]: cannot find type `PhantomData` in this scope
+ --> $DIR/issue-78372.rs:2:23
+ |
+LL | struct Smaht<T, MISC>(PhantomData);
+ | ^^^^^^^^^^^ not found in this scope
+ |
+help: consider importing this struct
+ |
+LL | use std::marker::PhantomData;
+ |
+
+error[E0412]: cannot find type `U` in this scope
+ --> $DIR/issue-78372.rs:3:31
+ |
+LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
+ | - ^ help: a type parameter with a similar name exists: `T`
+ | |
+ | similarly named type parameter `T` defined here
+
+error[E0412]: cannot find type `MISC` in this scope
+ --> $DIR/issue-78372.rs:3:34
+ |
+LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
+ | - ^^^^ not found in this scope
+ | |
+ | help: you might be missing a type parameter: `, MISC`
+
+error[E0658]: use of unstable library feature 'dispatch_from_dyn'
+ --> $DIR/issue-78372.rs:1:5
+ |
+LL | use std::ops::DispatchFromDyn;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: add `#![feature(dispatch_from_dyn)]` to the crate attributes to enable
+
+error[E0658]: use of unstable library feature 'dispatch_from_dyn'
+ --> $DIR/issue-78372.rs:3:9
+ |
+LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: add `#![feature(dispatch_from_dyn)]` to the crate attributes to enable
+
+error[E0378]: the trait `DispatchFromDyn` may only be implemented for a coercion between structures
+ --> $DIR/issue-78372.rs:3:1
+ |
+LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Smaht<[type error], [type error]>`)
+ --> $DIR/issue-78372.rs:3:6
+ |
+LL | impl<T> DispatchFromDyn<Smaht<U, MISC>> for T {}
+ | ^ type parameter `T` must be covered by another type when it appears before the first local type (`Smaht<[type error], [type error]>`)
+ |
+ = note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
+ = note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
+
+error: aborting due to 7 previous errors
+
+Some errors have detailed explanations: E0210, E0378, E0412, E0658.
+For more information about an error, try `rustc --explain E0210`.
diff --git a/src/test/ui/lint/lint-type-overflow2.stderr b/src/test/ui/lint/lint-type-overflow2.stderr
index 61e33b7..0f16229 100644
--- a/src/test/ui/lint/lint-type-overflow2.stderr
+++ b/src/test/ui/lint/lint-type-overflow2.stderr
@@ -17,7 +17,7 @@
LL | let x = -3.40282357e+38_f32;
| ^^^^^^^^^^^^^^^^^^
|
- = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `std::f32::INFINITY`
+ = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
error: literal out of range for `f32`
--> $DIR/lint-type-overflow2.rs:10:14
@@ -25,7 +25,7 @@
LL | let x = 3.40282357e+38_f32;
| ^^^^^^^^^^^^^^^^^^
|
- = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `std::f32::INFINITY`
+ = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY`
error: literal out of range for `f64`
--> $DIR/lint-type-overflow2.rs:11:14
@@ -33,7 +33,7 @@
LL | let x = -1.7976931348623159e+308_f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `std::f64::INFINITY`
+ = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
error: literal out of range for `f64`
--> $DIR/lint-type-overflow2.rs:12:14
@@ -41,7 +41,7 @@
LL | let x = 1.7976931348623159e+308_f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `std::f64::INFINITY`
+ = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY`
error: aborting due to 5 previous errors
diff --git a/src/test/ui/parser/fn-returns-fn-pointer.rs b/src/test/ui/parser/fn-returns-fn-pointer.rs
new file mode 100644
index 0000000..15590e3
--- /dev/null
+++ b/src/test/ui/parser/fn-returns-fn-pointer.rs
@@ -0,0 +1,6 @@
+// check-pass
+// Regression test for #78507.
+fn foo() -> Option<fn() -> Option<bool>> {
+ Some(|| Some(true))
+}
+fn main() {}
diff --git a/src/test/ui/parser/issue-24780.rs b/src/test/ui/parser/issue-24780.rs
index 20665b5..480d9bc 100644
--- a/src/test/ui/parser/issue-24780.rs
+++ b/src/test/ui/parser/issue-24780.rs
@@ -1,8 +1,8 @@
// Verify that '>' is not both expected and found at the same time, as it used
// to happen in #24780. For example, following should be an error:
-// expected one of ..., `>`, ... found `>`. No longer exactly this, but keeping for posterity.
+// expected one of ..., `>`, ... found `>`.
-fn foo() -> Vec<usize>> { //~ ERROR unmatched angle bracket
+fn foo() -> Vec<usize>> { //~ ERROR expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>`
Vec::new()
}
diff --git a/src/test/ui/parser/issue-24780.stderr b/src/test/ui/parser/issue-24780.stderr
index d12b13d..bdd089b 100644
--- a/src/test/ui/parser/issue-24780.stderr
+++ b/src/test/ui/parser/issue-24780.stderr
@@ -1,8 +1,8 @@
-error: unmatched angle bracket
+error: expected one of `!`, `+`, `::`, `;`, `where`, or `{`, found `>`
--> $DIR/issue-24780.rs:5:23
|
LL | fn foo() -> Vec<usize>> {
- | ^^ help: remove extra angle bracket
+ | ^ expected one of `!`, `+`, `::`, `;`, `where`, or `{`
error: aborting due to previous error
diff --git a/src/test/ui/rustc-rust-log.rs b/src/test/ui/rustc-rust-log.rs
index 1c4252b2..8ceb24d 100644
--- a/src/test/ui/rustc-rust-log.rs
+++ b/src/test/ui/rustc-rust-log.rs
@@ -8,7 +8,7 @@
// dont-check-compiler-stdout
// dont-check-compiler-stderr
// compile-flags: --error-format human
-
+// aux-build: rustc-rust-log-aux.rs
// rustc-env:RUSTC_LOG=debug
fn main() {}
diff --git a/src/tools/cargo b/src/tools/cargo
index dd83ae5..becb4c2 160000
--- a/src/tools/cargo
+++ b/src/tools/cargo
@@ -1 +1 @@
-Subproject commit dd83ae55c871d94f060524656abab62ec40b4c40
+Subproject commit becb4c282b8f37469efb8f5beda45a5501f9d367
diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh
index fc4dca5..321e00d 100644
--- a/src/tools/clippy/.github/driver.sh
+++ b/src/tools/clippy/.github/driver.sh
@@ -7,9 +7,9 @@
test "$sysroot" = "$(rustc --print sysroot)"
if [[ ${OS} == "Windows" ]]; then
- desired_sysroot=C:/tmp
+ desired_sysroot=C:/tmp
else
- desired_sysroot=/tmp
+ desired_sysroot=/tmp
fi
sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
test "$sysroot" = $desired_sysroot
@@ -22,20 +22,18 @@
# Run a lint and make sure it produces the expected output. It's also expected to exit with code 1
# FIXME: How to match the clippy invocation in compile-test.rs?
-./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/cast.rs 2> cast.stderr && exit 1
-sed -e "s,tests/ui,\$DIR," -e "/= help/d" cast.stderr > normalized.stderr
-diff normalized.stderr tests/ui/cast.stderr
-
+./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/double_neg.rs 2>double_neg.stderr && exit 1
+sed -e "s,tests/ui,\$DIR," -e "/= help/d" double_neg.stderr >normalized.stderr
+diff normalized.stderr tests/ui/double_neg.stderr
# make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same
-SYSROOT=`rustc --print sysroot`
+SYSROOT=$(rustc --print sysroot)
diff <(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver --rustc --version --verbose) <(rustc --version --verbose)
-
-echo "fn main() {}" > target/driver_test.rs
+echo "fn main() {}" >target/driver_test.rs
# we can't run 2 rustcs on the same file at the same time
-CLIPPY=`LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc`
-RUSTC=`rustc ./target/driver_test.rs`
+CLIPPY=$(LD_LIBRARY_PATH=${SYSROOT}/lib ./target/debug/clippy-driver ./target/driver_test.rs --rustc)
+RUSTC=$(rustc ./target/driver_test.rs)
diff <($CLIPPY) <($RUSTC)
# TODO: CLIPPY_CONF_DIR / CARGO_MANIFEST_DIR
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index d82f970..25f3b5d 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -1632,6 +1632,7 @@
[`assign_ops`]: https://rust-lang.github.io/rust-clippy/master/index.html#assign_ops
[`async_yields_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#async_yields_async
[`await_holding_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock
+[`await_holding_refcell_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_refcell_ref
[`bad_bit_mask`]: https://rust-lang.github.io/rust-clippy/master/index.html#bad_bit_mask
[`bind_instead_of_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
[`blacklisted_name`]: https://rust-lang.github.io/rust-clippy/master/index.html#blacklisted_name
@@ -1779,6 +1780,7 @@
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
[`large_enum_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_enum_variant
[`large_stack_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_stack_arrays
+[`large_types_passed_by_value`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value
[`len_without_is_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_without_is_empty
[`len_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#len_zero
[`let_and_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
@@ -1793,6 +1795,7 @@
[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
+[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
[`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
@@ -1841,6 +1844,7 @@
[`must_use_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#must_use_unit
[`mut_from_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_from_ref
[`mut_mut`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mut
+[`mut_mutex_lock`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_mutex_lock
[`mut_range_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#mut_range_bound
[`mutable_key_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutable_key_type
[`mutex_atomic`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_atomic
@@ -1936,6 +1940,7 @@
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
+[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
@@ -1979,6 +1984,7 @@
[`try_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#try_err
[`type_complexity`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
[`type_repetition_in_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#type_repetition_in_bounds
+[`undropped_manually_drops`]: https://rust-lang.github.io/rust-clippy/master/index.html#undropped_manually_drops
[`unicode_not_nfc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unicode_not_nfc
[`unimplemented`]: https://rust-lang.github.io/rust-clippy/master/index.html#unimplemented
[`uninit_assumed_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#uninit_assumed_init
@@ -2011,6 +2017,7 @@
[`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
[`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
+[`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings
[`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result
[`unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_used
[`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
similarity index 64%
rename from src/tools/clippy/clippy_lints/src/await_holding_lock.rs
rename to src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index 3675344..fcebb54 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -45,13 +45,52 @@
/// }
/// ```
pub AWAIT_HOLDING_LOCK,
- pedantic,
+ correctness,
"Inside an async function, holding a MutexGuard while calling await"
}
-declare_lint_pass!(AwaitHoldingLock => [AWAIT_HOLDING_LOCK]);
+declare_clippy_lint! {
+ /// **What it does:** Checks for calls to await while holding a
+ /// `RefCell` `Ref` or `RefMut`.
+ ///
+ /// **Why is this bad?** `RefCell` refs only check for exclusive mutable access
+ /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
+ /// risks panics from a mutable ref shared while other refs are outstanding.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust,ignore
+ /// use std::cell::RefCell;
+ ///
+ /// async fn foo(x: &RefCell<u32>) {
+ /// let b = x.borrow_mut()();
+ /// *ref += 1;
+ /// bar.await;
+ /// }
+ /// ```
+ ///
+ /// Use instead:
+ /// ```rust,ignore
+ /// use std::cell::RefCell;
+ ///
+ /// async fn foo(x: &RefCell<u32>) {
+ /// {
+ /// let b = x.borrow_mut();
+ /// *ref += 1;
+ /// }
+ /// bar.await;
+ /// }
+ /// ```
+ pub AWAIT_HOLDING_REFCELL_REF,
+ correctness,
+ "Inside an async function, holding a RefCell ref while calling await"
+}
-impl LateLintPass<'_> for AwaitHoldingLock {
+declare_lint_pass!(AwaitHolding => [AWAIT_HOLDING_LOCK, AWAIT_HOLDING_REFCELL_REF]);
+
+impl LateLintPass<'_> for AwaitHolding {
fn check_body(&mut self, cx: &LateContext<'_>, body: &'_ Body<'_>) {
use AsyncGeneratorKind::{Block, Closure, Fn};
if let Some(GeneratorKind::Async(Block | Closure | Fn)) = body.generator_kind {
@@ -78,6 +117,16 @@
"these are all the await points this lock is held through",
);
}
+ if is_refcell_ref(cx, adt.did) {
+ span_lint_and_note(
+ cx,
+ AWAIT_HOLDING_REFCELL_REF,
+ ty_cause.span,
+ "this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.",
+ ty_cause.scope_span.or(Some(span)),
+ "these are all the await points this ref is held through",
+ );
+ }
}
}
}
@@ -90,3 +139,7 @@
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
}
+
+fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
+ match_def_path(cx, def_id, &paths::REFCELL_REF) || match_def_path(cx, def_id, &paths::REFCELL_REFMUT)
+}
diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
index 1b73ced..736730d 100644
--- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
@@ -28,7 +28,6 @@
///
/// ```rust
/// # fn somefunc() -> bool { true };
- ///
/// // Bad
/// if { let x = somefunc(); x } { /* ... */ }
///
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 6c969c3..46ce92e 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -1,13 +1,8 @@
-use crate::utils::{eq_expr_value, in_macro, SpanlessEq, SpanlessHash};
-use crate::utils::{get_parent_expr, higher, if_sequence, snippet, span_lint_and_note, span_lint_and_then};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{Arm, Block, Expr, ExprKind, MatchSource, Pat, PatKind};
+use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
+use crate::utils::{get_parent_expr, higher, if_sequence, span_lint_and_note};
+use rustc_hir::{Block, Expr};
use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{Ty, TyS};
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::Symbol;
-use std::collections::hash_map::Entry;
-use std::hash::BuildHasherDefault;
declare_clippy_lint! {
/// **What it does:** Checks for consecutive `if`s with the same condition.
@@ -108,48 +103,7 @@
"`if` with the same `then` and `else` blocks"
}
-declare_clippy_lint! {
- /// **What it does:** Checks for `match` with identical arm bodies.
- ///
- /// **Why is this bad?** This is probably a copy & paste error. If arm bodies
- /// are the same on purpose, you can factor them
- /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
- ///
- /// **Known problems:** False positive possible with order dependent `match`
- /// (see issue
- /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
- ///
- /// **Example:**
- /// ```rust,ignore
- /// match foo {
- /// Bar => bar(),
- /// Quz => quz(),
- /// Baz => bar(), // <= oops
- /// }
- /// ```
- ///
- /// This should probably be
- /// ```rust,ignore
- /// match foo {
- /// Bar => bar(),
- /// Quz => quz(),
- /// Baz => baz(), // <= fixed
- /// }
- /// ```
- ///
- /// or if the original code was not a typo:
- /// ```rust,ignore
- /// match foo {
- /// Bar | Baz => bar(), // <= shows the intent better
- /// Quz => quz(),
- /// }
- /// ```
- pub MATCH_SAME_ARMS,
- pedantic,
- "`match` with identical arm bodies"
-}
-
-declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE, MATCH_SAME_ARMS]);
+declare_lint_pass!(CopyAndPaste => [IFS_SAME_COND, SAME_FUNCTIONS_IN_IF_CONDITION, IF_SAME_THEN_ELSE]);
impl<'tcx> LateLintPass<'tcx> for CopyAndPaste {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -167,7 +121,6 @@
lint_same_then_else(cx, &blocks);
lint_same_cond(cx, &conds);
lint_same_fns_in_if_cond(cx, &conds);
- lint_match_arms(cx, expr);
}
}
}
@@ -243,122 +196,6 @@
}
}
-/// Implementation of `MATCH_SAME_ARMS`.
-fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
- fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
- lhs.len() == rhs.len()
- && lhs
- .iter()
- .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
- }
-
- if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
- let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
- let mut h = SpanlessHash::new(cx);
- h.hash_expr(&arm.body);
- h.finish()
- };
-
- let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
- let min_index = usize::min(lindex, rindex);
- let max_index = usize::max(lindex, rindex);
-
- // Arms with a guard are ignored, those can’t always be merged together
- // This is also the case for arms in-between each there is an arm with a guard
- (min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
- SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
- // all patterns should have the same bindings
- same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
- };
-
- let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
- for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
- span_lint_and_then(
- cx,
- MATCH_SAME_ARMS,
- j.body.span,
- "this `match` has identical arm bodies",
- |diag| {
- diag.span_note(i.body.span, "same as this");
-
- // Note: this does not use `span_suggestion` on purpose:
- // there is no clean way
- // to remove the other arm. Building a span and suggest to replace it to ""
- // makes an even more confusing error message. Also in order not to make up a
- // span for the whole pattern, the suggestion is only shown when there is only
- // one pattern. The user should know about `|` if they are already using it…
-
- let lhs = snippet(cx, i.pat.span, "<pat1>");
- let rhs = snippet(cx, j.pat.span, "<pat2>");
-
- if let PatKind::Wild = j.pat.kind {
- // if the last arm is _, then i could be integrated into _
- // note that i.pat cannot be _, because that would mean that we're
- // hiding all the subsequent arms, and rust won't compile
- diag.span_note(
- i.body.span,
- &format!(
- "`{}` has the same arm body as the `_` wildcard, consider removing it",
- lhs
- ),
- );
- } else {
- diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
- }
- },
- );
- }
- }
-}
-
-/// Returns the list of bindings in a pattern.
-fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
- fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
- match pat.kind {
- PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
- PatKind::TupleStruct(_, pats, _) => {
- for pat in pats {
- bindings_impl(cx, pat, map);
- }
- },
- PatKind::Binding(.., ident, ref as_pat) => {
- if let Entry::Vacant(v) = map.entry(ident.name) {
- v.insert(cx.typeck_results().pat_ty(pat));
- }
- if let Some(ref as_pat) = *as_pat {
- bindings_impl(cx, as_pat, map);
- }
- },
- PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
- for pat in fields {
- bindings_impl(cx, pat, map);
- }
- },
- PatKind::Struct(_, fields, _) => {
- for pat in fields {
- bindings_impl(cx, &pat.pat, map);
- }
- },
- PatKind::Slice(lhs, ref mid, rhs) => {
- for pat in lhs {
- bindings_impl(cx, pat, map);
- }
- if let Some(ref mid) = *mid {
- bindings_impl(cx, mid, map);
- }
- for pat in rhs {
- bindings_impl(cx, pat, map);
- }
- },
- PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
- }
- }
-
- let mut result = FxHashMap::default();
- bindings_impl(cx, pat, &mut result);
- result
-}
-
fn search_same_sequenced<T, Eq>(exprs: &[T], eq: Eq) -> Option<(&T, &T)>
where
Eq: Fn(&T, &T) -> bool,
@@ -370,47 +207,3 @@
}
None
}
-
-fn search_common_cases<'a, T, Eq>(exprs: &'a [T], eq: &Eq) -> Option<(&'a T, &'a T)>
-where
- Eq: Fn(&T, &T) -> bool,
-{
- if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
- Some((&exprs[0], &exprs[1]))
- } else {
- None
- }
-}
-
-fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
-where
- Hash: Fn(&T) -> u64,
- Eq: Fn(&T, &T) -> bool,
-{
- if let Some(expr) = search_common_cases(&exprs, &eq) {
- return vec![expr];
- }
-
- let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
-
- let mut map: FxHashMap<_, Vec<&_>> =
- FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
-
- for expr in exprs {
- match map.entry(hash(expr)) {
- Entry::Occupied(mut o) => {
- for o in o.get() {
- if eq(o, expr) {
- match_expr_list.push((o, expr));
- }
- }
- o.get_mut().push(expr);
- },
- Entry::Vacant(v) => {
- v.insert(vec![expr]);
- },
- }
- }
-
- match_expr_list
-}
diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
index c588436..461c6e3 100644
--- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
@@ -172,3 +172,12 @@
pub DROP_BOUNDS,
"this lint has been uplifted to rustc and is now called `drop_bounds`"
}
+
+declare_deprecated_lint! {
+ /// **What it does:** Nothing. This lint has been deprecated.
+ ///
+ /// **Deprecation reason:** This lint has been uplifted to rustc and is now called
+ /// `temporary_cstring_as_ptr`.
+ pub TEMPORARY_CSTRING_AS_PTR,
+ "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`"
+}
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index bf8e030..c75efc6 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -1,6 +1,6 @@
use crate::utils::paths;
use crate::utils::{
- get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_path, span_lint_and_help,
+ get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, match_path, span_lint_and_help,
span_lint_and_note, span_lint_and_then,
};
use if_chain::if_chain;
@@ -193,10 +193,9 @@
hash_is_automatically_derived: bool,
) {
if_chain! {
- if match_path(&trait_ref.path, &paths::HASH);
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait();
- if let Some(def_id) = &trait_ref.trait_def_id();
- if !def_id.is_local();
+ if let Some(def_id) = trait_ref.trait_def_id();
+ if match_def_path(cx, def_id, &paths::HASH);
then {
// Look for the PartialEq implementations for `ty`
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
@@ -352,7 +351,8 @@
}
if_chain! {
- if match_path(&trait_ref.path, &paths::SERDE_DESERIALIZE);
+ if let Some(trait_def_id) = trait_ref.trait_def_id();
+ if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE);
if let ty::Adt(def, _) = ty.kind();
if let Some(local_def_id) = def.did.as_local();
let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 8b02291..1bf3b81 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -29,7 +29,6 @@
/// **Example:**
/// ```rust
/// # fn foo(bar: usize) {}
- ///
/// // Bad
/// let x = Box::new(1);
/// foo(*x);
diff --git a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
index 69818b4..c1c0859 100644
--- a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
+++ b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
@@ -1,7 +1,8 @@
-use crate::utils::{match_qpath, paths, span_lint_and_then, sugg};
+use crate::utils::{match_def_path, paths, span_lint_and_then, sugg};
use if_chain::if_chain;
use rustc_ast::util::parser::AssocOp;
use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
@@ -76,7 +77,8 @@
// right hand side matches either f32::EPSILON or f64::EPSILON
if let ExprKind::Path(ref epsilon_path) = rhs.kind;
- if match_qpath(epsilon_path, &paths::F32_EPSILON) || match_qpath(epsilon_path, &paths::F64_EPSILON);
+ if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
+ if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
// values of the substractions on the left hand side are of the type float
let t_val_l = cx.typeck_results().expr_ty(val_l);
diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs
index fd45a6d..9c0efef 100644
--- a/src/tools/clippy/clippy_lints/src/functions.rs
+++ b/src/tools/clippy/clippy_lints/src/functions.rs
@@ -579,9 +579,8 @@
if let hir::PatKind::Wild = pat.kind {
return false; // ignore `_` patterns
}
- let def_id = pat.hir_id.owner.to_def_id();
- if cx.tcx.has_typeck_results(def_id) {
- is_mutable_ty(cx, &cx.tcx.typeck(def_id.expect_local()).pat_ty(pat), pat.span, tys)
+ if cx.tcx.has_typeck_results(pat.hir_id.owner.to_def_id()) {
+ is_mutable_ty(cx, &cx.tcx.typeck(pat.hir_id.owner).pat_ty(pat), pat.span, tys)
} else {
false
}
@@ -694,11 +693,10 @@
Call(_, args) | MethodCall(_, _, args, _) => {
let mut tys = FxHashSet::default();
for arg in args {
- let def_id = arg.hir_id.owner.to_def_id();
- if self.cx.tcx.has_typeck_results(def_id)
+ if self.cx.tcx.has_typeck_results(arg.hir_id.owner.to_def_id())
&& is_mutable_ty(
self.cx,
- self.cx.tcx.typeck(def_id.expect_local()).expr_ty(arg),
+ self.cx.tcx.typeck(arg.hir_id.owner).expr_ty(arg),
arg.span,
&mut tys,
)
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index b5ca63c..2d37496 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -160,7 +160,7 @@
mod async_yields_async;
mod atomic_ordering;
mod attrs;
-mod await_holding_lock;
+mod await_holding_invalid;
mod bit_mask;
mod blacklisted_name;
mod blocks_in_if_conditions;
@@ -255,6 +255,7 @@
mod multiple_crate_versions;
mod mut_key;
mod mut_mut;
+mod mut_mutex_lock;
mod mut_reference;
mod mutable_debug_assertion;
mod mutex_atomic;
@@ -278,6 +279,7 @@
mod panic_in_result_fn;
mod panic_unimplemented;
mod partialeq_ne_impl;
+mod pass_by_ref_or_value;
mod path_buf_push_overwrite;
mod pattern_type_mismatch;
mod precedence;
@@ -311,9 +313,9 @@
mod trait_bounds;
mod transmute;
mod transmuting_null;
-mod trivially_copy_pass_by_ref;
mod try_err;
mod types;
+mod undropped_manually_drops;
mod unicode;
mod unit_return_expecting_ord;
mod unnamed_address;
@@ -486,6 +488,10 @@
"clippy::drop_bounds",
"this lint has been uplifted to rustc and is now called `drop_bounds`",
);
+ store.register_removed(
+ "clippy::temporary_cstring_as_ptr",
+ "this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr`",
+ );
// end deprecated lints, do not remove this comment, it’s used in `update_lints`
// begin register lints, do not remove this comment, it’s used in `update_lints`
@@ -509,7 +515,8 @@
&attrs::MISMATCHED_TARGET_OS,
&attrs::UNKNOWN_CLIPPY_LINTS,
&attrs::USELESS_ATTRIBUTE,
- &await_holding_lock::AWAIT_HOLDING_LOCK,
+ &await_holding_invalid::AWAIT_HOLDING_LOCK,
+ &await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
&bit_mask::BAD_BIT_MASK,
&bit_mask::INEFFECTIVE_BIT_MASK,
&bit_mask::VERBOSE_BIT_MASK,
@@ -525,7 +532,6 @@
&comparison_chain::COMPARISON_CHAIN,
&copies::IFS_SAME_COND,
&copies::IF_SAME_THEN_ELSE,
- &copies::MATCH_SAME_ARMS,
&copies::SAME_FUNCTIONS_IN_IF_CONDITION,
©_iterator::COPY_ITERATOR,
&create_dir::CREATE_DIR,
@@ -620,6 +626,7 @@
&literal_representation::LARGE_DIGIT_GROUPS,
&literal_representation::MISTYPED_LITERAL_SUFFIXES,
&literal_representation::UNREADABLE_LITERAL,
+ &literal_representation::UNUSUAL_BYTE_GROUPINGS,
&loops::EMPTY_LOOP,
&loops::EXPLICIT_COUNTER_LOOP,
&loops::EXPLICIT_INTO_ITER_LOOP,
@@ -633,6 +640,7 @@
&loops::NEEDLESS_RANGE_LOOP,
&loops::NEVER_LOOP,
&loops::SAME_ITEM_PUSH,
+ &loops::SINGLE_ELEMENT_LOOP,
&loops::WHILE_IMMUTABLE_CONDITION,
&loops::WHILE_LET_LOOP,
&loops::WHILE_LET_ON_ITERATOR,
@@ -654,6 +662,7 @@
&matches::MATCH_LIKE_MATCHES_MACRO,
&matches::MATCH_OVERLAPPING_ARM,
&matches::MATCH_REF_PATS,
+ &matches::MATCH_SAME_ARMS,
&matches::MATCH_SINGLE_BINDING,
&matches::MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
&matches::MATCH_WILD_ERR_ARM,
@@ -742,6 +751,7 @@
&multiple_crate_versions::MULTIPLE_CRATE_VERSIONS,
&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,
@@ -775,6 +785,8 @@
&panic_unimplemented::UNIMPLEMENTED,
&panic_unimplemented::UNREACHABLE,
&partialeq_ne_impl::PARTIALEQ_NE_IMPL,
+ &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,
@@ -784,6 +796,7 @@
&ptr_eq::PTR_EQ,
&ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
&question_mark::QUESTION_MARK,
+ &ranges::MANUAL_RANGE_CONTAINS,
&ranges::RANGE_MINUS_ONE,
&ranges::RANGE_PLUS_ONE,
&ranges::RANGE_ZIP_WITH_LEN,
@@ -834,7 +847,6 @@
&transmute::USELESS_TRANSMUTE,
&transmute::WRONG_TRANSMUTE,
&transmuting_null::TRANSMUTING_NULL,
- &trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF,
&try_err::TRY_ERR,
&types::ABSURD_EXTREME_COMPARISONS,
&types::BORROWED_BOX,
@@ -861,6 +873,7 @@
&types::UNIT_CMP,
&types::UNNECESSARY_CAST,
&types::VEC_BOX,
+ &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
&unicode::INVISIBLE_CHARACTERS,
&unicode::NON_ASCII_LITERAL,
&unicode::UNICODE_NOT_NFC,
@@ -882,6 +895,7 @@
&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS,
&utils::internal_lints::COMPILER_LINT_FUNCTIONS,
&utils::internal_lints::DEFAULT_LINT,
+ &utils::internal_lints::INVALID_PATHS,
&utils::internal_lints::LINT_WITHOUT_LINT_PASS,
&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM,
&utils::internal_lints::OUTER_EXPN_EXPN_DATA,
@@ -904,11 +918,12 @@
]);
// end register lints, do not remove this comment, it’s used in `update_lints`
- store.register_late_pass(|| box await_holding_lock::AwaitHoldingLock);
+ store.register_late_pass(|| box await_holding_invalid::AwaitHolding);
store.register_late_pass(|| box serde_api::SerdeAPI);
store.register_late_pass(|| box utils::internal_lints::CompilerLintFunctions::new());
store.register_late_pass(|| box utils::internal_lints::LintWithoutLintPass::default());
store.register_late_pass(|| box utils::internal_lints::OuterExpnDataPass);
+ store.register_late_pass(|| box utils::internal_lints::InvalidPaths);
store.register_late_pass(|| box utils::inspector::DeepCodeInspector);
store.register_late_pass(|| box utils::author::Author);
let vec_box_size_threshold = conf.vec_box_size_threshold;
@@ -1008,11 +1023,12 @@
store.register_late_pass(move || box large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold));
store.register_late_pass(|| box explicit_write::ExplicitWrite);
store.register_late_pass(|| box needless_pass_by_value::NeedlessPassByValue);
- let trivially_copy_pass_by_ref = trivially_copy_pass_by_ref::TriviallyCopyPassByRef::new(
+ let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new(
conf.trivial_copy_size_limit,
+ conf.pass_by_value_size_limit,
&sess.target,
);
- store.register_late_pass(move || box trivially_copy_pass_by_ref);
+ store.register_late_pass(move || box pass_by_ref_or_value);
store.register_late_pass(|| box try_err::TryErr);
store.register_late_pass(|| box use_self::UseSelf);
store.register_late_pass(|| box bytecount::ByteCount);
@@ -1108,6 +1124,7 @@
store.register_late_pass(|| box future_not_send::FutureNotSend);
store.register_late_pass(|| box utils::internal_lints::CollapsibleCalls);
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
+ store.register_late_pass(|| box mut_mutex_lock::MutMutexLock);
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
@@ -1136,6 +1153,7 @@
store.register_late_pass(move || box disallowed_method::DisallowedMethod::new(&disallowed_methods));
store.register_early_pass(|| box asm_syntax::InlineAsmX86AttSyntax);
store.register_early_pass(|| box asm_syntax::InlineAsmX86IntelSyntax);
+ store.register_late_pass(|| box undropped_manually_drops::UndroppedManuallyDrops);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
@@ -1187,10 +1205,8 @@
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
LintId::of(&attrs::INLINE_ALWAYS),
- LintId::of(&await_holding_lock::AWAIT_HOLDING_LOCK),
LintId::of(&bit_mask::VERBOSE_BIT_MASK),
LintId::of(&checked_conversions::CHECKED_CONVERSIONS),
- LintId::of(&copies::MATCH_SAME_ARMS),
LintId::of(&copies::SAME_FUNCTIONS_IN_IF_CONDITION),
LintId::of(©_iterator::COPY_ITERATOR),
LintId::of(&default_trait_access::DEFAULT_TRAIT_ACCESS),
@@ -1220,6 +1236,7 @@
LintId::of(&map_err_ignore::MAP_ERR_IGNORE),
LintId::of(&match_on_vec_items::MATCH_ON_VEC_ITEMS),
LintId::of(&matches::MATCH_BOOL),
+ 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),
@@ -1236,13 +1253,14 @@
LintId::of(&needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
LintId::of(&non_expressive_names::SIMILAR_NAMES),
LintId::of(&option_if_let_else::OPTION_IF_LET_ELSE),
+ LintId::of(&pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
+ LintId::of(&pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&ranges::RANGE_MINUS_ONE),
LintId::of(&ranges::RANGE_PLUS_ONE),
LintId::of(&shadow::SHADOW_UNRELATED),
LintId::of(&strings::STRING_ADD_ASSIGN),
LintId::of(&trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS),
LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
- LintId::of(&trivially_copy_pass_by_ref::TRIVIALLY_COPY_PASS_BY_REF),
LintId::of(&types::CAST_LOSSLESS),
LintId::of(&types::CAST_POSSIBLE_TRUNCATION),
LintId::of(&types::CAST_POSSIBLE_WRAP),
@@ -1267,6 +1285,7 @@
LintId::of(&utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
LintId::of(&utils::internal_lints::COMPILER_LINT_FUNCTIONS),
LintId::of(&utils::internal_lints::DEFAULT_LINT),
+ LintId::of(&utils::internal_lints::INVALID_PATHS),
LintId::of(&utils::internal_lints::LINT_WITHOUT_LINT_PASS),
LintId::of(&utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM),
LintId::of(&utils::internal_lints::OUTER_EXPN_EXPN_DATA),
@@ -1286,6 +1305,8 @@
LintId::of(&attrs::MISMATCHED_TARGET_OS),
LintId::of(&attrs::UNKNOWN_CLIPPY_LINTS),
LintId::of(&attrs::USELESS_ATTRIBUTE),
+ LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
+ LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(&bit_mask::BAD_BIT_MASK),
LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
LintId::of(&blacklisted_name::BLACKLISTED_NAME),
@@ -1351,6 +1372,7 @@
LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
LintId::of(&literal_representation::MISTYPED_LITERAL_SUFFIXES),
+ LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
LintId::of(&loops::FOR_KV_MAP),
@@ -1362,6 +1384,7 @@
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::NEVER_LOOP),
LintId::of(&loops::SAME_ITEM_PUSH),
+ LintId::of(&loops::SINGLE_ELEMENT_LOOP),
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
@@ -1439,6 +1462,7 @@
LintId::of(&misc_early::UNNEEDED_WILDCARD_PATTERN),
LintId::of(&misc_early::ZERO_PREFIXED_LITERAL),
LintId::of(&mut_key::MUTABLE_KEY_TYPE),
+ LintId::of(&mut_mutex_lock::MUT_MUTEX_LOCK),
LintId::of(&mut_reference::UNNECESSARY_MUT_PASSED),
LintId::of(&mutex_atomic::MUTEX_ATOMIC),
LintId::of(&needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE),
@@ -1467,6 +1491,7 @@
LintId::of(&ptr_eq::PTR_EQ),
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(&redundant_clone::REDUNDANT_CLONE),
@@ -1519,6 +1544,7 @@
LintId::of(&types::UNIT_CMP),
LintId::of(&types::UNNECESSARY_CAST),
LintId::of(&types::VEC_BOX),
+ LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
@@ -1568,6 +1594,7 @@
LintId::of(&len_zero::LEN_WITHOUT_IS_EMPTY),
LintId::of(&len_zero::LEN_ZERO),
LintId::of(&literal_representation::INCONSISTENT_DIGIT_GROUPING),
+ LintId::of(&literal_representation::UNUSUAL_BYTE_GROUPINGS),
LintId::of(&loops::EMPTY_LOOP),
LintId::of(&loops::FOR_KV_MAP),
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
@@ -1610,6 +1637,7 @@
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(&neg_multiply::NEG_MULTIPLY),
LintId::of(&new_without_default::NEW_WITHOUT_DEFAULT),
@@ -1622,6 +1650,7 @@
LintId::of(&ptr::PTR_ARG),
LintId::of(&ptr_eq::PTR_EQ),
LintId::of(&question_mark::QUESTION_MARK),
+ LintId::of(&ranges::MANUAL_RANGE_CONTAINS),
LintId::of(&redundant_field_names::REDUNDANT_FIELD_NAMES),
LintId::of(&redundant_static_lifetimes::REDUNDANT_STATIC_LIFETIMES),
LintId::of(®ex::TRIVIAL_REGEX),
@@ -1662,6 +1691,7 @@
LintId::of(&lifetimes::NEEDLESS_LIFETIMES),
LintId::of(&loops::EXPLICIT_COUNTER_LOOP),
LintId::of(&loops::MUT_RANGE_BOUND),
+ LintId::of(&loops::SINGLE_ELEMENT_LOOP),
LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&manual_strip::MANUAL_STRIP),
LintId::of(&manual_unwrap_or::MANUAL_UNWRAP_OR),
@@ -1731,6 +1761,8 @@
LintId::of(&attrs::DEPRECATED_SEMVER),
LintId::of(&attrs::MISMATCHED_TARGET_OS),
LintId::of(&attrs::USELESS_ATTRIBUTE),
+ LintId::of(&await_holding_invalid::AWAIT_HOLDING_LOCK),
+ LintId::of(&await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
LintId::of(&bit_mask::BAD_BIT_MASK),
LintId::of(&bit_mask::INEFFECTIVE_BIT_MASK),
LintId::of(&booleans::LOGIC_BUG),
@@ -1787,6 +1819,7 @@
LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
LintId::of(&types::CAST_REF_TO_MUT),
LintId::of(&types::UNIT_CMP),
+ LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
LintId::of(&unicode::INVISIBLE_CHARACTERS),
LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index d7043e7..c8a5a9c 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -414,7 +414,7 @@
let mut visitor = RefVisitor::new(cx);
// walk the type F, it may not contain LT refs
walk_ty(&mut visitor, &pred.bounded_ty);
- if !visitor.lts.is_empty() {
+ if !visitor.all_lts().is_empty() {
return true;
}
// if the bounds define new lifetimes, they are fine to occur
@@ -424,7 +424,9 @@
walk_param_bound(&mut visitor, bound);
}
// and check that all lifetimes are allowed
- return visitor.all_lts().iter().any(|it| !allowed_lts.contains(it));
+ if visitor.all_lts().iter().any(|it| !allowed_lts.contains(it)) {
+ return true;
+ }
},
WherePredicate::EqPredicate(ref pred) => {
let mut visitor = RefVisitor::new(cx);
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index c54103b..e8a7416 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -83,6 +83,25 @@
}
declare_clippy_lint! {
+ /// **What it does:** Warns if hexadecimal or binary literals are not grouped
+ /// by nibble or byte.
+ ///
+ /// **Why is this bad?** Negatively impacts readability.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// let x: u32 = 0xFFF_FFF;
+ /// let y: u8 = 0b01_011_101;
+ /// ```
+ pub UNUSUAL_BYTE_GROUPINGS,
+ style,
+ "binary or hex literals that aren't grouped by four"
+}
+
+declare_clippy_lint! {
/// **What it does:** Warns if the digits of an integral or floating-point
/// constant are grouped into groups that
/// are too large.
@@ -125,6 +144,7 @@
LargeDigitGroups,
DecimalRepresentation,
MistypedLiteralSuffix,
+ UnusualByteGroupings,
}
impl WarningType {
@@ -175,6 +195,15 @@
suggested_format,
Applicability::MachineApplicable,
),
+ Self::UnusualByteGroupings => span_lint_and_sugg(
+ cx,
+ UNUSUAL_BYTE_GROUPINGS,
+ span,
+ "digits of hex or binary literal not grouped by four",
+ "consider",
+ suggested_format,
+ Applicability::MachineApplicable,
+ ),
};
}
}
@@ -184,6 +213,7 @@
INCONSISTENT_DIGIT_GROUPING,
LARGE_DIGIT_GROUPS,
MISTYPED_LITERAL_SUFFIXES,
+ UNUSUAL_BYTE_GROUPINGS,
]);
impl EarlyLintPass for LiteralDigitGrouping {
@@ -217,9 +247,9 @@
let result = (|| {
- let integral_group_size = Self::get_group_size(num_lit.integer.split('_'))?;
+ let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix)?;
if let Some(fraction) = num_lit.fraction {
- let fractional_group_size = Self::get_group_size(fraction.rsplit('_'))?;
+ let fractional_group_size = Self::get_group_size(fraction.rsplit('_'), num_lit.radix)?;
let consistent = Self::parts_consistent(integral_group_size,
fractional_group_size,
@@ -229,6 +259,7 @@
return Err(WarningType::InconsistentDigitGrouping);
};
}
+
Ok(())
})();
@@ -237,6 +268,7 @@
let should_warn = match warning_type {
| WarningType::UnreadableLiteral
| WarningType::InconsistentDigitGrouping
+ | WarningType::UnusualByteGroupings
| WarningType::LargeDigitGroups => {
!in_macro(lit.span)
}
@@ -331,11 +363,15 @@
/// Returns the size of the digit groups (or None if ungrouped) if successful,
/// otherwise returns a `WarningType` for linting.
- fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>) -> Result<Option<usize>, WarningType> {
+ fn get_group_size<'a>(groups: impl Iterator<Item = &'a str>, radix: Radix) -> Result<Option<usize>, WarningType> {
let mut groups = groups.map(str::len);
let first = groups.next().expect("At least one group");
+ if (radix == Radix::Binary || radix == Radix::Hexadecimal) && groups.any(|i| i != 4 && i != 2) {
+ return Err(WarningType::UnusualByteGroupings);
+ }
+
if let Some(second) = groups.next() {
if !groups.all(|x| x == second) || first > second {
Err(WarningType::InconsistentDigitGrouping)
diff --git a/src/tools/clippy/clippy_lints/src/loops.rs b/src/tools/clippy/clippy_lints/src/loops.rs
index 63d7e31..c3f75f2 100644
--- a/src/tools/clippy/clippy_lints/src/loops.rs
+++ b/src/tools/clippy/clippy_lints/src/loops.rs
@@ -4,9 +4,10 @@
use crate::utils::usage::{is_unused, mutated_variables};
use crate::utils::{
contains_name, get_enclosing_block, get_parent_expr, get_trait_def_id, has_iter_method, higher, implements_trait,
- is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment, match_trait_method,
- match_type, match_var, multispan_sugg, qpath_res, snippet, snippet_with_applicability, snippet_with_macro_callsite,
- span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then, sugg, SpanlessEq,
+ indent_of, is_integer_const, is_no_std_crate, is_refutable, is_type_diagnostic_item, last_path_segment,
+ match_trait_method, match_type, match_var, multispan_sugg, qpath_res, single_segment_path, snippet,
+ snippet_with_applicability, snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_sugg,
+ span_lint_and_then, sugg, SpanlessEq,
};
use if_chain::if_chain;
use rustc_ast::ast;
@@ -293,9 +294,24 @@
declare_clippy_lint! {
/// **What it does:** Checks for empty `loop` expressions.
///
- /// **Why is this bad?** Those busy loops burn CPU cycles without doing
- /// anything. Think of the environment and either block on something or at least
- /// make the thread sleep for some microseconds.
+ /// **Why is this bad?** These busy loops burn CPU cycles without doing
+ /// anything. It is _almost always_ a better idea to `panic!` than to have
+ /// a busy loop.
+ ///
+ /// If panicking isn't possible, think of the environment and either:
+ /// - block on something
+ /// - sleep the thread for some microseconds
+ /// - yield or pause the thread
+ ///
+ /// For `std` targets, this can be done with
+ /// [`std::thread::sleep`](https://doc.rust-lang.org/std/thread/fn.sleep.html)
+ /// or [`std::thread::yield_now`](https://doc.rust-lang.org/std/thread/fn.yield_now.html).
+ ///
+ /// For `no_std` targets, doing this is more complicated, especially because
+ /// `#[panic_handler]`s can't panic. To stop/pause the thread, you will
+ /// probably need to invoke some target-specific intrinsic. Examples include:
+ /// - [`x86_64::instructions::hlt`](https://docs.rs/x86_64/0.12.2/x86_64/instructions/fn.hlt.html)
+ /// - [`cortex_m::asm::wfi`](https://docs.rs/cortex-m/0.6.3/cortex_m/asm/fn.wfi.html)
///
/// **Known problems:** None.
///
@@ -452,6 +468,31 @@
"the same item is pushed inside of a for loop"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks whether a for loop has a single element.
+ ///
+ /// **Why is this bad?** There is no reason to have a loop of a
+ /// single element.
+ /// **Known problems:** None
+ ///
+ /// **Example:**
+ /// ```rust
+ /// let item1 = 2;
+ /// for item in &[item1] {
+ /// println!("{}", item);
+ /// }
+ /// ```
+ /// could be written as
+ /// ```rust
+ /// let item1 = 2;
+ /// let item = &item1;
+ /// println!("{}", item);
+ /// ```
+ pub SINGLE_ELEMENT_LOOP,
+ complexity,
+ "there is no reason to have a single element loop"
+}
+
declare_lint_pass!(Loops => [
MANUAL_MEMCPY,
NEEDLESS_RANGE_LOOP,
@@ -469,6 +510,7 @@
MUT_RANGE_BOUND,
WHILE_IMMUTABLE_CONDITION,
SAME_ITEM_PUSH,
+ SINGLE_ELEMENT_LOOP,
]);
impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -502,13 +544,15 @@
// (even if the "match" or "if let" is used for declaration)
if let ExprKind::Loop(ref block, _, LoopSource::Loop) = expr.kind {
// also check for empty `loop {}` statements
+ // TODO(issue #6161): Enable for no_std crates (outside of #[panic_handler])
if block.stmts.is_empty() && block.expr.is_none() && !is_no_std_crate(cx.tcx.hir().krate()) {
- span_lint(
+ span_lint_and_help(
cx,
EMPTY_LOOP,
expr.span,
- "empty `loop {}` detected. You may want to either use `panic!()` or add \
- `std::thread::sleep(..);` to the loop body.",
+ "empty `loop {}` wastes CPU cycles",
+ None,
+ "You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.",
);
}
@@ -777,6 +821,7 @@
check_for_loop_arg(cx, pat, arg, expr);
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
check_for_mut_range_bound(cx, arg, body);
+ check_for_single_element_loop(cx, pat, arg, body, expr);
detect_same_item_push(cx, pat, arg, body, expr);
}
@@ -1866,6 +1911,43 @@
}
}
+fn check_for_single_element_loop<'tcx>(
+ cx: &LateContext<'tcx>,
+ pat: &'tcx Pat<'_>,
+ arg: &'tcx Expr<'_>,
+ body: &'tcx Expr<'_>,
+ expr: &'tcx Expr<'_>,
+) {
+ if_chain! {
+ if let ExprKind::AddrOf(BorrowKind::Ref, _, ref arg_expr) = arg.kind;
+ if let PatKind::Binding(.., target, _) = pat.kind;
+ if let ExprKind::Array(ref arg_expr_list) = arg_expr.kind;
+ if let [arg_expression] = arg_expr_list;
+ if let ExprKind::Path(ref list_item) = arg_expression.kind;
+ if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name);
+ if let ExprKind::Block(ref block, _) = body.kind;
+ if !block.stmts.is_empty();
+
+ then {
+ let for_span = get_span_of_entire_for_loop(expr);
+ let mut block_str = snippet(cx, block.span, "..").into_owned();
+ block_str.remove(0);
+ block_str.pop();
+
+
+ span_lint_and_sugg(
+ cx,
+ SINGLE_ELEMENT_LOOP,
+ for_span,
+ "for loop over a single element",
+ "try",
+ format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str),
+ Applicability::MachineApplicable
+ )
+ }
+ }
+}
+
struct MutatePairDelegate<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
hir_id_low: Option<HirId>,
@@ -1969,12 +2051,11 @@
span_low: None,
span_high: None,
};
- let def_id = body.hir_id.owner.to_def_id();
cx.tcx.infer_ctxt().enter(|infcx| {
ExprUseVisitor::new(
&mut delegate,
&infcx,
- def_id.expect_local(),
+ body.hir_id.owner,
cx.param_env,
cx.typeck_results(),
)
@@ -2920,7 +3001,14 @@
IterFunctionKind::IntoIter => String::new(),
IterFunctionKind::Len => String::from(".count()"),
IterFunctionKind::IsEmpty => String::from(".next().is_none()"),
- IterFunctionKind::Contains(span) => format!(".any(|x| x == {})", snippet(cx, *span, "..")),
+ IterFunctionKind::Contains(span) => {
+ let s = snippet(cx, *span, "..");
+ if let Some(stripped) = s.strip_prefix('&') {
+ format!(".any(|x| x == {})", stripped)
+ } else {
+ format!(".any(|x| x == *{})", s)
+ }
+ },
}
}
fn get_suggestion_text(&self) -> &'static str {
diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
index ddb8cc2..22aa37e 100644
--- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or.rs
@@ -1,8 +1,9 @@
use crate::consts::constant_simple;
use crate::utils;
+use crate::utils::sugg;
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{def, Arm, Expr, ExprKind, PatKind, QPath};
+use rustc_hir::{def, Arm, Expr, ExprKind, Pat, PatKind, QPath};
use rustc_lint::LintContext;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::lint::in_external_macro;
@@ -10,7 +11,7 @@
declare_clippy_lint! {
/// **What it does:**
- /// Finds patterns that reimplement `Option::unwrap_or`.
+ /// Finds patterns that reimplement `Option::unwrap_or` or `Result::unwrap_or`.
///
/// **Why is this bad?**
/// Concise code helps focusing on behavior instead of boilerplate.
@@ -33,7 +34,7 @@
/// ```
pub MANUAL_UNWRAP_OR,
complexity,
- "finds patterns that can be encoded more concisely with `Option::unwrap_or`"
+ "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`"
}
declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]);
@@ -43,32 +44,50 @@
if in_external_macro(cx.sess(), expr.span) {
return;
}
- lint_option_unwrap_or_case(cx, expr);
+ lint_manual_unwrap_or(cx, expr);
}
}
-fn lint_option_unwrap_or_case<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
- fn applicable_none_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
+#[derive(Copy, Clone)]
+enum Case {
+ Option,
+ Result,
+}
+
+impl Case {
+ fn unwrap_fn_path(&self) -> &str {
+ match self {
+ Case::Option => "Option::unwrap_or",
+ Case::Result => "Result::unwrap_or",
+ }
+ }
+}
+
+fn lint_manual_unwrap_or<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+ fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
if_chain! {
if arms.len() == 2;
if arms.iter().all(|arm| arm.guard.is_none());
- if let Some((idx, none_arm)) = arms.iter().enumerate().find(|(_, arm)|
- if let PatKind::Path(ref qpath) = arm.pat.kind {
- utils::match_qpath(qpath, &utils::paths::OPTION_NONE)
- } else {
- false
+ if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
+ match arm.pat.kind {
+ PatKind::Path(ref some_qpath) =>
+ utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE),
+ PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
+ utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR),
+ _ => false,
}
);
- let some_arm = &arms[1 - idx];
- if let PatKind::TupleStruct(ref some_qpath, &[some_binding], _) = some_arm.pat.kind;
- if utils::match_qpath(some_qpath, &utils::paths::OPTION_SOME);
- if let PatKind::Binding(_, binding_hir_id, ..) = some_binding.kind;
- if let ExprKind::Path(QPath::Resolved(_, body_path)) = some_arm.body.kind;
+ let unwrap_arm = &arms[1 - idx];
+ if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
+ if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
+ || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
+ if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
+ if let ExprKind::Path(QPath::Resolved(_, body_path)) = unwrap_arm.body.kind;
if let def::Res::Local(body_path_hir_id) = body_path.res;
if body_path_hir_id == binding_hir_id;
- if !utils::usage::contains_return_break_continue_macro(none_arm.body);
+ if !utils::usage::contains_return_break_continue_macro(or_arm.body);
then {
- Some(none_arm)
+ Some(or_arm)
} else {
None
}
@@ -78,24 +97,29 @@
if_chain! {
if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
let ty = cx.typeck_results().expr_ty(scrutinee);
- if utils::is_type_diagnostic_item(cx, ty, sym!(option_type));
- if let Some(none_arm) = applicable_none_arm(match_arms);
- if let Some(scrutinee_snippet) = utils::snippet_opt(cx, scrutinee.span);
- if let Some(none_body_snippet) = utils::snippet_opt(cx, none_arm.body.span);
+ if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym!(option_type)) {
+ Some(Case::Option)
+ } else if utils::is_type_diagnostic_item(cx, ty, sym!(result_type)) {
+ Some(Case::Result)
+ } else {
+ None
+ };
+ if let Some(or_arm) = applicable_or_arm(match_arms);
+ if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span);
if let Some(indent) = utils::indent_of(cx, expr.span);
- if constant_simple(cx, cx.typeck_results(), none_arm.body).is_some();
+ if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
then {
- let reindented_none_body =
- utils::reindent_multiline(none_body_snippet.into(), true, Some(indent));
+ let reindented_or_body =
+ utils::reindent_multiline(or_body_snippet.into(), true, Some(indent));
utils::span_lint_and_sugg(
cx,
MANUAL_UNWRAP_OR, expr.span,
- "this pattern reimplements `Option::unwrap_or`",
+ &format!("this pattern reimplements `{}`", case.unwrap_fn_path()),
"replace with",
format!(
"{}.unwrap_or({})",
- scrutinee_snippet,
- reindented_none_body,
+ sugg::Sugg::hir(cx, scrutinee, "..").maybe_par(),
+ reindented_or_body,
),
Applicability::MachineApplicable,
);
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index b1a4e06..4bdfca1 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -1,5 +1,4 @@
use crate::consts::{constant, miri_to_const, Constant};
-use crate::utils::paths;
use crate::utils::sugg::Sugg;
use crate::utils::usage::is_unused;
use crate::utils::{
@@ -8,8 +7,10 @@
snippet_block, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
span_lint_and_then,
};
+use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir::def::CtorKind;
use rustc_hir::{
@@ -18,10 +19,12 @@
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TyS};
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
+use rustc_span::Symbol;
use std::cmp::Ordering;
+use std::collections::hash_map::Entry;
use std::collections::Bound;
declare_clippy_lint! {
@@ -36,7 +39,6 @@
/// ```rust
/// # fn bar(stool: &str) {}
/// # let x = Some("abc");
- ///
/// // Bad
/// match x {
/// Some(ref foo) => bar(foo),
@@ -239,7 +241,6 @@
/// ```rust
/// # enum Foo { A(usize), B(usize) }
/// # let x = Foo::B(1);
- ///
/// // Bad
/// match x {
/// Foo::A(_) => {},
@@ -477,6 +478,47 @@
"a match that could be written with the matches! macro"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for `match` with identical arm bodies.
+ ///
+ /// **Why is this bad?** This is probably a copy & paste error. If arm bodies
+ /// are the same on purpose, you can factor them
+ /// [using `|`](https://doc.rust-lang.org/book/patterns.html#multiple-patterns).
+ ///
+ /// **Known problems:** False positive possible with order dependent `match`
+ /// (see issue
+ /// [#860](https://github.com/rust-lang/rust-clippy/issues/860)).
+ ///
+ /// **Example:**
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => bar(), // <= oops
+ /// }
+ /// ```
+ ///
+ /// This should probably be
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar => bar(),
+ /// Quz => quz(),
+ /// Baz => baz(), // <= fixed
+ /// }
+ /// ```
+ ///
+ /// or if the original code was not a typo:
+ /// ```rust,ignore
+ /// match foo {
+ /// Bar | Baz => bar(), // <= shows the intent better
+ /// Quz => quz(),
+ /// }
+ /// ```
+ pub MATCH_SAME_ARMS,
+ pedantic,
+ "`match` with identical arm bodies"
+}
+
#[derive(Default)]
pub struct Matches {
infallible_destructuring_match_linted: bool,
@@ -497,7 +539,8 @@
INFALLIBLE_DESTRUCTURING_MATCH,
REST_PAT_IN_FULLY_BOUND_STRUCTS,
REDUNDANT_PATTERN_MATCHING,
- MATCH_LIKE_MATCHES_MACRO
+ MATCH_LIKE_MATCHES_MACRO,
+ MATCH_SAME_ARMS,
]);
impl<'tcx> LateLintPass<'tcx> for Matches {
@@ -507,7 +550,9 @@
}
redundant_pattern_match::check(cx, expr);
- check_match_like_matches(cx, expr);
+ if !check_match_like_matches(cx, expr) {
+ lint_match_arms(cx, expr);
+ }
if let ExprKind::Match(ref ex, ref arms, MatchSource::Normal) = expr.kind {
check_single_match(cx, ex, arms, expr);
@@ -1065,32 +1110,47 @@
}
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
-fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
match match_source {
MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
- _ => return,
+ _ => false,
}
+ } else {
+ false
}
}
/// Lint a `match` or desugared `if let` for replacement by `matches!`
-fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) {
+fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool {
if_chain! {
- if arms.len() == 2;
+ if arms.len() >= 2;
if cx.typeck_results().expr_ty(expr).is_bool();
- if is_wild(&arms[1].pat);
- if let Some(first) = find_bool_lit(&arms[0].body.kind, desugared);
- if let Some(second) = find_bool_lit(&arms[1].body.kind, desugared);
- if first != second;
+ if let Some((b1_arm, b0_arms)) = arms.split_last();
+ if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared);
+ if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared);
+ if is_wild(&b1_arm.pat);
+ if b0 != b1;
+ let if_guard = &b0_arms[0].guard;
+ if if_guard.is_none() || b0_arms.len() == 1;
+ if b0_arms[1..].iter()
+ .all(|arm| {
+ find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
+ arm.guard.is_none()
+ });
then {
let mut applicability = Applicability::MachineApplicable;
-
- let pat_and_guard = if let Some(Guard::If(g)) = arms[0].guard {
- format!("{} if {}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability), snippet_with_applicability(cx, g.span, "..", &mut applicability))
+ let pat = {
+ use itertools::Itertools as _;
+ b0_arms.iter()
+ .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability))
+ .join(" | ")
+ };
+ let pat_and_guard = if let Some(Guard::If(g)) = if_guard {
+ format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
} else {
- format!("{}", snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability))
+ pat
};
span_lint_and_sugg(
cx,
@@ -1100,12 +1160,15 @@
"try this",
format!(
"{}matches!({}, {})",
- if first { "" } else { "!" },
+ if b0 { "" } else { "!" },
snippet_with_applicability(cx, ex.span, "..", &mut applicability),
pat_and_guard,
),
applicability,
- )
+ );
+ true
+ } else {
+ false
}
}
}
@@ -1659,3 +1722,119 @@
],)
);
}
+
+/// Implementation of `MATCH_SAME_ARMS`.
+fn lint_match_arms<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+ fn same_bindings<'tcx>(lhs: &FxHashMap<Symbol, Ty<'tcx>>, rhs: &FxHashMap<Symbol, Ty<'tcx>>) -> bool {
+ lhs.len() == rhs.len()
+ && lhs
+ .iter()
+ .all(|(name, l_ty)| rhs.get(name).map_or(false, |r_ty| TyS::same_type(l_ty, r_ty)))
+ }
+
+ if let ExprKind::Match(_, ref arms, MatchSource::Normal) = expr.kind {
+ let hash = |&(_, arm): &(usize, &Arm<'_>)| -> u64 {
+ let mut h = SpanlessHash::new(cx);
+ h.hash_expr(&arm.body);
+ h.finish()
+ };
+
+ let eq = |&(lindex, lhs): &(usize, &Arm<'_>), &(rindex, rhs): &(usize, &Arm<'_>)| -> bool {
+ let min_index = usize::min(lindex, rindex);
+ let max_index = usize::max(lindex, rindex);
+
+ // Arms with a guard are ignored, those can’t always be merged together
+ // This is also the case for arms in-between each there is an arm with a guard
+ (min_index..=max_index).all(|index| arms[index].guard.is_none()) &&
+ SpanlessEq::new(cx).eq_expr(&lhs.body, &rhs.body) &&
+ // all patterns should have the same bindings
+ same_bindings(&bindings(cx, &lhs.pat), &bindings(cx, &rhs.pat))
+ };
+
+ let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
+ for (&(_, i), &(_, j)) in search_same(&indexed_arms, hash, eq) {
+ span_lint_and_then(
+ cx,
+ MATCH_SAME_ARMS,
+ j.body.span,
+ "this `match` has identical arm bodies",
+ |diag| {
+ diag.span_note(i.body.span, "same as this");
+
+ // Note: this does not use `span_suggestion` on purpose:
+ // there is no clean way
+ // to remove the other arm. Building a span and suggest to replace it to ""
+ // makes an even more confusing error message. Also in order not to make up a
+ // span for the whole pattern, the suggestion is only shown when there is only
+ // one pattern. The user should know about `|` if they are already using it…
+
+ let lhs = snippet(cx, i.pat.span, "<pat1>");
+ let rhs = snippet(cx, j.pat.span, "<pat2>");
+
+ if let PatKind::Wild = j.pat.kind {
+ // if the last arm is _, then i could be integrated into _
+ // note that i.pat cannot be _, because that would mean that we're
+ // hiding all the subsequent arms, and rust won't compile
+ diag.span_note(
+ i.body.span,
+ &format!(
+ "`{}` has the same arm body as the `_` wildcard, consider removing it",
+ lhs
+ ),
+ );
+ } else {
+ diag.span_help(i.pat.span, &format!("consider refactoring into `{} | {}`", lhs, rhs));
+ }
+ },
+ );
+ }
+ }
+}
+
+/// Returns the list of bindings in a pattern.
+fn bindings<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> FxHashMap<Symbol, Ty<'tcx>> {
+ fn bindings_impl<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, map: &mut FxHashMap<Symbol, Ty<'tcx>>) {
+ match pat.kind {
+ PatKind::Box(ref pat) | PatKind::Ref(ref pat, _) => bindings_impl(cx, pat, map),
+ PatKind::TupleStruct(_, pats, _) => {
+ for pat in pats {
+ bindings_impl(cx, pat, map);
+ }
+ },
+ PatKind::Binding(.., ident, ref as_pat) => {
+ if let Entry::Vacant(v) = map.entry(ident.name) {
+ v.insert(cx.typeck_results().pat_ty(pat));
+ }
+ if let Some(ref as_pat) = *as_pat {
+ bindings_impl(cx, as_pat, map);
+ }
+ },
+ PatKind::Or(fields) | PatKind::Tuple(fields, _) => {
+ for pat in fields {
+ bindings_impl(cx, pat, map);
+ }
+ },
+ PatKind::Struct(_, fields, _) => {
+ for pat in fields {
+ bindings_impl(cx, &pat.pat, map);
+ }
+ },
+ PatKind::Slice(lhs, ref mid, rhs) => {
+ for pat in lhs {
+ bindings_impl(cx, pat, map);
+ }
+ if let Some(ref mid) = *mid {
+ bindings_impl(cx, mid, map);
+ }
+ for pat in rhs {
+ bindings_impl(cx, pat, map);
+ }
+ },
+ PatKind::Lit(..) | PatKind::Range(..) | PatKind::Wild | PatKind::Path(..) => (),
+ }
+ }
+
+ let mut result = FxHashMap::default();
+ bindings_impl(cx, pat, &mut result);
+ result
+}
diff --git a/src/tools/clippy/clippy_lints/src/misc_early.rs b/src/tools/clippy/clippy_lints/src/misc_early.rs
index 9cb1cfb..5bc45c8 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early.rs
@@ -231,7 +231,6 @@
/// ```rust
/// # struct TupleStruct(u32, u32, u32);
/// # let t = TupleStruct(1, 2, 3);
- ///
/// // Bad
/// match t {
/// TupleStruct(0, .., _) => (),
diff --git a/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
new file mode 100644
index 0000000..df1cecb
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/mut_mutex_lock.rs
@@ -0,0 +1,68 @@
+use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, Mutability};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for `&mut Mutex::lock` calls
+ ///
+ /// **Why is this bad?** `Mutex::lock` is less efficient than
+ /// calling `Mutex::get_mut`. In addition you also have a statically
+ /// guarantee that the mutex isn't locked, instead of just a runtime
+ /// guarantee.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// use std::sync::{Arc, Mutex};
+ ///
+ /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+ /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+ ///
+ /// let mut value = value_mutex.lock().unwrap();
+ /// *value += 1;
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::sync::{Arc, Mutex};
+ ///
+ /// let mut value_rc = Arc::new(Mutex::new(42_u8));
+ /// let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+ ///
+ /// let value = value_mutex.get_mut().unwrap();
+ /// *value += 1;
+ /// ```
+ pub MUT_MUTEX_LOCK,
+ style,
+ "`&mut Mutex::lock` does unnecessary locking"
+}
+
+declare_lint_pass!(MutMutexLock => [MUT_MUTEX_LOCK]);
+
+impl<'tcx> LateLintPass<'tcx> for MutMutexLock {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
+ if_chain! {
+ if let ExprKind::MethodCall(path, method_span, args, _) = &ex.kind;
+ if path.ident.name == sym!(lock);
+ let ty = cx.typeck_results().expr_ty(&args[0]);
+ if let ty::Ref(_, inner_ty, Mutability::Mut) = ty.kind();
+ if is_type_diagnostic_item(cx, inner_ty, sym!(mutex_type));
+ then {
+ span_lint_and_sugg(
+ cx,
+ MUT_MUTEX_LOCK,
+ *method_span,
+ "calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference",
+ "change this to",
+ "get_mut".to_owned(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index dc5aa66..a799a64 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -3,7 +3,9 @@
//! This lint is **warn** by default
use crate::utils::sugg::Sugg;
-use crate::utils::{higher, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg};
+use crate::utils::{
+ higher, is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg,
+};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@@ -233,6 +235,9 @@
cx.typeck_results().expr_ty(left_side),
cx.typeck_results().expr_ty(right_side),
);
+ if is_expn_of(left_side.span, "cfg").is_some() || is_expn_of(right_side.span, "cfg").is_some() {
+ return;
+ }
if l_ty.is_bool() && r_ty.is_bool() {
let mut applicability = Applicability::MachineApplicable;
@@ -295,7 +300,14 @@
message: &str,
conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
) {
- let hint = Sugg::hir_with_applicability(cx, expr, "..", &mut applicability);
+ let hint = if expr.span.from_expansion() {
+ if applicability != Applicability::Unspecified {
+ applicability = Applicability::MaybeIncorrect;
+ }
+ Sugg::hir_with_macro_callsite(cx, expr, "..")
+ } else {
+ Sugg::hir_with_applicability(cx, expr, "..", &mut applicability)
+ };
span_lint_and_sugg(
cx,
BOOL_COMPARISON,
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
new file mode 100644
index 0000000..28816c3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -0,0 +1,256 @@
+use std::cmp;
+
+use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
+use if_chain::if_chain;
+use rustc_ast::attr;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node, PatKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::Span;
+use rustc_target::abi::LayoutOf;
+use rustc_target::spec::abi::Abi;
+use rustc_target::spec::Target;
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for functions taking arguments by reference, where
+ /// the argument type is `Copy` and small enough to be more efficient to always
+ /// pass by value.
+ ///
+ /// **Why is this bad?** In many calling conventions instances of structs will
+ /// be passed through registers if they fit into two or less general purpose
+ /// registers.
+ ///
+ /// **Known problems:** This lint is target register size dependent, it is
+ /// limited to 32-bit to try and reduce portability problems between 32 and
+ /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
+ /// will be different.
+ ///
+ /// The configuration option `trivial_copy_size_limit` can be set to override
+ /// this limit for a project.
+ ///
+ /// This lint attempts to allow passing arguments by reference if a reference
+ /// to that argument is returned. This is implemented by comparing the lifetime
+ /// of the argument and return value for equality. However, this can cause
+ /// false positives in cases involving multiple lifetimes that are bounded by
+ /// each other.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// // Bad
+ /// fn foo(v: &u32) {}
+ /// ```
+ ///
+ /// ```rust
+ /// // Better
+ /// fn foo(v: u32) {}
+ /// ```
+ pub TRIVIALLY_COPY_PASS_BY_REF,
+ pedantic,
+ "functions taking small copyable arguments by reference"
+}
+
+declare_clippy_lint! {
+ /// **What it does:** Checks for functions taking arguments by value, where
+ /// the argument type is `Copy` and large enough to be worth considering
+ /// passing by reference. Does not trigger if the function is being exported,
+ /// because that might induce API breakage, if the parameter is declared as mutable,
+ /// or if the argument is a `self`.
+ ///
+ /// **Why is this bad?** Arguments passed by value might result in an unnecessary
+ /// shallow copy, taking up more space in the stack and requiring a call to
+ /// `memcpy`, which which can be expensive.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// #[derive(Clone, Copy)]
+ /// struct TooLarge([u8; 2048]);
+ ///
+ /// // Bad
+ /// fn foo(v: TooLarge) {}
+ /// ```
+ /// ```rust
+ /// #[derive(Clone, Copy)]
+ /// struct TooLarge([u8; 2048]);
+ ///
+ /// // Good
+ /// fn foo(v: &TooLarge) {}
+ /// ```
+ pub LARGE_TYPES_PASSED_BY_VALUE,
+ pedantic,
+ "functions taking large arguments by value"
+}
+
+#[derive(Copy, Clone)]
+pub struct PassByRefOrValue {
+ ref_min_size: u64,
+ value_max_size: u64,
+}
+
+impl<'tcx> PassByRefOrValue {
+ pub fn new(ref_min_size: Option<u64>, value_max_size: u64, target: &Target) -> Self {
+ let ref_min_size = ref_min_size.unwrap_or_else(|| {
+ let bit_width = u64::from(target.pointer_width);
+ // Cap the calculated bit width at 32-bits to reduce
+ // portability problems between 32 and 64-bit targets
+ let bit_width = cmp::min(bit_width, 32);
+ #[allow(clippy::integer_division)]
+ let byte_width = bit_width / 8;
+ // Use a limit of 2 times the register byte width
+ byte_width * 2
+ });
+
+ Self {
+ ref_min_size,
+ value_max_size,
+ }
+ }
+
+ fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option<Span>) {
+ let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
+
+ let fn_sig = cx.tcx.fn_sig(fn_def_id);
+ let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
+
+ let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
+
+ for (index, (input, &ty)) in decl.inputs.iter().zip(fn_sig.inputs()).enumerate() {
+ // All spans generated from a proc-macro invocation are the same...
+ match span {
+ Some(s) if s == input.span => return,
+ _ => (),
+ }
+
+ match ty.kind() {
+ ty::Ref(input_lt, ty, Mutability::Not) => {
+ // Use lifetimes to determine if we're returning a reference to the
+ // argument. In that case we can't switch to pass-by-value as the
+ // argument will not live long enough.
+ let output_lts = match *fn_sig.output().kind() {
+ ty::Ref(output_lt, _, _) => vec![output_lt],
+ ty::Adt(_, substs) => substs.regions().collect(),
+ _ => vec![],
+ };
+
+ if_chain! {
+ if !output_lts.contains(&input_lt);
+ if is_copy(cx, ty);
+ if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
+ if size <= self.ref_min_size;
+ if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind;
+ then {
+ let value_type = if is_self_ty(decl_ty) {
+ "self".into()
+ } else {
+ snippet(cx, decl_ty.span, "_").into()
+ };
+ span_lint_and_sugg(
+ cx,
+ TRIVIALLY_COPY_PASS_BY_REF,
+ input.span,
+ &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
+ "consider passing by value instead",
+ value_type,
+ Applicability::Unspecified,
+ );
+ }
+ }
+ },
+
+ ty::Adt(_, _) | ty::Array(_, _) | ty::Tuple(_) => {
+ // if function has a body and parameter is annotated with mut, ignore
+ if let Some(param) = fn_body.and_then(|body| body.params.get(index)) {
+ match param.pat.kind {
+ PatKind::Binding(BindingAnnotation::Unannotated, _, _, _) => {},
+ _ => continue,
+ }
+ }
+
+ if_chain! {
+ if !cx.access_levels.is_exported(hir_id);
+ if is_copy(cx, ty);
+ if !is_self_ty(input);
+ if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
+ if size > self.value_max_size;
+ then {
+ span_lint_and_sugg(
+ cx,
+ LARGE_TYPES_PASSED_BY_VALUE,
+ input.span,
+ &format!("this argument ({} byte) is passed by value, but might be more efficient if passed by reference (limit: {} byte)", size, self.value_max_size),
+ "consider passing by reference instead",
+ format!("&{}", snippet(cx, input.span, "_")),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ },
+
+ _ => {},
+ }
+ }
+ }
+}
+
+impl_lint_pass!(PassByRefOrValue => [TRIVIALLY_COPY_PASS_BY_REF, LARGE_TYPES_PASSED_BY_VALUE]);
+
+impl<'tcx> LateLintPass<'tcx> for PassByRefOrValue {
+ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
+ if item.span.from_expansion() {
+ return;
+ }
+
+ if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
+ self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None);
+ }
+ }
+
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'tcx>,
+ kind: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ _body: &'tcx Body<'_>,
+ span: Span,
+ hir_id: HirId,
+ ) {
+ if span.from_expansion() {
+ return;
+ }
+
+ match kind {
+ FnKind::ItemFn(.., header, _, attrs) => {
+ if header.abi != Abi::Rust {
+ return;
+ }
+ for a in attrs {
+ if let Some(meta_items) = a.meta_item_list() {
+ if a.has_name(sym!(proc_macro_derive))
+ || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
+ {
+ return;
+ }
+ }
+ }
+ },
+ FnKind::Method(..) => (),
+ FnKind::Closure(..) => return,
+ }
+
+ // Exclude non-inherent impls
+ if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
+ if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
+ ItemKind::Trait(..))
+ {
+ return;
+ }
+ }
+
+ self.check_poly_fn(cx, hir_id, decl, Some(span));
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index cc49291..de54711 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -2,15 +2,19 @@
use if_chain::if_chain;
use rustc_ast::ast::RangeLimits;
use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
+use rustc_span::source_map::{Span, Spanned};
+use rustc_span::symbol::Ident;
use std::cmp::Ordering;
use crate::utils::sugg::Sugg;
-use crate::utils::{get_parent_expr, is_integer_const, snippet, snippet_opt, span_lint, span_lint_and_then};
+use crate::utils::{
+ get_parent_expr, is_integer_const, single_segment_path, snippet, snippet_opt, snippet_with_applicability,
+ span_lint, span_lint_and_sugg, span_lint_and_then,
+};
use crate::utils::{higher, SpanlessEq};
declare_clippy_lint! {
@@ -128,43 +132,51 @@
"reversing the limits of range expressions, resulting in empty ranges"
}
+declare_clippy_lint! {
+ /// **What it does:** Checks for expressions like `x >= 3 && x < 8` that could
+ /// be more readably expressed as `(3..8).contains(x)`.
+ ///
+ /// **Why is this bad?** `contains` expresses the intent better and has less
+ /// failure modes (such as fencepost errors or using `||` instead of `&&`).
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// // given
+ /// let x = 6;
+ ///
+ /// assert!(x >= 3 && x < 8);
+ /// ```
+ /// Use instead:
+ /// ```rust
+ ///# let x = 6;
+ /// assert!((3..8).contains(&x));
+ /// ```
+ pub MANUAL_RANGE_CONTAINS,
+ style,
+ "manually reimplementing {`Range`, `RangeInclusive`}`::contains`"
+}
+
declare_lint_pass!(Ranges => [
RANGE_ZIP_WITH_LEN,
RANGE_PLUS_ONE,
RANGE_MINUS_ONE,
REVERSED_EMPTY_RANGES,
+ MANUAL_RANGE_CONTAINS,
]);
impl<'tcx> LateLintPass<'tcx> for Ranges {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
- let name = path.ident.as_str();
- if name == "zip" && args.len() == 2 {
- let iter = &args[0].kind;
- let zip_arg = &args[1];
- if_chain! {
- // `.iter()` call
- if let ExprKind::MethodCall(ref iter_path, _, ref iter_args , _) = *iter;
- if iter_path.ident.name == sym!(iter);
- // range expression in `.zip()` call: `0..x.len()`
- if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
- if is_integer_const(cx, start, 0);
- // `.len()` call
- if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
- if len_path.ident.name == sym!(len) && len_args.len() == 1;
- // `.iter()` and `.len()` called on same `Path`
- if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
- if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
- if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
- then {
- span_lint(cx,
- RANGE_ZIP_WITH_LEN,
- expr.span,
- &format!("it is more idiomatic to use `{}.iter().enumerate()`",
- snippet(cx, iter_args[0].span, "_")));
- }
- }
- }
+ match expr.kind {
+ ExprKind::MethodCall(ref path, _, ref args, _) => {
+ check_range_zip_with_len(cx, path, args, expr.span);
+ },
+ ExprKind::Binary(ref op, ref l, ref r) => {
+ check_possible_range_contains(cx, op.node, l, r, expr.span);
+ },
+ _ => {},
}
check_exclusive_range_plus_one(cx, expr);
@@ -173,6 +185,148 @@
}
}
+fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'_>, r: &Expr<'_>, span: Span) {
+ let combine_and = match op {
+ BinOpKind::And | BinOpKind::BitAnd => true,
+ BinOpKind::Or | BinOpKind::BitOr => false,
+ _ => return,
+ };
+ // value, name, order (higher/lower), inclusiveness
+ if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) =
+ (check_range_bounds(cx, l), check_range_bounds(cx, r))
+ {
+ // we only lint comparisons on the same name and with different
+ // direction
+ if lname != rname || lord == rord {
+ return;
+ }
+ let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
+ if combine_and && ord == Some(rord) {
+ // order lower bound and upper bound
+ let (l_span, u_span, l_inc, u_inc) = if rord == Ordering::Less {
+ (lval_span, rval_span, linc, rinc)
+ } else {
+ (rval_span, lval_span, rinc, linc)
+ };
+ // we only lint inclusive lower bounds
+ if !l_inc {
+ return;
+ }
+ let (range_type, range_op) = if u_inc {
+ ("RangeInclusive", "..=")
+ } else {
+ ("Range", "..")
+ };
+ let mut applicability = Applicability::MachineApplicable;
+ let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
+ let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
+ let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ MANUAL_RANGE_CONTAINS,
+ span,
+ &format!("manual `{}::contains` implementation", range_type),
+ "use",
+ format!("({}{}{}).contains(&{})", lo, range_op, hi, name),
+ applicability,
+ );
+ } else if !combine_and && ord == Some(lord) {
+ // `!_.contains(_)`
+ // order lower bound and upper bound
+ let (l_span, u_span, l_inc, u_inc) = if lord == Ordering::Less {
+ (lval_span, rval_span, linc, rinc)
+ } else {
+ (rval_span, lval_span, rinc, linc)
+ };
+ if l_inc {
+ return;
+ }
+ let (range_type, range_op) = if u_inc {
+ ("Range", "..")
+ } else {
+ ("RangeInclusive", "..=")
+ };
+ let mut applicability = Applicability::MachineApplicable;
+ let name = snippet_with_applicability(cx, name_span, "_", &mut applicability);
+ let lo = snippet_with_applicability(cx, l_span, "_", &mut applicability);
+ let hi = snippet_with_applicability(cx, u_span, "_", &mut applicability);
+ span_lint_and_sugg(
+ cx,
+ MANUAL_RANGE_CONTAINS,
+ span,
+ &format!("manual `!{}::contains` implementation", range_type),
+ "use",
+ format!("!({}{}{}).contains(&{})", lo, range_op, hi, name),
+ applicability,
+ );
+ }
+ }
+}
+
+fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> {
+ if let ExprKind::Binary(ref op, ref l, ref r) = ex.kind {
+ let (inclusive, ordering) = match op.node {
+ BinOpKind::Gt => (false, Ordering::Greater),
+ BinOpKind::Ge => (true, Ordering::Greater),
+ BinOpKind::Lt => (false, Ordering::Less),
+ BinOpKind::Le => (true, Ordering::Less),
+ _ => return None,
+ };
+ if let Some(id) = match_ident(l) {
+ if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
+ return Some((c, id, l.span, r.span, ordering, inclusive));
+ }
+ } else if let Some(id) = match_ident(r) {
+ if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
+ return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
+ }
+ }
+ }
+ None
+}
+
+fn match_ident(e: &Expr<'_>) -> Option<Ident> {
+ if let ExprKind::Path(ref qpath) = e.kind {
+ if let Some(seg) = single_segment_path(qpath) {
+ if seg.args.is_none() {
+ return Some(seg.ident);
+ }
+ }
+ }
+ None
+}
+
+fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
+ let name = path.ident.as_str();
+ if name == "zip" && args.len() == 2 {
+ let iter = &args[0].kind;
+ let zip_arg = &args[1];
+ if_chain! {
+ // `.iter()` call
+ if let ExprKind::MethodCall(ref iter_path, _, ref iter_args, _) = *iter;
+ if iter_path.ident.name == sym!(iter);
+ // range expression in `.zip()` call: `0..x.len()`
+ if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
+ if is_integer_const(cx, start, 0);
+ // `.len()` call
+ if let ExprKind::MethodCall(ref len_path, _, ref len_args, _) = end.kind;
+ if len_path.ident.name == sym!(len) && len_args.len() == 1;
+ // `.iter()` and `.len()` called on same `Path`
+ if let ExprKind::Path(QPath::Resolved(_, ref iter_path)) = iter_args[0].kind;
+ if let ExprKind::Path(QPath::Resolved(_, ref len_path)) = len_args[0].kind;
+ if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
+ then {
+ span_lint(cx,
+ RANGE_ZIP_WITH_LEN,
+ span,
+ &format!("it is more idiomatic to use `{}.iter().enumerate()`",
+ snippet(cx, iter_args[0].span, "_"))
+ );
+ }
+ }
+ }
+}
+
// exclusive range plus one: `x..(y+1)`
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
diff --git a/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs b/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs
deleted file mode 100644
index e90ea0f..0000000
--- a/src/tools/clippy/clippy_lints/src/trivially_copy_pass_by_ref.rs
+++ /dev/null
@@ -1,183 +0,0 @@
-use std::cmp;
-
-use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
-use if_chain::if_chain;
-use rustc_ast::attr;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, FnDecl, HirId, ItemKind, MutTy, Mutability, Node};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-use rustc_target::abi::LayoutOf;
-use rustc_target::spec::abi::Abi;
-use rustc_target::spec::Target;
-
-declare_clippy_lint! {
- /// **What it does:** Checks for functions taking arguments by reference, where
- /// the argument type is `Copy` and small enough to be more efficient to always
- /// pass by value.
- ///
- /// **Why is this bad?** In many calling conventions instances of structs will
- /// be passed through registers if they fit into two or less general purpose
- /// registers.
- ///
- /// **Known problems:** This lint is target register size dependent, it is
- /// limited to 32-bit to try and reduce portability problems between 32 and
- /// 64-bit, but if you are compiling for 8 or 16-bit targets then the limit
- /// will be different.
- ///
- /// The configuration option `trivial_copy_size_limit` can be set to override
- /// this limit for a project.
- ///
- /// This lint attempts to allow passing arguments by reference if a reference
- /// to that argument is returned. This is implemented by comparing the lifetime
- /// of the argument and return value for equality. However, this can cause
- /// false positives in cases involving multiple lifetimes that are bounded by
- /// each other.
- ///
- /// **Example:**
- ///
- /// ```rust
- /// // Bad
- /// fn foo(v: &u32) {}
- /// ```
- ///
- /// ```rust
- /// // Better
- /// fn foo(v: u32) {}
- /// ```
- pub TRIVIALLY_COPY_PASS_BY_REF,
- pedantic,
- "functions taking small copyable arguments by reference"
-}
-
-#[derive(Copy, Clone)]
-pub struct TriviallyCopyPassByRef {
- limit: u64,
-}
-
-impl<'tcx> TriviallyCopyPassByRef {
- pub fn new(limit: Option<u64>, target: &Target) -> Self {
- let limit = limit.unwrap_or_else(|| {
- let bit_width = u64::from(target.pointer_width);
- // Cap the calculated bit width at 32-bits to reduce
- // portability problems between 32 and 64-bit targets
- let bit_width = cmp::min(bit_width, 32);
- #[allow(clippy::integer_division)]
- let byte_width = bit_width / 8;
- // Use a limit of 2 times the register byte width
- byte_width * 2
- });
- Self { limit }
- }
-
- fn check_poly_fn(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, decl: &FnDecl<'_>, span: Option<Span>) {
- let fn_def_id = cx.tcx.hir().local_def_id(hir_id);
-
- let fn_sig = cx.tcx.fn_sig(fn_def_id);
- let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);
-
- // Use lifetimes to determine if we're returning a reference to the
- // argument. In that case we can't switch to pass-by-value as the
- // argument will not live long enough.
- let output_lts = match *fn_sig.output().kind() {
- ty::Ref(output_lt, _, _) => vec![output_lt],
- ty::Adt(_, substs) => substs.regions().collect(),
- _ => vec![],
- };
-
- for (input, &ty) in decl.inputs.iter().zip(fn_sig.inputs()) {
- // All spans generated from a proc-macro invocation are the same...
- match span {
- Some(s) if s == input.span => return,
- _ => (),
- }
-
- if_chain! {
- if let ty::Ref(input_lt, ty, Mutability::Not) = ty.kind();
- if !output_lts.contains(&input_lt);
- if is_copy(cx, ty);
- if let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes());
- if size <= self.limit;
- if let hir::TyKind::Rptr(_, MutTy { ty: ref decl_ty, .. }) = input.kind;
- then {
- let value_type = if is_self_ty(decl_ty) {
- "self".into()
- } else {
- snippet(cx, decl_ty.span, "_").into()
- };
- span_lint_and_sugg(
- cx,
- TRIVIALLY_COPY_PASS_BY_REF,
- input.span,
- &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.limit),
- "consider passing by value instead",
- value_type,
- Applicability::Unspecified,
- );
- }
- }
- }
- }
-}
-
-impl_lint_pass!(TriviallyCopyPassByRef => [TRIVIALLY_COPY_PASS_BY_REF]);
-
-impl<'tcx> LateLintPass<'tcx> for TriviallyCopyPassByRef {
- fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
- if item.span.from_expansion() {
- return;
- }
-
- if let hir::TraitItemKind::Fn(method_sig, _) = &item.kind {
- self.check_poly_fn(cx, item.hir_id, &*method_sig.decl, None);
- }
- }
-
- fn check_fn(
- &mut self,
- cx: &LateContext<'tcx>,
- kind: FnKind<'tcx>,
- decl: &'tcx FnDecl<'_>,
- _body: &'tcx Body<'_>,
- span: Span,
- hir_id: HirId,
- ) {
- if span.from_expansion() {
- return;
- }
-
- match kind {
- FnKind::ItemFn(.., header, _, attrs) => {
- if header.abi != Abi::Rust {
- return;
- }
- for a in attrs {
- if let Some(meta_items) = a.meta_item_list() {
- if a.has_name(sym!(proc_macro_derive))
- || (a.has_name(sym!(inline)) && attr::list_contains_name(&meta_items, sym!(always)))
- {
- return;
- }
- }
- }
- },
- FnKind::Method(..) => (),
- FnKind::Closure(..) => return,
- }
-
- // Exclude non-inherent impls
- if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
- if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
- ItemKind::Trait(..))
- {
- return;
- }
- }
-
- self.check_poly_fn(cx, hir_id, decl, Some(span));
- }
-}
diff --git a/src/tools/clippy/clippy_lints/src/types.rs b/src/tools/clippy/clippy_lints/src/types.rs
index 9a948af..6a33aaa 100644
--- a/src/tools/clippy/clippy_lints/src/types.rs
+++ b/src/tools/clippy/clippy_lints/src/types.rs
@@ -11,7 +11,7 @@
use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
use rustc_hir::{
BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
- ImplItemKind, Item, ItemKind, Lifetime, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
+ ImplItemKind, Item, ItemKind, Lifetime, Lit, Local, MatchSource, MutTy, Mutability, Node, QPath, Stmt, StmtKind,
TraitFn, TraitItem, TraitItemKind, TyKind, UnOp,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -1224,7 +1224,8 @@
}
declare_clippy_lint! {
- /// **What it does:** Checks for casts to the same type.
+ /// **What it does:** Checks for casts to the same type, casts of int literals to integer types
+ /// and casts of float literals to float types.
///
/// **Why is this bad?** It's just unnecessary.
///
@@ -1233,6 +1234,14 @@
/// **Example:**
/// ```rust
/// let _ = 2i32 as i32;
+ /// let _ = 0.5 as f32;
+ /// ```
+ ///
+ /// Better:
+ ///
+ /// ```rust
+ /// let _ = 2_i32;
+ /// let _ = 0.5_f32;
/// ```
pub UNNECESSARY_CAST,
complexity,
@@ -1598,7 +1607,9 @@
if let ExprKind::Cast(ref ex, _) = expr.kind {
let (cast_from, cast_to) = (cx.typeck_results().expr_ty(ex), cx.typeck_results().expr_ty(expr));
lint_fn_to_numeric_cast(cx, expr, ex, cast_from, cast_to);
- if let ExprKind::Lit(ref lit) = ex.kind {
+ if let Some(lit) = get_numeric_literal(ex) {
+ let literal_str = snippet_opt(cx, ex.span).unwrap_or_default();
+
if_chain! {
if let LitKind::Int(n, _) = lit.node;
if let Some(src) = snippet_opt(cx, lit.span);
@@ -1608,19 +1619,19 @@
let to_nbits = fp_ty_mantissa_nbits(cast_to);
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal();
then {
- span_lint_and_sugg(
- cx,
- UNNECESSARY_CAST,
- expr.span,
- &format!("casting integer literal to `{}` is unnecessary", cast_to),
- "try",
- format!("{}_{}", n, cast_to),
- Applicability::MachineApplicable,
- );
+ let literal_str = if is_unary_neg(ex) { format!("-{}", num_lit.integer) } else { num_lit.integer.into() };
+ show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
return;
}
}
+
match lit.node {
+ LitKind::Int(_, LitIntType::Unsuffixed) if cast_to.is_integral() => {
+ show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
+ },
+ LitKind::Float(_, LitFloatType::Unsuffixed) if cast_to.is_floating_point() => {
+ show_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
+ },
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
_ => {
if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
@@ -1646,6 +1657,37 @@
}
}
+fn is_unary_neg(expr: &Expr<'_>) -> bool {
+ matches!(expr.kind, ExprKind::Unary(UnOp::UnNeg, _))
+}
+
+fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option<&'e Lit> {
+ match expr.kind {
+ ExprKind::Lit(ref lit) => Some(lit),
+ ExprKind::Unary(UnOp::UnNeg, e) => {
+ if let ExprKind::Lit(ref lit) = e.kind {
+ Some(lit)
+ } else {
+ None
+ }
+ },
+ _ => None,
+ }
+}
+
+fn show_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" };
+ span_lint_and_sugg(
+ cx,
+ UNNECESSARY_CAST,
+ expr.span,
+ &format!("casting {} literal to `{}` is unnecessary", literal_kind_name, cast_to),
+ "try",
+ format!("{}_{}", literal_str, cast_to),
+ Applicability::MachineApplicable,
+ );
+}
+
fn lint_numeric_casts<'tcx>(
cx: &LateContext<'tcx>,
expr: &Expr<'tcx>,
diff --git a/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
new file mode 100644
index 0000000..5443f16
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/undropped_manually_drops.rs
@@ -0,0 +1,50 @@
+use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help};
+use rustc_hir::{lang_items, Expr};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+ /// **What it does:** Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
+ ///
+ /// **Why is this bad?** The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
+ ///
+ /// **Known problems:** Does not catch cases if the user binds `std::mem::drop`
+ /// to a different name and calls it that way.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// struct S;
+ /// drop(std::mem::ManuallyDrop::new(S));
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// struct S;
+ /// unsafe {
+ /// std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
+ /// }
+ /// ```
+ pub UNDROPPED_MANUALLY_DROPS,
+ correctness,
+ "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
+}
+
+declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
+
+impl LateLintPass<'tcx> for UndroppedManuallyDrops {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+ if let Some(ref args) = match_function_call(cx, expr, &paths::DROP) {
+ let ty = cx.typeck_results().expr_ty(&args[0]);
+ if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop) {
+ span_lint_and_help(
+ cx,
+ UNDROPPED_MANUALLY_DROPS,
+ expr.span,
+ "the inner value of this ManuallyDrop will not be dropped",
+ None,
+ "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
+ );
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/conf.rs b/src/tools/clippy/clippy_lints/src/utils/conf.rs
index dd2fd0b..0ac8fff 100644
--- a/src/tools/clippy/clippy_lints/src/utils/conf.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/conf.rs
@@ -150,6 +150,8 @@
(literal_representation_threshold, "literal_representation_threshold": u64, 16384),
/// Lint: TRIVIALLY_COPY_PASS_BY_REF. The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by reference.
(trivial_copy_size_limit, "trivial_copy_size_limit": Option<u64>, None),
+ /// Lint: LARGE_TYPE_PASS_BY_MOVE. The minimum size (in bytes) to consider a type for passing by reference instead of by value.
+ (pass_by_value_size_limit, "pass_by_value_size_limit": u64, 256),
/// Lint: TOO_MANY_LINES. The maximum number of lines a function or method can have
(too_many_lines_threshold, "too_many_lines_threshold": u64, 100),
/// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS. The maximum allowed size for arrays on the stack
diff --git a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
index c9e639e..e4ad105 100644
--- a/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/hir_utils.rs
@@ -261,14 +261,8 @@
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
// The == of idents doesn't work with different contexts,
// we have to be explicit about hygiene
- if left.ident.as_str() != right.ident.as_str() {
- return false;
- }
- match (&left.args, &right.args) {
- (&None, &None) => true,
- (&Some(ref l), &Some(ref r)) => self.eq_path_parameters(l, r),
- _ => false,
- }
+ left.ident.as_str() == right.ident.as_str()
+ && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
}
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index bfe426a..6ca72d8 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -1,3 +1,4 @@
+use crate::consts::{constant_simple, Constant};
use crate::utils::{
is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, qpath_res, run_lints,
snippet, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
@@ -14,9 +15,11 @@
use rustc_hir::{Crate, Expr, ExprKind, HirId, Item, MutTy, Mutability, Node, Path, StmtKind, Ty, TyKind};
use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
+use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::{Span, Spanned};
use rustc_span::symbol::{Symbol, SymbolStr};
+use rustc_typeck::hir_ty_to_ty;
use std::borrow::{Borrow, Cow};
@@ -229,6 +232,21 @@
"using `utils::match_type()` instead of `utils::is_type_diagnostic_item()`"
}
+declare_clippy_lint! {
+ /// **What it does:**
+ /// Checks the paths module for invalid paths.
+ ///
+ /// **Why is this bad?**
+ /// It indicates a bug in the code.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:** None.
+ pub INVALID_PATHS,
+ internal,
+ "invalid path"
+}
+
declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
impl EarlyLintPass for ClippyLintsInternal {
@@ -761,3 +779,64 @@
None
}
+
+// This is not a complete resolver for paths. It works on all the paths currently used in the paths
+// module. That's all it does and all it needs to do.
+pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
+ if path_to_res(cx, path).is_some() {
+ return true;
+ }
+
+ // Some implementations can't be found by `path_to_res`, particularly inherent
+ // implementations of native types. Check lang items.
+ let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
+ let lang_items = cx.tcx.lang_items();
+ for lang_item in lang_items.items() {
+ if let Some(def_id) = lang_item {
+ let lang_item_path = cx.get_def_path(*def_id);
+ if path_syms.starts_with(&lang_item_path) {
+ if let [item] = &path_syms[lang_item_path.len()..] {
+ for child in cx.tcx.item_children(*def_id) {
+ if child.ident.name == *item {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ false
+}
+
+declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
+ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
+ let local_def_id = &cx.tcx.parent_module(item.hir_id);
+ let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
+ if_chain! {
+ if mod_name.as_str() == "paths";
+ if let hir::ItemKind::Const(ty, body_id) = item.kind;
+ let ty = hir_ty_to_ty(cx.tcx, ty);
+ if let ty::Array(el_ty, _) = &ty.kind();
+ if let ty::Ref(_, el_ty, _) = &el_ty.kind();
+ if el_ty.is_str();
+ let body = cx.tcx.hir().body(body_id);
+ let typeck_results = cx.tcx.typeck_body(body_id);
+ if let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, &body.value);
+ let path: Vec<&str> = path.iter().map(|x| {
+ if let Constant::Str(s) = x {
+ s.as_str()
+ } else {
+ // We checked the type of the constant above
+ unreachable!()
+ }
+ }).collect();
+ if !check_path(cx, &path[..]);
+ then {
+ span_lint(cx, CLIPPY_LINTS_INTERNAL, item.span, "invalid path");
+ }
+ }
+ }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index a9d26d4..0a8a4a5 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -27,11 +27,14 @@
pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
use std::borrow::Cow;
+use std::collections::hash_map::Entry;
+use std::hash::BuildHasherDefault;
use std::mem;
use if_chain::if_chain;
use rustc_ast::ast::{self, Attribute, LitKind};
use rustc_attr as attr;
+use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
@@ -268,6 +271,7 @@
krate: *krate,
index: CRATE_DEF_INDEX,
};
+ let mut current_item = None;
let mut items = cx.tcx.item_children(krate);
let mut path_it = path.iter().skip(1).peekable();
@@ -277,6 +281,12 @@
None => return None,
};
+ // `get_def_path` seems to generate these empty segments for extern blocks.
+ // We can just ignore them.
+ if segment.is_empty() {
+ continue;
+ }
+
let result = SmallVec::<[_; 8]>::new();
for item in mem::replace(&mut items, cx.tcx.arena.alloc_slice(&result)).iter() {
if item.ident.name.as_str() == *segment {
@@ -284,10 +294,28 @@
return Some(item.res);
}
+ current_item = Some(item);
items = cx.tcx.item_children(item.res.def_id());
break;
}
}
+
+ // The segment isn't a child_item.
+ // Try to find it under an inherent impl.
+ if_chain! {
+ if path_it.peek().is_none();
+ if let Some(current_item) = current_item;
+ let item_def_id = current_item.res.def_id();
+ if cx.tcx.def_kind(item_def_id) == DefKind::Struct;
+ then {
+ // Bad `find_map` suggestion. See #4193.
+ #[allow(clippy::find_map)]
+ return cx.tcx.inherent_impls(item_def_id).iter()
+ .flat_map(|&impl_def_id| cx.tcx.item_children(impl_def_id))
+ .find(|item| item.ident.name.as_str() == *segment)
+ .map(|item| item.res);
+ }
+ }
}
} else {
None
@@ -299,7 +327,7 @@
hir::QPath::Resolved(_, path) => path.res,
hir::QPath::TypeRelative(..) | hir::QPath::LangItem(..) => {
if cx.tcx.has_typeck_results(id.owner.to_def_id()) {
- cx.tcx.typeck(id.owner.to_def_id().expect_local()).qpath_res(qpath, id)
+ cx.tcx.typeck(id.owner).qpath_res(qpath, id)
} else {
Res::Err
}
@@ -1440,6 +1468,41 @@
None
}
+/// returns list of all pairs (a, b) from `exprs` such that `eq(a, b)`
+/// `hash` must be comformed with `eq`
+pub fn search_same<T, Hash, Eq>(exprs: &[T], hash: Hash, eq: Eq) -> Vec<(&T, &T)>
+where
+ Hash: Fn(&T) -> u64,
+ Eq: Fn(&T, &T) -> bool,
+{
+ if exprs.len() == 2 && eq(&exprs[0], &exprs[1]) {
+ return vec![(&exprs[0], &exprs[1])];
+ }
+
+ let mut match_expr_list: Vec<(&T, &T)> = Vec::new();
+
+ let mut map: FxHashMap<_, Vec<&_>> =
+ FxHashMap::with_capacity_and_hasher(exprs.len(), BuildHasherDefault::default());
+
+ for expr in exprs {
+ match map.entry(hash(expr)) {
+ Entry::Occupied(mut o) => {
+ for o in o.get() {
+ if eq(o, expr) {
+ match_expr_list.push((o, expr));
+ }
+ }
+ o.get_mut().push(expr);
+ },
+ Entry::Vacant(v) => {
+ v.insert(vec![expr]);
+ },
+ }
+ }
+
+ match_expr_list
+}
+
#[macro_export]
macro_rules! unwrap_cargo_metadata {
($cx: ident, $lint: ident, $deps: expr) => {{
diff --git a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
index 52d3c2c..d02603d 100644
--- a/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/numeric_literal.rs
@@ -1,6 +1,6 @@
use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Copy, Clone)]
pub enum Radix {
Binary,
Octal,
@@ -11,8 +11,8 @@
impl Radix {
/// Returns a reasonable digit group size for this radix.
#[must_use]
- fn suggest_grouping(&self) -> usize {
- match *self {
+ fn suggest_grouping(self) -> usize {
+ match self {
Self::Binary | Self::Hexadecimal => 4,
Self::Octal | Self::Decimal => 3,
}
diff --git a/src/tools/clippy/clippy_lints/src/utils/paths.rs b/src/tools/clippy/clippy_lints/src/utils/paths.rs
index 7566da8..cd72fdd 100644
--- a/src/tools/clippy/clippy_lints/src/utils/paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/paths.rs
@@ -31,10 +31,10 @@
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
-pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
+pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
pub const EXIT: [&str; 3] = ["std", "process", "exit"];
-pub const F32_EPSILON: [&str; 2] = ["f32", "EPSILON"];
-pub const F64_EPSILON: [&str; 2] = ["f64", "EPSILON"];
+pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
+pub const F64_EPSILON: [&str; 4] = ["core", "f64", "<impl f64>", "EPSILON"];
pub const FILE: [&str; 3] = ["std", "fs", "File"];
pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
@@ -46,7 +46,7 @@
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
-pub const HASH: [&str; 2] = ["hash", "Hash"];
+pub const HASH: [&str; 3] = ["core", "hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
@@ -57,7 +57,7 @@
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
-pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
+pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
pub const LINT: [&str; 3] = ["rustc_session", "lint", "Lint"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
@@ -85,13 +85,15 @@
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
-pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
-pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
+pub const PTR_NULL: [&str; 3] = ["core", "ptr", "null"];
+pub const PTR_NULL_MUT: [&str; 3] = ["core", "ptr", "null_mut"];
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
pub const RECEIVER: [&str; 4] = ["std", "sync", "mpsc", "Receiver"];
+pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
+pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
@@ -104,7 +106,7 @@
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
pub const RWLOCK_READ_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockReadGuard"];
pub const RWLOCK_WRITE_GUARD: [&str; 4] = ["std", "sync", "rwlock", "RwLockWriteGuard"];
-pub const SERDE_DESERIALIZE: [&str; 2] = ["_serde", "Deserialize"];
+pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
diff --git a/src/tools/clippy/clippy_lints/src/utils/usage.rs b/src/tools/clippy/clippy_lints/src/utils/usage.rs
index 2fd6046..8b327b2 100644
--- a/src/tools/clippy/clippy_lints/src/utils/usage.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/usage.rs
@@ -19,12 +19,11 @@
used_mutably: FxHashSet::default(),
skip: false,
};
- let def_id = expr.hir_id.owner.to_def_id();
cx.tcx.infer_ctxt().enter(|infcx| {
ExprUseVisitor::new(
&mut delegate,
&infcx,
- def_id.expect_local(),
+ expr.hir_id.owner,
cx.param_env,
cx.typeck_results(),
)
diff --git a/src/tools/clippy/src/lintlist/mod.rs b/src/tools/clippy/src/lintlist/mod.rs
index dcbb8a6..c2e63ec 100644
--- a/src/tools/clippy/src/lintlist/mod.rs
+++ b/src/tools/clippy/src/lintlist/mod.rs
@@ -62,10 +62,17 @@
},
Lint {
name: "await_holding_lock",
- group: "pedantic",
+ group: "correctness",
desc: "Inside an async function, holding a MutexGuard while calling await",
deprecation: None,
- module: "await_holding_lock",
+ module: "await_holding_invalid",
+ },
+ Lint {
+ name: "await_holding_refcell_ref",
+ group: "correctness",
+ desc: "Inside an async function, holding a RefCell ref while calling await",
+ deprecation: None,
+ module: "await_holding_invalid",
},
Lint {
name: "bad_bit_mask",
@@ -1062,6 +1069,13 @@
module: "large_stack_arrays",
},
Lint {
+ name: "large_types_passed_by_value",
+ group: "pedantic",
+ desc: "functions taking large arguments by value",
+ deprecation: None,
+ module: "pass_by_ref_or_value",
+ },
+ Lint {
name: "len_without_is_empty",
group: "style",
desc: "traits or impls with a public `len` method but no corresponding `is_empty` method",
@@ -1160,6 +1174,13 @@
module: "manual_non_exhaustive",
},
Lint {
+ name: "manual_range_contains",
+ group: "style",
+ desc: "manually reimplementing {`Range`, `RangeInclusive`}`::contains`",
+ deprecation: None,
+ module: "ranges",
+ },
+ Lint {
name: "manual_saturating_arithmetic",
group: "style",
desc: "`.chcked_add/sub(x).unwrap_or(MAX/MIN)`",
@@ -1183,7 +1204,7 @@
Lint {
name: "manual_unwrap_or",
group: "complexity",
- desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or`",
+ desc: "finds patterns that can be encoded more concisely with `Option::unwrap_or` or `Result::unwrap_or`",
deprecation: None,
module: "manual_unwrap_or",
},
@@ -1283,7 +1304,7 @@
group: "pedantic",
desc: "`match` with identical arm bodies",
deprecation: None,
- module: "copies",
+ module: "matches",
},
Lint {
name: "match_single_binding",
@@ -1489,6 +1510,13 @@
module: "mut_mut",
},
Lint {
+ name: "mut_mutex_lock",
+ group: "style",
+ desc: "`&mut Mutex::lock` does unnecessary locking",
+ deprecation: None,
+ module: "mut_mutex_lock",
+ },
+ Lint {
name: "mut_range_bound",
group: "complexity",
desc: "for loop over a range where one of the bounds is a mutable variable",
@@ -2126,6 +2154,13 @@
module: "single_component_path_imports",
},
Lint {
+ name: "single_element_loop",
+ group: "complexity",
+ desc: "there is no reason to have a single element loop",
+ deprecation: None,
+ module: "loops",
+ },
+ Lint {
name: "single_match",
group: "style",
desc: "a `match` statement with a single nontrivial arm (i.e., where the other arm is `_ => {}`) instead of `if let`",
@@ -2382,7 +2417,7 @@
group: "pedantic",
desc: "functions taking small copyable arguments by reference",
deprecation: None,
- module: "trivially_copy_pass_by_ref",
+ module: "pass_by_ref_or_value",
},
Lint {
name: "try_err",
@@ -2406,6 +2441,13 @@
module: "trait_bounds",
},
Lint {
+ name: "undropped_manually_drops",
+ group: "correctness",
+ desc: "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value",
+ deprecation: None,
+ module: "undropped_manually_drops",
+ },
+ Lint {
name: "unicode_not_nfc",
group: "pedantic",
desc: "using a Unicode literal not in NFC normal form (see [Unicode tr15](http://www.unicode.org/reports/tr15/) for further information)",
@@ -2595,6 +2637,13 @@
module: "unused_unit",
},
Lint {
+ name: "unusual_byte_groupings",
+ group: "style",
+ desc: "binary or hex literals that aren\'t grouped by four",
+ deprecation: None,
+ module: "literal_representation",
+ },
+ Lint {
name: "unwrap_in_result",
group: "restriction",
desc: "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`",
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 103ec27..a58e7e9 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -1,4 +1,4 @@
-error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
+error: error reading Clippy's configuration file `$DIR/clippy.toml`: unknown field `foobar`, expected one of `blacklisted-names`, `cognitive-complexity-threshold`, `cyclomatic-complexity-threshold`, `doc-valid-idents`, `too-many-arguments-threshold`, `type-complexity-threshold`, `single-char-binding-names-threshold`, `too-large-for-stack`, `enum-variant-name-threshold`, `enum-variant-size-threshold`, `verbose-bit-mask-threshold`, `literal-representation-threshold`, `trivial-copy-size-limit`, `pass-by-value-size-limit`, `too-many-lines-threshold`, `array-size-threshold`, `vec-box-size-threshold`, `max-trait-bounds`, `max-struct-bools`, `max-fn-params-bools`, `warn-on-all-wildcard-imports`, `disallowed-methods`, `third-party` at line 5 column 1
error: aborting due to previous error
diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs
new file mode 100644
index 0000000..8884159
--- /dev/null
+++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs
@@ -0,0 +1,86 @@
+// edition:2018
+#![warn(clippy::await_holding_refcell_ref)]
+
+use std::cell::RefCell;
+
+async fn bad(x: &RefCell<u32>) -> u32 {
+ let b = x.borrow();
+ baz().await
+}
+
+async fn bad_mut(x: &RefCell<u32>) -> u32 {
+ let b = x.borrow_mut();
+ baz().await
+}
+
+async fn good(x: &RefCell<u32>) -> u32 {
+ {
+ let b = x.borrow_mut();
+ let y = *b + 1;
+ }
+ baz().await;
+ let b = x.borrow_mut();
+ 47
+}
+
+async fn baz() -> u32 {
+ 42
+}
+
+async fn also_bad(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let b = x.borrow_mut();
+
+ let second = baz().await;
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+async fn less_bad(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let b = x.borrow_mut();
+
+ let second = baz().await;
+
+ drop(b);
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+async fn not_good(x: &RefCell<u32>) -> u32 {
+ let first = baz().await;
+
+ let second = {
+ let b = x.borrow_mut();
+ baz().await
+ };
+
+ let third = baz().await;
+
+ first + second + third
+}
+
+#[allow(clippy::manual_async_fn)]
+fn block_bad(x: &RefCell<u32>) -> impl std::future::Future<Output = u32> + '_ {
+ async move {
+ let b = x.borrow_mut();
+ baz().await
+ }
+}
+
+fn main() {
+ let rc = RefCell::new(100);
+ good(&rc);
+ bad(&rc);
+ bad_mut(&rc);
+ also_bad(&rc);
+ less_bad(&rc);
+ not_good(&rc);
+ block_bad(&rc);
+}
diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
new file mode 100644
index 0000000..b504f04
--- /dev/null
+++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
@@ -0,0 +1,95 @@
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
+ --> $DIR/await_holding_refcell_ref.rs:7:9
+ |
+LL | let b = x.borrow();
+ | ^
+ |
+ = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings`
+note: these are all the await points this ref is held through
+ --> $DIR/await_holding_refcell_ref.rs:7:5
+ |
+LL | / let b = x.borrow();
+LL | | baz().await
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
+ --> $DIR/await_holding_refcell_ref.rs:12:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
+ --> $DIR/await_holding_refcell_ref.rs:12:5
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
+ --> $DIR/await_holding_refcell_ref.rs:33:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
+ --> $DIR/await_holding_refcell_ref.rs:33:5
+ |
+LL | / let b = x.borrow_mut();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
+ --> $DIR/await_holding_refcell_ref.rs:45:9
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
+ --> $DIR/await_holding_refcell_ref.rs:45:5
+ |
+LL | / let b = x.borrow_mut();
+LL | |
+LL | | let second = baz().await;
+LL | |
+... |
+LL | | first + second + third
+LL | | }
+ | |_^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
+ --> $DIR/await_holding_refcell_ref.rs:60:13
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
+ --> $DIR/await_holding_refcell_ref.rs:60:9
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | };
+ | |_____^
+
+error: this RefCell Ref is held across an 'await' point. Consider ensuring the Ref is dropped before calling await.
+ --> $DIR/await_holding_refcell_ref.rs:72:13
+ |
+LL | let b = x.borrow_mut();
+ | ^
+ |
+note: these are all the await points this ref is held through
+ --> $DIR/await_holding_refcell_ref.rs:72:9
+ |
+LL | / let b = x.borrow_mut();
+LL | | baz().await
+LL | | }
+ | |_____^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed
index 9121176..5a012ff 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.fixed
+++ b/src/tools/clippy/tests/ui/bool_comparison.fixed
@@ -1,6 +1,7 @@
// run-rustfix
-#[warn(clippy::bool_comparison)]
+#![warn(clippy::bool_comparison)]
+
fn main() {
let x = true;
if x {
@@ -127,3 +128,40 @@
if b == a {};
if !b == !a {};
}
+
+macro_rules! m {
+ ($func:ident) => {
+ $func()
+ };
+}
+
+fn func() -> bool {
+ true
+}
+
+#[allow(dead_code)]
+fn issue3973() {
+ // ok, don't lint on `cfg` invocation
+ if false == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == false {}
+ if true == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == true {}
+
+ // lint, could be simplified
+ if !m!(func) {}
+ if !m!(func) {}
+ if m!(func) {}
+ if m!(func) {}
+
+ // no lint with a variable
+ let is_debug = false;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+ let is_debug = true;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+}
diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs
index 01ee358..c534bc2 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.rs
+++ b/src/tools/clippy/tests/ui/bool_comparison.rs
@@ -1,6 +1,7 @@
// run-rustfix
-#[warn(clippy::bool_comparison)]
+#![warn(clippy::bool_comparison)]
+
fn main() {
let x = true;
if x == true {
@@ -127,3 +128,40 @@
if b == a {};
if !b == !a {};
}
+
+macro_rules! m {
+ ($func:ident) => {
+ $func()
+ };
+}
+
+fn func() -> bool {
+ true
+}
+
+#[allow(dead_code)]
+fn issue3973() {
+ // ok, don't lint on `cfg` invocation
+ if false == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == false {}
+ if true == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == true {}
+
+ // lint, could be simplified
+ if false == m!(func) {}
+ if m!(func) == false {}
+ if true == m!(func) {}
+ if m!(func) == true {}
+
+ // no lint with a variable
+ let is_debug = false;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+ let is_debug = true;
+ if is_debug == cfg!(feature = "debugging") {}
+ if cfg!(feature = "debugging") == is_debug {}
+ if is_debug == m!(func) {}
+ if m!(func) == is_debug {}
+}
diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr
index 55d94b8..31522d4 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.stderr
+++ b/src/tools/clippy/tests/ui/bool_comparison.stderr
@@ -1,5 +1,5 @@
error: equality checks against true are unnecessary
- --> $DIR/bool_comparison.rs:6:8
+ --> $DIR/bool_comparison.rs:7:8
|
LL | if x == true {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
@@ -7,106 +7,130 @@
= note: `-D clippy::bool-comparison` implied by `-D warnings`
error: equality checks against false can be replaced by a negation
- --> $DIR/bool_comparison.rs:11:8
+ --> $DIR/bool_comparison.rs:12:8
|
LL | if x == false {
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: equality checks against true are unnecessary
- --> $DIR/bool_comparison.rs:16:8
+ --> $DIR/bool_comparison.rs:17:8
|
LL | if true == x {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: equality checks against false can be replaced by a negation
- --> $DIR/bool_comparison.rs:21:8
+ --> $DIR/bool_comparison.rs:22:8
|
LL | if false == x {
| ^^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:26:8
+ --> $DIR/bool_comparison.rs:27:8
|
LL | if x != true {
| ^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against false are unnecessary
- --> $DIR/bool_comparison.rs:31:8
+ --> $DIR/bool_comparison.rs:32:8
|
LL | if x != false {
| ^^^^^^^^^^ help: try simplifying it as shown: `x`
error: inequality checks against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:36:8
+ --> $DIR/bool_comparison.rs:37:8
|
LL | if true != x {
| ^^^^^^^^^ help: try simplifying it as shown: `!x`
error: inequality checks against false are unnecessary
- --> $DIR/bool_comparison.rs:41:8
+ --> $DIR/bool_comparison.rs:42:8
|
LL | if false != x {
| ^^^^^^^^^^ help: try simplifying it as shown: `x`
error: less than comparison against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:46:8
+ --> $DIR/bool_comparison.rs:47:8
|
LL | if x < true {
| ^^^^^^^^ help: try simplifying it as shown: `!x`
error: greater than checks against false are unnecessary
- --> $DIR/bool_comparison.rs:51:8
+ --> $DIR/bool_comparison.rs:52:8
|
LL | if false < x {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: greater than checks against false are unnecessary
- --> $DIR/bool_comparison.rs:56:8
+ --> $DIR/bool_comparison.rs:57:8
|
LL | if x > false {
| ^^^^^^^^^ help: try simplifying it as shown: `x`
error: less than comparison against true can be replaced by a negation
- --> $DIR/bool_comparison.rs:61:8
+ --> $DIR/bool_comparison.rs:62:8
|
LL | if true > x {
| ^^^^^^^^ help: try simplifying it as shown: `!x`
error: order comparisons between booleans can be simplified
- --> $DIR/bool_comparison.rs:67:8
+ --> $DIR/bool_comparison.rs:68:8
|
LL | if x < y {
| ^^^^^ help: try simplifying it as shown: `!x & y`
error: order comparisons between booleans can be simplified
- --> $DIR/bool_comparison.rs:72:8
+ --> $DIR/bool_comparison.rs:73:8
|
LL | if x > y {
| ^^^^^ help: try simplifying it as shown: `x & !y`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:120:8
+ --> $DIR/bool_comparison.rs:121:8
|
LL | if a == !b {};
| ^^^^^^^ help: try simplifying it as shown: `a != b`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:121:8
+ --> $DIR/bool_comparison.rs:122:8
|
LL | if !a == b {};
| ^^^^^^^ help: try simplifying it as shown: `a != b`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:125:8
+ --> $DIR/bool_comparison.rs:126:8
|
LL | if b == !a {};
| ^^^^^^^ help: try simplifying it as shown: `b != a`
error: this comparison might be written more concisely
- --> $DIR/bool_comparison.rs:126:8
+ --> $DIR/bool_comparison.rs:127:8
|
LL | if !b == a {};
| ^^^^^^^ help: try simplifying it as shown: `b != a`
-error: aborting due to 18 previous errors
+error: equality checks against false can be replaced by a negation
+ --> $DIR/bool_comparison.rs:151:8
+ |
+LL | if false == m!(func) {}
+ | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
+
+error: equality checks against false can be replaced by a negation
+ --> $DIR/bool_comparison.rs:152:8
+ |
+LL | if m!(func) == false {}
+ | ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
+
+error: equality checks against true are unnecessary
+ --> $DIR/bool_comparison.rs:153:8
+ |
+LL | if true == m!(func) {}
+ | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
+
+error: equality checks against true are unnecessary
+ --> $DIR/bool_comparison.rs:154:8
+ |
+LL | if m!(func) == true {}
+ | ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
+
+error: aborting due to 22 previous errors
diff --git a/src/tools/clippy/tests/ui/cast_size_32bit.stderr b/src/tools/clippy/tests/ui/cast_size_32bit.stderr
index 2eec518..140676a 100644
--- a/src/tools/clippy/tests/ui/cast_size_32bit.stderr
+++ b/src/tools/clippy/tests/ui/cast_size_32bit.stderr
@@ -124,7 +124,7 @@
--> $DIR/cast_size_32bit.rs:34:5
|
LL | 3_999_999_999usize as f64;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3999999999_f64`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `3_999_999_999_f64`
|
= note: `-D clippy::unnecessary-cast` implied by `-D warnings`
diff --git a/src/tools/clippy/tests/ui/crashes/ice-360.stderr b/src/tools/clippy/tests/ui/crashes/ice-360.stderr
index 84e31ea..bb03ce4 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-360.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-360.stderr
@@ -12,13 +12,14 @@
|
= note: `-D clippy::while-let-loop` implied by `-D warnings`
-error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
+error: empty `loop {}` wastes CPU cycles
--> $DIR/ice-360.rs:10:9
|
LL | loop {}
| ^^^^^^^
|
= note: `-D clippy::empty-loop` implied by `-D warnings`
+ = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: aborting due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui/deprecated.rs b/src/tools/clippy/tests/ui/deprecated.rs
index 9e32fe3..5675559 100644
--- a/src/tools/clippy/tests/ui/deprecated.rs
+++ b/src/tools/clippy/tests/ui/deprecated.rs
@@ -9,5 +9,6 @@
#[warn(clippy::unused_label)]
#[warn(clippy::regex_macro)]
#[warn(clippy::drop_bounds)]
+#[warn(clippy::temporary_cstring_as_ptr)]
fn main() {}
diff --git a/src/tools/clippy/tests/ui/deprecated.stderr b/src/tools/clippy/tests/ui/deprecated.stderr
index d3400a7..37b726f 100644
--- a/src/tools/clippy/tests/ui/deprecated.stderr
+++ b/src/tools/clippy/tests/ui/deprecated.stderr
@@ -66,11 +66,17 @@
LL | #[warn(clippy::drop_bounds)]
| ^^^^^^^^^^^^^^^^^^^
+error: lint `clippy::temporary_cstring_as_ptr` has been removed: `this lint has been uplifted to rustc and is now called `temporary_cstring_as_ptr``
+ --> $DIR/deprecated.rs:12:8
+ |
+LL | #[warn(clippy::temporary_cstring_as_ptr)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: lint `clippy::str_to_string` has been removed: `using `str::to_string` is common even today and specialization will likely happen soon`
--> $DIR/deprecated.rs:1:8
|
LL | #[warn(clippy::str_to_string)]
| ^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 12 previous errors
+error: aborting due to 13 previous errors
diff --git a/src/tools/clippy/tests/ui/empty_loop.stderr b/src/tools/clippy/tests/ui/empty_loop.stderr
index e44c58e..fd3979f 100644
--- a/src/tools/clippy/tests/ui/empty_loop.stderr
+++ b/src/tools/clippy/tests/ui/empty_loop.stderr
@@ -1,22 +1,27 @@
-error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
+error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:9:5
|
LL | loop {}
| ^^^^^^^
|
= note: `-D clippy::empty-loop` implied by `-D warnings`
+ = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
-error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
+error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:11:9
|
LL | loop {}
| ^^^^^^^
+ |
+ = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
-error: empty `loop {}` detected. You may want to either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
+error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:15:9
|
LL | 'inner: loop {}
| ^^^^^^^^^^^^^^^
+ |
+ = help: You should either use `panic!()` or add `std::thread::sleep(..);` to the loop body.
error: aborting due to 3 previous errors
diff --git a/src/tools/clippy/tests/ui/issue-3746.rs b/src/tools/clippy/tests/ui/empty_loop_no_std.rs
similarity index 100%
rename from src/tools/clippy/tests/ui/issue-3746.rs
rename to src/tools/clippy/tests/ui/empty_loop_no_std.rs
diff --git a/src/tools/clippy/tests/ui/eq_op.rs b/src/tools/clippy/tests/ui/eq_op.rs
index 272b090..4e09d19 100644
--- a/src/tools/clippy/tests/ui/eq_op.rs
+++ b/src/tools/clippy/tests/ui/eq_op.rs
@@ -6,6 +6,7 @@
#[allow(clippy::no_effect, unused_variables, clippy::unnecessary_operation, clippy::short_circuit_statement)]
#[allow(clippy::nonminimal_bool)]
#[allow(unused)]
+#[allow(clippy::unnecessary_cast)]
fn main() {
// simple values and comparisons
1 == 1;
diff --git a/src/tools/clippy/tests/ui/eq_op.stderr b/src/tools/clippy/tests/ui/eq_op.stderr
index 5b80e60..ad81b35 100644
--- a/src/tools/clippy/tests/ui/eq_op.stderr
+++ b/src/tools/clippy/tests/ui/eq_op.stderr
@@ -1,5 +1,5 @@
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:11:5
+ --> $DIR/eq_op.rs:12:5
|
LL | 1 == 1;
| ^^^^^^
@@ -7,157 +7,157 @@
= note: `-D clippy::eq-op` implied by `-D warnings`
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:12:5
+ --> $DIR/eq_op.rs:13:5
|
LL | "no" == "no";
| ^^^^^^^^^^^^
error: equal expressions as operands to `!=`
- --> $DIR/eq_op.rs:14:5
+ --> $DIR/eq_op.rs:15:5
|
LL | false != false;
| ^^^^^^^^^^^^^^
error: equal expressions as operands to `<`
- --> $DIR/eq_op.rs:15:5
+ --> $DIR/eq_op.rs:16:5
|
LL | 1.5 < 1.5;
| ^^^^^^^^^
error: equal expressions as operands to `>=`
- --> $DIR/eq_op.rs:16:5
+ --> $DIR/eq_op.rs:17:5
|
LL | 1u64 >= 1u64;
| ^^^^^^^^^^^^
error: equal expressions as operands to `&`
- --> $DIR/eq_op.rs:19:5
+ --> $DIR/eq_op.rs:20:5
|
LL | (1 as u64) & (1 as u64);
| ^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `^`
- --> $DIR/eq_op.rs:20:5
+ --> $DIR/eq_op.rs:21:5
|
LL | 1 ^ ((((((1))))));
| ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `<`
- --> $DIR/eq_op.rs:23:5
+ --> $DIR/eq_op.rs:24:5
|
LL | (-(2) < -(2));
| ^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:24:5
+ --> $DIR/eq_op.rs:25:5
|
LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&`
- --> $DIR/eq_op.rs:24:6
+ --> $DIR/eq_op.rs:25:6
|
LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
| ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&`
- --> $DIR/eq_op.rs:24:27
+ --> $DIR/eq_op.rs:25:27
|
LL | ((1 + 1) & (1 + 1) == (1 + 1) & (1 + 1));
| ^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:25:5
+ --> $DIR/eq_op.rs:26:5
|
LL | (1 * 2) + (3 * 4) == 1 * 2 + 3 * 4;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `!=`
- --> $DIR/eq_op.rs:28:5
+ --> $DIR/eq_op.rs:29:5
|
LL | ([1] != [1]);
| ^^^^^^^^^^^^
error: equal expressions as operands to `!=`
- --> $DIR/eq_op.rs:29:5
+ --> $DIR/eq_op.rs:30:5
|
LL | ((1, 2) != (1, 2));
| ^^^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:33:5
+ --> $DIR/eq_op.rs:34:5
|
LL | 1 + 1 == 2;
| ^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:34:5
+ --> $DIR/eq_op.rs:35:5
|
LL | 1 - 1 == 0;
| ^^^^^^^^^^
error: equal expressions as operands to `-`
- --> $DIR/eq_op.rs:34:5
+ --> $DIR/eq_op.rs:35:5
|
LL | 1 - 1 == 0;
| ^^^^^
error: equal expressions as operands to `-`
- --> $DIR/eq_op.rs:36:5
+ --> $DIR/eq_op.rs:37:5
|
LL | 1 - 1;
| ^^^^^
error: equal expressions as operands to `/`
- --> $DIR/eq_op.rs:37:5
+ --> $DIR/eq_op.rs:38:5
|
LL | 1 / 1;
| ^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:38:5
+ --> $DIR/eq_op.rs:39:5
|
LL | true && true;
| ^^^^^^^^^^^^
error: equal expressions as operands to `||`
- --> $DIR/eq_op.rs:40:5
+ --> $DIR/eq_op.rs:41:5
|
LL | true || true;
| ^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:46:5
+ --> $DIR/eq_op.rs:47:5
|
LL | a == b && b == a;
| ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:47:5
+ --> $DIR/eq_op.rs:48:5
|
LL | a != b && b != a;
| ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:48:5
+ --> $DIR/eq_op.rs:49:5
|
LL | a < b && b > a;
| ^^^^^^^^^^^^^^
error: equal expressions as operands to `&&`
- --> $DIR/eq_op.rs:49:5
+ --> $DIR/eq_op.rs:50:5
|
LL | a <= b && b >= a;
| ^^^^^^^^^^^^^^^^
error: equal expressions as operands to `==`
- --> $DIR/eq_op.rs:52:5
+ --> $DIR/eq_op.rs:53:5
|
LL | a == a;
| ^^^^^^
error: equal expressions as operands to `/`
- --> $DIR/eq_op.rs:62:20
+ --> $DIR/eq_op.rs:63:20
|
LL | const D: u32 = A / A;
| ^^^^^
diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.rs b/src/tools/clippy/tests/ui/if_same_then_else2.rs
index 3cc2180..8d54f75 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else2.rs
+++ b/src/tools/clippy/tests/ui/if_same_then_else2.rs
@@ -3,7 +3,8 @@
clippy::blacklisted_name,
clippy::collapsible_if,
clippy::ifs_same_cond,
- clippy::needless_return
+ clippy::needless_return,
+ clippy::single_element_loop
)]
fn if_same_then_else2() -> Result<&'static str, ()> {
diff --git a/src/tools/clippy/tests/ui/if_same_then_else2.stderr b/src/tools/clippy/tests/ui/if_same_then_else2.stderr
index f5d087f..da2be6c 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else2.stderr
+++ b/src/tools/clippy/tests/ui/if_same_then_else2.stderr
@@ -1,5 +1,5 @@
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:19:12
+ --> $DIR/if_same_then_else2.rs:20:12
|
LL | } else {
| ____________^
@@ -13,7 +13,7 @@
|
= note: `-D clippy::if-same-then-else` implied by `-D warnings`
note: same as this
- --> $DIR/if_same_then_else2.rs:10:13
+ --> $DIR/if_same_then_else2.rs:11:13
|
LL | if true {
| _____________^
@@ -26,7 +26,7 @@
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:33:12
+ --> $DIR/if_same_then_else2.rs:34:12
|
LL | } else {
| ____________^
@@ -36,7 +36,7 @@
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:31:13
+ --> $DIR/if_same_then_else2.rs:32:13
|
LL | if true {
| _____________^
@@ -45,7 +45,7 @@
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:40:12
+ --> $DIR/if_same_then_else2.rs:41:12
|
LL | } else {
| ____________^
@@ -55,7 +55,7 @@
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:38:13
+ --> $DIR/if_same_then_else2.rs:39:13
|
LL | if true {
| _____________^
@@ -64,7 +64,7 @@
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:90:12
+ --> $DIR/if_same_then_else2.rs:91:12
|
LL | } else {
| ____________^
@@ -74,7 +74,7 @@
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:88:21
+ --> $DIR/if_same_then_else2.rs:89:21
|
LL | let _ = if true {
| _____________________^
@@ -83,7 +83,7 @@
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:97:12
+ --> $DIR/if_same_then_else2.rs:98:12
|
LL | } else {
| ____________^
@@ -93,7 +93,7 @@
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:95:13
+ --> $DIR/if_same_then_else2.rs:96:13
|
LL | if true {
| _____________^
@@ -102,7 +102,7 @@
| |_____^
error: this `if` has identical blocks
- --> $DIR/if_same_then_else2.rs:122:12
+ --> $DIR/if_same_then_else2.rs:123:12
|
LL | } else {
| ____________^
@@ -112,7 +112,7 @@
| |_____^
|
note: same as this
- --> $DIR/if_same_then_else2.rs:119:20
+ --> $DIR/if_same_then_else2.rs:120:20
|
LL | } else if true {
| ____________________^
diff --git a/src/tools/clippy/tests/ui/invalid_paths.rs b/src/tools/clippy/tests/ui/invalid_paths.rs
new file mode 100644
index 0000000..01e28ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_paths.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::internal)]
+
+mod paths {
+ // Good path
+ pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
+
+ // Path to method on inherent impl of a primitive type
+ pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
+
+ // Path to method on inherent impl
+ pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
+
+ // Path with empty segment
+ pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
+
+ // Path with bad crate
+ pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
+
+ // Path with bad module
+ pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/invalid_paths.stderr b/src/tools/clippy/tests/ui/invalid_paths.stderr
new file mode 100644
index 0000000..bd69d66
--- /dev/null
+++ b/src/tools/clippy/tests/ui/invalid_paths.stderr
@@ -0,0 +1,16 @@
+error: invalid path
+ --> $DIR/invalid_paths.rs:17:5
+ |
+LL | pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::clippy-lints-internal` implied by `-D warnings`
+
+error: invalid path
+ --> $DIR/invalid_paths.rs:20:5
+ |
+LL | pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/large_digit_groups.fixed b/src/tools/clippy/tests/ui/large_digit_groups.fixed
index 859fad2..3430c13 100644
--- a/src/tools/clippy/tests/ui/large_digit_groups.fixed
+++ b/src/tools/clippy/tests/ui/large_digit_groups.fixed
@@ -11,7 +11,7 @@
let _good = (
0b1011_i64,
0o1_234_u32,
- 0x1_234_567,
+ 0x0123_4567,
1_2345_6789,
1234_f32,
1_234.12_f32,
diff --git a/src/tools/clippy/tests/ui/large_digit_groups.stderr b/src/tools/clippy/tests/ui/large_digit_groups.stderr
index b6d9672..13d108b 100644
--- a/src/tools/clippy/tests/ui/large_digit_groups.stderr
+++ b/src/tools/clippy/tests/ui/large_digit_groups.stderr
@@ -1,24 +1,30 @@
-error: digit groups should be smaller
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/large_digit_groups.rs:14:9
+ |
+LL | 0x1_234_567,
+ | ^^^^^^^^^^^ help: consider: `0x0123_4567`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: digits of hex or binary literal not grouped by four
--> $DIR/large_digit_groups.rs:22:9
|
LL | 0b1_10110_i64,
| ^^^^^^^^^^^^^ help: consider: `0b11_0110_i64`
- |
- = note: `-D clippy::large-digit-groups` implied by `-D warnings`
-error: digits grouped inconsistently by underscores
+error: digits of hex or binary literal not grouped by four
--> $DIR/large_digit_groups.rs:23:9
|
LL | 0xd_e_adbee_f_usize,
| ^^^^^^^^^^^^^^^^^^^ help: consider: `0xdead_beef_usize`
- |
- = note: `-D clippy::inconsistent-digit-grouping` implied by `-D warnings`
error: digit groups should be smaller
--> $DIR/large_digit_groups.rs:24:9
|
LL | 1_23456_f32,
| ^^^^^^^^^^^ help: consider: `123_456_f32`
+ |
+ = note: `-D clippy::large-digit-groups` implied by `-D warnings`
error: digit groups should be smaller
--> $DIR/large_digit_groups.rs:25:9
@@ -38,5 +44,5 @@
LL | 1_23456.12345_6_f64,
| ^^^^^^^^^^^^^^^^^^^ help: consider: `123_456.123_456_f64`
-error: aborting due to 6 previous errors
+error: aborting due to 7 previous errors
diff --git a/src/tools/clippy/tests/ui/large_types_passed_by_value.rs b/src/tools/clippy/tests/ui/large_types_passed_by_value.rs
new file mode 100644
index 0000000..e4a2e9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_types_passed_by_value.rs
@@ -0,0 +1,66 @@
+// normalize-stderr-test "\(\d+ byte\)" -> "(N byte)"
+// normalize-stderr-test "\(limit: \d+ byte\)" -> "(limit: N byte)"
+
+#![warn(clippy::large_types_passed_by_value)]
+
+pub struct Large([u8; 2048]);
+
+#[derive(Clone, Copy)]
+pub struct LargeAndCopy([u8; 2048]);
+
+pub struct Small([u8; 4]);
+
+#[derive(Clone, Copy)]
+pub struct SmallAndCopy([u8; 4]);
+
+fn small(a: Small, b: SmallAndCopy) {}
+fn not_copy(a: Large) {}
+fn by_ref(a: &Large, b: &LargeAndCopy) {}
+fn mutable(mut a: LargeAndCopy) {}
+fn bad(a: LargeAndCopy) {}
+pub fn bad_but_pub(a: LargeAndCopy) {}
+
+impl LargeAndCopy {
+ fn self_is_ok(self) {}
+ fn other_is_not_ok(self, other: LargeAndCopy) {}
+ fn unless_other_can_change(self, mut other: LargeAndCopy) {}
+ pub fn or_were_in_public(self, other: LargeAndCopy) {}
+}
+
+trait LargeTypeDevourer {
+ fn devoure_array(&self, array: [u8; 6666]);
+ fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy));
+ fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
+}
+
+pub trait PubLargeTypeDevourer {
+ fn devoure_array_in_public(&self, array: [u8; 6666]);
+}
+
+struct S {}
+impl LargeTypeDevourer for S {
+ fn devoure_array(&self, array: [u8; 6666]) {
+ todo!();
+ }
+ fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy)) {
+ todo!();
+ }
+ fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy)) {
+ todo!();
+ }
+}
+
+#[inline(always)]
+fn foo_always(x: LargeAndCopy) {
+ todo!();
+}
+#[inline(never)]
+fn foo_never(x: LargeAndCopy) {
+ todo!();
+}
+#[inline]
+fn foo(x: LargeAndCopy) {
+ todo!();
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/large_types_passed_by_value.stderr b/src/tools/clippy/tests/ui/large_types_passed_by_value.stderr
new file mode 100644
index 0000000..5f42dcf
--- /dev/null
+++ b/src/tools/clippy/tests/ui/large_types_passed_by_value.stderr
@@ -0,0 +1,52 @@
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:20:11
+ |
+LL | fn bad(a: LargeAndCopy) {}
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+ |
+ = note: `-D clippy::large-types-passed-by-value` implied by `-D warnings`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:25:37
+ |
+LL | fn other_is_not_ok(self, other: LargeAndCopy) {}
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:31:36
+ |
+LL | fn devoure_array(&self, array: [u8; 6666]);
+ | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:32:34
+ |
+LL | fn devoure_tuple(&self, tup: (LargeAndCopy, LargeAndCopy));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:33:50
+ |
+LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
+ | ^^^^^^^^^^ help: consider passing by reference instead: `&[u8; 6666]`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:33:67
+ |
+LL | fn devoure_array_and_tuple_wow(&self, array: [u8; 6666], tup: (LargeAndCopy, LargeAndCopy));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider passing by reference instead: `&(LargeAndCopy, LargeAndCopy)`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:58:17
+ |
+LL | fn foo_never(x: LargeAndCopy) {
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+
+error: this argument (N byte) is passed by value, but might be more efficient if passed by reference (limit: N byte)
+ --> $DIR/large_types_passed_by_value.rs:62:11
+ |
+LL | fn foo(x: LargeAndCopy) {
+ | ^^^^^^^^^^^^ help: consider passing by reference instead: `&LargeAndCopy`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/literals.rs b/src/tools/clippy/tests/ui/literals.rs
index c299b16..a72a74b 100644
--- a/src/tools/clippy/tests/ui/literals.rs
+++ b/src/tools/clippy/tests/ui/literals.rs
@@ -33,4 +33,9 @@
let fail19 = 12_3456_21;
let fail22 = 3__4___23;
let fail23 = 3__16___23;
+
+ let fail24 = 0xAB_ABC_AB;
+ let fail25 = 0b01_100_101;
+ let ok26 = 0x6_A0_BF;
+ let ok27 = 0b1_0010_0101;
}
diff --git a/src/tools/clippy/tests/ui/literals.stderr b/src/tools/clippy/tests/ui/literals.stderr
index 0b3af2d..64ceeb3 100644
--- a/src/tools/clippy/tests/ui/literals.stderr
+++ b/src/tools/clippy/tests/ui/literals.stderr
@@ -69,5 +69,19 @@
LL | let fail23 = 3__16___23;
| ^^^^^^^^^^ help: consider: `31_623`
-error: aborting due to 8 previous errors
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/literals.rs:37:18
+ |
+LL | let fail24 = 0xAB_ABC_AB;
+ | ^^^^^^^^^^^ help: consider: `0x0ABA_BCAB`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/literals.rs:38:18
+ |
+LL | let fail25 = 0b01_100_101;
+ | ^^^^^^^^^^^^ help: consider: `0b0110_0101`
+
+error: aborting due to 10 previous errors
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
index a8736f1..5aa5a43 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.fixed
@@ -1,7 +1,8 @@
// run-rustfix
#![allow(dead_code)]
+#![allow(unused_variables)]
-fn unwrap_or() {
+fn option_unwrap_or() {
// int case
Some(1).unwrap_or(42);
@@ -65,4 +66,74 @@
};
}
+fn result_unwrap_or() {
+ // int case
+ Ok::<i32, &str>(1).unwrap_or(42);
+
+ // int case, scrutinee is a binding
+ let a = Ok::<i32, &str>(1);
+ a.unwrap_or(42);
+
+ // int case, suggestion must surround Result expr with parenthesis
+ (Ok(1) as Result<i32, &str>).unwrap_or(42);
+
+ // method call case, suggestion must not surround Result expr `s.method()` with parenthesis
+ struct S {}
+ impl S {
+ fn method(self) -> Option<i32> {
+ Some(42)
+ }
+ }
+ let s = S {};
+ s.method().unwrap_or(42);
+
+ // int case reversed
+ Ok::<i32, &str>(1).unwrap_or(42);
+
+ // richer none expr
+ Ok::<i32, &str>(1).unwrap_or(1 + 42);
+
+ // multiline case
+ #[rustfmt::skip]
+ Ok::<i32, &str>(1).unwrap_or({
+ 42 + 42
+ + 42 + 42 + 42
+ + 42 + 42 + 42
+ });
+
+ // string case
+ Ok::<&str, &str>("Bob").unwrap_or("Alice");
+
+ // don't lint
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i + 2,
+ Err(_) => 42,
+ };
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => return,
+ };
+ for j in 0..4 {
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => continue,
+ };
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => break,
+ };
+ }
+
+ // don't lint, Err value is used
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => s,
+ };
+ // could lint, but unused_variables takes care of it
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => "Bob",
+ };
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.rs b/src/tools/clippy/tests/ui/manual_unwrap_or.rs
index bede8cf..df534031 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or.rs
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.rs
@@ -1,7 +1,8 @@
// run-rustfix
#![allow(dead_code)]
+#![allow(unused_variables)]
-fn unwrap_or() {
+fn option_unwrap_or() {
// int case
match Some(1) {
Some(i) => i,
@@ -80,4 +81,98 @@
};
}
+fn result_unwrap_or() {
+ // int case
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => 42,
+ };
+
+ // int case, scrutinee is a binding
+ let a = Ok::<i32, &str>(1);
+ match a {
+ Ok(i) => i,
+ Err(_) => 42,
+ };
+
+ // int case, suggestion must surround Result expr with parenthesis
+ match Ok(1) as Result<i32, &str> {
+ Ok(i) => i,
+ Err(_) => 42,
+ };
+
+ // method call case, suggestion must not surround Result expr `s.method()` with parenthesis
+ struct S {}
+ impl S {
+ fn method(self) -> Option<i32> {
+ Some(42)
+ }
+ }
+ let s = S {};
+ match s.method() {
+ Some(i) => i,
+ None => 42,
+ };
+
+ // int case reversed
+ match Ok::<i32, &str>(1) {
+ Err(_) => 42,
+ Ok(i) => i,
+ };
+
+ // richer none expr
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => 1 + 42,
+ };
+
+ // multiline case
+ #[rustfmt::skip]
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => {
+ 42 + 42
+ + 42 + 42 + 42
+ + 42 + 42 + 42
+ }
+ };
+
+ // string case
+ match Ok::<&str, &str>("Bob") {
+ Ok(i) => i,
+ Err(_) => "Alice",
+ };
+
+ // don't lint
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i + 2,
+ Err(_) => 42,
+ };
+ match Ok::<i32, &str>(1) {
+ Ok(i) => i,
+ Err(_) => return,
+ };
+ for j in 0..4 {
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => continue,
+ };
+ match Ok::<i32, &str>(j) {
+ Ok(i) => i,
+ Err(_) => break,
+ };
+ }
+
+ // don't lint, Err value is used
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => s,
+ };
+ // could lint, but unused_variables takes care of it
+ match Ok::<&str, &str>("Alice") {
+ Ok(s) => s,
+ Err(s) => "Bob",
+ };
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr
index 674f295..fc174c4 100644
--- a/src/tools/clippy/tests/ui/manual_unwrap_or.stderr
+++ b/src/tools/clippy/tests/ui/manual_unwrap_or.stderr
@@ -1,5 +1,5 @@
error: this pattern reimplements `Option::unwrap_or`
- --> $DIR/manual_unwrap_or.rs:6:5
+ --> $DIR/manual_unwrap_or.rs:7:5
|
LL | / match Some(1) {
LL | | Some(i) => i,
@@ -10,7 +10,7 @@
= note: `-D clippy::manual-unwrap-or` implied by `-D warnings`
error: this pattern reimplements `Option::unwrap_or`
- --> $DIR/manual_unwrap_or.rs:12:5
+ --> $DIR/manual_unwrap_or.rs:13:5
|
LL | / match Some(1) {
LL | | None => 42,
@@ -19,7 +19,7 @@
| |_____^ help: replace with: `Some(1).unwrap_or(42)`
error: this pattern reimplements `Option::unwrap_or`
- --> $DIR/manual_unwrap_or.rs:18:5
+ --> $DIR/manual_unwrap_or.rs:19:5
|
LL | / match Some(1) {
LL | | Some(i) => i,
@@ -28,7 +28,7 @@
| |_____^ help: replace with: `Some(1).unwrap_or(1 + 42)`
error: this pattern reimplements `Option::unwrap_or`
- --> $DIR/manual_unwrap_or.rs:25:5
+ --> $DIR/manual_unwrap_or.rs:26:5
|
LL | / match Some(1) {
LL | | Some(i) => i,
@@ -49,7 +49,7 @@
|
error: this pattern reimplements `Option::unwrap_or`
- --> $DIR/manual_unwrap_or.rs:35:5
+ --> $DIR/manual_unwrap_or.rs:36:5
|
LL | / match Some("Bob") {
LL | | Some(i) => i,
@@ -57,5 +57,89 @@
LL | | };
| |_____^ help: replace with: `Some("Bob").unwrap_or("Alice")`
-error: aborting due to 5 previous errors
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:86:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Ok(i) => i,
+LL | | Err(_) => 42,
+LL | | };
+ | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:93:5
+ |
+LL | / match a {
+LL | | Ok(i) => i,
+LL | | Err(_) => 42,
+LL | | };
+ | |_____^ help: replace with: `a.unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:99:5
+ |
+LL | / match Ok(1) as Result<i32, &str> {
+LL | | Ok(i) => i,
+LL | | Err(_) => 42,
+LL | | };
+ | |_____^ help: replace with: `(Ok(1) as Result<i32, &str>).unwrap_or(42)`
+
+error: this pattern reimplements `Option::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:112:5
+ |
+LL | / match s.method() {
+LL | | Some(i) => i,
+LL | | None => 42,
+LL | | };
+ | |_____^ help: replace with: `s.method().unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:118:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Err(_) => 42,
+LL | | Ok(i) => i,
+LL | | };
+ | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:124:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Ok(i) => i,
+LL | | Err(_) => 1 + 42,
+LL | | };
+ | |_____^ help: replace with: `Ok::<i32, &str>(1).unwrap_or(1 + 42)`
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:131:5
+ |
+LL | / match Ok::<i32, &str>(1) {
+LL | | Ok(i) => i,
+LL | | Err(_) => {
+LL | | 42 + 42
+... |
+LL | | }
+LL | | };
+ | |_____^
+ |
+help: replace with
+ |
+LL | Ok::<i32, &str>(1).unwrap_or({
+LL | 42 + 42
+LL | + 42 + 42 + 42
+LL | + 42 + 42 + 42
+LL | });
+ |
+
+error: this pattern reimplements `Result::unwrap_or`
+ --> $DIR/manual_unwrap_or.rs:141:5
+ |
+LL | / match Ok::<&str, &str>("Bob") {
+LL | | Ok(i) => i,
+LL | | Err(_) => "Alice",
+LL | | };
+ | |_____^ help: replace with: `Ok::<&str, &str>("Bob").unwrap_or("Alice")`
+
+error: aborting due to 13 previous errors
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
index f3e1909..7f4ebf5 100644
--- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
@@ -1,7 +1,7 @@
// run-rustfix
#![warn(clippy::match_like_matches_macro)]
-#![allow(unreachable_patterns)]
+#![allow(unreachable_patterns, dead_code)]
fn main() {
let x = Some(5);
@@ -33,4 +33,70 @@
_ => true,
None => false,
};
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ };
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = matches!(x, E::A(_) | E::B(_));
+ }
+ {
+ // lint
+ let _ans = !matches!(x, E::B(_) | E::C);
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
}
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
index fbae7c1..aee56dd 100644
--- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
@@ -1,7 +1,7 @@
// run-rustfix
#![warn(clippy::match_like_matches_macro)]
-#![allow(unreachable_patterns)]
+#![allow(unreachable_patterns, dead_code)]
fn main() {
let x = Some(5);
@@ -45,4 +45,78 @@
_ => true,
None => false,
};
+
+ enum E {
+ A(u32),
+ B(i32),
+ C,
+ D,
+ };
+ let x = E::A(2);
+ {
+ // lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
+ {
+ // lint
+ let _ans = match x {
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => false,
+ E::C => true,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => true,
+ E::B(_) => false,
+ E::C => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) if a < 10 => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(a) if a < 10 => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(a) => a == 10,
+ E::B(_) => false,
+ _ => true,
+ };
+ }
+ {
+ // no lint
+ let _ans = match x {
+ E::A(_) => false,
+ E::B(_) => true,
+ _ => false,
+ };
+ }
}
diff --git a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
index 4668f85..c52e41c 100644
--- a/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
+++ b/src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
@@ -48,5 +48,27 @@
LL | let _zzz = if let Some(5) = x { true } else { false };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `matches!(x, Some(5))`
-error: aborting due to 5 previous errors
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:58:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::A(_) => true,
+LL | | E::B(_) => true,
+LL | | _ => false,
+LL | | };
+ | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
+
+error: match expression looks like `matches!` macro
+ --> $DIR/match_expr_like_matches_macro.rs:66:20
+ |
+LL | let _ans = match x {
+ | ____________________^
+LL | | E::B(_) => false,
+LL | | E::C => false,
+LL | | _ => true,
+LL | | };
+ | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
+
+error: aborting due to 7 previous errors
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs
index e1401d2..06d9149 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms2.rs
@@ -119,6 +119,22 @@
unreachable!();
},
}
+
+ match_expr_like_matches_macro_priority();
+}
+
+fn match_expr_like_matches_macro_priority() {
+ enum E {
+ A,
+ B,
+ C,
+ }
+ let x = E::A;
+ let _ans = match x {
+ E::A => false,
+ E::B => false,
+ _ => true,
+ };
}
fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr
index 26c65f3..fccaf80 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr
@@ -141,5 +141,18 @@
| ^^^^^
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
-error: aborting due to 7 previous errors
+error: match expression looks like `matches!` macro
+ --> $DIR/match_same_arms2.rs:133:16
+ |
+LL | let _ans = match x {
+ | ________________^
+LL | | E::A => false,
+LL | | E::B => false,
+LL | | _ => true,
+LL | | };
+ | |_____^ help: try this: `!matches!(x, E::A | E::B)`
+ |
+ = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
+
+error: aborting due to 8 previous errors
diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed
new file mode 100644
index 0000000..36bc52e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![allow(dead_code, unused_mut)]
+#![warn(clippy::mut_mutex_lock)]
+
+use std::sync::{Arc, Mutex};
+
+fn mut_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+
+ let mut value = value_mutex.get_mut().unwrap();
+ *value += 1;
+}
+
+fn no_owned_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let mut value = value_rc.lock().unwrap();
+ *value += 1;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.rs b/src/tools/clippy/tests/ui/mut_mutex_lock.rs
new file mode 100644
index 0000000..ea60df5
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mutex_lock.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+#![allow(dead_code, unused_mut)]
+#![warn(clippy::mut_mutex_lock)]
+
+use std::sync::{Arc, Mutex};
+
+fn mut_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let value_mutex = Arc::get_mut(&mut value_rc).unwrap();
+
+ let mut value = value_mutex.lock().unwrap();
+ *value += 1;
+}
+
+fn no_owned_mutex_lock() {
+ let mut value_rc = Arc::new(Mutex::new(42_u8));
+ let mut value = value_rc.lock().unwrap();
+ *value += 1;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr
new file mode 100644
index 0000000..21c1b34
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr
@@ -0,0 +1,10 @@
+error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference
+ --> $DIR/mut_mutex_lock.rs:11:33
+ |
+LL | let mut value = value_mutex.lock().unwrap();
+ | ^^^^ help: change this to: `get_mut`
+ |
+ = note: `-D clippy::mut-mutex-lock` implied by `-D warnings`
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.rs b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
index 4cf03e8..4f6e535 100644
--- a/src/tools/clippy/tests/ui/needless_collect_indirect.rs
+++ b/src/tools/clippy/tests/ui/needless_collect_indirect.rs
@@ -16,4 +16,10 @@
.into_iter()
.map(|x| (*x, *x + 1))
.collect::<HashMap<_, _>>();
+
+ // #6202
+ let a = "a".to_string();
+ let sample = vec![a.clone(), "b".to_string(), "c".to_string()];
+ let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
+ non_copy_contains.contains(&a);
}
diff --git a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
index 0c1e61d..fb807da 100644
--- a/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
+++ b/src/tools/clippy/tests/ui/needless_collect_indirect.stderr
@@ -48,8 +48,21 @@
help: Check if the original Iterator contains an element instead of collecting then checking
|
LL |
-LL | sample.iter().any(|x| x == &&5);
+LL | sample.iter().any(|x| x == &5);
|
-error: aborting due to 4 previous errors
+error: avoid using `collect()` when not needed
+ --> $DIR/needless_collect_indirect.rs:23:5
+ |
+LL | / let non_copy_contains = sample.into_iter().collect::<Vec<_>>();
+LL | | non_copy_contains.contains(&a);
+ | |____^
+ |
+help: Check if the original Iterator contains an element instead of collecting then checking
+ |
+LL |
+LL | sample.into_iter().any(|x| x == a);
+ |
+
+error: aborting due to 5 previous errors
diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs
index d482d46..6001ef3 100644
--- a/src/tools/clippy/tests/ui/needless_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs
@@ -357,4 +357,15 @@
}
}
+mod issue6159 {
+ use std::ops::Deref;
+ pub fn apply_deref<'a, T, F, R>(x: &'a T, f: F) -> R
+ where
+ T: Deref,
+ F: FnOnce(&'a T::Target) -> R,
+ {
+ f(x.deref())
+ }
+}
+
fn main() {}
diff --git a/src/tools/clippy/tests/ui/range_contains.fixed b/src/tools/clippy/tests/ui/range_contains.fixed
new file mode 100644
index 0000000..632a659
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_contains.fixed
@@ -0,0 +1,41 @@
+// run-rustfix
+
+#[warn(clippy::manual_range_contains)]
+#[allow(unused)]
+#[allow(clippy::no_effect)]
+#[allow(clippy::short_circuit_statement)]
+#[allow(clippy::unnecessary_operation)]
+fn main() {
+ let x = 9_u32;
+
+ // order shouldn't matter
+ (8..12).contains(&x);
+ (21..42).contains(&x);
+ (1..100).contains(&x);
+
+ // also with inclusive ranges
+ (9..=99).contains(&x);
+ (1..=33).contains(&x);
+ (1..=999).contains(&x);
+
+ // and the outside
+ !(8..12).contains(&x);
+ !(21..42).contains(&x);
+ !(1..100).contains(&x);
+
+ // also with the outside of inclusive ranges
+ !(9..=99).contains(&x);
+ !(1..=33).contains(&x);
+ !(1..=999).contains(&x);
+
+ // not a range.contains
+ x > 8 && x < 12; // lower bound not inclusive
+ x < 8 && x <= 12; // same direction
+ x >= 12 && 12 >= x; // same bounds
+ x < 8 && x > 12; // wrong direction
+
+ x <= 8 || x >= 12;
+ x >= 8 || x >= 12;
+ x < 12 || 12 < x;
+ x >= 8 || x <= 12;
+}
diff --git a/src/tools/clippy/tests/ui/range_contains.rs b/src/tools/clippy/tests/ui/range_contains.rs
new file mode 100644
index 0000000..6af0d03
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_contains.rs
@@ -0,0 +1,41 @@
+// run-rustfix
+
+#[warn(clippy::manual_range_contains)]
+#[allow(unused)]
+#[allow(clippy::no_effect)]
+#[allow(clippy::short_circuit_statement)]
+#[allow(clippy::unnecessary_operation)]
+fn main() {
+ let x = 9_u32;
+
+ // order shouldn't matter
+ x >= 8 && x < 12;
+ x < 42 && x >= 21;
+ 100 > x && 1 <= x;
+
+ // also with inclusive ranges
+ x >= 9 && x <= 99;
+ x <= 33 && x >= 1;
+ 999 >= x && 1 <= x;
+
+ // and the outside
+ x < 8 || x >= 12;
+ x >= 42 || x < 21;
+ 100 <= x || 1 > x;
+
+ // also with the outside of inclusive ranges
+ x < 9 || x > 99;
+ x > 33 || x < 1;
+ 999 < x || 1 > x;
+
+ // not a range.contains
+ x > 8 && x < 12; // lower bound not inclusive
+ x < 8 && x <= 12; // same direction
+ x >= 12 && 12 >= x; // same bounds
+ x < 8 && x > 12; // wrong direction
+
+ x <= 8 || x >= 12;
+ x >= 8 || x >= 12;
+ x < 12 || 12 < x;
+ x >= 8 || x <= 12;
+}
diff --git a/src/tools/clippy/tests/ui/range_contains.stderr b/src/tools/clippy/tests/ui/range_contains.stderr
new file mode 100644
index 0000000..69b009e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/range_contains.stderr
@@ -0,0 +1,76 @@
+error: manual `Range::contains` implementation
+ --> $DIR/range_contains.rs:12:5
+ |
+LL | x >= 8 && x < 12;
+ | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)`
+ |
+ = note: `-D clippy::manual-range-contains` implied by `-D warnings`
+
+error: manual `Range::contains` implementation
+ --> $DIR/range_contains.rs:13:5
+ |
+LL | x < 42 && x >= 21;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)`
+
+error: manual `Range::contains` implementation
+ --> $DIR/range_contains.rs:14:5
+ |
+LL | 100 > x && 1 <= x;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:17:5
+ |
+LL | x >= 9 && x <= 99;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:18:5
+ |
+LL | x <= 33 && x >= 1;
+ | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)`
+
+error: manual `RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:19:5
+ |
+LL | 999 >= x && 1 <= x;
+ | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:22:5
+ |
+LL | x < 8 || x >= 12;
+ | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:23:5
+ |
+LL | x >= 42 || x < 21;
+ | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)`
+
+error: manual `!Range::contains` implementation
+ --> $DIR/range_contains.rs:24:5
+ |
+LL | 100 <= x || 1 > x;
+ | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:27:5
+ |
+LL | x < 9 || x > 99;
+ | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:28:5
+ |
+LL | x > 33 || x < 1;
+ | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)`
+
+error: manual `!RangeInclusive::contains` implementation
+ --> $DIR/range_contains.rs:29:5
+ |
+LL | 999 < x || 1 > x;
+ | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/single_element_loop.fixed b/src/tools/clippy/tests/ui/single_element_loop.fixed
new file mode 100644
index 0000000..8ca0682
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_element_loop.fixed
@@ -0,0 +1,11 @@
+// run-rustfix
+// Tests from for_loop.rs that don't have suggestions
+
+#[warn(clippy::single_element_loop)]
+fn main() {
+ let item1 = 2;
+ {
+ let item = &item1;
+ println!("{}", item);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_element_loop.rs b/src/tools/clippy/tests/ui/single_element_loop.rs
new file mode 100644
index 0000000..57e9336
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_element_loop.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+// Tests from for_loop.rs that don't have suggestions
+
+#[warn(clippy::single_element_loop)]
+fn main() {
+ let item1 = 2;
+ for item in &[item1] {
+ println!("{}", item);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/single_element_loop.stderr b/src/tools/clippy/tests/ui/single_element_loop.stderr
new file mode 100644
index 0000000..90be1dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/single_element_loop.stderr
@@ -0,0 +1,19 @@
+error: for loop over a single element
+ --> $DIR/single_element_loop.rs:7:5
+ |
+LL | / for item in &[item1] {
+LL | | println!("{}", item);
+LL | | }
+ | |_____^
+ |
+ = note: `-D clippy::single-element-loop` implied by `-D warnings`
+help: try
+ |
+LL | {
+LL | let item = &item1;
+LL | println!("{}", item);
+LL | }
+ |
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.rs b/src/tools/clippy/tests/ui/undropped_manually_drops.rs
new file mode 100644
index 0000000..f4cfc92
--- /dev/null
+++ b/src/tools/clippy/tests/ui/undropped_manually_drops.rs
@@ -0,0 +1,26 @@
+#![warn(clippy::undropped_manually_drops)]
+
+struct S;
+
+fn main() {
+ let f = std::mem::drop;
+ let g = std::mem::ManuallyDrop::drop;
+ let mut manual1 = std::mem::ManuallyDrop::new(S);
+ let mut manual2 = std::mem::ManuallyDrop::new(S);
+ let mut manual3 = std::mem::ManuallyDrop::new(S);
+ let mut manual4 = std::mem::ManuallyDrop::new(S);
+
+ // These lines will not drop `S` and should be linted
+ drop(std::mem::ManuallyDrop::new(S));
+ drop(manual1);
+
+ // FIXME: this line is not linted, though it should be
+ f(manual2);
+
+ // These lines will drop `S` and should be okay.
+ unsafe {
+ std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
+ std::mem::ManuallyDrop::drop(&mut manual3);
+ g(&mut manual4);
+ }
+}
diff --git a/src/tools/clippy/tests/ui/undropped_manually_drops.stderr b/src/tools/clippy/tests/ui/undropped_manually_drops.stderr
new file mode 100644
index 0000000..2ac0fe9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/undropped_manually_drops.stderr
@@ -0,0 +1,19 @@
+error: the inner value of this ManuallyDrop will not be dropped
+ --> $DIR/undropped_manually_drops.rs:14:5
+ |
+LL | drop(std::mem::ManuallyDrop::new(S));
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::undropped-manually-drops` implied by `-D warnings`
+ = help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
+
+error: the inner value of this ManuallyDrop will not be dropped
+ --> $DIR/undropped_manually_drops.rs:15:5
+ |
+LL | drop(manual1);
+ | ^^^^^^^^^^^^^
+ |
+ = help: to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed
index fb89a9f..350da49 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.fixed
@@ -8,16 +8,31 @@
100_f32;
100_f64;
100_f64;
+ let _ = -100_f32;
+ let _ = -100_f64;
+ let _ = -100_f64;
// Should not trigger
#[rustfmt::skip]
let v = vec!(1);
&v as &[i32];
- 1.0 as f64;
- 1 as u64;
0x10 as f32;
0o10 as f32;
0b10 as f32;
0x11 as f64;
0o11 as f64;
0b11 as f64;
+
+ 1_u32;
+ 0x10_i32;
+ 0b10_usize;
+ 0o73_u16;
+ 1_000_000_000_u32;
+
+ 1.0_f64;
+ 0.5_f32;
+
+ 1.0 as u16;
+
+ let _ = -1_i32;
+ let _ = -1.0_f32;
}
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs
index 4a0c862..ad2fb2e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.rs
@@ -8,16 +8,31 @@
100 as f32;
100 as f64;
100_i32 as f64;
+ let _ = -100 as f32;
+ let _ = -100 as f64;
+ let _ = -100_i32 as f64;
// Should not trigger
#[rustfmt::skip]
let v = vec!(1);
&v as &[i32];
- 1.0 as f64;
- 1 as u64;
0x10 as f32;
0o10 as f32;
0b10 as f32;
0x11 as f64;
0o11 as f64;
0b11 as f64;
+
+ 1 as u32;
+ 0x10 as i32;
+ 0b10 as usize;
+ 0o73 as u16;
+ 1_000_000_000 as u32;
+
+ 1.0 as f64;
+ 0.5 as f32;
+
+ 1.0 as u16;
+
+ let _ = -1 as i32;
+ let _ = -1.0 as f32;
}
diff --git a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr
index 8ff1e5d..5a210fc 100644
--- a/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_cast_fixable.stderr
@@ -18,5 +18,77 @@
LL | 100_i32 as f64;
| ^^^^^^^^^^^^^^ help: try: `100_f64`
-error: aborting due to 3 previous errors
+error: casting integer literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:11:13
+ |
+LL | let _ = -100 as f32;
+ | ^^^^^^^^^^^ help: try: `-100_f32`
+
+error: casting integer literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:12:13
+ |
+LL | let _ = -100 as f64;
+ | ^^^^^^^^^^^ help: try: `-100_f64`
+
+error: casting integer literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:13:13
+ |
+LL | let _ = -100_i32 as f64;
+ | ^^^^^^^^^^^^^^^ help: try: `-100_f64`
+
+error: casting integer literal to `u32` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:25:5
+ |
+LL | 1 as u32;
+ | ^^^^^^^^ help: try: `1_u32`
+
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:26:5
+ |
+LL | 0x10 as i32;
+ | ^^^^^^^^^^^ help: try: `0x10_i32`
+
+error: casting integer literal to `usize` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:27:5
+ |
+LL | 0b10 as usize;
+ | ^^^^^^^^^^^^^ help: try: `0b10_usize`
+
+error: casting integer literal to `u16` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:28:5
+ |
+LL | 0o73 as u16;
+ | ^^^^^^^^^^^ help: try: `0o73_u16`
+
+error: casting integer literal to `u32` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:29:5
+ |
+LL | 1_000_000_000 as u32;
+ | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32`
+
+error: casting float literal to `f64` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:31:5
+ |
+LL | 1.0 as f64;
+ | ^^^^^^^^^^ help: try: `1.0_f64`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:32:5
+ |
+LL | 0.5 as f32;
+ | ^^^^^^^^^^ help: try: `0.5_f32`
+
+error: casting integer literal to `i32` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:36:13
+ |
+LL | let _ = -1 as i32;
+ | ^^^^^^^^^ help: try: `-1_i32`
+
+error: casting float literal to `f32` is unnecessary
+ --> $DIR/unnecessary_cast_fixable.rs:37:13
+ |
+LL | let _ = -1.0 as f32;
+ | ^^^^^^^^^^^ help: try: `-1.0_f32`
+
+error: aborting due to 15 previous errors
diff --git a/src/tools/clippy/tests/ui/unreadable_literal.fixed b/src/tools/clippy/tests/ui/unreadable_literal.fixed
index 3f358d9..4043d53 100644
--- a/src/tools/clippy/tests/ui/unreadable_literal.fixed
+++ b/src/tools/clippy/tests/ui/unreadable_literal.fixed
@@ -14,7 +14,7 @@
let _good = (
0b1011_i64,
0o1_234_u32,
- 0x1_234_567,
+ 0x0123_4567,
65536,
1_2345_6789,
1234_f32,
diff --git a/src/tools/clippy/tests/ui/unreadable_literal.stderr b/src/tools/clippy/tests/ui/unreadable_literal.stderr
index 1b2ff6b..8645cab 100644
--- a/src/tools/clippy/tests/ui/unreadable_literal.stderr
+++ b/src/tools/clippy/tests/ui/unreadable_literal.stderr
@@ -1,3 +1,11 @@
+error: digits of hex or binary literal not grouped by four
+ --> $DIR/unreadable_literal.rs:17:9
+ |
+LL | 0x1_234_567,
+ | ^^^^^^^^^^^ help: consider: `0x0123_4567`
+ |
+ = note: `-D clippy::unusual-byte-groupings` implied by `-D warnings`
+
error: long literal lacking separators
--> $DIR/unreadable_literal.rs:25:17
|
@@ -54,5 +62,5 @@
LL | let _fail12: i128 = 0xabcabcabcabcabcabc;
| ^^^^^^^^^^^^^^^^^^^^ help: consider: `0x00ab_cabc_abca_bcab_cabc`
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index ed3c83a..b7b20b4 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -1,7 +1,7 @@
[relabel]
allow-unauthenticated = [
- "C-*", "A-*", "E-*", "L-*", "M-*", "O-*", "S-*",
- "good first issue", "needs test"
+ "A-*", "C-*", "E-*", "L-*", "M-*", "O-*", "P-*", "S-*", "T-*",
+ "good first issue"
]
[assign]
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index d85558e..666e5d4 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1775,6 +1775,11 @@
let mut aux_rustc =
aux_cx.make_compile_args(input_file, aux_output, EmitMetadata::No, AllowUnused::No);
+ for key in &aux_props.unset_rustc_env {
+ aux_rustc.env_remove(key);
+ }
+ aux_rustc.envs(aux_props.rustc_env.clone());
+
let (dylib, crate_type) = if aux_props.no_prefer_dynamic {
(true, None)
} else if self.config.target.contains("cloudabi")
diff --git a/src/tools/miri b/src/tools/miri
index 88da675..6359b1b 160000
--- a/src/tools/miri
+++ b/src/tools/miri
@@ -1 +1 @@
-Subproject commit 88da6757d7fe75c853767e4615a6255e9466b67a
+Subproject commit 6359b1b0eb724f2432fd4776c3fc5f5cad9e67d0