Merge commit '6ed6f1e6a1' into clippyup

This commit is contained in:
flip1995 2021-03-12 15:30:50 +01:00
parent 36a27ecaac
commit f2f2a005b4
297 changed files with 15401 additions and 12253 deletions

View file

@ -17,3 +17,7 @@ rustc-semver="1.1.0"
[features]
internal-lints = []
[package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)]
rustc_private = true

View file

@ -25,11 +25,7 @@ pub fn until(s: &str) -> usize {
return i;
}
}
if up {
last_i
} else {
s.len()
}
if up { last_i } else { s.len() }
}
/// Returns index of the last camel-case component of `s`.

View file

@ -9,7 +9,7 @@
//! - or-fun-call
//! - option-if-let-else
use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item, match_type, paths};
use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit;
@ -101,7 +101,7 @@ fn identify_some_potentially_expensive_patterns<'tcx>(cx: &LateContext<'tcx>, ex
ExprKind::Index(obj, _) => {
let ty = self.cx.typeck_results().expr_ty(obj);
is_type_diagnostic_item(self.cx, ty, sym::hashmap_type)
|| match_type(self.cx, ty, &paths::BTREEMAP)
|| is_type_diagnostic_item(self.cx, ty, sym::BTreeMap)
},
ExprKind::MethodCall(..) => true,
_ => false,

View file

@ -1,5 +1,5 @@
use crate::consts::{constant_context, constant_simple};
use crate::differing_macro_contexts;
use crate::{differing_macro_contexts, snippet_opt};
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@ -9,6 +9,7 @@ use rustc_hir::{
GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::LateContext;
use rustc_middle::ich::StableHashingContextProvider;
use rustc_middle::ty::TypeckResults;
@ -110,8 +111,54 @@ impl HirEqInterExpr<'_, '_, '_> {
/// Checks whether two blocks are the same.
fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r))
&& both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
match (left.stmts, left.expr, right.stmts, right.expr) {
([], None, [], None) => {
// For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
// expanded to nothing, or the cfg attribute was used.
let (left, right) = match (
snippet_opt(self.inner.cx, left.span),
snippet_opt(self.inner.cx, right.span),
) {
(Some(left), Some(right)) => (left, right),
_ => return true,
};
let mut left_pos = 0;
let left = tokenize(&left)
.map(|t| {
let end = left_pos + t.len;
let s = &left[left_pos..end];
left_pos = end;
(t, s)
})
.filter(|(t, _)| {
!matches!(
t.kind,
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
)
})
.map(|(_, s)| s);
let mut right_pos = 0;
let right = tokenize(&right)
.map(|t| {
let end = right_pos + t.len;
let s = &right[right_pos..end];
right_pos = end;
(t, s)
})
.filter(|(t, _)| {
!matches!(
t.kind,
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
)
})
.map(|(_, s)| s);
left.eq(right)
},
_ => {
over(&left.stmts, &right.stmts, |l, r| self.eq_stmt(l, r))
&& both(&left.expr, &right.expr, |l, r| self.eq_expr(l, r))
},
}
}
#[allow(clippy::similar_names)]
@ -131,7 +178,10 @@ impl HirEqInterExpr<'_, '_, '_> {
}
}
let is_eq = match (&reduce_exprkind(&left.kind), &reduce_exprkind(&right.kind)) {
let is_eq = match (
&reduce_exprkind(self.inner.cx, &left.kind),
&reduce_exprkind(self.inner.cx, &right.kind),
) {
(&ExprKind::AddrOf(lb, l_mut, ref le), &ExprKind::AddrOf(rb, r_mut, ref re)) => {
lb == rb && l_mut == r_mut && self.eq_expr(le, re)
},
@ -360,11 +410,30 @@ impl HirEqInterExpr<'_, '_, '_> {
}
/// Some simple reductions like `{ return }` => `return`
fn reduce_exprkind<'hir>(kind: &'hir ExprKind<'hir>) -> &ExprKind<'hir> {
fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'hir ExprKind<'hir> {
if let ExprKind::Block(block, _) = kind {
match (block.stmts, block.expr) {
// From an `if let` expression without an `else` block. The arm for the implicit wild pattern is an empty
// block with an empty span.
([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
// `{}` => `()`
([], None) => &ExprKind::Tup(&[]),
([], None) => match snippet_opt(cx, block.span) {
// Don't reduce if there are any tokens contained in the braces
Some(snip)
if tokenize(&snip)
.map(|t| t.kind)
.filter(|t| {
!matches!(
t,
TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
)
})
.ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().cloned()) =>
{
kind
},
_ => &ExprKind::Tup(&[]),
},
([], Some(expr)) => match expr.kind {
// `{ return .. }` => `return ..`
ExprKind::Ret(..) => &expr.kind,

View file

@ -13,6 +13,7 @@ extern crate rustc_data_structures;
extern crate rustc_errors;
extern crate rustc_hir;
extern crate rustc_infer;
extern crate rustc_lexer;
extern crate rustc_lint;
extern crate rustc_middle;
extern crate rustc_mir;
@ -61,27 +62,29 @@ use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
use rustc_hir::Node;
use rustc_hir::{
def, Arm, Block, Body, Constness, Expr, ExprKind, FnDecl, HirId, ImplItem, ImplItemKind, Item, ItemKind,
MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef, TyKind, Unsafety,
def, Arm, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item,
ItemKind, LangItem, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef,
TyKind, Unsafety,
};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::exports::Export;
use rustc_middle::hir::map::Map;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
use rustc_semver::RustcVersion;
use rustc_session::Session;
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::hygiene::{self, ExpnKind, MacroKind};
use rustc_span::source_map::original_sp;
use rustc_span::sym;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Pos, Span, DUMMY_SP};
use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP};
use rustc_target::abi::Integer;
use rustc_trait_selection::traits::query::normalize::AtExt;
use smallvec::SmallVec;
use crate::consts::{constant, Constant};
use std::collections::HashMap;
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
if let Ok(version) = RustcVersion::parse(msrv) {
@ -229,6 +232,45 @@ pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangI
}
}
/// Checks if the first type parameter is a lang item.
pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
let ty = get_qpath_generic_tys(qpath).next()?;
if let TyKind::Path(qpath) = &ty.kind {
cx.qpath_res(qpath, ty.hir_id)
.opt_def_id()
.map_or(false, |id| {
cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
})
.then(|| ty)
} else {
None
}
}
/// Checks if the first type parameter is a diagnostic item.
pub fn is_ty_param_diagnostic_item(
cx: &LateContext<'_>,
qpath: &QPath<'tcx>,
item: Symbol,
) -> Option<&'tcx hir::Ty<'tcx>> {
let ty = get_qpath_generic_tys(qpath).next()?;
if let TyKind::Path(qpath) = &ty.kind {
cx.qpath_res(qpath, ty.hir_id)
.opt_def_id()
.map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
.then(|| ty)
} else {
None
}
}
/// Return `true` if the passed `typ` is `isize` or `usize`.
pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
}
/// Checks if the method call given in `expr` belongs to the given trait.
pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
@ -236,6 +278,22 @@ pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str])
trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
}
/// Checks if the method call given in `expr` belongs to a trait or other container with a given
/// diagnostic item
pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
cx.tcx
.opt_associated_item(def_id)
.and_then(|associated_item| match associated_item.container {
ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
ty::Adt(adt, _) => Some(adt.did),
ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
_ => None,
},
})
.map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id))
}
/// Checks if an expression references a variable of the given name.
pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
@ -254,6 +312,27 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
}
}
pub fn get_qpath_generics(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
match path {
QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
QPath::TypeRelative(_, s) => s.args,
QPath::LangItem(..) => None,
}
}
pub fn get_qpath_generic_tys(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
get_qpath_generics(path)
.map_or([].as_ref(), |a| a.args)
.iter()
.filter_map(|a| {
if let hir::GenericArg::Type(ty) = a {
Some(ty)
} else {
None
}
})
}
pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
match *path {
QPath::Resolved(_, ref path) => path.segments.get(0),
@ -455,6 +534,18 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
}
}
/// Checks whether a type can be partially moved.
pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
if has_drop(cx, ty) || is_copy(cx, ty) {
return false;
}
match ty.kind() {
ty::Param(_) => false,
ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
_ => true,
}
}
/// Returns the method names and argument list of nested method call expressions that make up
/// `expr`. method/span lists are sorted with the most recent call first.
pub fn method_calls<'tcx>(
@ -745,6 +836,35 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
reindent_multiline(snip, true, indent)
}
/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
/// will result in the macro call, rather then the expansion, if the span is from a child context.
/// If the span is not from a child context, it will be used directly instead.
///
/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
/// would result in `box []`. If given the context of the address of expression, this function will
/// correctly get a snippet of `vec![]`.
pub fn snippet_with_context(
cx: &LateContext<'_>,
span: Span,
outer: SyntaxContext,
default: &'a str,
applicability: &mut Applicability,
) -> Cow<'a, str> {
let outer_span = hygiene::walk_chain(span, outer);
let span = if outer_span.ctxt() == outer {
outer_span
} else {
// The span is from a macro argument, and the outer context is the macro using the argument
if *applicability != Applicability::Unspecified {
*applicability = Applicability::MaybeIncorrect;
}
// TODO: get the argument span.
span
};
snippet_with_applicability(cx, span, default, applicability)
}
/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
/// line.
///
@ -923,6 +1043,21 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
})
}
/// Gets the parent node if it's an impl block.
pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
let map = tcx.hir();
match map.parent_iter(id).next() {
Some((
_,
Node::Item(Item {
kind: ItemKind::Impl(imp),
..
}),
)) => Some(imp),
_ => None,
}
}
/// Returns the base type for HIR references and pointers.
pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
match ty.kind {
@ -1359,13 +1494,49 @@ pub fn match_function_call<'tcx>(
None
}
// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
// this function can be removed once the `normalizie` method does not panic when normalization does
// not succeed
/// Checks if `Ty` is normalizable. This function is useful
/// to avoid crashes on `layout_of`.
pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
cx.tcx.infer_ctxt().enter(|infcx| {
is_normalizable_helper(cx, param_env, ty, &mut HashMap::new())
}
fn is_normalizable_helper<'tcx>(
cx: &LateContext<'tcx>,
param_env: ty::ParamEnv<'tcx>,
ty: Ty<'tcx>,
cache: &mut HashMap<Ty<'tcx>, bool>,
) -> bool {
if let Some(&cached_result) = cache.get(ty) {
return cached_result;
}
// prevent recursive loops, false-negative is better than endless loop leading to stack overflow
cache.insert(ty, false);
let result = cx.tcx.infer_ctxt().enter(|infcx| {
let cause = rustc_middle::traits::ObligationCause::dummy();
infcx.at(&cause, param_env).normalize(ty).is_ok()
})
if infcx.at(&cause, param_env).normalize(ty).is_ok() {
match ty.kind() {
ty::Adt(def, substs) => def.variants.iter().all(|variant| {
variant
.fields
.iter()
.all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
}),
_ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
GenericArgKind::Type(inner_ty) if inner_ty != ty => {
is_normalizable_helper(cx, param_env, inner_ty, cache)
},
_ => true, // if inner_ty == ty, we've already checked it
}),
}
} else {
false
}
});
cache.insert(ty, result);
result
}
pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
@ -1546,12 +1717,12 @@ pub fn is_trait_impl_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
/// ```
pub fn fn_has_unsatisfiable_preds(cx: &LateContext<'_>, did: DefId) -> bool {
use rustc_trait_selection::traits;
let predicates =
cx.tcx
.predicates_of(did)
.predicates
.iter()
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
let predicates = cx
.tcx
.predicates_of(did)
.predicates
.iter()
.filter_map(|(p, _)| if p.is_global() { Some(*p) } else { None });
traits::impossible_predicates(
cx.tcx,
traits::elaborate_predicates(cx.tcx, predicates)

View file

@ -12,11 +12,9 @@ pub(super) const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
pub(super) const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BOX: [&str; 3] = ["alloc", "boxed", "Box"];
pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
@ -43,9 +41,6 @@ pub const FILE_TYPE: [&str; 3] = ["std", "fs", "FileType"];
pub const FMT_ARGUMENTS_NEW_V1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEW_V1_FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FN: [&str; 3] = ["core", "ops", "Fn"];
pub const FN_MUT: [&str; 3] = ["core", "ops", "FnMut"];
pub const FN_ONCE: [&str; 3] = ["core", "ops", "FnOnce"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_ITERATOR: [&str; 5] = ["core", "iter", "traits", "collect", "FromIterator"];
pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
@ -89,7 +84,6 @@ pub const OPTION: [&str; 3] = ["core", "option", "Option"];
pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub(super) const PANICKING_PANIC: [&str; 3] = ["core", "panicking", "panic"];
@ -113,7 +107,6 @@ pub const PTR_SLICE_FROM_RAW_PARTS_MUT: [&str; 3] = ["core", "ptr", "slice_from_
pub const PTR_SWAP_NONOVERLAPPING: [&str; 3] = ["core", "ptr", "swap_nonoverlapping"];
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 REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
@ -141,7 +134,6 @@ pub const STD_CONVERT_IDENTITY: [&str; 3] = ["std", "convert", "identity"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
pub const STD_MEM_TRANSMUTE: [&str; 3] = ["std", "mem", "transmute"];
pub const STD_PTR_NULL: [&str; 3] = ["std", "ptr", "null"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
@ -160,9 +152,7 @@ pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol",
pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
#[cfg(feature = "internal-lints")]
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];

View file

@ -36,11 +36,7 @@ fn extract_clone_suggestions<'tcx>(
abort: false,
};
visitor.visit_body(body);
if visitor.abort {
None
} else {
Some(visitor.spans)
}
if visitor.abort { None } else { Some(visitor.spans) }
}
struct PtrCloneVisitor<'a, 'tcx> {